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

MVVM中刷新ObservableCollection后保留DataGrid选中项的方案咨询

解决DataGrid刷新集合时丢失选中项的问题

问题根源

当前代码通过Clear()清空整个ObservableCollection后重新添加所有新项,这会导致DataGrid绑定的旧实例全部被移除,新项都是全新对象,原来的选中项实例不存在于集合中,因此选中状态丢失。

解决方案

方案1:全量刷新时恢复选中项

这种方案改动较小,适合快速实现:

  1. 记录刷新前的选中项标识:利用数据的唯一主键(比如Id)保存当前选中项的标识
  2. 刷新集合:保持原有的全量清空、添加逻辑
  3. 恢复选中状态:刷新完成后,根据保存的标识找到新集合中的对应项,重新设置选中

修改后的loadDataGridMainCollection方法:

private void loadDataGridMainCollection()
{
    // 记录当前选中项的Id(SelectedRow需实现INotifyPropertyChanged)
    int? selectedId = SelectedRow?.Id;

    // 获取新数据
    DataTable dt = mySql.selectSql("SELECT id, datum, artikel, kurztext, stueckzahl, blechstaerke, rohrmasse, " +
        "material, kundenauftrag, ansprechpartner, abteilung, geaendertAm, twoWeeks, prio, ordnerpfad, dateiname FROM auftragstool.orders WHERE zustand = 'zu schachteln' ORDER BY import ASC, blechstaerke DESC");

    // 刷新集合
    DataGridMainCollection.Clear();
    if (dt.Rows.Count > 0)
    {
        foreach (DataRow r in dt.Rows) 
        {
            DataGridMainCollection.Add(new SchachtelEintrag
            {
                Id = Convert.ToInt32(r["id"].ToString()),
                Datum = Convert.ToDateTime(r["datum"].ToString()),
                Artikel = r["artikel"].ToString(),
                Kurztext = r["kurztext"].ToString(),
                Stueckzahl = Convert.ToInt32(r["stueckzahl"].ToString()),
                Blechstaerke = r["blechstaerke"].ToString(),
                Rohrmasse = r["rohrmasse"].ToString(),
                Material = r["material"].ToString(),
                Kundenauftrag = r["kundenauftrag"].ToString(),
                Ansprechpartner = r["ansprechpartner"].ToString(),
                Abteilung = r["abteilung"].ToString(),
                GeaendertAm = r["geaendertAm"].ToString(),
                TwoWeeks = r["twoWeeks"].ToString(),
                Prio = r["prio"].ToString(),
                Ordnerpfad = r["ordnerpfad"].ToString(),
                Dateiname = r["dateiname"].ToString()
            });
        }
    }

    // 恢复选中项(如果之前有选中)
    if (selectedId.HasValue)
    {
        var targetItem = DataGridMainCollection.FirstOrDefault(item => item.Id == selectedId.Value);
        if (targetItem != null)
        {
            SelectedRow = targetItem;
        }
    }
}

关键配套修改

  • 修正XAML绑定:DataGrid没有Selected属性,应使用SelectedItem绑定选中项:
    <DataGrid ItemsSource="{Binding DataGridMainCollection}"
              SelectedItem="{Binding SelectedRow, Mode=TwoWay}">
    
  • 确保SelectedRow实现属性通知:ViewModel中的SelectedRow需实现INotifyPropertyChanged,才能通知UI更新选中状态:
    private SchachtelEintrag _selectedRow;
    public SchachtelEintrag SelectedRow
    {
        get => _selectedRow;
        set
        {
            if (_selectedRow != value)
            {
                _selectedRow = value;
                OnPropertyChanged(nameof(SelectedRow));
            }
        }
    }
    
    // ViewModel需实现INotifyPropertyChanged接口
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

方案2:增量更新集合(更优)

避免全量清空集合,仅对新增、删除、修改的项进行操作,这样原有选中项(未被删除的情况下)会保留在集合中,无需额外恢复选中状态,同时减少UI闪烁:

  1. 获取新数据并转换为列表
  2. 对比新旧数据,执行增量操作:
    • 移除旧集合中不存在于新数据的项
    • 添加新数据中不存在于旧集合的项
    • 更新新旧都存在的项的属性值

修改后的loadDataGridMainCollection方法:

