这是一个非常常见的功能,要求也很简单,在
Column Header
上显示一个小三角表示表示现在是在哪个
Header
上的正序还是倒序就可以了。微软的
MSDN
也已经
提供了实现方式
。微软的方法中,是通过
ColumnHeader Template
实现的,一共要维护至少两个
Header Template
,一个显示正三角,一个显示倒三角。在用户点击
Header
的时候同时切换使用的
Template
。如果你的
ListView
只提供
Sort
功能,这个方法就可以了。但是如果你的
ListView
还在在
Header
中提供
Filter
功能呢?如果还需要用户可以配置是否开启
Sort
和
Filter
功能呢?那么你就需要
6
个
Template
来处理
Sort
和
Filter
的组合。如果在
Header
本来就有好几种(文字
Header
、画片
Header
等)或是要放入更多的功能呢?显然微软的这个方式只能用于技术演示(当然
MSDN
本来就是这个目的),面对实际项目时就会力不从心。
Google
搜索
WPF ListView Sort
,可以找到很多不同的实现方式。
1.
SwitchOnTheCode
:使用
Adorner Layer
,重写
Adorner
的
OnRender
方法,
画出
一个三角形。画个三角可以,要画个有发光、渐变、动画效果的三角,代码会变得很难维护。而且不能用
Blend
去编辑这个样式。不过思路是很好的,因为它不会占用控件的现有的任何属性,就不会有微软的方式中功能组合的问题。
2.
Jeol Rumerman’s Blog
:继承
GridViewColumn
,扩展出
Sort
功能。还是用
Header Template
,更糟糕的是,为加一个功能而使用继承本身就不是一个很好的设计。同理要加个
Filter
功能,是不是还要继承出一个
FilteredGridViewColumn
和
FilteredAndSortedGridViewColumn
呢?不仅要处理
Template
的组合,还会产生类膨胀,实在是不可取。
3.
CodeProject WPFListViewSorter
:与微软的方式一样,只是通过自定义
Sorter
函数解决了微软的方式中,把
Column Header
上的名字,当作
Sort Property
的问题。
4.
Thejoyofcode
:通过
Attached Property
解决了同样的问题,而且没有界面显示的实现。而且还继承出了一个
SortableListView
。缺点就不再解释了。
5.
Marlongrech
:提供了
Disable/Enable Sort
功能。不过也是用
HeaderTemplate
做界面实现。(突然发现
Wordpress
可以访问了)
没有找到一个满足我要求的实现方式,每个解决方案都只是关注于自己要解决的问题的那一个点上。当然在
Blog
里让示例简单一些也没有错。那就让我把他们所解决的问题集成到一个示例中。要求也不多。
1.
不影响现有功能。
2.
不独占现有属性。
3.
使用组合,而不是继承。
第一个方案,使用
Adorner Layer
是个很好的思路,
Adorner Layer
相当于一个画板,我画在这里,别的功能画在那里就是了,所以不会影响现有功能也不算独占现有属性。它的示例中占用了
Tag
来描述使用哪个属性排序。我们用
Attached Property
替换掉就可以了。然后就剩下一个问题了——不要画三角。我们想用
Template
。这样不同的地方的样子可以有不一样的界面效果而又不用修改代码。但是问题是
Adorner
是没有
Template
的。
参考了两篇关于
Adorner Layer
的文章。
1.
Adorners in WPF
2.
Visual Level Programming vs Logical Level Programming
写了一个晚上的代码,终于搞出一个自我感觉良好的实现出来。在
ListView
上添加
Sort
功能,只需要添加一个
Attached Property
就可以。代码如下。
Add Sort to ListView
<
ListView
ext:ListViewBehavior.HeaderSort
="True"
ItemsSource
="
{Binding}
"
>
<
ListView.View
>
<
GridView
>
<
GridViewColumn
Header
="Name"
DisplayMemberBinding
="
{Binding ItemName}
"
ext:ListViewBehavior.SortField
="ItemName"
/>
<
GridViewColumn
Header
="Value"
DisplayMemberBinding
="
{Binding ItemValue}
"
ext:ListViewBehavior.SortField
="ItemValue"
/>
</
GridView
>
</
ListView.View
>
</
ListView
>
在
GridViewColumn
中也使用
Attached Property
指定按哪一列排序。如果不指定,就默认使用
Header
的名称做为排序属性。
使用了
Sheva
的示例中的
UIElementAdorner
(略有改动)把一个自定义控件
ListSortDecorator
放在当前排序列上。代码如下。
UIElementAdorner
using
System.Collections;
using
System.Windows;
using
System.Windows.Documents;
using
System.Windows.Media;
namespace
SortListView.Extention
{
public
class
UIElementAdorner : Adorner
{
private
UIElement child;
///
<summary>
///
///
</summary>
///
<param name="element"></param>
///
<param name="direction"></param>
public
UIElementAdorner(UIElement element, UIElement child)
:
base
(element)
{
this
.child
=
child;
AddLogicalChild(child);
AddVisualChild(child);
}
protected
override
Size ArrangeOverride(Size finalSize)
{
child.Arrange(
new
Rect(finalSize));
return
finalSize;
}
protected
override
Size MeasureOverride(Size constraint)
{
child.Measure(constraint);
return
AdornedElement.RenderSize;
}
protected
override
int
VisualChildrenCount
{
get
{
return
1
; }
}
protected
override
Visual GetVisualChild(
int
index)
{
return
child;
}
protected
override
IEnumerator LogicalChildren
{
get
{
ArrayList list
=
new
ArrayList();
list.Add(child);
return
(IEnumerator)list.GetEnumerator();
}
}
///
<summary>
///
///
</summary>
public
UIElement Child
{
get
{
return
child; }
}
}
}
三角形的样子就可以由ListSortDecorator
来定义了。默认的样子如下。
ListSortDecorator
<
ControlTemplate
TargetType
="
{x:Type ext:ListSortDecorator}
"
>
<
Path
x:Name
="path"
Data
="M0,0L2,0 1,1z"
Fill
="
{TemplateBinding Foreground}
"
Stroke
="
{TemplateBinding Foreground}
"
HorizontalAlignment
="
{TemplateBinding HorizontalContentAlignment}
"
VerticalAlignment
="
{TemplateBinding VerticalContentAlignment}
"
Width
="7"
Height
="4"
Stretch
="Fill"
/>
<
ControlTemplate.Triggers
>
<
Trigger
Property
="SortDirection"
Value
="Descending"
>
<
Setter
TargetName
="path"
Property
="Data"
Value
="M0,1L2,1L1,0Z"
/>
</
Trigger
>
</
ControlTemplate.Triggers
>
</
ControlTemplate
>
这样就可以方便地在
Blend
中绘制三角形的样子并制作动画了。显示效果如图所示。
图
1.
界面效果
整个的示例代码可以从
这里下载
。错误之处,欢迎大家指正。