对不含数据源的DataGridView实现自定义排序

我们知道如果对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 Sub

End 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 If

End 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 If

End 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 Class

End Namespace

你可能感兴趣的:(datagridview)