博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对不含数据源的DataGridView实现自定义排序
阅读量:6188 次
发布时间:2019-06-21

本文共 16878 字,大约阅读时间需要 56 分钟。

我们知道如果对DataGridView直接设置数据源进行绑定,并且启用“排序”的话,直接点击列名就可以实现绑定。现在的问题在于如果这个DataGridView没有设定数据源(数据是动态添加的),如何对这样的数据进行排序呢?

[C#]

public partial class Form1 : Form    {        DataGridView dv = new DataGridView();        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            dv.Parent = this;            dv.Dock = DockStyle.Fill;            dv.Columns.Add("DSLL", "DSLL");            //Generate random numbers as String            Random r = new Random(Guid.NewGuid().GetHashCode());            for (int i = 1; i < 101; i++)            {                dv.Rows.Add(r.Next(1, 101).ToString());            }                    }……………………

[VB.NET]

Public Partial Class Form1    Inherits Form    Private dv As New DataGridView()    Public Sub New()        InitializeComponent()    End Sub    Private Sub Form1_Load(sender As Object, e As EventArgs)        dv.Parent = Me        dv.Dock = DockStyle.Fill        dv.Columns.Add("DSLL", "DSLL")        'Generate random numbers as String        Dim r As New Random(Guid.NewGuid().GetHashCode())        For i As Integer = 1 To 100            dv.Rows.Add(r.[Next](1, 101).ToString())        Next……………………    End SubEnd Class

现在问题是:如果你这样直接点击DSLL列的话“数字列”根本不是按照原先的样子进行排序——究竟原因,是因为现有列(代码中)存储的是String类型,如果点击排序的话默认将直接按照String进行大小比较,而不是真实的数值型进行比较;倘若你使用反射工具就可以看到其内部工作原理,这里截取关键部分:

1)当点击某一列的时候,实际上程序内部调用公开的方法Sort——该方法有两个重载版本(一个是实现IComparer接口的自定义排序对象,另外一个是指定某列按照什么顺序进行排序的,稍后对他们进行深入讲解……).

Sort方法调用必须满足以下两个条件之一:

i)整个DataGridView不是VirtualMode模式。

ii)排序的该列已经绑定到数据源的某个对应的列中。

具体可以透过以下源码证明:

public virtual void Sort(DataGridViewColumn dataGridViewColumn, ListSortDirection direction){    if (dataGridViewColumn != null)    {        if (direction == ListSortDirection.Ascending || direction == ListSortDirection.Descending)        {            if (dataGridViewColumn.DataGridView == this)            {                if (!this.VirtualMode || dataGridViewColumn.IsDataBound)                {                    this.SortInternal(null, dataGridViewColumn, direction);                    return;                }                else                {                    throw new InvalidOperationException(SR.GetString("DataGridView_OperationDisabledInVirtualMode"));                }            }            else            {                throw new ArgumentException(SR.GetString("DataGridView_ColumnDoesNotBelongToDataGridView"));            }        }        else        {            throw new InvalidEnumArgumentException("direction", direction, typeof(ListSortDirection));        }    }    else    {        throw new ArgumentNullException("dataGridViewColumn");    }}

[VB.NET]

Public Overridable Sub Sort(ByVal dataGridViewColumn As DataGridViewColumn, ByVal direction As ListSortDirection)    If (dataGridViewColumn <> Nothing) Then        If (direction = ListSortDirection.Ascending OrElse direction = ListSortDirection.Descending) Then            If (dataGridViewColumn.DataGridView = Me) Then                If (Not Me.VirtualMode OrElse dataGridViewColumn.IsDataBound) Then                    Me.SortInternal(Nothing, dataGridViewColumn, direction)                    Return                Else                    Throw New InvalidOperationException(SR.GetString("DataGridView_OperationDisabledInVirtualMode"))                End If            Else                Throw New ArgumentException(SR.GetString("DataGridView_ColumnDoesNotBelongToDataGridView"))            End If        Else            Throw New InvalidEnumArgumentException("direction", direction, GetType(ListSortDirection))        End If    Else        Throw New ArgumentNullException("dataGridViewColumn")    End IfEnd Sub

看得出内部调用了一个SortInternal方法,继续跟踪(此处代码极多,故显示主要部分):

