WPF中更新ItemsSource后ComboBox未刷新问题求助
问题:ComboBox绑定Dictionary后添加新条目不更新下拉列表
项目中存在ComboBox和TreeView控件:
- ComboBox的ItemsSource绑定
Dictionary<string, ObservableCollection<ViewItem>>,DisplayMemberPath设为Key,用于展示本地JSON文件名; - TreeView的ItemsSource绑定ComboBox的SelectedItem.Value;
- 点击CREATE按钮执行命令向字典添加新条目,调试确认字典已更新,但ComboBox下拉列表未刷新显示新增值,已实现INotifyPropertyChanged接口。
相关代码
XAML代码
<GroupBox Header="Template" Grid.Column="1" Grid.Row="0" Grid.RowSpan="6" Margin="10,10,10,10"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="10*"/> </Grid.RowDefinitions> <ComboBox x:Name="templateCombobox" Grid.Row="0" Margin="5,10,5,10" ItemsSource="{Binding LoadedTemplateViews,Mode=TwoWay,NotifyOnSourceUpdated=True,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Key" SelectedValue="{Binding SelectedItem}" SelectedValuePath="Key"/> <TreeView Name="templateTreeview" ItemsSource="{Binding ElementName=templateCombobox, Path=SelectedItem.Value}" FontSize="14" Grid.Row="2" AllowDrop="True" Margin="5,5,5,10"> <i:Interaction.Behaviors> <local:DragDropBehavior /> </i:Interaction.Behaviors> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="True" /> </Style> </TreeView.ItemContainerStyle> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type model:ViewItem}" ItemsSource="{Binding Path=ViewItems}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Path=ItemName}" Margin="0,0,10,0" /> <Button Name="DeleteTreeViewItem" Background="Transparent" BorderBrush="Transparent" Click="DeleteTreeViewItem_Click" Visibility="{Binding Path=IsVisible,Converter={StaticResource IntConverter}}"> <Image Source="delete_cross.png" Height="15" /> </Button> </StackPanel> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> </Grid> </GroupBox> <Button Content="CREATE" Grid.Column="1" Grid.Row="1" Command="{Binding CreateTemplateJsonCommand}" />
ViewModelBase类
public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
简化后的ViewModel
private Dictionary<string, ObservableCollection<ViewItem>> _loadedTemplateViews; public Dictionary<string, ObservableCollection<ViewItem>> LoadedTemplateViews { get { return _loadedTemplateViews; } set { _loadedTemplateViews = value; OnPropertyChanged(nameof(LoadedTemplateViews)); } } public TemplateViewModel(TemplateDetailsStore templateDetailsStore) { LoadedTemplateViews = templateViewUIDataService.TemplatesInfo; CreateTemplateJsonCommand = new CreateTemplateJsonCommand(templateDetailsStore, this); }
简化后的CreateTemplateJsonCommand
internal class CreateTemplateJsonCommand : CommandBase { private readonly TemplateDetailsStore _store; private readonly TemplateView1Model _viewModel; public CreateTemplateJsonCommand(TemplateDetailsStore store, TemplateView1Model viewModel) { _store = store; _viewModel = viewModel; } public override void Execute(object parameter) { _viewModel.LoadedTemplateViews.Add(_viewModel.templateFileName, new ObservableCollection<ViewItem> { _store.currentTemplateDetails.TreeviewDetail } ); } } }
解决方案
核心原因
Dictionary<TKey, TValue>没有实现INotifyCollectionChanged接口,WPF无法检测到集合内部的条目增减操作,只有当LoadedTemplateViews属性的引用被替换时(即重新赋值),才会触发UI更新。
方法1:替换为ObservableCollection<KeyValuePair<string, ObservableCollection>>(推荐)
ObservableCollection实现了INotifyCollectionChanged,集合内的任何变化都会自动通知UI更新。
- 修改ViewModel中的集合类型:
private ObservableCollection<KeyValuePair<string, ObservableCollection<ViewItem>>> _loadedTemplateViews; public ObservableCollection<KeyValuePair<string, ObservableCollection<ViewItem>>> LoadedTemplateViews { get { return _loadedTemplateViews; } set { _loadedTemplateViews = value; OnPropertyChanged(nameof(LoadedTemplateViews)); } } // 构造函数中初始化:将Dictionary转换为ObservableCollection public TemplateViewModel(TemplateDetailsStore templateDetailsStore) { var templateDict = templateViewUIDataService.TemplatesInfo; LoadedTemplateViews = new ObservableCollection<KeyValuePair<string, ObservableCollection<ViewItem>>>(templateDict); CreateTemplateJsonCommand = new CreateTemplateJsonCommand(templateDetailsStore, this); }
- 修改Command的添加逻辑:
public override void Execute(object parameter) { _viewModel.LoadedTemplateViews.Add(new KeyValuePair<string, ObservableCollection<ViewItem>>( _viewModel.templateFileName, new ObservableCollection<ViewItem> { _store.currentTemplateDetails.TreeviewDetail } )); }
XAML中的ComboBox绑定无需修改,DisplayMemberPath="Key"依然有效,因为KeyValuePair包含Key和Value属性。
方法2:手动触发PropertyChanged通知(临时方案)
如果不想替换集合类型,可以在添加条目后手动触发属性变更通知,强制UI重新绑定整个集合:
修改Command的Execute方法:
public override void Execute(object parameter) { _viewModel.LoadedTemplateViews.Add(_viewModel.templateFileName, new ObservableCollection<ViewItem> { _store.currentTemplateDetails.TreeviewDetail } ); // 手动触发LoadedTemplateViews的属性变更通知 _viewModel.OnPropertyChanged(nameof(_viewModel.LoadedTemplateViews)); }
注意:此方法会让ComboBox重新加载整个集合,性能上不如第一种方案,适合临时场景。
内容的提问来源于stack exchange,提问作者BabyHai
相关产品推荐
相关产品推荐

