У меня есть ComboBox, которому связали SelectedItem с ViewModel.
<ComboBox SelectedItem="{Binding SelItem, Mode=TwoWay}" ItemsSource="{Binding MyItems}">
Когда пользователь выбирает новый Объект в Представлении ComboBox, я хочу отобразить подсказку и проверить, что они хотят внести изменение.
В методе set Свойства SetItem в Модели Представления я отображаю Диалоговое окно для подтверждения выбора. Когда они говорят да, это хорошо работает.
Моя проблема, когда пользователь нажимает на "No", я не уверен, кто заставить ComboBox возвращаться назад к предыдущему значению. Свойство в ViewModel имеет корректное более старое значение, однако в Представлении, ComboBox отображает недавно Выбранное Значение.
Я хочу, чтобы пользователь выбрал объект, подтвердил, что они хотят продолжить его, и если они решают не к, я хочу, чтобы ComboBox вернулся назад к предыдущему объекту.
Как я могу выполнить это?Спасибо!
Когда пользователь говорит «нет», WPF не знает, что значение изменилось. Что касается WPF, значение равно тому, что выбрал пользователь.
Вы можете попробовать вызвать уведомление об изменении свойства:
public object SelItem
{
get { ... }
set
{
if (!CancelChange())
{
this.selItem = value;
}
OnPropertyChanged("SelItem");
}
}
Проблема в том, что уведомление об изменении происходит в том же контексте, что и событие выбора. Таким образом, WPF игнорирует это, потому что он уже знает, что свойство изменилось - на элемент, выбранный пользователем!
Что вам нужно сделать, так это создать событие уведомления в отдельном сообщении:
public object SelItem
{
get { ... }
set
{
if (CancelChange())
{
Dispatcher.BeginInvoke((ThreadStart)delegate
{
OnPropertyChanged("SelItem");
});
return;
}
this.selItem = value;
OnPropertyChanged("SelItem");
}
}
WPF затем обработает это сообщение после того, как завершит обработку события изменения выбора и, следовательно, вернет значение в представлении. назад к тому, что должно быть.
Очевидно, вашей виртуальной машине потребуется доступ к текущему диспетчеру
. См. мое сообщение в блоге о базовом классе ВМ, если вам нужны некоторые указатели, как это сделать.
Другой способ сделать это (убедитесь, что вы также прочитали комментарии):
http://amazedsaint.blogspot.com/2008/06/wpf-combo-box-cancelling-selection.html
По ссылке: Еще одно решение проблемы рекурсивного вызова обработчика событий без глобальной переменной - отменить назначение обработчика перед программным изменением выбора и переназначить его после этого.
Пример:
cmb.SelectionChanged -= ComboBox_SelectionChanged;
cmb.SelectedValue = oldSel.Key;
cmb.SelectionChanged += ComboBox_SelectionChanged;
Спасибо за этот вопрос и ответы. Dispatcher.BeginInvoke помог мне и был частью моего окончательного решения, но указанное выше решение не совсем работало в моем приложении WPF 4.
Я собрал небольшую выборку, чтобы выяснить, почему. Мне пришлось добавить код, который на самом деле временно изменил значение базовой переменной-члена, чтобы, когда WPF повторно запросил геттер, он увидел, что значение изменилось. В противном случае пользовательский интерфейс не отражал отмену должным образом, и вызов BeginInvoke () ничего не делал.
Вот запись в моем блоге с моим примером, показывающим неработающую и работающую реализации.
Мой сеттер выглядел так:
private Person _CurrentPersonCancellable;
public Person CurrentPersonCancellable
{
get
{
Debug.WriteLine("Getting CurrentPersonCancellable.");
return _CurrentPersonCancellable;
}
set
{
// Store the current value so that we can
// change it back if needed.
var origValue = _CurrentPersonCancellable;
// If the value hasn't changed, don't do anything.
if (value == _CurrentPersonCancellable)
return;
// Note that we actually change the value for now.
// This is necessary because WPF seems to query the
// value after the change. The combo box
// likes to know that the value did change.
_CurrentPersonCancellable = value;
if (
MessageBox.Show(
"Allow change of selected item?",
"Continue",
MessageBoxButton.YesNo
) != MessageBoxResult.Yes
)
{
Debug.WriteLine("Selection Cancelled.");
// change the value back, but do so after the
// UI has finished it's current context operation.
Application.Current.Dispatcher.BeginInvoke(
new Action(() =>
{
Debug.WriteLine(
"Dispatcher BeginInvoke " +
"Setting CurrentPersonCancellable."
);
// Do this against the underlying value so
// that we don't invoke the cancellation question again.
_CurrentPersonCancellable = origValue;
OnPropertyChanged("CurrentPersonCancellable");
}),
DispatcherPriority.ContextIdle,
null
);
// Exit early.
return;
}
// Normal path. Selection applied.
// Raise PropertyChanged on the field.
Debug.WriteLine("Selection applied.");
OnPropertyChanged("CurrentPersonCancellable");
}
}