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