现在逻辑多层的设计方式已经深入人心。一般业务层会返回一个对象集合供其它层来使用,这个对象集合有的用数组来装载、有的用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地址,方便我发送。
发表于 @ 2007年02月17日 10:17:00|评论(loading...)|编辑