【问题标题】:vb.net sorting mixed integer and string column in ListViewvb.net在ListView中排序混合整数和字符串列
【发布时间】:2016-03-13 20:52:31
【问题描述】:

我遇到了一个非常大的问题(对我来说)。我找不到任何解决方案。我的 ListView 中有四列:

ID = integer
Name = string
Response = boolean
Memory = mixed integer with string (1'000 KB)

在 [ColumnClick] 之后,我可以“正常”对前 3 列进行排序(asc/desc),但是当我尝试对第四列进行排序时,而不是

1 KB / 5 KB / 1'000 KB

我确实收到了这样的东西:

1 KB / 1'000 KB / 5 KB

第四列是这样打印的:

ListView1.Items(Count).SubItems.Add(FormatNumber(pMem, 0) & " KB")

我在想这个:

    If e.Column.ToString = 3 Then
        Dim final As Integer
        For Each value In ListView1.Items
            Replace(value.SubItems(3), "'", "")
            Replace(value.SubItems(3), " KB", "")
            final = value
        Next
    Else
    ...

然后以与 ID 相同的方式对整数进行排序,然后以某种方式将它们放回 ListView。但我不知道怎么做。

表格中的排序:

Private Sub ListView1_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles ListView1.ColumnClick
    Dim ListViewSorter As New ListViewSorter

    With ListViewSorter
        .SortingOrder = 1
        .ColumnIndex = e.Column
    End With

    ListView1.ListViewItemSorter = ListViewSorter
End Sub

还有我的 ListViewSorter.vb

Public Class ListViewSorter
Implements IComparer

Private ColumnId As Integer
Private SortOrder As SortOrder
Private ItemComparer As CaseInsensitiveComparer

Public Sub New()
    ColumnId = 0
    SortOrder = 0
    ItemComparer = New CaseInsensitiveComparer()
End Sub

Public Property ColumnIndex() As Integer
    Get
        Return ColumnId
    End Get
    Set(Value As Integer)
        ColumnId = Value
    End Set
End Property

Public Property SortingOrder() As SortOrder
    Get
        Return SortOrder
    End Get
    Set(Value As SortOrder)
        SortOrder = Value
    End Set
End Property

Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
    Dim myResults As Integer
    Dim strX As String = DirectCast(x, ListViewItem).SubItems(ColumnId).Text
    Dim strY As String = DirectCast(y, ListViewItem).SubItems(ColumnId).Text
    Dim num As Point

    If Integer.TryParse(strX, num.X) And Integer.TryParse(strY, num.Y) Then
        myResults = ItemComparer.Compare(num.X, num.Y)
    Else
        myResults = ItemComparer.Compare(strX, strY)
    End If

    If SortOrder = 1 Then
        Return myResults
    ElseIf SortOrder = 2 Then
        Return -myResults
    Else
        Return 0
    End If
End Function
End Class

【问题讨论】:

  • 请展示您如何“正常”对列 1-3 进行排序。单独的 LV 只对 Item 进行排序(col 0)

标签: .net vb.net sorting listview


【解决方案1】:

最佳解决方案是使用DataGridView。使用DataSource,而不是创建和添加ListViewItems,然后ListViewSubItems,您可以用一行代码填充控件。它还使用类型化的列,因此 KB 或 KG 列会像数字一样排序。然后可以使用一个简单的 linq 表达式对DataSource 重新排序。

值得考虑在ColumnHeader 中添加“KB”名称,这样它就不会在文本中一遍又一遍地重复,并且必须被剥离以进行排序。

由于ListView仅存储文本,您可能会遇到其他问题:“9 KB”将排序高于“1000 KB”,因为那里没有整数 - 它们是数字。在许多情况下,您可以将 NaturalSort 用于包含数字的字符串(“就像 explorer”),但无论出于何种原因,它都不喜欢 ' 数字组分隔符。

这给我们留下了ListViewItemSorter。您可能已经有一个用于排序第 1 列和第 2 列。需要更多逻辑来从 KB 列中提取一个实际数值以进行排序:

Public Class ListViewKBItemComparer
    Implements IComparer
    Private mySortFlipper As Int32 = 1
    Public Property SortColumn As Int32 = 0

    Public Sub New()

    End Sub

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

    Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
        Dim result As Int32
        Dim lvX = CType(x, ListViewItem)
        Dim lvY = CType(y, ListViewItem)

        If SortColumn = 3 Then
            result = KBCompare(lvX, lvY)
        Else
            result = String.Compare(lvX.SubItems(SortColumn).Text,
                                    lvY.SubItems(SortColumn).Text)
        End If

        Return (result * mySortFlipper)
    End Function


    Private Function KBCompare(x As ListViewItem, y As ListViewItem) As Int32
        ' strip the text
        Dim xs = x.SubItems(SortColumn).Text.Replace(" KB", "")
        Dim ys = y.SubItems(SortColumn).Text.Replace(" KB", "")

        ' convert to decimal
        Dim decX As Decimal = -1
        Decimal.TryParse(xs, decX)

        Dim decY As Decimal = -1
        Decimal.TryParse(ys, decY)
        ' return comparison result
        Return decX.CompareTo(decY)

    End Function
End Class

我使用Decimal 以防某些字符串也可能包含分数。如果没有,只需将Decimal 更改为Int32

用法:

myLV.ListViewItemSorter = New ListViewKBItemComparer(e.Column, SortAsc)
myLV.Sort()

e.Column 是来自ColumnClick 事件的参数; SortAscSortOrder,表示 Asc 或 Desc。

【讨论】:

  • 非常感谢您的帮助和解释,感谢我在 ASCII 表中使用 Replace.(Chr(39), "") 的撇号,一切正常。
  • 如果它是当前文化的有效分隔符,它应该解析。我不知道要测试哪种文化。 x.SubItems(SortColumn).Text.Replace(" KB", "")..Replace("'"c, "") 也应该可以工作
【解决方案2】:

是的,您应该使用给定的示例,在稍微调整后删除数组中的文本。

如果它是一个字符串数组,我怀疑它是,你应该使用你的替换方法来删除撇号,但你应该在将它们添加到 ListBox 之前替换它们。

您可以这样做:

For Each value In FormatNumber
        FormatNumber(count) = Replace(value, "'", "")
        count +=1
Next

我猜测 FormatNumber 是一个字符串数组,所以你需要将它转换为一个整数。 为此,我只是将第一个响应中的示例复制到this question.

在上面的 For 循环之后添加以下代码行。

Dim intArray = Array.ConvertAll(FormatNumber, Function(str) Int32.Parse(str))

现在将 intArray 的每个值排序后添加到 Listbox 中,看看它是否有效。

【讨论】:

  • ListVIEW 不是列表框
  • 开枪!感谢您指出了这一点。我不确定如何使用列表视图,所以你能告诉我答案是否仍然适用于这里吗?如果没有,我会删除它。
【解决方案3】:

我遇到了同样的问题,但似乎没有一个答案有效。最后我 做了一个排序,将每个lv SubItem 转换为string array,然后转换 将值排序为string,然后将该值转换为Integer, 设置上升/下降方向,如果需要交换,打包 从string array 到相反的LV subitem 的每个项目。这里是 我使用的代码。它适用于我的应用程序。

Private Sub SortStandings()
    'Sorts Records by won/loss Percentage for Year
    'Listview Items to be worked with
    Dim LVItemX As ListViewItem
    Dim LVItemY As ListViewItem
    'String Copy of Value to Compare from List View Item
    Dim Strx As String
    Dim Stry As String
    'Integer Value of Value to Compare From List (Cannot Cast LV Value directly to String
    Dim XX As Integer
    Dim YY As Integer
    'Sort Direction Indicator
    Dim J As Integer

    Dim ITemX(ColLst.Pct) As String
    Dim ItemY(ColLst.Pct) As String
    Dim Altx As Integer
    Dim Alty As Integer
    'For each List View Item except the Last one
    For x = 0 To lvDisplay.Items.Count - 2
        LVItemX = lvDisplay.Items(x)
        'Compare it to each lvItem below it
        For y = x + 1 To lvDisplay.Items.Count - 1
            LVItemY = lvDisplay.Items(y)
            'Convert values to Sort to String (Cannot go directly to Integer
            Strx = LVItemX.SubItems.Item(ColLst.Pct).Text
            Stry = LVItemY.SubItems.Item(ColLst.Pct).Text
            'Take Care of Possible Discrepancy in Format
            If Strx = "" Then Strx = "0%"
            If Stry = "" Then Stry = "0%"
            'Convert Strings to Integer
            XX = CInt(Microsoft.VisualBasic.Left(Strx, Len(Strx) - 1))
            YY = CInt(Microsoft.VisualBasic.Left(Stry, Len(Stry) - 1))
            'Set up a tie Breaker Based on as Different Column
            Altx = CInt(LVItemX.SubItems.Item(ColLst.YW).Text)
            Alty = CInt(LVItemY.SubItems.Item(ColLst.YW).Text)
            'Set up  Comparison for Descending ( Change > to < for Ascending )
            J = 0
            If XX < YY Then
                J = -1
            End If
            If XX = YY And Altx < Alty Then
                J = -1
            End If
            'rebuild Both LV Items in New Locations
            If J = -1 Then
                For n = 0 To ColLst.Pct
                    ITemX(n) = LVItemX.SubItems.Item(n).Text
                    ItemY(n) = LVItemY.SubItems.Item(n).Text
                Next
                For n = 0 To ColLst.Pct
                    LVItemX.SubItems.Item(n).Text = ItemY(n)
                    LVItemY.SubItems.Item(n).Text = ITemX(n)
                Next
            End If
        Next
    Next
End Sub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 2018-08-22
    • 2013-06-10
    • 1970-01-01
    • 2013-06-25
    相关资源
    最近更新 更多