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

替换ItemsSource中的项后DataGrid排序异常,如何保持行顺序?

解决WPF DataGrid替换项时行顺序错乱的问题

问题场景回顾

你遇到的情况是:WPF DataGrid开启排序后,选中某一项并从数据源刷新该条目时,直接替换集合中的对象实例会导致对应行位置移动——哪怕排序列的所有值完全相同。

问题根源

当你执行Persons[previousIndex] = updatedPerson替换集合中的项时,本质是把集合里的旧对象引用换成了全新的对象实例。DataGrid的排序机制会重新评估这个新项的位置:即使新项的属性值和旧项完全一致,由于是新的对象引用,DataGrid内部的排序缓存或比较逻辑会把它当作“新数据”处理,在排序列值相同的场景下,就会出现行位置随机移动的问题。

最优解决方案:更新现有对象属性而非替换实例

这也是你最终采用的AutoMapper方案的核心思路——不要替换集合里的对象,而是直接更新现有对象的属性值。这样做有两个关键好处:

  1. ObservableCollection不会触发“项替换”的通知,只会因为对象属性变化(如果实现了INotifyPropertyChanged)触发UI更新;
  2. DataGrid的排序逻辑不会被触发,因为对象引用没变,排序列的属性值也没变,行位置自然保持不变。

完善你的实现

你的AutoMapper映射已经走对了方向,这里再补充两个关键细节:

  1. 确保Person类实现INotifyPropertyChanged,否则DataGrid无法感知到属性变化,UI不会自动刷新:
public class Person : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnPropertyChanged(nameof(Name));
        }
    }

    private int _age;
    public int Age
    {
        get => _age;
        set
        {
            _age = value;
            OnPropertyChanged(nameof(Age));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  1. 你的ViewModel里的SelectedPersonChanged方法可以保持当前的AutoMapper写法,它会把新数据源的属性值映射到已选中的对象实例上,完美避免替换对象:
private void SelectedPersonChanged()
{
    if (SelectedPerson != null)
    {
        // 模拟从数据源获取刷新后的实例
        Person updatedPerson = new Person() { Age = SelectedPerson.Age, Name = SelectedPerson.Name };
        // 将新数据映射到现有对象实例,而非替换
        Mapper.Map(updatedPerson, SelectedPerson);
    }
}

备选方案:如果必须替换对象实例

如果你因为某些原因必须替换集合中的对象实例(比如数据源返回的是不可修改的对象),可以通过临时禁用排序再恢复的方式避免行移动:

private void SelectedPersonChanged()
{
    if (SelectedPerson != null)
    {
        // 假设你能通过ViewModel访问到DataGrid实例,或者通过绑定传递排序状态
        var dataGrid = YourDataGridInstance;
        // 记录当前排序状态
        var sortDescriptions = new List<SortDescription>(dataGrid.Items.SortDescriptions);
        // 临时清除排序
        dataGrid.Items.SortDescriptions.Clear();
        // 替换对象实例
        int previousIndex = Persons.IndexOf(SelectedPerson);
        Person updatedPerson = new Person() { Age = SelectedPerson.Age, Name = SelectedPerson.Name };
        Persons[previousIndex] = updatedPerson;
        _selectedPerson = updatedPerson;
        // 恢复排序状态
        foreach (var sort in sortDescriptions)
        {
            dataGrid.Items.SortDescriptions.Add(sort);
        }
    }
}

不过这个方案需要额外处理DataGrid的排序状态,复杂度更高,不如更新属性的方案优雅,所以优先推荐前者。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:20:31