[英]Custom comparer datagridview sort
我有一個以綁定源作為數據源的datagridview,而綁定源有一個數據表作為數據源。 有些列是字符串,但我希望它們以特定的方式排序。
網格將它們排序為1、10、10.0 0、44a,6c。
但我希望它們進行排序:1、6c,10、44a,100,就好像我只會從值中提取數字並對它們進行相應的排序一樣。
在對某些列進行排序時,是否可以添加自定義比較器? 如果不更改網格,綁定源,數據表架構,則可以進行任何其他修改。
Is there a way I can add a custom comparer
?
當DGV綁定到DataSource
,您必須對源而不是DGV本身進行操作(排序)。 這排除了一些選項,例如使用SortCompare
事件。 下面的方法使用DataView
。
首先,我從這個答案開始使用自然字符串排序器,並進行了一些更改:
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
從鏈接的問題可以證明,比較器可以以多種方式使用。 它通常用於諸如文件名List
之類的事情。 由於此處的排序目標是DB數據,因此當遇到空數據時,在Compare
中添加了幾行。 (OP,mvaculisteanu,發現傳遞空值時速度很慢)。
這也可以工作,作為一個單獨的步驟處理,可以輕松添加其他邊緣情況:
Return (mySortFlipper * NativeMethods.NaturalStringCompare(If(x, ""), If(y,""))
我不知道您如何使用BindingSource
,因此我不得不對配置進行一些猜測。 我的測試DataTable
有3列,將#1設置為以編程方式實現比較器。 使用的表單級對象變量(以便您了解我的配置-希望它是相似的):
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
諸如此類的魔術發生在ColumnHeaderMouseClick
事件中:
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
然后,一個幫助函數實現排序並創建一個新的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
因為您傳遞了列名,所以當有多個這樣的列時,它應該易於使用。 結果:
在頂部排序無,然后在下面排序升序和降序
保留用戶對列的更改,例如順序和寬度。 如果沒有 BindingSource
這也可以正常工作 。 只需將您的DataView
用作DataSource
:
dgvBS.DataSource = dgvDV
因為必須復制表,所以將DataTable
用作DataSource
可能會出現問題且“重”。 DataView
使這一過程變得非常簡單。
我還找到了用於Java的AlphaNumeric排序器 。 出於好奇,我將其轉換為.NET進行比較。 它運作良好,但不完全相同。 給定相同的起點,通常1000序列中的25-35會以不同的方式出現:
PInvoke: 03, 03, 03s, 3A
Alphanum: 03, 3A...3RB, 03s, 3X
並非完全錯誤, 03s
位置正確,之后同步結果會備份一會兒。 它也以不同的方式處理前導破折號,並且比PInvoke慢一些。 它確實可以處理任何值。
是的,但是我認為您將不得不再寫一列,因為計算機先讀取第一個數字,然后讀取第二個數字。 這就是為什么當您有10個字符時,它會在6之前讀取它。(1小於6)我過去做過的一種處理方法是使用前導零。 因此,在1個隱藏列中,您將擁有前導零,例如:000006、000100,那么對於用戶而言,可見列將具有原始數據,而sort列將是您的隱藏列。
起初看起來很簡單,然后又很辛苦又乏味,最后畢竟變得非常簡單。LINQ的萬歲!
首先編寫一個將string
列轉換為數值的函數:
int noLetters(string text)
{
char c = text[text.Length - 1];
if (c < '0' || c > '9') text = text.Substring(0, text.Length - 1);
int n = 0;
Int32.TryParse(text, out n);
return n;
}
注意:該函數僅測試最后一個字母,並截除非數字的任何內容。 請檢查此規則,並添加異常處理 !
現在,通過EnumerableRowCollection
將具有新功能的數據排序到DataView
:
EnumerableRowCollection<DataRow> sortedQuery =
from row in dt.AsEnumerable()
orderby noLetters(row.Field<string>("yourColumnName"))
select row ;
DataView sortedView = sortedQuery.AsDataView();
yourBindingSource.DataSource = sortedView ;
要通過單擊列標題來觸發它,只需編寫ColumnHeaderMouseClick
事件即可。
當然,您可以添加邏輯以在orderby
和orderbydescending
之間切換
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.