У меня есть ComboBox, связанный с MVVM.
<ComboBox
ItemsSource = "{Binding RootPathItems, Mode=OneTime}"
DisplayMemberPath = "DisplayName"
SelectedItem = "{Binding SelectedRootPathItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsSynchronizedWithCurrentItem = "True">
</ComboBox>
Все элементы в ComboBox уникальны в памяти и создаются при запуске окна.
Когда пользователь меняет выбор в раскрывающемся списке, вызывается установщик свойств ViewModel, и установщику передается правильный объект. Затем я запускаю какое-то синхронное действие на основе выбора, а затем хочу изменить выбор на выбор по умолчанию.
private RootPathItem _selectedRootPathItem;
public RootPathItem SelectedRootPathItem
{
get => _selectedRootPathItem;
set
{
if (_selectedRootPathItem != value)
{
_selectedRootPathItem = value;
this.OnPropertyChanged();
SomeAction();
}
}
}
...
//in SomeAction():
this.SelectedRootPathItem = _nothingComboBoxItem;
Внутренние компоненты .Net снова вызовут метод получения свойств и получат _nothingComboBoxItem
, но пользовательский интерфейс останется на ранее выбранном элементе и не переключится на значение по умолчанию.
Я тоже попробовал связать SelectedIndex
с тем же эффектом.
Я предполагаю, что это не работает, потому что я все еще нахожусь в стеке вызовов установки свойств, когда устанавливаю новый элемент, но я на самом деле не знаю, что здесь происходит не так.
🤔 А знаете ли вы, что...
C# поддерживает асинхронное и параллельное программирование с помощью ключевых слов async и await.
Что ж, просматривая вашу базу кода и проверяя ее, мне кажется, что вы правы насчет теории «все еще в настройке свойств». Кажется, ComboBox
не обновит представление, если вы не добавите строку this.SelectedRootPathItem = _nothingComboBoxItem
в задачу.
И я также видел, что вы пытались использовать поведение взаимодействия, чтобы подписаться на событие изменения выбора ComboBox
, что я считаю гораздо более понятным подходом, чем выполнение множества действий в средстве установки свойств. Что случилось с этой идеей? Я вижу, что код xaml закомментирован.
Я вернул код поведения взаимодействия в xaml (без CommandParameter), удалил строку _rootPathItemSelectionChangedCommand.Execute(null);
в установщике, и он работает безупречно.
Ксамл:
<ComboBox Grid.Column = "0" Grid.Row = "0" VerticalAlignment = "Center" Margin = "10"
x:Name = "CBRootPathComboBox"
ItemsSource = "{Binding RootPathItems, Mode=OneTime}"
DisplayMemberPath = "DisplayName"
SelectedItem = "{Binding SelectedRootPathItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName = "SelectionChanged">
<i:InvokeCommandAction Command = "{Binding RootPathItemSelectionChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
И в MainViewModel
просто не выполняйте команду:
public RootPathItem SelectedRootPathItem
{
get => _selectedRootPathItem;
set
{
if (_selectedRootPathItem != value)
{
_selectedRootPathItem = value;
this.OnPropertyChanged();
}
}
}
Это должно сработать!
Выйти из стека вызовов можно с помощью диспетчера или async
/await
. Только не делайте этого в самом установщике. Вы можете использовать обработчик событий:
public ViewModel()
{
...
this.PropertyChanged += HandleSetValue;
}
private void HandleSetValue(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SelectedRootPathItem))
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
SomeAction);
}
}
public RootPathItem SelectedRootPathItem
{
get => _selectedRootPathItem;
set
{
if (_selectedRootPathItem != value)
{
_selectedRootPathItem = value;
this.OnPropertyChanged();
}
}
}