private void loadDataGridMainCollection()
{
    // 获取新数据并转换为列表
    DataTable dt = mySql.selectSql("SELECT id, datum, artikel, kurztext, stueckzahl, blechstaerke, rohrmasse, " +
        "material, kundenauftrag, ansprechpartner, abteilung, geaendertAm, twoWeeks, prio, ordnerpfad, dateiname FROM auftragstool.orders WHERE zustand = 'zu schachteln' ORDER BY import ASC, blechstaerke DESC");

    List<SchachtelEintrag> newItems = new List<SchachtelEintrag>();
    if (dt.Rows.Count > 0)
    {
        foreach (DataRow r in dt.Rows) 
        {
            newItems.Add(new SchachtelEintrag
            {
                Id = Convert.ToInt32(r["id"].ToString()),
                Datum = Convert.ToDateTime(r["datum"].ToString()),
                Artikel = r["artikel"].ToString(),
                Kurztext = r["kurztext"].ToString(),
                Stueckzahl = Convert.ToInt32(r["stueckzahl"].ToString()),
                Blechstaerke = r["blechstaerke"].ToString(),
                Rohrmasse = r["rohrmasse"].ToString(),
                Material = r["material"].ToString(),
                Kundenauftrag = r["kundenauftrag"].ToString(),
                Ansprechpartner = r["ansprechpartner"].ToString(),
                Abteilung = r["abteilung"].ToString(),
                GeaendertAm = r["geaendertAm"].ToString(),
                TwoWeeks = r["twoWeeks"].ToString(),
                Prio = r["prio"].ToString(),
                Ordnerpfad = r["ordnerpfad"].ToString(),
                Dateiname = r["dateiname"].ToString()
            });
        }
    }

    // 1. 移除旧集合中不存在于新数据的项
    var itemsToRemove = DataGridMainCollection.Where(oldItem => !newItems.Any(newItem => newItem.Id == oldItem.Id)).ToList();
    foreach (var item in itemsToRemove)
    {
        DataGridMainCollection.Remove(item);
    }

    // 2. 添加新项或更新现有项属性
    foreach (var newItem in newItems)
    {
        var existingItem = DataGridMainCollection.FirstOrDefault(oldItem => oldItem.Id == newItem.Id);
        if (existingItem == null)
        {
            DataGridMainCollection.Add(newItem);
        }
        else
        {
            // 更新属性,需SchachtelEintrag实现INotifyPropertyChanged
            existingItem.Datum = newItem.Datum;
            existingItem.Artikel = newItem.Artikel;
            existingItem.Kurztext = newItem.Kurztext;
            existingItem.Stueckzahl = newItem.Stueckzahl;
            existingItem.Blechstaerke = newItem.Blechstaerke;
            existingItem.Rohrmasse = newItem.Rohrmasse;
            existingItem.Material = newItem.Material;
            existingItem.Kundenauftrag = newItem.Kundenauftrag;
            existingItem.Ansprechpartner = newItem.Ansprechpartner;
            existingItem.Abteilung = newItem.Abteilung;
            existingItem.GeaendertAm = newItem.GeaendertAm;
            existingItem.TwoWeeks = newItem.TwoWeeks;
            existingItem.Prio = newItem.Prio;
            existingItem.Ordnerpfad = newItem.Ordnerpfad;
            existingItem.Dateiname = newItem.Dateiname;
        }
    }

    // 可选:处理选中项被删除的情况
    if (SelectedRow != null && !DataGridMainCollection.Any(item => item.Id == SelectedRow.Id))
    {
        SelectedRow = DataGridMainCollection.FirstOrDefault();
    }
}

配套要求

SchachtelEintrag类需实现INotifyPropertyChanged,确保属性更新时UI同步:

public class SchachtelEintrag : INotifyPropertyChanged
{
    private int _id;
    public int Id
    {
        get => _id;
        set
        {
            if (_id != value)
            {
                _id = value;
                OnPropertyChanged(nameof(Id));
            }
        }
    }

    private DateTime _datum;
    public DateTime Datum
    {
        get => _datum;
        set
        {
            if (_datum != value)
            {
                _datum = value;
                OnPropertyChanged(nameof(Datum));
            }
        }
    }

    // 其他属性按相同模式实现,省略...

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:35:56