zoukankan      html  css  js  c++  java
  • 对不含数据源的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
  • 相关阅读:
    109 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 03 新增功能及实现 05 问题解析--通过一个方法完成学生和专业的双向关联
    108 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 03 新增功能及实现 04 问题解析--数组未实例化造成的空指针异常
    107 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 03 新增功能及实现 03 编写方法完成学生个数统计功能
    106 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 03 新增功能及实现 02 新增属性完成学生信息存储
    105 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 03 新增功能及实现 01 新增需求及分析
    session与cookie的区别和联系
    session和cookie的区别
    Web服务器主动推送技术
    webSocket的场景应用
    TCP、Http和Socket 优劣比较
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2438612.html
Copyright © 2011-2022 走看看