WinUI3入门16:Order自定义排序
初级代码游戏的专栏介绍与文章目录-CSDN博客
我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
源码指引:github源码指引_初级代码游戏的博客-CSDN博客
C#是我多年以来的业余爱好,新搞的东西能用C#的就用C#了。
接上一篇继续研究排序问题。上一篇:WinUI3入门15:DataGrid排序-CSDN博客
前一篇使用OrderBy对指定列排序,如果要同时对多列排序,或许可以用一串OrderBy来实现(因为OrderBy是稳定排序,如果相等,不会改变相对顺序)。
但是首先写一串OrderBy相当繁琐,效果存在不确定性(依赖算法特性),其次多次排序性能可能存在问题,当然这都不是关键问题。
关键问题是这不符合完全自定义这个目标。传统上(指C++)我们用传递一个比较函数(或函数对象)的方法来实现自定义的排序规则,在C#同样可以通过传递特定的接口来实现。
目录
一、Enumerable.Order 方法
二、ICompare
三、设计通用的比较接口
一、Enumerable.Order 方法
ObservableCollection<>实现了IEnumerable<T> 接口,因此可以用Order方法进行排序。Order方法接受一个IComparer<T>参数:
public static System.Linq.IOrderedEnumerable<T> Order<T> (this System.Collections.Generic.IEnumerable<T> source, System.Collections.Generic.IComparer<T>? comparer);
很明显这个ICompare<T>就是用作比较的方法,所以问题就归结为编写ICompare<T>。
二、ICompare<T>
ICompare<T>要求如下:
public int Compare (T? x, T? y);
这个我们看着很眼熟,返回值也很眼熟:0代表相等,大于0代表x>y,小于0代表x<y,跟我们传统的x-y是一样的。
三、设计通用的比较接口
通常为了多列比较我们需要下面的信息:
- 哪些列用作比较
- 这些列的比较顺序(优先级)
- 每个列的比较方法(字符串、数值、升序降序)
因为这里是直接用属性比较,那么字符串还是数值是不需要额外记录的,所以要记住的就是列和升序降序,我们可以用下面的类来描述:
public class SortColumn{public String name = "";public bool sortOrderAscending = true;//false Descending}
再定义一个列表就可以描述列的顺序了:
public List<SortColumn> sortColumns = new();
现在我们考虑把描述规则放在一个类里,而具体的比较由类自身的方法来实现。理论上通过上一篇用的动态类型(PropertyInfo)处理是可以实现完美的通用比较类的,不过有时候自定义一下也没什么不好,可能更简单、更高效。
整个通用部分如下:
public interface IMyOrder<T> where T : IMyOrder<T>{int CompareTo(MyOrder<T> order, T tmp);}public class MyOrder<T> : System.Collections.Generic.IComparer<T> where T : IMyOrder<T> {public class SortColumn{public String name = "";public bool sortOrderAscending = true;//false Descending}public List<SortColumn> sortColumns = new();public MyOrder(){}//清除排序规则public void ClearSortColumn(){sortColumns.Clear();}//指定进行排序的列public void SetSortColumn(String colname){SortColumn? sortColumn = null;int index = -1;if (sortColumns.Count != 0){for (int i = 0; i < sortColumns.Count; ++i){SortColumn tmpColumn =sortColumns[i];if (tmpColumn.name == colname){sortColumn=tmpColumn;sortColumns.Remove(tmpColumn);index = i;}}}if (null == sortColumn){sortColumn = new SortColumn();sortColumn.name = colname;sortColumns.Add(sortColumn);}else{if (0 == index) sortColumn.sortOrderAscending = !sortColumn.sortOrderAscending;sortColumns.Insert(0, sortColumn);}}public int Compare(T? x, T? y){if(null==x && null==y)return 0;if (null == x) return -1;if(null==y)return 1;return x.CompareTo(this, y);}}
前面定义了一个接口IMyOrder<T>用来由实际的数据实现比较函数。而MyOrder<T>的IComparer<T>的实现“public int Compare(T? x, T? y)”则调用IMyOrder<T>的CompareTo来实现真正的比较。
最复杂的是SetSortColumn,要检查是否是已经存在的排序列,最新点击的排第一,如果连续点击第一个就改变正序逆序。
数据那边则要增加对IMyOrder<T>的实现:
public class Data : INotifyPropertyChanged, IMyOrder<Data>{。。。。。。public int CompareTo(MyOrder<Data> order,Data tmp){for (int i = 0; i < order.sortColumns.Count; ++i){int ret = 0;MyOrder<Data>.SortColumn sortColumn = order.sortColumns[i];if (sortColumn.name == "Dir") ret = _dir.CompareTo(tmp._dir);if (sortColumn.name == "File") ret = _file.CompareTo(tmp._file);if (sortColumn.name == "Ext") ret = _ext.CompareTo(tmp._ext);if (sortColumn.name == "Type") ret = _type.CompareTo(tmp._type);if (sortColumn.name == "Encode") ret = _encode.CompareTo(tmp._encode);if (sortColumn.name == "BOM") ret = _bom.CompareTo(tmp._bom);if (sortColumn.name == "CR") ret = _cr.CompareTo(tmp._cr);if (sortColumn.name == "CRLF") ret = _crlf.CompareTo(tmp._crlf);if (sortColumn.name == "LF") ret = _lf.CompareTo(tmp._lf);if (sortColumn.name == "Length") ret = _length.CompareTo(tmp._length);if (sortColumn.name == "State") ret = _state.CompareTo(tmp._state);if (!sortColumn.sortOrderAscending) ret = -ret;if (0 != ret) return ret;}return 0;}}
主代码中的主要过程:
MyOrder<Data> myOrder = new();//Sorting事件添加排序列myOrder.SetSortColumn(e.Column.Header.ToString());排序newdatas = datas.Order(myOrder);
(这里是文档结束)