簡體   English   中英

自定義比較器datagridview排序

[英]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事件即可。

當然,您可以添加邏輯以在orderbyorderbydescending之間切換

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM