В процессе работы над проектом возникла необходимость сделать ComboBox при выборе элемента которого выдавался бы запрос, Вроде - "А Вы уверены?", если нет возвращать старое значение. Вопрос что я делаю не так? FrameWork 4.0. Заранее спасибо.
Сейчас у меня получилось следующее:
<x:Array x:Key="UnitArray" Type="sys:UInt32"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:UInt32>0x0</sys:UInt32>
<sys:UInt32>0xFFFFFFFF</sys:UInt32>
</x:Array>
<DataTemplate x:Key="ComboboxOfUnit" >
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" >
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="0x0">
<Setter Property="Text" Value="value1"/>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="0xFFFFFFFF">
<Setter Property="Text" Value="value2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
в блоке ресурсов и
<ComboBox ItemsSource="{StaticResource ResourceKey=UnitArray}"
SelectedItem="{Binding UnitOf}"
ItemTemplate="{StaticResource ComboboxOfUnit}"/>
в основном теле xaml кода в модели имеем следующее
uint _prev_UnitOf;
public uint PrevUnitOf
{
get { return _prev_UnitOf; }
set
{
_prev_UnitOf = value;
if (!_prev_UnitOf.Equals(_UnitOf))
{
// HasHasChangedArray[_UnitOf_] = true;
//bfUnitOf = true;
/HasChanged = true;
}
else
{
//HasHasChangedArray[_UnitOf_] = false;
//bfUnitOf = false;
//HasChanged = isChangedArray();
}
OnPropertyChanged(() => PrevUnitOf);
}
}
uint _UnitOf;
public uint UnitOf
{
get { return _UnitOf; }
set
{
if (!_UnitOf.Equals(value))
if (AppContext.Instance.ErrorReportMSG("Вы уверены?", "", Common.Interfaces.MsgType.Question))
{
_UnitOf = value;
if (!_prev_UnitOf.Equals(_UnitOf))
{
//HasHasChangedArray[_UnitOf_] = true;
//bfUnitOf = true;
//HasChanged = true;
}
else
{
//HasHasChangedArray[_UnitOf_] = false;
//bfUnitOf = false;
//HasChanged = isChangedArray();
}
}
else
{
_UnitOf = _prev_UnitOf;
//bfUnitOf = false;
//HasChanged = isChangedArray();
}
OnPropertyChanged(() => UnitOf);
}
}
где AppContext.Instance.ErrorReportMSG("Вы уверены?", "", Common.Interfaces.MsgType.Question) обертка над messagebox
и OnPropertyChanged(() => UnitOf)
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Метод генерации события изменения свойства объекта
/// </summary>
/// <typeparam name="T">Тип объекта</typeparam>
/// <param name="action">Лямбда функция доступа свойству</param>
protected void OnPropertyChanged<T>(Expression<Func<T>> action)
{
var propertyName = GetPropertyName(action);
OnPropertyChanged(propertyName);
}
/// <summary>
/// Метод получения имени параметра
/// </summary>
/// <typeparam name="T">Тип объекта</typeparam>
/// <param name="action">Лямбда функция доступа свойству</param>
/// <returns>Возвращает имя параметра</returns>
private static string GetPropertyName<T>(Expression<Func<T>> action)
{
var expression = (MemberExpression)action.Body;
var propertyName = expression.Member.Name;
return propertyName;
}
По-началу вопрос мне показался достаточно простым, однако на практике я столкнулся с той же проблемой: ComboBox не хочет "отпрыгивать назад" в случае отмены изменения привязанного свойства SelectedItem. Решение нашлось путем гугления и экспериментов, вот ссылка 1 и ссылка 2. Наиболее полезной оказалась последняя.
Единственный недостаток в таком решении, по моему мнению, в том, что приходится использовать во ViewModel Dispatcher.BeginInvoke(), т.е. подключать using System.Windows; и using System.Windows.Threading;, что для ViewModel как бы не комильфо.
Для примера я использовал свойство List со строками, а не как у автора с подключенным ресурсным массивом, но по сути это ничего не меняет.
Такой XAML
<Canvas>
<ComboBox Canvas.Left="30"
Canvas.Top="40"
Width="120"
ItemsSource="{Binding Units}"
SelectedIndex="{Binding SelectedUnit}" />
</Canvas>
А вот такая вьюмодель
public class MainViewModel : INotifyPropertyChanged
{
private readonly IMainWindow _mainWindow;
public event PropertyChangedEventHandler PropertyChanged;
//ctor
public MainViewModel(IMainWindow mainWindow)
{
_mainWindow = mainWindow;
}
public List<string> Units { get; set; } = new List<string> { "0x0", "0xFFFFFFFF" };
private string _SelectedUnit;
public string SelectedUnit
{
get => _SelectedUnit;
set
{
// сохраняем старое значение
var origValue = _SelectedUnit;
//меняем значение в обычном порядке
_SelectedUnit = value;
//спрашиваем пользователя
if (!_mainWindow.ShowYesNo("Изменить значение?"))
{
// возвращаем предыдущее значение, но только после того как
// UI закончит свое обновление.
Application.Current.Dispatcher.BeginInvoke(
new Action(() =>
{
_SelectedUnit = origValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedUnit)));
}),
DispatcherPriority.ContextIdle,
null
);
//и выходим
return;
}
//Оповещаем как обычно изменение, сделанное до if (!_mainWindow.ShowYesNo("Изменить значение?"))
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedUnit)));
//а здесь уже преобразуем изменившиеся значение
//в необходимое uint
//SetNewUnit(_SelectedUnit);
}
}
}
Иллюстрация работы
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости