WPF MVVM:基于DataGrid选中项更新另一DataGrid并保留选中状态
WPF MVVM双DataGrid联动解决方案(适配你的需求)
我之前在项目里遇到过几乎完全一致的场景,给你分享一下纯MVVM的实现方案,完美解决你提到的三个核心需求:
核心思路拆解
我们的ViewModel需要管理两个数据集、选中状态,同时处理集合变更和选中项变更的联动逻辑,全程不碰View的后台代码。
1. 基础ViewModel架构(实现INotifyPropertyChanged)
首先,你的ViewModel必须实现INotifyPropertyChanged接口,这是MVVM中UI和ViewModel双向绑定的基础。另外要用ObservableCollection<T>作为DataGrid的数据源,因为它自带集合变更通知,能自动刷新UI。
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Collections.Specialized; using System.Linq; public class MainViewModel : INotifyPropertyChanged { // 第一个DataGrid的数据源 private ObservableCollection<MainModel> _mainItems; public ObservableCollection<MainModel> MainItems { get => _mainItems; set { if (_mainItems != null) { _mainItems.CollectionChanged -= MainItems_CollectionChanged; } _mainItems = value; OnPropertyChanged(); // 订阅集合变更事件,用于后续同步选中项的详情数据 _mainItems.CollectionChanged += MainItems_CollectionChanged; } } // 第二个DataGrid的数据源 private ObservableCollection<DetailModel> _detailItems; public ObservableCollection<DetailModel> DetailItems { get => _detailItems; set { _detailItems = value; OnPropertyChanged(); } } // 第一个DataGrid的选中项 private MainModel _selectedMainItem; public MainModel SelectedMainItem { get => _selectedMainItem; set { if (_selectedMainItem != null) { _selectedMainItem.PropertyChanged -= SelectedMainItem_PropertyChanged; } _selectedMainItem = value; OnPropertyChanged(); if (_selectedMainItem != null) { _selectedMainItem.PropertyChanged += SelectedMainItem_PropertyChanged; } // 选中项变更时,立即更新第二个DataGrid的数据 LoadDetailData(); } } public MainViewModel() { // 初始化集合(可以替换成从服务/数据库加载) MainItems = new ObservableCollection<MainModel>(); DetailItems = new ObservableCollection<DetailModel>(); } // 加载选中项对应的详情数据 private void LoadDetailData() { if (SelectedMainItem == null) { DetailItems.Clear(); return; } // 这里替换成你的实际逻辑:比如从SelectedMainItem获取子集合,或者调用API var details = GetDetailsForMainItem(SelectedMainItem.Id); DetailItems.Clear(); foreach (var detail in details) { DetailItems.Add(detail); } } // 处理MainItems集合变更事件 private void MainItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // 如果是集合中的项被替换/更新,且正好是当前选中项 if (e.Action == NotifyCollectionChangedAction.Replace && e.NewItems?[0] is MainModel updatedItem) { if (updatedItem.Id == SelectedMainItem?.Id) { // 同步更新详情数据 SelectedMainItem = updatedItem; // 触发setter里的LoadDetailData } } // 如果是移除项,若移除的是选中项则清空详情 else if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems?[0] is MainModel removedItem) { if (removedItem.Id == SelectedMainItem?.Id) { SelectedMainItem = null; } } } // 处理选中项的属性变更 private void SelectedMainItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { // 可以根据变更的属性决定是否刷新详情,或者直接全量刷新 LoadDetailData(); } // 实现INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // 模拟获取详情数据的方法 private List<DetailModel> GetDetailsForMainItem(int mainItemId) { // 这里替换成你的实际数据获取逻辑 return new List<DetailModel>(); } } // 示例实体类(需实现INotifyPropertyChanged以支持属性变更通知) public class MainModel : INotifyPropertyChanged { private int _id; public int Id { get => _id; set { _id = value; OnPropertyChanged(); } } private string _name; public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public class DetailModel { public int DetailId { get; set; } public string Content { get; set; } }
2. View层绑定配置
在XAML里,只需要把两个DataGrid和ViewModel的属性绑定即可,不需要任何后台代码:
<!-- 第一个DataGrid --> <DataGrid ItemsSource="{Binding MainItems}" SelectedItem="{Binding SelectedMainItem, Mode=TwoWay}" AutoGenerateColumns="False" Margin="10"> <!-- 你的列配置 --> <DataGrid.Columns> <DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="*"/> <DataGridTextColumn Header="名称" Binding="{Binding Name}" Width="*"/> </DataGrid.Columns> </DataGrid> <!-- 第二个DataGrid --> <DataGrid ItemsSource="{Binding DetailItems}" AutoGenerateColumns="False" Margin="10"> <!-- 你的详情列配置 --> <DataGrid.Columns> <DataGridTextColumn Header="详情ID" Binding="{Binding DetailId}" Width="*"/> <DataGridTextColumn Header="详情内容" Binding="{Binding Content}" Width="*"/> </DataGrid.Columns> </DataGrid>
3. 关键需求的针对性处理
- 第一个DataGrid选中项更新第二个DataGrid:通过
SelectedMainItem的setter触发LoadDetailData方法,直接更新DetailItems,UI会自动同步。 - 第二个DataGrid数据源变更时保留第一个选中状态:只要
MainItems没有被整体替换,SelectedMainItem的引用保持不变,第一个DataGrid的选中状态就会保留。如果需要替换MainItems,建议在替换前记录选中项的唯一标识(比如Id),替换后重新找到对应项赋值给SelectedMainItem:// 替换MainItems的示例代码 var selectedId = SelectedMainItem?.Id; var newMainItems = GetNewMainItems(); // 新的集合 MainItems = new ObservableCollection<MainModel>(newMainItems); // 恢复选中状态 if (selectedId != null) { SelectedMainItem = MainItems.FirstOrDefault(item => item.Id == selectedId); } - 第一个DataGrid集合更新时自动同步第二个的对应数据:通过两个维度实现:
- 订阅
MainItems.CollectionChanged事件,当集合中的选中项被替换时,重新触发详情加载; - 订阅
SelectedMainItem.PropertyChanged事件,当选中项的属性单独变更时,立即刷新详情数据。
- 订阅
注意事项
- 确保你的
MainModel实现INotifyPropertyChanged,这样单个属性变更才能触发UI和详情的同步。 - 更新
DetailItems时用Clear()+Add()的方式,而非直接替换整个集合,这样能保留第二个DataGrid的用户操作状态(比如排序、筛选、滚动位置)。 - 如果涉及异步加载数据,记得在UI线程更新集合(可以用
Application.Current.Dispatcher.Invoke或者async/await配合Dispatcher)。
内容的提问来源于stack exchange,提问作者shital chakrawar
相关产品推荐
相关产品推荐

