相关文章推荐
痴情的帽子  ·  Unity对于VR的支持 - 简书·  1 年前    · 
酒量大的风衣  ·  android - React ...·  1 年前    · 

这是一个非常常见的功能,要求也很简单,在 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. 界面效果

整个的示例代码可以从 这里下载 。错误之处,欢迎大家指正。