[C#]

private void SortInternal(IComparer comparer, DataGridViewColumn dataGridViewColumn, ListSortDirection direction){  ……………………            if (comparer != null)            {                this.sortedColumn = null;                this.sortOrder = SortOrder.None;            }            else            {                this.sortedColumn = dataGridViewColumn;                DataGridView dataGridView = this;                if (direction == ListSortDirection.Ascending)                {                    num = 1;                }                else                {                    num = 2;                }                dataGridView.sortOrder = (SortOrder)num;                if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && dataGridViewColumn.HasHeaderCell)                {                    dataGridViewColumn.HeaderCell.SortGlyphDirection = this.sortOrder;                }            }            if (this.DataSource != null)            {                this.SortDataBoundDataGridView_PerformCheck(dataGridViewColumn);                this.dataConnection.Sort(dataGridViewColumn, direction);            }            else            {                this.UpdateRowsDisplayedState(false);                this.Rows.Sort(comparer, direction == ListSortDirection.Ascending);            } ………………}

[VB.NET]

Private Sub SortInternal(ByVal comparer As IComparer, ByVal dataGridViewColumn As DataGridViewColumn, ByVal direction As ListSortDirection)    ………………            If (comparer <> Nothing) Then                Me.sortedColumn = Nothing                Me.sortOrder = SortOrder.None            Else                Me.sortedColumn = dataGridViewColumn                Dim dataGridView As DataGridView = Me                If (direction = ListSortDirection.Ascending) Then                    num = 1                Else                    num = 2                End If                dataGridView.sortOrder = DirectCast(num, SortOrder)                If (dataGridViewColumn.SortMode = DataGridViewColumnSortMode.Automatic AndAlso dataGridViewColumn.HasHeaderCell) Then                    dataGridViewColumn.HeaderCell.SortGlyphDirection = Me.sortOrder                End If            End If            If (Me.DataSource <> Nothing) Then                Me.SortDataBoundDataGridView_PerformCheck(dataGridViewColumn)                Me.dataConnection.Sort(dataGridViewColumn, direction)            Else                Me.UpdateRowsDisplayedState(False)                Me.Rows.Sort(comparer, direction = ListSortDirection.Ascending)            End If           ……………………End Sub

首先内部方法先判断你是否已经传入了一个实现了IComparer接口排序类,如果实现纯粹根据IComparer指定的列排序,没有必要指定排序列以及排序顺序(因为原则上实现IComparer只实现了针对一个列的一个方向的排序——要不升序,要不降序);此外,如果没有实现该接口,那么就做Else的部分——先判断该列是否为自动排序,并且有没有抬头单元格:如果都有,那么设置该列的排序方向图标(列抬头单元格旁边的小箭头)是“升”还是“降”;因此,像本示例不是“自动排序”的话,排序方向默认是不会显示的,需要你手动设置SortGlyhDirection属性。然后接着判断是否绑定了数据源:如果绑定了,则直接根据数据源进行排序;否则人工手动排序。

为了继续研究下去,看Rows.Sort方法:

[C#]

internal void Sort(IComparer customComparer, bool ascending){    if (this.items.Count > 0)    {        RowComparer rowComparer = new RowComparer(this, customComparer, ascending);        this.items.CustomSort(rowComparer);    }}

[VB.NET]

Friend Sub Sort(ByVal customComparer As IComparer, ByVal ascending As Boolean)    If (Me.items.Count > 0) Then        Dim rowComparer As RowComparer = New RowComparer(Me, customComparer, ascending)        Me.items.CustomSort(rowComparer)    End IfEnd Sub

Rows的Sort方法先调用了一个RowComparer类进行排序,继续跟踪看其关键部分:

[C#]

private class RowComparer{    ………………    internal int CompareObjects(object value1, object value2, int rowIndex1, int rowIndex2)    {        if (value1 as ComparedObjectMax == null)        {            if (value2 as ComparedObjectMax == null)            {                int num = 0;                if (this.customComparer != null)                {                    num = this.customComparer.Compare(value1, value2);                }                else                {                    if (!this.dataGridView.OnSortCompare(this.dataGridViewSortedColumn, value1, value2, rowIndex1, rowIndex2, out num))                    {                        if (value1 as IComparable != null || value2 as IComparable != null)                        {                            num = Comparer.Default.Compare(value1, value2);                        }                        else                        {                            if (value1 != null)                            {                                if (value2 != null)                                {                                    num = Comparer.Default.Compare(value1.ToString(), value2.ToString());                                }                                else                                {                                    num = -1;                                }                            }                            else                            {                                if (value2 != null)                                {                                    num = 1;                                }                                else                                {                                    num = 0;                                }                            }                        }                        if (num == 0)                        {                            if (!this.@ascending)                            {                                num = rowIndex2 - rowIndex1;                            }                            else                            {                                num = rowIndex1 - rowIndex2;                            }                        }                    }                }                if (!this.@ascending)                {                    return -num;                }                else                {                    return num;                }            }            else            {                return -1;            }        }        else        {            return 1;        }    }………………}

[VB.NET]

Private Class RowComparer   ……………………    Friend Function CompareObjects(ByVal value1 As Object, ByVal value2 As Object, ByVal rowIndex1 As Integer, ByVal rowIndex2 As Integer) As Integer         If (TryCast(value1, ComparedObjectMax) = Nothing) Then            If (TryCast(value2, ComparedObjectMax) = Nothing) Then                Dim num As Integer = 0                If (Me.customComparer <> Nothing) Then                    num = Me.customComparer.Compare(value1, value2)                Else                    If (Not Me.dataGridView.OnSortCompare(Me.dataGridViewSortedColumn, value1, value2, rowIndex1, rowIndex2, num)) Then                        If (TryCast(value1, IComparable) <> Nothing OrElse TryCast(value2, IComparable) <> Nothing) Then                            num = Comparer.Default.Compare(value1, value2)                        Else                            If (value1 <> Nothing) Then                                If (value2 <> Nothing) Then                                    num = Comparer.Default.Compare(value1.ToString(), value2.ToString())                                Else                                    num = -1                                End If                            Else                                If (value2 <> Nothing) Then                                    num = 1                                Else                                    num = 0                                End If                            End If                        End If                        If (num = 0) Then                            If (Not Me.ascending) Then                                num = rowIndex2 - rowIndex1                            Else                                num = rowIndex1 - rowIndex2                            End If                        End If                    End If                End If                If (Not Me.ascending) Then                    Return -num                Else                    Return num                End If            Else                Return -1            End If        Else            Return 1        End If    End Function………………End Class

这里就明确告诉了你——如果你已经实现了IComparer接口则直接使用此进行排序;否则先调用内部的OnSortCompare函数(此函数引发一个SortCompare事件)判断该事件是否被人为“Handle”了,如果是,那么直接按照事件中的代码进行排序处理——因为value1和value2都是字符串型,则排序按照字符串进行排列,自然不是我们预期的效果。

【解决方案】

既然绕了一大圈为了清楚了解DataGridView排序的内幕干了什么,我们自然顺理成章可以得出这个结论:

0)预备:VirtualMode=false,指定DSLL为Programmic(自定义排序)。

1)先引发Sort的第二个重载函数,指定要排序的列和排序顺序。

2)然后HandleSortCompare事件,进一步自己处理如何排序。

至于“引发”Sort函数,我们可以考虑在ColumnMouseHeaderClick事件中(此事件是点击“列”时候引发)处理——首先判断那个列是不是要排序的列,继而调用不同的升序、降序方法进行排列即可。

