您的位置:首页 > 其它

浅谈WinForm下ListView的扩展(一):单击列头实现排序

2009-09-15 23:36 316 查看
我们在开发Windows Form 应用程序时,往往需要使用ListView控件——通过设置其View属性为Details——来显示列表形式的详细数据。遗憾的是,.NET自带的这个ListView控件并没有提供一些我们常用的功能,例如:单击列头实现文本升降序排序、滚动条滚动事件等等。

得益于.NET强大的面向对象的支持,我们可以继承ListView并加入我们想要实现的功能,从而实现ListView的扩展。

今天讨论一下在C#中如何实现“单击列头实现排序”的功能。部分参考http://msdn.microsoft.com/zh-cn/library/system.windows.forms.listview.listviewitemsorter.aspx

一、 知识准备

要实现排序功能,首先我们得了解ListView以及.NET本身自带的一些关于排序、比较方面的接口和对象的属性、方法或事件。

ListView.ListViewItemSorter 属性: 获取或设置用于控件的排序比较器。

该属性类型为System.Collections.IComparer,顾名思义,这是一个实现对象比较的接口,其中只定义了一个方法:int Compare(object x, object y);通过实现该方法来比较两个对象并返回比较结果。

二、 实现思路

我们知道,为ListViewItemSorter属性赋值后,则ListView自动调用Sort方法实现排序。

既然ListViewItemSorter的类型为System.Collections.IComparer,那么我们自定义一个类并实现IComparer接口,然后在列单击事件里面将该类的实例赋给ListViewItemSorter属性即可。需要说明的是,传入方法:int Compare(object x, object y)的参数x和y均是ListViewItem类型的对象。

三、 具体实现

1. 定义一个类ListViewEx继承于ListView。

public class ListViewEx : ListView

2. 在该类中定义一个私有类ListViewItemComparer,并且该类实现System.Collections.IComparer接口。

private class ListViewItemComparer : System.Collections.IComparer

3. 因为方法:int Compare(object x, object y)的参数x和y均是ListViewItem类型的对象,所以要实现某列的排序,就必须有该列的index值。我们通过构造函数传参的方式将列的index传入。

private int _column;

public ListViewItemComparer(int column)

{

_column = column;

}

4. 关键步骤1:实现int Compare(object x, object y)方法。

public int Compare(object x, object y)

{

return (string.Compare(((ListViewItem)x).SubItems[_column].Text, ((ListViewItem)y).SubItems[_column].Text));

}

5. 关键步骤2:重写(override)ListView的OnColumnClick方法。

protected override void OnColumnClick(ColumnClickEventArgs e)

{

base.OnColumnClick(e);

this.ListViewItemSorter = new ListViewItemComparer(e.Column);

}

6. 全部代码,这里省去VS自动生成的部分——InitializeComponent()。

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Diagnostics;

using System.Linq;

using System.Text;

using System.Windows.Forms;

namespace ListViewExtand

{

public partial class ListViewEx : ListView

{

public ListViewEx()

{

InitializeComponent();

}

public ListViewEx(IContainer container)

{

container.Add(this);

InitializeComponent();

}

protected override void OnColumnClick(ColumnClickEventArgs e)

{

base.OnColumnClick(e);

this.ListViewItemSorter = new ListViewItemComparer(e.Column);

}

private class ListViewItemComparer : System.Collections.IComparer

{

private int _column;

public ListViewItemComparer(int column)

{

this. _column = column;

}

#region IComparer Members

public int Compare(object x, object y)

{

return (-string.Compare(((ListViewItem)x).SubItems[_column].Text, ((ListViewItem)y).SubItems[_column].Text));

}

#endregion

}

}

}

四、 缺陷及改进

可以发现,这样做出来的ListView扩展控件,在单击列头时始终是按照文本升序进行排序。我们需要是这样的实用功能:某列头单击一次按文本升序排序,再次单击则按降序排序。

那么现在就拿上面的半成品继续改进。相信有了以上的基础,这次改进会比较容易,只需要理清实现思路即可:某列头单击一次按文本升序排序后,应该能够存储当前该列排序的状态(即列索引index值、排序方向),以便在再次单击时,能够改变该列的排序状态从而实现重新排序。

1. 在私有类中增加2个属性int SortColumn和SortOrder Order,同时自定义构造函数public ListViewItemComparer(int column)和public ListViewItemComparer(int column, SortOrder sortOrder)。


