65.9K
CodeProject 正在变化。 阅读更多。
Home

DataGridView 多列排序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (16投票s)

2007年4月17日

CPOL

4分钟阅读

viewsIcon

300870

downloadIcon

10972

一篇介绍按多列对 DataGridView 进行排序的方法的文章

Screenshot - SortedGrid.gif

引言

开箱即用的 DataGridView 只允许按单列排序。本文介绍了使 DataGridView 能够按多列对数据进行排序的代码。派生的 DataGridView 类可用于支持 IComparable 接口的任何数据类型(包括所有基本的 .NET 类型)。

该项目是使用 Visual C# 2005 Express Edition 构建的。

使用代码

普通代码使用

将排序的 DataGrid 视图添加到您的项目中

  1. 使用上面的链接下载库,并将 _SortedDataGridView.dll_ 解压到方便的位置。
  2. 通过右键单击工具箱并选择“选择项...”然后“浏览...”将您刚刚解压的 _SortedDataGridView.dll_ 添加到您的工具箱中。
  3. 现在,工具箱应该会显示 SortedDataGridView 作为新组件,您可以直接将其添加到您的窗体中,并以与普通 DataGridView 相同的方式进行配置。

SortedDataGridView 还有一个额外的属性:MaxSortColumns。此属性设置为允许用户排序网格的最大列数,或设置为 0 表示无限制。我发现按超过 3 列排序会令人困惑,因此倾向于将列数限制为 3 或 4 列,具体取决于显示的数据。我发现使用高级用户界面时很容易感到困惑,并且会丢失排序顺序的记录。

高级代码使用

如果您正在将现有项目转换为使用此代码,那么您可能会想使用此方法。

将库 _SortedDataGridView.dll_ 添加到您的项目中。

手动编辑窗体的 _Designer.cs_ 或 _Designer.vb_ 文件,将您的 DataGridView 的定义从 `System.Windows.Forms.DataGridView` 更改为 `Mobius.Utility.SortedDataGridView`。以同样的方式更改变量的实例化。

private void InitializeComponent()
{
    this.PersonGrid = new Mobius.Utility.SortedDataGridView();
}

private Mobius.Utility.SortedDataGridView PersonGrid;

就是这样,您现在拥有一个按多列排序的 DataGridView。

用户界面

正常使用

要按 A、B 和 C 列排序,请按反序(C、B、A)单击列标题。

高级用法

在使用网格时,我发现能够反转其中一列的排序顺序而不改变其优先级很有用,因此我添加了代码来实现这一点。按住 Control 键即可访问此功能。

在按 A、B、C 排序后,按住 Control 键单击列标题即可反转其中一列的排序顺序。

这样做的副作用是,您可以通过按顺序单击列(同时按住 Control 键)来按 A、B、C 排序,因为如果一列尚未排序,它将被添加到排序列表中。

如果您已选择限制排序的列数,当您单击一个尚未排序的列,并且您当前正在按最大列数进行排序时,最后一列将被您刚刚单击的列替换。

关注点

DataGridView 接口

该代码重写了 `OnColumnHeaderMouseClick` 函数以启动排序,并使用实现了 IComparer 接口的自定义排序类重写了 `DataGridView.Sort` 函数。

出于两个原因,我将尽可能多的功能放入排序类中:

  1. 它更适合那里。
  2. 它使 DataGridView 类保持整洁。

原因 2 意味着将此处的附加功能添加到另一个现有的派生 DataGridView 类会容易得多。

排序顺序描述

网格公开了一个名为 `SortOrderDescription` 的属性,该属性返回一个已排序列的描述,适合用作列标题的工具提示或在状态栏中使用。演示项目在状态栏中显示了它的用法。

排序

排序使用一个派生的 IComparer 类 `DataGridComparer`,该类依次按每列进行排序。

代码很简单。首先,我将 IComparer 接口的对象参数进行转换,然后在单独的函数中进行比较。

public int Compare(object x, object y)
{
    DataGridViewRow lhs = x as DataGridViewRow;
    DataGridViewRow rhs = y as DataGridViewRow;

    return Compare(lhs.Cells, rhs.Cells);
}

public int Compare(DataGridViewCellCollection lhs, DataGridViewCellCollection rhs)
{
    foreach (SortColDefn colDefn in _sortedColumns)
    {
            int retval = Comparer.Default.Compare(
            lhs[colDefn.colNum].Value,
            rhs[colDefn.colNum].Value);

        if (retval != 0)
            return (colDefn.ascending ? retval : -retval);
    }

    // These two rows are indistinguishable.
    return 0;
}

匿名委托

当用户请求对某一列进行排序时,代码需要确定该列是否已在排序中。如果是,则将其提升为主要排序列(在基本用户界面中,或在使用 Control 键时交换顺序)。

保存排序顺序的结构是 `SortColDefn`。

private struct SortColDefn
{
    internal Int16 colNum;
    internal bool ascending;

    internal SortColDefn(int columnNum, SortOrder sortOrder)
    {
        colNum = Convert.ToInt16(columnNum);
        ascending = (sortOrder != SortOrder.Descending);
    }
}

当前排序的列存储在一个简单的数组中。

    List<SortColDefn> _sortedColumns;

当您需要检查提供的列索引是否在数组中时,您可以简单地循环遍历数组并检查 `SortColDefn.colNum` 是否等于传递的列索引。

但是,使用 `List.FindIndex` 函数和匿名委托更有趣。

    int sortPriority = _sortedColumns.FindIndex(
            delegate(SortColDefn cd) { return cd.colNum == columnIndex; });

返回值 `sortPriority` 将为 `-1`(如果未找到 `columnIndex`),或者等于具有相同 `columnIndex` 的 `SortColDefn` 在 `_sortedColumns` 数组中的**索引**。

历史

版本 0.8 - 2007 年 4 月 11 日 - 首次发布,还有一半的路要走!

© . All rights reserved.