listviewitem sorting with some items "pinned" together

22 Views Asked by At

I have a listview in .net winforms, there is limitation where items with over 259 charaacters are truncated. Most of the item strings I am dealing with are usually well below 250, but I'd like to be able to handle these long ones when they occasionally crop up.

To overcome this I am detecting when there is a string over 250, splitting it into smallers substrings, and creating a listviewitem for each.

This works fine, until the listview needs sorting. How can I "pin" these items together so that the pinned group is sorted by the first item in said group?

I have tried implementing a custom Icomparer class, however, this results in the items being correctly pinned together, but they are not in the correct postion in the list overall.

Imports System.Globalization

Public Class LinkedListViewColumnSorter
    Implements System.Collections.IComparer

    ''' <summary>
    ''' The column to sort
    ''' </summary>
    Private ColumnToSort As Integer
    ''' <summary>
    ''' The order of sort
    ''' </summary>
    Private OrderOfSort As SortOrder
    ''' <summary>
    ''' The object compare
    ''' </summary>
    Private ObjectCompare As CaseInsensitiveComparer


    ''' <summary>
    ''' Initializes a new instance of the <see cref="LinkedListViewColumnSorter" /> class.
    ''' </summary>
    Public Sub New()
        ' Initialize the column to '0'.
        ColumnToSort = 0

        ' Initialize the sort order to 'none'.
        OrderOfSort = SortOrder.None

        ' Initialize the CaseInsensitiveComparer object.
        ObjectCompare = New CaseInsensitiveComparer()

    End Sub


    ''' <summary>
    ''' Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
    ''' </summary>
    ''' <param name="x">The first object to compare.</param>
    ''' <param name="y">The second object to compare.</param>
    ''' <returns>
    ''' A signed integer that indicates the relative values of <paramref name="x" /> and <paramref name="y" />, as shown in the following table.Value Meaning Less than zero <paramref name="x" /> is less than <paramref name="y" />. Zero <paramref name="x" /> equals <paramref name="y" />. Greater than zero <paramref name="x" /> is greater than <paramref name="y" />.
    ''' </returns>
    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare

        Dim compareResult As Integer
        Dim listviewX As ListViewItem
        Dim listviewY As ListViewItem

        ' Cast the objects to be compared to ListViewItem objects.
        listviewX = CType(x, ListViewItem)
        listviewY = CType(y, ListViewItem)


        '// exit if there is no data for the given column
        If (ColumnToSort > listviewX.SubItems.Count - 1) OrElse (ColumnToSort > listviewY.SubItems.Count - 1) Then
            Return 0
        End If


        Dim XValue As String = listviewX.SubItems(ColumnToSort).Text
        Dim YValue As String = listviewY.SubItems(ColumnToSort).Text


        Dim DecX, DecY As Decimal

        Dim XInfo As LinkedListViewRowInfo = TryCast(listviewX.Tag, LinkedListViewRowInfo)
        Dim YInfo As LinkedListViewRowInfo = TryCast(listviewY.Tag, LinkedListViewRowInfo)


        If XInfo IsNot Nothing AndAlso XInfo.MultiRow AndAlso YInfo IsNot Nothing AndAlso YInfo.MultiRow Then

            Return ObjectCompare.Compare(XInfo?.Index, YInfo?.Index)

        ElseIf XInfo IsNot Nothing AndAlso XInfo.MultiRow Then

            Return XInfo?.Index.CompareTo(0)

        ElseIf YInfo IsNot Nothing AndAlso YInfo.MultiRow Then

            Return 1
        End If


        '// Handle numbers stored as strings (sort based on numberic value not starting digit)
        Dim XisDecimal As Boolean = Decimal.TryParse(XValue, NumberStyles.Any, CultureInfo.CurrentCulture, DecX)
        Dim YisDecimal As Boolean = Decimal.TryParse(YValue, NumberStyles.Any, CultureInfo.CurrentCulture, DecY)

        ' Compare the two items.
        If XisDecimal AndAlso YisDecimal Then
            compareResult = ObjectCompare.Compare(DecX, DecY)
        Else
            compareResult = ObjectCompare.Compare(listviewX.SubItems(ColumnToSort).Text, listviewY.SubItems(ColumnToSort).Text)
        End If


        ' Calculate the correct return value based on the object 
        ' comparison.
        If (OrderOfSort = SortOrder.Ascending) Then
            ' Ascending sort is selected, return typical result of 
            ' compare operation.
            Return compareResult
        ElseIf (OrderOfSort = SortOrder.Descending) Then
            ' Descending sort is selected, return negative result of 
            ' compare operation.
            Return (-compareResult)
        Else
            ' Return '0' to indicate that they are equal.
            Return 0
        End If

    End Function


    ''' <summary>
    ''' Gets or sets the sort column.
    ''' </summary>
    ''' <value>
    ''' The sort column.
    ''' </value>
    Public Property SortColumn() As Integer
        Set(ByVal Value As Integer)
            ColumnToSort = Value
        End Set

        Get
            Return ColumnToSort
        End Get
    End Property


    ''' <summary>
    ''' Gets or sets the order.
    ''' </summary>
    ''' <value>
    ''' The order.
    ''' </value>
    Public Property Order() As SortOrder
        Set(ByVal Value As SortOrder)
            OrderOfSort = Value
        End Set

        Get
            Return OrderOfSort
        End Get
    End Property

End Class

0

There are 0 best solutions below