public ListViewItemComparer()

{

this.SortColumn = 0;

this.Order = SortOrder.None;

}

public ListViewItemComparer(int column)

: this()

{

this.SortColumn = column;

}

public ListViewItemComparer(int column, SortOrder sortOrder)

: this(column)

{

this.Order = sortOrder;

}

public int SortColumn

{

get;

set;

}

public SortOrder Order

{

get;

set;

}



2. 重新实现int Compare(object x, object y)方法:当获知当前按升序排序时(根据属性Order),返回对象x和y的比较结果;否则若是按降序排序,则返回对象x和y的比较结果的相反数;再则返回0,表示不排序。

public int Compare(object x, object y)

{

int result = string.Compare(((ListViewItem)x).SubItems[this.SortColumn].Text, ((ListViewItem)y).SubItems[this.SortColumn].Text);

if (this.Order == SortOrder.Ascending)

{

return result;

}

else if (this.Order == SortOrder.Descending)

{

return (-result);

}

else

{

return 0;

}

}

3. 在类ListViewEx中重新实现重写(override)ListView的OnColumnClick方法。

protected override void OnColumnClick(ColumnClickEventArgs e)

{

base.OnColumnClick(e);

if (this.ListViewItemSorter == null)

{

this.ListViewItemSorter = new ListViewItemComparer(e.Column, SortOrder.Ascending);

}

else

{

ListViewItemComparer comparer = this.ListViewItemSorter as ListViewItemComparer;

if (comparer.SortColumn == e.Column)

{

if (comparer.Order == SortOrder.Ascending)

{

comparer.Order = SortOrder.Descending;

}

else

{

comparer.Order = SortOrder.Ascending;

}

}

else

{

comparer.SortColumn = e.Column;

comparer.Order = SortOrder.Ascending;

}

//仅仅改变了ListViewItemSorter属性值,这里不会自动调用Sort()方法,需要显式指定执行Sort()方法实现排序。

this.Sort();

}

}

4. 全部代码,这里省去VS自动生成的部分——InitializeComponent()。

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Diagnostics;

using System.Linq;

using System.Text;

using System.Windows.Forms;

namespace ListViewExtand

{

public partial class ListViewEx : ListView

{

public ListViewEx()

{

InitializeComponent();

}

public ListViewEx(IContainer container)

{

container.Add(this);

InitializeComponent();

}

protected override void OnColumnClick(ColumnClickEventArgs e)

{

base.OnColumnClick(e);

if (this.ListViewItemSorter == null)

{

this.ListViewItemSorter = new ListViewItemComparer(e.Column, SortOrder.Ascending);

}

else

{

ListViewItemComparer comparer = this.ListViewItemSorter as ListViewItemComparer;

if (comparer.SortColumn == e.Column)

{

if (comparer.Order == SortOrder.Ascending)

{

comparer.Order = SortOrder.Descending;

}

else

{

comparer.Order = SortOrder.Ascending;

}

}

else

{

comparer.SortColumn = e.Column;

comparer.Order = SortOrder.Ascending;

}

//仅仅改变了ListViewItemSorter属性值,这里不会自动调用Sort()方法,需要显式指定执行Sort()方法实现排序。

this.Sort();

}

}

private class ListViewItemComparer : System.Collections.IComparer

{

public ListViewItemComparer()

{

this.SortColumn = 0;

this.Order = SortOrder.None;

}

public ListViewItemComparer(int column)

: this()

{

this.SortColumn = column;

}

public ListViewItemComparer(int column, SortOrder sortOrder)

: this(column)

{

this.Order = sortOrder;

}

public int SortColumn

{

get;

set;

}

public SortOrder Order

{

get;

set;

}

#region IComparer Members

public int Compare(object x, object y)

{

int result = string.Compare(((ListViewItem)x).SubItems[this.SortColumn].Text, ((ListViewItem)y).SubItems[this.SortColumn].Text);

if (this.Order == SortOrder.Ascending)

{

return result;

}

else if (this.Order == SortOrder.Descending)

{

return (-result);

}

else

{

return 0;

}

}

#endregion

}

}

}

五、 小结

只需按照以上方法,自定义一个类实现IComparer接口,并将其实例赋给ListView的属性即可完美实现“点击列头排序”。

终于写完了,不知读者看过后有何感想,是否还有需要该进的地方?比如说ListViewItemComparer作为内部私有类这种处理方式的优缺点,等等欢迎讨论。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: