两年前,曾经写过一篇文章:一种简单易用的WPF ListView点击列排序的方法,演示通过WPF中的CollectionView来对ListView进行排序,具体排序的执行是这样的:
在.NET 3.5+环境下,如果目标数据是IList,程序会使用Expression Tree来生成并设置ListCollectionView类型的CustomSort属性从而进行排序。
在.NET 3.0+环境下,会使用最原始CollectionView的SortDescriptions属性来排序。
但是上述排序方法不是很灵活,因此本文演示在WPF下使用LINQ来排序的方法,当然开发者不需要手动写LINQ排序代码,程序创建了一个叫LinqCollectionView的类型,通过它来把排序方式定义好就可以了,程序内部会使用LINQ来执行排序。当然LinqCollectionView仅用于排序,和WPF的ICollectionView接口没有任何继承关系,且目前不支持ObservableCollection<T>。
WPF的CollectionView诞生在.NET 3.0,而.NET 3.5后LINQ才出现,而Windows 8中WinRT的CollectionView就没有了SortDescriptions和GroupDescriptions属性,微软推荐开发者直接使用LINQ来操作数据。
下图:本文示例程序
比如我们的数据元素类型是这样一个简单的类:
publicclassItem
{
public Item(string name, int score)
{
Name = name;
Score = score;
}
publicstring Name { get; privateset; }
publicint Score { get; privateset; }
publicstaticItem[] GetData()
{
returnnewItem[]
{
newItem("Liu", 86),
newItem("Zhang", 32),
newItem("Li", 21),
newItem("Wang", 100),
newItem("Zhao", 78)
};
}
}
Name代表姓名,Score代表成绩。GetData方法用来生成测试数据。
排序规则这样定义:
姓名默认递增排序,且不区分大小写。
程序默认递减排序。
在XAML上,定义好ListView:
<ListView Name="listView">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="姓名"/>
<GridViewColumn Header="成绩">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ProgressBar Height="20" Value="{Binding Score,Mode=OneTime}" MinWidth="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
然后需要加入执行代码:
var collectionView =newLinqCollectionView<Item>(
Item.GetData(),
newColumnSortData<Item>((x, y) =>StringComparer.OrdinalIgnoreCase.Compare(, )),
newColumnSortData<Item>(i => i.Score, ColumnSortDirection.Descending));
LinqCollectionView<Item>.SetListView(listView, collectionView);
程序就OK了。
程序的主要类型就是LinqCollectionView<T>,T就是数据元素的类型,LinqCollectionView继承自MVVM Light中的ViewModelBase,因为需要提供集合的改变通知。注意这里集合改变通知不是指那个INotifyCollectionChanged接口,集合本身不会发生变化,LINQ排序后会生成一个新的IEnumerable<T>,所以这个新的属性会代替原来的集合属性。
该类型的构造函数是这样的:
public LinqCollectionView(IEnumerable<T> source, paramsColumnSortData<T>[] sortData)
source当然就是原始的集合。sortData就是具体排序的设置数据,我们来看ColumnSortData<T>这个类型。
其中ColumnSortDirection枚举代表排序的方向:
publicenumColumnSortDirection
{
Ascending,
Descending
}
Ascending代表递增,Descending代表递减。
那么ColumnSortData<T>的InitialDirection属性代表着此项排序第一次进行时的排序方向,也就是当这一列的GridViewColumnHeader第一次被点击后,是进行递增还是递减的排序。
Status属性的类型是可为null的ColumnSortDirection类型,当为null时则代表当前列没有被排序,当然也可能是排序后用户又点击了其他列,那么本列顺序被打乱,Status属性也会是null。
最后两个属性是两种排序方式(只能二选一),KeySelector属性是通过一个Func<T, Object>委托,来抽取集合项目中的一个属性,然后对这个属性进行排序。类似WPF CollectionView中SortDescription中的属性名称(当然KeySelector是直接返回属性,SortDescription是通过反射获取属性,KeySelector拥有绝对性能优势)。
另一个种排序方式是通过Comparison<T>委托,这个委托不是我定义的,就在System命名空间里,.NET 2.0就有。这种方式类似ListCollectionView中的CustomSort属性中的IComparer类型,只不过直接通过一个委托,这样用户就不用再执行IComparer类型了。
因此ColumnSortData有两种构造方式:
public ColumnSortData(Func<T, object> selector, ColumnSortDirection direction =ColumnSortDirection.Ascending)
public ColumnSortData(Comparison<T> comparison, ColumnSortDirection direction =ColumnSortDirection.Ascending)
那么再看上面的LinqCollectionView的创建代码,这回加上注释,是这样的:
var collectionView =newLinqCollectionView<Item>(
Item.GetData(),
newColumnSortData<Item>((x, y) =>StringComparer.OrdinalIgnoreCase.Compare(, )),
//使用Comparison<T>直接返回排序结果
newColumnSortData<Item>(i => i.Score, ColumnSortDirection.Descending));
//使用KeySelector来返回Score属性
当前版本的源代码下载 下载页面 注意:链接是微软SkyDrive页面,下载时请用浏览器直接下载,用某些下载工具可能无法下载 源代码环境:Microsoft Visual Studio Express 2012 for Windows Desktop 注意:源代码不包含引用的外部类库文件:MVVM Light
作者:Mgen


















