簡體   English   中英

如何自然字符串對數據綁定到數據表的 datagridview 進行排序

[英]How can I natural string sort a datagridview that is databound to a datatable

在我的程序中,我有一個使用綁定源綁定到數據表的 datagridview。 我想要完成的是能夠使用自然字符串排序按列對 datagridview 進行排序。

示例列數據:

XAB-1
XAB-2
XAB-11
XAB-3
XAB-1A
XAB-10
XAB-1B

期望的結果:

XAB-1
XAB-1A
XAB-1B
XAB-2
XAB-3
XAB-10
XAB-11

我曾嘗試使用傳入自然字符串 Icomparer 的 datagridview.sort 方法,但是當 datagridview 為數據綁定時,無法使用排序 function。

我也嘗試了這個線程Link的解決方案,但涉及將 bindingsource 源從數據表更改為數據視圖。 這是一個問題,因為數據正在數據表中更新,並且沒有反映在數據視圖中。

有什么方法可以執行自然字符串排序,同時仍然保持綁定源到數據表? 或者有什么方法可以將上面鏈接中的解決方案與數據視圖一起使用,但是讓數據視圖以某種方式將數據與數據表同步?

這是我嘗試使用的 Icomparer:

Imports System.Runtime.InteropServices
Imports System.Text.RegularExpressions

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

Public Class NumStrCmp
    Implements IComparer

    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
        Dim regex As Regex = New Regex("(?<NumPart>\d+)(?<StrPart>\D*)", RegexOptions.Compiled)
        Dim mx = regex.Match(x.ToString)
        Dim my = regex.Match(y.ToString)
        Dim ret = Integer.Parse(mx.Groups("NumPart").Value).CompareTo(Integer.Parse(my.Groups("NumPart").Value))
        If ret <> 0 Then Return ret
        Return mx.Groups("StrPart").Value.CompareTo(my.Groups("StrPart").Value)
    End Function
End Class

API 聲明:

Imports System.Runtime.InteropServices

Public Module NativeMethods

    <DllImport("shlwapi.dll", CharSet:=CharSet.Unicode)>
    Public Function StrCmpLogicalW(x As String, y As String) As Integer
    End Function

End Module

自定義比較器:

Public Class NaturalStringComparer
    Implements IComparer(Of String)

    Public Function Compare(x As String, y As String) As Integer Implements IComparer(Of String).Compare
        Return NativeMethods.StrCmpLogicalW(x, y)
    End Function

End Class

以下測試代碼需要具有DataGridView和具有默認名稱的BindingSource的表單:

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'Create standard table.
        Dim table As New DataTable

        With table.Columns
            .Add("Id", GetType(Integer))
            .Add("Code", GetType(String))
        End With

        With table.Rows
            .Add(1, "XAB-1")
            .Add(2, "XAB-2")
            .Add(3, "XAB-11")
            .Add(4, "XAB-3")
            .Add(5, "XAB-1A")
            .Add(6, "XAB-10")
            .Add(7, "XAB-1B")
        End With

        'Add order column.
        table.Columns.Add("Order", GetType(Integer))

        'Set the row order.
        OrderTableRows(table, "Code", "Order")

        'Bind and display in appropriate sort order.
        BindingSource1.DataSource = table
        BindingSource1.Sort = "Order"
        DataGridView1.DataSource = BindingSource1
    End Sub

    Private Sub OrderTableRows(table As DataTable, sortColumnName As String, orderColumnName As String)
        Dim rows = table.Rows.Cast(Of DataRow)().ToArray()

        'Get the value to sort by for each row.
        Dim sortValues = Array.ConvertAll(rows, Function(row) row.Field(Of String)(sortColumnName))

        'Sort the rows by the sort values using a natural comparison.
        Array.Sort(sortValues, rows, New NaturalStringComparer)

        'Number the rows sequentially based on the sort order.
        For i = 0 To rows.GetUpperBound(0)
            rows(i)(orderColumnName) = i
        Next
    End Sub

End Class

這將按您想要的順序顯示記錄。 如果您對Code列進行了任何更改,即編輯現有行或添加新行,那么您將需要再次調用OrderTableRows並且數據將正確使用。

在真實的應用程序中,您可能不想顯示Order列,您可以通過顯式隱藏它來實現,或者在設計器中添加網格列並省略該列,然后在代碼中將AutoGenerateColumns設置為False 如果您希望能夠單擊網格列 header 進行排序,則需要將SortMode設置為Programmatic ,然后在后台使用此排序方法。

編輯:

我擴展了上面的示例,以便在單擊Code列 header 單元格時啟用排序。 首先,我在設計器中添加了IdCode列。 這是生成的代碼:

'
'idColumn
'
Me.idColumn.DataPropertyName = "Id"
Me.idColumn.HeaderText = "Id"
Me.idColumn.Name = "idColumn"
'
'codeColumn
'
Me.codeColumn.DataPropertyName = "Code"
Me.codeColumn.HeaderText = "Code"
Me.codeColumn.Name = "codeColumn"
Me.codeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Programmatic

您可以在設計器中添加列,然后相應地設置這些屬性。 然后我禁用了自動生成列,這樣就不會為Order表列生成網格列:

'Bind and display in appropriate sort order.
BindingSource1.DataSource = table
BindingSource1.Sort = "Order"
DataGridView1.AutoGenerateColumns = False
DataGridView1.DataSource = BindingSource1

最后,我檢測到對Code列 header 的點擊,並按Order列對BindingSource進行了排序。 如果排序當前是按不同的列,我按升序排序,否則我切換方向:

Private Sub DataGridView1_ColumnHeaderMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.ColumnHeaderMouseClick
    Dim column = DataGridView1.Columns(NameOf(codeColumn))

    If e.ColumnIndex = column.Index Then
        'Sort by Order as a proxy for Code. Use ascending order by default.
        Dim sort = "Order"
        Dim direction = SortOrder.Ascending

        If DataGridView1.SortedColumn Is Nothing AndAlso
           BindingSource1.Sort?.StartsWith("Order", StringComparison.InvariantCultureIgnoreCase) AndAlso
           Not BindingSource1.Sort?.EndsWith("DESC", StringComparison.InvariantCultureIgnoreCase) Then
            'Already sorted in ascending direction by Order as a proxy for Code so reverse direction.
            sort &= " DESC"
            direction = SortOrder.Descending
        End If

        BindingSource1.Sort = sort
        column.HeaderCell.SortGlyphDirection = direction
    End If
End Sub

對用戶來說,它看起來就像沒有Order列,而Code列自然是自動排序的。

暫無
暫無

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

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