WPF MVVM模式下实现DataGrid绑定不同类型ObservableCollection切换
问题
在WPF应用中,需要实现点击按钮后让DataGrid切换显示不同数据类型的List/ObservableCollection。原本通过直接赋值ItemsSource的方式已经实现需求,现在要改为MVVM数据绑定模式。现有绑定用的XAML代码如下:
<DataGrid ItemsSource="{Binding MyList}" Width="400" />
尝试的示例代码(存在泛型类型问题):
public void test(bool changeList) { ObservableCollection<type1> list1 = new ObservableCollection<type1>(); ObservableCollection<type2> list2 = new ObservableCollection<type2>(); ObservableCollection<T> MyList; if (changeList) { MyList = list1; } else { MyList = list2; } }
已知需要使用属性变更通知机制(如RaisePropertyChanged),询问如何用通用的集合类型完成绑定功能。
原非绑定实现的代码:
XAML:
<DataGrid x:Name="dataGrid" IsReadOnly="{Binding Path=IsChecked, ElementName=CbDisableList}" CurrentCellChanged="DGChangedList"/>
后台代码:
if (changeList) { dataGrid.ItemsSource = list1; } else { dataGrid.ItemsSource = list2; }
解决方案
1. 实现ViewModel基类(INotifyPropertyChanged)
先做一个基础ViewModel类,实现属性变更通知接口,确保属性更新时能同步通知UI:
using System.ComponentModel; using System.Runtime.CompilerServices; public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 定义主ViewModel,声明通用数据源属性
因为type1和type2是不同类型,数据源属性要用ObservableCollection<object>(或IEnumerable<object>)来兼容不同类型集合,同时实现切换数据源的命令:
using System.Collections.ObjectModel; using System.Windows.Input; public class MainViewModel : ViewModelBase { private ObservableCollection<object> _myList; public ObservableCollection<object> MyList { get => _myList; set { _myList = value; RaisePropertyChanged(); // 触发属性变更通知,更新UI } } // 切换数据源的命令 public ICommand SwitchDataCommand { get; } // 预定义的两个不同类型集合 private readonly ObservableCollection<type1> _list1; private readonly ObservableCollection<type2> _list2; public MainViewModel() { // 初始化示例数据 _list1 = new ObservableCollection<type1> { new type1 { /* 给type1的属性赋值 */ }, new type1 { /* 给type1的属性赋值 */ } }; _list2 = new ObservableCollection<type2> { new type2 { /* 给type2的属性赋值 */ }, new type2 { /* 给type2的属性赋值 */ } }; // 初始化命令,绑定切换逻辑 SwitchDataCommand = new RelayCommand<bool>(SwitchDataSource); } private void SwitchDataSource(bool changeList) { // 直接转换原集合为ObservableCollection<object>,保留原集合的变更通知能力 MyList = changeList ? _list1 as ObservableCollection<object> : _list2 as ObservableCollection<object>; } }
3. 实现RelayCommand(基础命令类)
如果没使用MVVM框架(如Prism、MvvmLight),自己实现一个简单的RelayCommand来绑定按钮点击:
public class RelayCommand<T> : ICommand { private readonly Action<T> _execute; private readonly Predicate<T> _canExecute; public RelayCommand(Action<T> execute, Predicate<T> canExecute = null) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } public bool CanExecute(object parameter) { return _canExecute?.Invoke((T)parameter) ?? true; } public void Execute(object parameter) { _execute((T)parameter); } public event EventHandler CanExecuteChanged { add => CommandManager.RequerySuggested += value; remove => CommandManager.RequerySuggested -= value; } }
4. 调整XAML绑定
修改XAML,绑定DataGrid的ItemsSource到ViewModel的MyList,并给按钮绑定切换命令:
<Window x:Class="YourNamespace.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:YourNamespace" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <local:MainViewModel /> </Window.DataContext> <StackPanel> <Button Content="切换到type1列表" Command="{Binding SwitchDataCommand}" CommandParameter="True" Margin="5"/> <Button Content="切换到type2列表" Command="{Binding SwitchDataCommand}" CommandParameter="False" Margin="5"/> <DataGrid ItemsSource="{Binding MyList}" Width="400" IsReadOnly="{Binding Path=IsChecked, ElementName=CbDisableList}" Margin="5"/> <CheckBox x:Name="CbDisableList" Content="只读模式" Margin="5"/> </StackPanel> </Window>
关键注意事项
- 集合类型兼容:
ObservableCollection<object>可以兼容任何类型的ObservableCollection,因为所有类型都继承自object。 - 属性变更通知:必须在
MyList的setter中调用RaisePropertyChanged,否则UI不会响应数据源切换。 - 集合自身变更通知:直接转换原集合赋值时,原集合的增删改操作依然会通知UI,因为
ObservableCollection本身实现了INotifyCollectionChanged接口。 - DataGrid列自动刷新:切换数据源时,DataGrid会根据当前元素类型自动重新生成列,无需额外处理。
内容的提问来源于stack exchange,提问作者Wirthi
相关产品推荐
相关产品推荐

