tangjichuan的专栏

自然之美

导航

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

 

我们在开发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)的参数xy均是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)的参数xy均是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:重写(overrideListViewOnColumnClick方法。

 

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 SortColumnSortOrder 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),返回对象xy的比较结果;否则若是按降序排序,则返回对象xy的比较结果的相反数;再则返回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中重新实现重写(overrideListViewOnColumnClick方法。

 

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作为内部私有类这种处理方式的优缺点,等等欢迎讨论。

posted on 2009-09-15 23:36  Adam_Tang  阅读(3012)  评论(3编辑  收藏  举报