替换ItemsSource中的项后DataGrid排序异常,如何保持行顺序?
解决WPF DataGrid替换项时行顺序错乱的问题
问题场景回顾
你遇到的情况是:WPF DataGrid开启排序后,选中某一项并从数据源刷新该条目时,直接替换集合中的对象实例会导致对应行位置移动——哪怕排序列的所有值完全相同。
问题根源
当你执行Persons[previousIndex] = updatedPerson替换集合中的项时,本质是把集合里的旧对象引用换成了全新的对象实例。DataGrid的排序机制会重新评估这个新项的位置:即使新项的属性值和旧项完全一致,由于是新的对象引用,DataGrid内部的排序缓存或比较逻辑会把它当作“新数据”处理,在排序列值相同的场景下,就会出现行位置随机移动的问题。
最优解决方案:更新现有对象属性而非替换实例
这也是你最终采用的AutoMapper方案的核心思路——不要替换集合里的对象,而是直接更新现有对象的属性值。这样做有两个关键好处:
- ObservableCollection不会触发“项替换”的通知,只会因为对象属性变化(如果实现了
INotifyPropertyChanged)触发UI更新; - DataGrid的排序逻辑不会被触发,因为对象引用没变,排序列的属性值也没变,行位置自然保持不变。
完善你的实现
你的AutoMapper映射已经走对了方向,这里再补充两个关键细节:
- 确保
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)); } }
- 你的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
相关产品推荐
相关产品推荐

