Как я отменяю пользовательский выбор в связанном с данными WPF ListBox? Исходное свойство установлено правильно, но выбор ListBox вне синхронизации.
У меня есть приложение MVVM, которое должно отменить пользовательский выбор в WPF ListBox, если определенные условия проверки перестали работать. Проверка инициирована выбором в ListBox, а не Кнопкой отправки.
ListBox.SelectedItem
свойство связывается с a ViewModel.CurrentDocument
свойство. Если проверка перестала работать, метод set для выходов свойства модели представления, не изменяя свойство. Так, свойство, к который ListBox.SelectedItem
связывается не становится измененным.
Если это происходит, метод set свойства модели представления действительно генерирует событие PropertyChanged, прежде чем это выйдет, который я принял, будет достаточно для сброса ListBox назад к старому выбору. Но это не работает - ListBox все еще показывает новый пользовательский выбор. Я должен переопределить тот выбор и вернуть его в синхронизации с исходным свойством.
На всякий случай это не ясно, вот пример: ListBox имеет два объекта, Document1 и Document2; Document1 выбран. Пользователь выбирает Document2, но Document1 не удается проверить. ViewModel.CurrentDocument
свойство все еще установлено на Document1, но ListBox показывает, что Document2 выбран. Я должен вернуть выбор ListBox к Document1.
Вот моя Привязка ListBox:
<ListBox
ItemsSource="{Binding Path=SearchResults, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=CurrentDocument, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Я действительно пытался использовать обратный вызов от ViewModel (как событие) к Представлению (который подписывается на событие), для сдерживания свойства SelectedItem к старому выбору. Я передаю старый Документ с событием, и это - корректное (старый выбор), но выбор ListBox не возвращается.
Так, как я возвращаю выбор ListBox в синхронизации со свойством модели представления к который SelectedItem
свойство связывается?Спасибо за помощь.
-snip-
Хорошо, забудьте то, что я написал выше.
Я только что провел эксперимент, и действительно, SelectedItem выходит из синхронизации всякий раз, когда вы делаете что-нибудь более причудливое в сеттере. Я полагаю, вам нужно подождать, пока сеттер вернется, а затем асинхронно изменить свойство обратно в вашей ViewModel.
Быстрое и грязное рабочее решение (проверенное в моем простом проекте) с использованием помощников MVVM Light:
var dp = DispatcherHelper.UIDispatcher;
if (dp != null)
dp.BeginInvoke(
(new Action(() => {
currentDocument = previousDocument;
RaisePropertyChanged("CurrentDocument");
})), DispatcherPriority.ContextIdle);
В вашем сеттере, чтобы вернуться к предыдущему значению CurrentDocument
var dp = DispatcherHelper.UIDispatcher;
if (dp != null)
dp.BeginInvoke(
(new Action(() => {
currentDocument = previousDocument;
RaisePropertyChanged("CurrentDocument");
})), DispatcherPriority.ContextIdle);
он, по сути, ставит изменение свойства в очередь на поток UI, приоритет ContextIdle гарантирует, что он будет ждать, пока UI не будет в согласованном состоянии.
К сожалению, это создает связь между вашей моделью представления и вашим представлением, и это уродливый хак.
Чтобы заставить DispatcherHelper.UIDispatcher работать, вам нужно сначала выполнить DispatcherHelper.Initialize().
Понятно! Я собираюсь принять ответ majocha, потому что его комментарий под его ответом привел меня к решению.
Вот что я сделал: Я создал обработчик события SelectionChanged
для ListBox в code-behind. Да, он уродлив, но он работает. В code-behind также содержится переменная уровня модуля, m_OldSelectedIndex
, которая инициализирована в -1. Обработчик SelectionChanged
вызывает метод Validate()
ViewModel и получает обратно булево значение, указывающее, действителен ли Документ. Если документ действителен, обработчик устанавливает m_OldSelectedIndex
в текущий ListBox.SelectedIndex
и выходит. Если документ недействителен, обработчик сбрасывает ListBox.SelectedIndex
на m_OldSelectedIndex
. Вот код обработчика события:
private void OnSearchResultsBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var viewModel = (MainViewModel) this.DataContext;
if (viewModel.Validate() == null)
{
m_OldSelectedIndex = SearchResultsBox.SelectedIndex;
}
else
{
SearchResultsBox.SelectedIndex = m_OldSelectedIndex;
}
}
Обратите внимание, что в этом решении есть хитрость: Вы должны использовать свойство SelectedIndex
; это не работает со свойством SelectedItem
.
Спасибо за помощь majocha, и надеюсь, это поможет кому-нибудь еще на этом пути. Например, мне, через шесть месяцев, когда я забуду это решение...