自定义比较器datagridview排序 [英] Custom comparer datagridview sort

查看:75
本文介绍了自定义比较器datagridview排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个以绑定源作为数据源的datagridview,而绑定源有一个数据表作为数据源.有些列是字符串,但我希望它们以特定的方式排序.

网格将它们排序为1,10,10,0 44a,6c.

但是我希望它们进行排序:1、6c,10、44a,100,就好像我只会从值中提取数字并对它们进行相应的排序一样.

是否可以对某些列进行排序时添加自定义比较器?如果不更改网格,绑定源,数据表架构,则可以进行其他任何修改.

解决方案

是否可以添加自定义比较器是!

当DGV绑定到 DataSource 时,您必须对源而不是DGV本身进行操作(排序).这排除了一些选项,例如使用 SortCompare 事件.下面的方法使用 DataView .

首先,我从这个答案开始

在顶部不排序,然后在下面按升序和降序排序

保留用户对列的更改,例如顺序和宽度.在没有 BindingSource 的情况下,也可以正常工作.只需将您的 DataView 用作 DataSource :

 <代码> dgvBS.DataSource = dgvDV 

使用 DataTable 作为 DataSource 可能会出现问题,并且较重",因为您必须复制表. DataView 使这个过程非常简单.


我还找到了用于Java的字母数字排序器.出于好奇,我将其转换为.NET进行比较.它运作良好,但不完全相同.在相同的起点下,通常1000序列中的25-35会以不同的方式出现:

 <代码> P调用:03、03、03s,3AAlphanum:03,3A ... 3RB,03s,3X 

这不是完全错误, 03s 位于正确的区域,之后同步结果备份了一段时间.它也以不同的方式处理前导破折号,并且比PInvoke慢一些.它确实可以处理任何值.

I have a datagridview with a bindingsource as datasource, and the bindingsource has a datatable as a datasource. Some columns are strings but I want them to be sorted in a specific way.

The grid sorts them as 1, 10, 10,0 44a, 6c.

But I want them to sorted: 1, 6c, 10, 44a, 100 as if i would take the numbers only from the values and sort them accordingly.

Is there a way I cand add a custom comparer when certain columns are being sorted? Any other soulutions would be ok if the grid, bindingsource, datatable schema is not changed.

解决方案

Is there a way I can add a custom comparer Yes!

When the DGV is bound to a DataSource, you have to act on (sort) the source not the DGV itself. This rules out some options like using the SortCompare event. The method below uses a DataView.

First, I started with the Natural String Sorter from this answer and made a few changes:

Imports System.Runtime.InteropServices

Partial Class NativeMethods
    <DllImport("shlwapi.dll", CharSet:=CharSet.Unicode)>
    Private Shared Function StrCmpLogicalW(s1 As String, s2 As String) As Int32
    End Function

    Friend Shared Function NaturalStringCompare(str1 As String, str2 As String) As Int32
        Return StrCmpLogicalW(str1, str2)
    End Function
End Class

Public Class NaturalStringComparer
    Implements IComparer(Of String)
    Private mySortFlipper As Int32 = 1

    Public Sub New()

    End Sub

    Public Sub New(sort As SortOrder)
        mySortFlipper = If(sort = SortOrder.Ascending, 1, -1)
    End Sub

   Public Function Compare(x As String, y As String) As Integer _
            Implements IComparer(Of String).Compare

        ' convert DBNull to empty string
        Dim x1 = If(String.IsNullOrEmpty(x), String.Empty, x)
        Dim y1 = If(String.IsNullOrEmpty(y), String.Empty, y)

        Return (mySortFlipper * NativeMethods.NaturalStringCompare(x1, y1))
    End Function
End Class

That Comparer can be used in a variety ways as evidenced from the linked question. It is typically used for things like a List of file names. Since the sort target here is DB data, a couple of lines were added to Compare for when it encounters null data. (The OP, mvaculisteanu, discovered it was slow when null values were passed).

This would also work, handled as a separate step other edge cases can be easily added:

Return (mySortFlipper * NativeMethods.NaturalStringCompare(If(x, ""), If(y,""))

I don't know how you are using the BindingSource, so I had to make some guesses on the configuration. My test DataTable has 3 columns, #1 is set to programmatic to implement the comparer. Form level object variables used (so you understand my configuration - hopefully it is similar):

Private dgvDV As DataView
Private dgvBS As BindingSource

' config:
dgvDV = New DataView(dgvDT)

dgvBS = New BindingSource()
dgvBS.DataMember = "myDT"
dgvBS.DataSource = dgvDT

dgv2.Columns(0).SortMode = DataGridViewColumnSortMode.Automatic
dgv2.Columns(1).SortMode = DataGridViewColumnSortMode.Programmatic
dgv2.Columns(2).SortMode = DataGridViewColumnSortMode.Automatic

The magic, such as it is, happens in the ColumnHeaderMouseClick event:

Private SortO As SortOrder = SortOrder.Ascending
Private Sub dgv2_ColumnHeaderMouseClick(sender As Object...etc

    ' the special column we want to sort:
    If e.ColumnIndex = 1 Then
        ' create new DV
        dgvDV = DGVNaturalColumnSort("Text", SortO)

        ' reset the BindingSource:
        dgvBS.DataSource = dgvDV
        ' update glyph
        dgv2.Columns(1).HeaderCell.SortGlyphDirection = SortO

        ' flip order for next time:
        SortO = If(SortO = SortOrder.Ascending, SortOrder.Descending, SortOrder.Ascending)
    End If
End Sub

Then, a helper function which implements the sort and create a new DataView:

Private Function DGVNaturalColumnSort(colName As String, sortt As SortOrder) As DataView
    Dim NComparer As New NaturalStringComparer(sortt)
    Dim tempDT = dgvDV.Table.AsEnumerable().
        OrderBy(Function(s) s.Field(Of String)(colName), NComparer).
        CopyToDataTable

    Return New DataView(tempDT)
End Function

Because you pass the name of the column, it should be easy to use when there are multiple such columns. Results:

Sort None on top, then Sort Asc and Desc below

User changes to the column(s) such as the order and width are preserved. This also works just fine without a BindingSource. Just use your DataView as the DataSource:

  dgvBS.DataSource = dgvDV

Using a DataTable as the DataSource could be problematic and "heavier" since you would have to copy the table. A DataView makes this quite simple.


I also found this AlphaNumeric sorter for java. Being curious, I converted it to .NET to compare them. It works well but not quite the same. Given the same starting point, 25-35 of 1000 sequences will typically come out differently:

 PInvoke:  03, 03, 03s, 3A
Alphanum:  03, 3A...3RB, 03s, 3X 

Its not totally wrong, the 03s is in the right area and the results synch back up for awhile after that. It also treats leading dashes differently and is a bit slower than PInvoke. It does handle Nothing values fine though.

这篇关于自定义比较器datagridview排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