现在逻辑多层的设计方式已经深入人心。一般业务层会返回一个对象集合供其它层来使用,这个对象集合有的用数组来装载、有的用DataTable来装载、有的用类型化的DataSet来装载、有的用泛型List对象来装载。在使用泛型List对象来装载的方法时会遇到当这个集合绑定到GridView等可排序控件后并不能很好的实现排序功能。默认的List<T>支持排序方法Sort(Icomparer<T>) 和Sort(Comparison<T>),这并不能帮助我们排序任何类型的属性,要知道一般一个类的属性类型是非常的多。
在这篇文章中我们以一个非常常见的User类为例子,来介绍如何利用SortableList<T>来实现排序任意类型属性值的方法。User类包括ID(int),UserName(string),JoinDate(DateTime),UserType(自定义枚举类型),isActive(bool)。我们用Sortable<T>来装载User对象的集合,SortableList<T>继承自List<T>, 为Sortable<T>中的Sort方法提供两个参数要排序的属性名称和排序的方向(升序或降序)。
User类
public enum UserTypeEnum{ Manager, ProjectLead, TeamLead, SeniorSoftwareEngineer, SoftwareEngineer} /// <summary> /// User类 /// </summary> public class User{ public User( int id, string userName,DateTime joinDate, UserTypeEnum userType, bool isActive) { this .id = id; this .userName = userName; this .joinDate = joinDate; this .userType = userType; this .isActive = isActive; } private int id; public int Id { get { return id; } set { id = value; } } private string userName = string .Empty ; public string UserName { get { return userName; } set { userName = value; } } private DateTime joinDate = DateTime.MinValue; public DateTime JoinDate { get { return joinDate; } set { joinDate = value; } } private UserTypeEnum userType = UserTypeEnum.SoftwareEngineer; public UserTypeEnum UserType { get { return userType; } set { userType = value; } } private bool isActive = false ; public bool IsActive { get { return isActive; } set { isActive = value; } }}
SortableList类
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; /// <summary> /// 可排序集合类 /// </summary> public class SortableList < T > : List < T > { private string _propertyName; private bool _ascending; /// <summary> /// 排序 /// </summary> /// <param name="propertyName"> 属性名称 </param> /// <param name="ascending"> 如果设置 <c> true </c> 升序 </param> /// 2007-2-16 23:35 KOSTECH-ACER public void Sort( string propertyName, bool ascending) { if (_propertyName == propertyName && _ascending == ascending) _ascending = ! ascending; else { _propertyName = propertyName; _ascending = ascending; } PropertyDescriptorCollection properties = TypeDescriptor.GetProperties( typeof (T)); PropertyDescriptor propertyDesc = properties.Find(propertyName, true ); // 应用排序 PropertyComparer < T > pc = new PropertyComparer < T > (propertyDesc, (_ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending); this .Sort(pc); }}
PropertyComparer<T>类
using System; using System.ComponentModel; using System.Collections.Generic; using System.Reflection; /// <summary> /// 属性比较类 /// </summary> public class PropertyComparer < T > : System.Collections.Generic.IComparer < T > { private PropertyDescriptor _property; private ListSortDirection _direction; public PropertyComparer(PropertyDescriptor property, ListSortDirection direction) { _property = property; _direction = direction; } #region IComparer<T> public int Compare(T xWord, T yWord) { // 获取属性 object xValue = GetPropertyValue(xWord, _property.Name); object yValue = GetPropertyValue(yWord, _property.Name); // 调用升序或降序方法 if (_direction == ListSortDirection.Ascending) { return CompareAscending(xValue, yValue); } else { return CompareDescending(xValue, yValue); } } public bool Equals(T xWord, T yWord) { return xWord.Equals(yWord); } public int GetHashCode(T obj) { return obj.GetHashCode(); } #endregion /// <summary> /// 比较任意类型属性升序 /// </summary> /// <param name="xValue"> X值 </param> /// <param name="yValue"> Y值 </param> /// <returns></returns> /// 2007-2-16 23:41 KOSTECH-ACER private int CompareAscending( object xValue, object yValue) { int result; // 如果值实现了IComparer接口 if (xValue is IComparable) { result = ((IComparable)xValue).CompareTo(yValue); } // 如果值没有实现IComparer接口,但是它们是相等的 else if (xValue.Equals(yValue)) { result = 0 ; } // 值没有实现IComparer接口且它们是不相等的, 按照字符串进行比较 else result = xValue.ToString().CompareTo(yValue.ToString()); return result; } /// <summary> /// 比较任意类型属性降序 /// </summary> /// <param name="xValue"> X值 </param> /// <param name="yValue"> Y值 </param> /// <returns></returns> /// 2007-2-16 23:42 KOSTECH-ACER private int CompareDescending( object xValue, object yValue) { return CompareAscending(xValue, yValue) * - 1 ; } /// <summary> /// 获取属性值 /// </summary> /// <param name="value"> 对象 </param> /// <param name="property"> 属性 </param> /// <returns></returns> /// 2007-2-16 23:42 KOSTECH-ACER private object GetPropertyValue(T value, string property) { // 获取属性 PropertyInfo propertyInfo = value.GetType().GetProperty(property); // 返回值 return propertyInfo.GetValue(value, null ); }}
TypeDescriptor类的GetProperties方法将返回一个组件或是一个类型的所有属性集合,PropertyDescriptorCollection的Find方法将返回指定属性名称的PropertyDescriptor对象,其中用bool型来表示是否忽略属性名称的大小写。PropertyDescriptor是指定的属性, 如果指定的属性不存在的话将返回NULL。
现在我们可以比较任意的属性值了。这里我们使用Rockford Lhotka在MSDN中写的PropertyComaparer<T> 类。
PropertyComparer建立的比较逻辑是基于Rockford Lhotka的文章。 (注意:关于比较的细节超出了本文的范围, 如果需要更细致的了解,我建议你去仔细阅读 Rocky's的文章)。
下面是在ASP.NET页面上实现的排序功能代码。
Default.aspx.cs
public partial class _Default : System.Web.UI.Page { protected void Page_Load( object sender, EventArgs e) { if ( ! IsPostBack) { SortableList < User > list = BuildList(); Push( list ); UsersGridView.DataSource = list; UsersGridView.DataBind(); } } /// <summary> /// 处理分页事件 /// </summary> /// <param name="sender"> The source of the event. </param> /// <param name="e"> The <see cref="System.Web.UI.WebControls.GridViewPageEventArgs"/> instance containing the event data. </param> /// 2007-2-16 23:31 KOSTECH-ACER protected void UsersGridView_PageIndexChanging( object sender, GridViewPageEventArgs e) { SortableList < User > list = Cache[ " Users " ] as SortableList < User > ; UsersGridView.PageIndex = e.NewPageIndex; UsersGridView.DataSource = list; UsersGridView.DataBind(); } /// <summary> /// 处理排序事件 /// </summary> /// <param name="sender"> The source of the event. </param> /// <param name="e"> The <see cref="System.Web.UI.WebControls.GridViewSortEventArgs"/> instance containing the event data. </param> /// 2007-2-16 23:29 KOSTECH-ACER protected void UsersGridView_Sorting( object sender, GridViewSortEventArgs e) { SortableList < User > list = Cache[ " Users " ] as SortableList < User > ; list.Sort(e.SortExpression, (e.SortDirection == SortDirection.Ascending)); Push( list ); UsersGridView.DataSource = list; UsersGridView.DataBind(); } /// <summary> /// 对象集合放入缓存 /// </summary> /// <param name="list"> The list. </param> /// 2007-2-16 23:33 KOSTECH-ACER private void Push ( SortableList < User > list ) { // 排序后集合放入缓存 Cache.Insert( " Users " , list, null , System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null ); } /// <summary> /// 创建User对象集合 /// </summary> /// <returns></returns> /// 2007-2-16 23:29 KOSTECH-ACER public SortableList < User > BuildList() { SortableList < User > list = new SortableList < User > (); list.Add( new User( 1 , " 张三 " , DateTime.Parse( " 24/May/2006 " ), UserTypeEnum.TeamLead, false )); list.Add( new User( 2 , " 李四 " , DateTime.Parse( " 24/Jun/2006 " ), UserTypeEnum.SoftwareEngineer, true )); list.Add( new User( 3 , " 王五 " , DateTime.Parse( " 12/Apr/2006 " ), UserTypeEnum.ProjectLead, true )); list.Add( new User( 4 , " 赵六 " , DateTime.Parse( " 12/Mar/2006 " ), UserTypeEnum.TeamLead, false )); list.Add( new User( 5 , " 刘德华 " , DateTime.Parse( " 31/May/2006 " ), UserTypeEnum.SeniorSoftwareEngineer, true )); list.Add( new User( 6 , " 张学友 " , DateTime.Parse( " 30/Sep/2006 " ), UserTypeEnum.Manager, true )); list.Add( new User( 7 , " 黎明 " , DateTime.Parse( " 1/Jul/2006 " ), UserTypeEnum.ProjectLead, true )); list.Add( new User( 8 , " 郭富城 " , DateTime.Parse( " 22/Aug/2006 " ), UserTypeEnum.SoftwareEngineer, true )); list.Add( new User( 9 , " 姚明 " , DateTime.Parse( " 17/Jan/2006 " ), UserTypeEnum.SoftwareEngineer, true )); return list; }}
原文链接:http://www.codeproject.com/csharp/ASPNet_Sorting.asp
运行环境Windows Server 2003+IE7+VS 2005
我已经整理了全部代码有需要的请留下email地址,方便我发送。