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
  • 相关阅读:
    web selenium 小笔记
    C#全局变量的使用
    一些传统阻止了新事物的发展
    javascript 类
    无聊的文档
    asp.net调用c++写的dll或ocx 方法
    在ASP.NET中的DatePicker控件
    保护眼睛的背景色
    web引用程序调用 windows的服务
    asp.net常用数据类型
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2438612.html
Copyright © 2011-2022 走看看