
现在逻辑多层的设计方式已经深入人心。一般业务层会返回一个对象集合供其它层来使用,这个对象集合有的用数组来装载、有的用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类
{
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.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.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
{
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地址,方便我发送。