You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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集合更新时自动同步第二个的对应数据:通过两个维度实现:
    1. 订阅MainItems.CollectionChanged事件,当集合中的选中项被替换时,重新触发详情加载;
    2. 订阅SelectedMainItem.PropertyChanged事件,当选中项的属性单独变更时,立即刷新详情数据。

注意事项

  • 确保你的MainModel实现INotifyPropertyChanged,这样单个属性变更才能触发UI和详情的同步。
  • 更新DetailItems时用Clear()+Add()的方式,而非直接替换整个集合,这样能保留第二个DataGrid的用户操作状态(比如排序、筛选、滚动位置)。
  • 如果涉及异步加载数据,记得在UI线程更新集合(可以用Application.Current.Dispatcher.Invoke或者async/await配合Dispatcher)。

内容的提问来源于stack exchange,提问作者shital chakrawar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:31:24