[C#]

namespace WinFormCSharp{    public partial class Form1 : Form    {        DataGridView dv = new DataGridView();        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            dv.Parent = this;            dv.AllowUserToAddRows = false;            dv.Dock = DockStyle.Fill;            dv.Columns.Add("DSLL", "DSLL");               //随机生成测试数据            Random r = new Random(Guid.NewGuid().GetHashCode());            for (int i = 1; i < 101; i++)            {                dv.Rows.Add(r.Next(1, 101).ToString());            }            //设置该列为手动排序列            dv.Columns[0].SortMode = DataGridViewColumnSortMode.Programmatic;            //设定点击列的事件以便处理排序            dv.ColumnHeaderMouseClick += dv_ColumnHeaderMouseClick;            //默认先按照升序排列            dv.Sort(dv.Columns[0],ListSortDirection.Ascending);            dv.SortedColumn.HeaderCell.SortGlyphDirection = SortOrder.Ascending;            //处理自定义排序的事件            dv.SortCompare += dv_SortCompare;            dv.VirtualMode = false;                    }        void dv_SortCompare(object sender, DataGridViewSortCompareEventArgs e)        {            if (e.Column.Name.Equals("DSLL"))            {                int value1 = Convert.ToInt32(e.CellValue1);                int value2 = Convert.ToInt32(e.CellValue2);                e.SortResult = value1 > value2 ? 1 : (value1 == value2 ? 0 : -1);            }            e.Handled = true;        }        void dv_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)        {           //左键才处理            if (e.Button == MouseButtons.Left && e.ColumnIndex==0)            {                if (dv.SortOrder == SortOrder.Ascending)                {                    dv.Sort(dv.Columns[0], ListSortDirection.Descending);                    dv.SortedColumn.HeaderCell.SortGlyphDirection = SortOrder.Descending;                }                else                {                    dv.Sort(dv.Columns[0], ListSortDirection.Ascending);                    dv.SortedColumn.HeaderCell.SortGlyphDirection = SortOrder.Ascending;                }            }        }    }}

[VB.NET]

Namespace WinFormCSharp    Public Partial Class Form1        Inherits Form        Private dv As New DataGridView()        Public Sub New()            InitializeComponent()        End Sub        Private Sub Form1_Load(sender As Object, e As EventArgs)            dv.Parent = Me            dv.AllowUserToAddRows = False            dv.Dock = DockStyle.Fill            dv.Columns.Add("DSLL", "DSLL")            '随机生成测试数据            Dim r As New Random(Guid.NewGuid().GetHashCode())            For i As Integer = 1 To 100                dv.Rows.Add(r.[Next](1, 101).ToString())            Next            '设置该列为手动排序列            dv.Columns(0).SortMode = DataGridViewColumnSortMode.Programmatic            '设定点击列的事件以便处理排序            AddHandler dv.ColumnHeaderMouseClick, AddressOf dv_ColumnHeaderMouseClick            '默认先按照升序排列            dv.Sort(dv.Columns(0), ListSortDirection.Ascending)            dv.SortedColumn.HeaderCell.SortGlyphDirection = SortOrder.Ascending            '处理自定义排序的事件            AddHandler dv.SortCompare, AddressOf dv_SortCompare            dv.VirtualMode = False        End Sub        Private Sub dv_SortCompare(sender As Object, e As DataGridViewSortCompareEventArgs)            If e.Column.Name.Equals("DSLL") Then                Dim value1 As Integer = Convert.ToInt32(e.CellValue1)                Dim value2 As Integer = Convert.ToInt32(e.CellValue2)                e.SortResult = IIf(value1 > value2, 1, (IIf(value1 = value2, 0, -1)))            End If            e.Handled = True        End Sub        Private Sub dv_ColumnHeaderMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs)            '左键才处理            If e.Button = MouseButtons.Left AndAlso e.ColumnIndex = 0 Then                If dv.SortOrder = SortOrder.Ascending Then                    dv.Sort(dv.Columns(0), ListSortDirection.Descending)                    dv.SortedColumn.HeaderCell.SortGlyphDirection = SortOrder.Descending                Else                    dv.Sort(dv.Columns(0), ListSortDirection.Ascending)                    dv.SortedColumn.HeaderCell.SortGlyphDirection = SortOrder.Ascending                End If            End If        End Sub    End ClassEnd Namespace

转载于:https://www.cnblogs.com/ServiceboyNew/archive/2012/04/10/2438612.html

你可能感兴趣的文章
97 个 Linux 常用命令大全
查看>>
Apple移动设备处理器指令集 armv6、armv7、armv7s及arm64
查看>>
apache日志管理
查看>>
Ubuntu系统lamp环境下安装wordpress、zencert和mangento程序网站
查看>>
SecureCRT中文显示乱码
查看>>
获取当前粘贴板数据
查看>>
我的友情链接
查看>>
nginx优化
查看>>
Android中onActivityResult/startActivityForResult用法
查看>>
Android四大组件每个组件的作用?它们都可以开启多进程吗?
查看>>
Linux下tomcat的catalina.out文件过大,以及目录更改解决办法
查看>>
CentOS 安装 PPTP ××× 客户端安装脚本
查看>>
关于域名的那些“彩蛋”
查看>>
iOS SDK:预览和打开文档
查看>>
我的友情链接
查看>>
前端资源(19)
查看>>
文本处理三剑客之gawk
查看>>
思科三层+TPAC200+TP AP实现每个SSID独立网段
查看>>
4、elasticsearch安装head插件
查看>>
vs 2017 无法安装任何 nuget package,提示“库没有注册。。。”
查看>>