vb.net在ListView中排序混合整数和字符串列

时间:2016-03-13 20:52:31

标签: .net vb.net sorting listview

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

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

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

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

3 个答案:

答案 0 :(得分:2)

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

值得考虑在ColumnHeader中添加“KB”标识,因此它不会在文本中反复重复,必须将其删除以进行排序。

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

这给我们留下了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。

答案 1 :(得分:0)

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

如果它是一个字符串数组,我怀疑它是,你应该使用你的替换方法删除撇号,但你应该在将它们添加到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的每个值,看看它是否有效。

答案 2 :(得分:0)

我遇到了同样的问题,似乎没有任何答案可行。最后我 做了一种将每个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