自定义比较器datagridview排序 [英] Custom comparer datagridview sort
问题描述
我有一个以绑定源作为数据源的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屋!