如何在Reactive DynamicData中恢复集合的原始排序顺序?
解决DynamicData集合恢复原始未排序顺序的问题
我之前用DynamicData处理WPF集合排序时也碰到过这个问题——排序操作很顺手,但要回到原始顺序得找对路子。核心逻辑是:你需要给DoSort()传入一个能还原数据源原始顺序的IComparer<T>实现,因为DynamicData的Sort操作完全依赖传入的比较器来决定元素排列顺序。
具体解决方案
首先,我们需要一个专门的比较器,它能记住每个元素在原始数据源中的位置,然后按照这个位置来排序:
public class OriginalOrderComparer<T> : IComparer<T> { private readonly Dictionary<T, int> _originalPositionMap; public OriginalOrderComparer(IEnumerable<T> originalSource) { // 记录每个元素在原始集合中的索引 _originalPositionMap = originalSource .Select((item, index) => (Item: item, Index: index)) .ToDictionary(pair => pair.Item, pair => pair.Index); } public int Compare(T x, T y) { // 处理后续新增的元素(如果有的话),默认放到集合末尾 var xPos = _originalPositionMap.TryGetValue(x, out var xIndex) ? xIndex : int.MaxValue; var yPos = _originalPositionMap.TryGetValue(y, out var yIndex) ? yIndex : int.MaxValue; return xPos.CompareTo(yPos); } }
整合到你的代码中
- 先在类里添加字段保存原始数据源的快照和位置映射:
private IEnumerable<T> _originalSourceSnapshot; private Dictionary<T, int> _originalPositionMap; // 用于动态更新新增元素
- 修改
ItemSet()方法,初始化时保存原始数据并监听新增元素:
public ReadOnlyObservableCollection<T> ItemSet() { if (_itemset is null) { _sorting = new Subject<IComparer<T>>(); _resorting = new Subject<Unit>(); // 将源集合转为ObservableList以便获取初始项和监听新增 var sourceList = set.AsObservableList(); _originalSourceSnapshot = sourceList.Items.ToList(); _originalPositionMap = _originalSourceSnapshot .Select((item, index) => (item, index)) .ToDictionary(x => x.item, x => x.index); // 监听新增元素,更新位置映射(如果你的集合会动态新增项) sourceList.ItemsAdded.Subscribe(newItem => { if (!_originalPositionMap.ContainsKey(newItem)) { _originalPositionMap[newItem] = _originalPositionMap.Count; } }); set.Sort(_sorting, resort: _resorting) .ObserveOnDispatcher() .Bind(out var list) .Subscribe(); _itemset = list; DefaultSort(); // 默认使用原始顺序 } return _itemset; }
- 新增恢复原始顺序的方法,或者直接在需要的地方调用
DoSort()传入原始比较器:
public void RestoreOriginalOrder() { var originalComparer = new OriginalOrderComparer<T>(_originalSourceSnapshot); DoSort(originalComparer); } // 或者修改DefaultSort直接用原始顺序 private void DefaultSort() { DoSort(new OriginalOrderComparer<T>(_originalSourceSnapshot)); }
注意事项
- 如果你的集合包含重复元素,上面的字典会因为键冲突报错,这时候可以给每个元素添加一个唯一标识属性(比如
Guid Id),用标识来做字典的键,而不是元素本身。 - 如果你的集合不会动态新增元素,那可以省略监听
ItemsAdded的部分,简化代码。
这样,当你需要恢复原始顺序时,只需要调用RestoreOriginalOrder(),或者直接给DoSort()传入这个OriginalOrderComparer实例就可以了。
内容的提问来源于stack exchange,提问作者MiddleTommy
相关产品推荐
相关产品推荐

