Results 1 to 12 of 12

Thread: Listview - Next item when not ordered by Index

  1. #1

    Thread Starter
    New Member
    Join Date
    Jun 2018
    Posts
    5

    Listview - Next item when not ordered by Index

    Hi,

    I'm really having trouble finding the next Item(Row) from a Listview which Items are not ordered by Index.
    Normally something like this would selecet the next item:

    Code:
            Dim selected As Integer = Listview1.SelectedItems(0).Index
            Listview1.Items(selected + 1).Selected = True
    My Problem ist that my Listview ist custom sorted - the next item is not necessarily the Item with Index + 1.

    Here is a screenshot, the column difficulty displays the item index for debugging.
    Name:  Untitled.jpg
Views: 1270
Size:  35.6 KB

    How can i find the next item/row here?

    thanks

    Lunex

  2. #2
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: Listview - Next item when not ordered by Index

    You are nearly there.

    Code:
    SelectedItems(0).SubItems(....)

  3. #3
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: Listview - Next item when not ordered by Index

    Can you explain how you're sorting? I don't use ListViews much but I was under the impression that the Index would change if you sorted. In fact, can you provide some basic code that demonstrates the issue that we can copy and paste to test it?

  4. #4

    Thread Starter
    New Member
    Join Date
    Jun 2018
    Posts
    5

    Re: Listview - Next item when not ordered by Index

    Hi,

    thank you for your help!


    Quote Originally Posted by ident View Post
    Code:
    SelectedItems(0).SubItems(....)
    I'm looking for the next row, so the next item vertically not horizontally, but thanks!


    Quote Originally Posted by jmcilhinney View Post
    Can you explain how you're sorting? I don't use ListViews much but I was under the impression that the Index would change if you sorted. In fact, can you provide some basic code that demonstrates the issue that we can copy and paste to test it?
    The sorting is quite compex:
    Code:
        Private Sub LVMain_ColumnClick(sender As Object, e As System.Windows.Forms.ColumnClickEventArgs) Handles LVMain.ColumnClick
            On Error Resume Next
            If Scenarios.Working Then Exit Sub 'Keine Aktualisierung wärend des einlesen
    
            Dim new_sorting_column As ColumnHeader = LVMain.Columns(e.Column)
            Dim sort_order As System.Windows.Forms.SortOrder
    
            If m_SortingColumn Is Nothing Then
                sort_order = SortOrder.Ascending
            Else
                If new_sorting_column.Equals(m_SortingColumn) Then
                    If m_SortingColumn.Text.StartsWith("> ") Then
                        sort_order = SortOrder.Descending
                    Else
                        sort_order = SortOrder.Ascending
                    End If
                Else
                    sort_order = SortOrder.Ascending
                End If
                m_SortingColumn.Text = m_SortingColumn.Text.Substring(2)
            End If
            m_SortingColumn = new_sorting_column
            If sort_order = SortOrder.Ascending Then
                m_SortingColumn.Text = "> " & m_SortingColumn.Text
            Else
                m_SortingColumn.Text = "< " & m_SortingColumn.Text
            End If
            LVMain.ListViewItemSorter = New ListViewComparer(e.Column, sort_order)
            LVMain.Sort()
        End Sub
    This provides sorting asc & desc per column clicked.
    The Index of each item stays the same though sorting, it works like a table index in a database.

    I wonder if at this point it might be easier to emulate sending the up/down key to the listview control as this seems to select the next/prev item reliable...

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: Listview - Next item when not ordered by Index

    I suspect that the issues is because you have enabled grouping in your ListView. That's actually not something that I have ever done myself but I think that you need to ignore the Items collection of the ListView itself and look at the Items collection of each group. Without having tested it, I think this code should pretty much do the trick, if my suspicions are correct:
    vb.net Code:
    1. Private Sub SelectNextItem()
    2.     Dim selectedItem = ListView1.SelectedItems(0)
    3.     Dim selectedGroup = selectedItem.Group
    4.     Dim selectedItemIndex = selectedGroup.Items.IndexOf(selectedItem)
    5.  
    6.     selectedItem.Selected = False
    7.  
    8.     If selectedItemIndex = selectedGroup.Items.Count - 1 Then
    9.         'The last item in a group is selected so select the first item in the next group.
    10.         Dim selectedGroupIndex = ListView1.Groups.IndexOf(selectedGroup)
    11.  
    12.         Do
    13.             If selectedGroupIndex = ListView1.Groups.Count - 1 Then
    14.                 'This is the last group so wrap to the first.
    15.                 selectedGroupIndex = 0
    16.             Else
    17.                 'Select the next group.
    18.                 selectedGroupIndex += 1
    19.             End If
    20.  
    21.             selectedGroup = ListView1.Groups(selectedGroupIndex)
    22.         Loop Until selectedGroup.Items.Count > 0 'Skip empty groups.
    23.  
    24.         'Select the first item in the next group.
    25.         selectedItemIndex = 0
    26.     Else
    27.         'Select the next item in the same group.
    28.         selectedItemIndex += 1
    29.     End If
    30.  
    31.     selectedItem = selectedGroup.Items(selectedItemIndex)
    32.     selectedItem.Selected = True
    33. End Sub

  6. #6

    Thread Starter
    New Member
    Join Date
    Jun 2018
    Posts
    5

    Re: Listview - Next item when not ordered by Index

    Thank you very much for your effort, but sadly it doesn't solve the index problem.
    I've made a gif to demonstrate the problem.

    The second column displays the item index and the next button just increments the selected item index as it would normally be to select the next item:

    Attached Images Attached Images  

  7. #7
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,532

    Re: Listview - Next item when not ordered by Index

    You want it to go in order in which you've added them to the LV, but then you've also grouped them, so that changed where they are in the LV. Seeing that it does go from index 6 to index 7 in a different group seems to support the theory that the code is in fact working as written. It's just not working as you want it to.
    I think jmc's code is what you need... in what way did it not work?

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: Listview - Next item when not ordered by Index

    EDIT: I'll leave this post here for completeness but improved code is provided in the next post.

    The issue is related to the fact that you have grouping enabled but it appears that the Items collection of each group is not sorted. I created a new form with a ListView and a Button and then added the following code:
    vb.net Code:
    1. Public Class Form1
    2.  
    3.     Private sortColumnIndex As Integer
    4.  
    5.     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    6.         With ListView1
    7.             .View = View.Details
    8.             .ShowGroups = True
    9.             .HideSelection = False
    10.  
    11.             .Columns.Add("Description", "Description", 100)
    12.             .Columns.Add("Index", "Index")
    13.  
    14.             Dim oddGroup = .Groups.Add("Odds", "Odds")
    15.             Dim evenGroup = .Groups.Add("Evens", "Evens")
    16.  
    17.             AddItem("First", oddGroup)
    18.             AddItem("Second", evenGroup)
    19.             AddItem("Third", oddGroup)
    20.             AddItem("Fourth", evenGroup)
    21.             AddItem("Fifth", oddGroup)
    22.             AddItem("Sixth", evenGroup)
    23.             AddItem("Seventh", oddGroup)
    24.             AddItem("Eighth", evenGroup)
    25.             AddItem("Ninth", oddGroup)
    26.             AddItem("Tenth", evenGroup)
    27.         End With
    28.     End Sub
    29.  
    30.     Private Sub AddItem(text As String, group As ListViewGroup)
    31.         Dim item As New ListViewItem With {.Name = text, .Text = text, .Group = group}
    32.  
    33.         ListView1.Items.Add(item).SubItems.Add(item.Index.ToString())
    34.     End Sub
    35.  
    36.     Private Sub ListView1_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles ListView1.ColumnClick
    37.         sortColumnIndex = e.Column
    38.         ListView1.ListViewItemSorter = New ListViewItemSorter(e.Column)
    39.  
    40.         For Each item As ListViewItem In ListView1.Items
    41.             item.SubItems(1).Text = item.Index.ToString()
    42.         Next
    43.     End Sub
    44.  
    45.     Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    46.         SelectNextItem()
    47.     End Sub
    48.  
    49.     Private Sub SelectNextItem()
    50.         Dim selectedItem = ListView1.SelectedItems(0)
    51.         Dim selectedGroup = selectedItem.Group
    52.         Dim groupItems = selectedGroup.Items.Cast(Of ListViewItem)().ToArray()
    53.  
    54.         'Sort the group items in the same way as the ListView.
    55.         Array.Sort(groupItems, New ListViewItemSorter(sortColumnIndex))
    56.  
    57.         'Get the displayed index of the selected item.
    58.         Dim selectedIndex = Array.IndexOf(groupItems, selectedItem)
    59.  
    60.         'Deselect the currently selected item.
    61.         selectedItem.Selected = False
    62.  
    63.         If selectedIndex = groupItems.GetUpperBound(0) Then
    64.             'The last item in the group is selected so select the first item in the next populated group.
    65.  
    66.             Dim selectedGroupIndex = ListView1.Groups.IndexOf(selectedGroup)
    67.  
    68.             Do
    69.                 selectedGroupIndex += 1
    70.  
    71.                 If selectedGroupIndex = ListView1.Groups.Count Then
    72.                     'Wrap to the first group after the last.
    73.                     selectedGroupIndex = 0
    74.                 End If
    75.  
    76.                 selectedGroup = ListView1.Groups(selectedGroupIndex)
    77.             Loop Until selectedGroup.Items.Count > 0
    78.  
    79.             groupItems = selectedGroup.Items.Cast(Of ListViewItem)().ToArray()
    80.             Array.Sort(groupItems, New ListViewItemSorter(sortColumnIndex))
    81.             selectedItem = groupItems(0)
    82.         Else
    83.             'Get the next item in the same group.
    84.             selectedIndex += 1
    85.             selectedItem = groupItems(selectedIndex)
    86.         End If
    87.  
    88.         'Select the new item.
    89.         selectedItem.Selected = True
    90.     End Sub
    91.  
    92. End Class
    93.  
    94.  
    95. Public Class ListViewItemSorter
    96.     Implements IComparer
    97.  
    98.     Private ReadOnly columnIndex As Integer
    99.  
    100.     Public Sub New(columnIndex As Integer)
    101.         Me.columnIndex = columnIndex
    102.     End Sub
    103.  
    104.     Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
    105.         Dim itemX = DirectCast(x, ListViewItem)
    106.         Dim itemY = DirectCast(y, ListViewItem)
    107.  
    108.         Select Case columnIndex
    109.             Case 0
    110.                 Return itemX.Text.CompareTo(itemY.Text)
    111.             Case 1
    112.                 Return CInt(itemX.SubItems(1).Text).CompareTo(CInt(itemY.SubItems(1).Text))
    113.         End Select
    114.  
    115.         Return 0
    116.     End Function
    117.  
    118. End Class
    I was then able to click the header of the Description column, select an item and then click the Button and have the selection advance as you desire.

  9. #9
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: Listview - Next item when not ordered by Index

    Actually, I realised that that can be simplified slightly. When you sort the ListView, the Index property values of the items actually do change. That means that, within a group, you can simply sort the items by their Index rather than having to sort them the same way you sort the entire ListView. That means that the 'sortColumnIndex' field is no longer needed. Here's the revised code:
    vb.net Code:
    1. Public Class Form1
    2.  
    3.     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4.         With ListView1
    5.             .View = View.Details
    6.             .ShowGroups = True
    7.             .HideSelection = False
    8.  
    9.             .Columns.Add("Description", "Description", 100)
    10.             .Columns.Add("Index", "Index")
    11.  
    12.             Dim oddGroup = .Groups.Add("Odds", "Odds")
    13.             Dim evenGroup = .Groups.Add("Evens", "Evens")
    14.  
    15.             AddItem("First", oddGroup)
    16.             AddItem("Second", evenGroup)
    17.             AddItem("Third", oddGroup)
    18.             AddItem("Fourth", evenGroup)
    19.             AddItem("Fifth", oddGroup)
    20.             AddItem("Sixth", evenGroup)
    21.             AddItem("Seventh", oddGroup)
    22.             AddItem("Eighth", evenGroup)
    23.             AddItem("Ninth", oddGroup)
    24.             AddItem("Tenth", evenGroup)
    25.         End With
    26.     End Sub
    27.  
    28.     Private Sub AddItem(text As String, group As ListViewGroup)
    29.         Dim item As New ListViewItem With {.Name = text, .Text = text, .Group = group}
    30.  
    31.         ListView1.Items.Add(item).SubItems.Add(item.Index.ToString())
    32.     End Sub
    33.  
    34.     Private Sub ListView1_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles ListView1.ColumnClick
    35.         ListView1.ListViewItemSorter = New ListViewItemSorter(e.Column)
    36.  
    37.         For Each item As ListViewItem In ListView1.Items
    38.             item.SubItems(1).Text = item.Index.ToString()
    39.         Next
    40.     End Sub
    41.  
    42.     Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    43.         SelectNextItem()
    44.     End Sub
    45.  
    46.     Private Sub SelectNextItem()
    47.         Dim selectedItem = ListView1.SelectedItems(0)
    48.         Dim selectedGroup = selectedItem.Group
    49.  
    50.         'Get the item in the same group with the next highest index.
    51.         Dim nextItem = selectedGroup.Items.
    52.                                      Cast(Of ListViewItem)().
    53.                                      OrderBy(Function(item) item.Index).
    54.                                      FirstOrDefault(Function(item) item.Index > selectedItem.Index)
    55.  
    56.         If nextItem Is Nothing Then
    57.             'The selected item is the last in its group so find the next group that contains items.
    58.             Dim selectedGroupIndex = ListView1.Groups.IndexOf(selectedGroup)
    59.  
    60.             Do
    61.                 selectedGroupIndex += 1
    62.  
    63.                 If selectedGroupIndex = ListView1.Groups.Count Then
    64.                     'Wrap to the first group after the last.
    65.                     selectedGroupIndex = 0
    66.                 End If
    67.  
    68.                 selectedGroup = ListView1.Groups(selectedGroupIndex)
    69.             Loop Until selectedGroup.Items.Count > 0
    70.  
    71.             'Get the first item in the next group.
    72.             nextItem = selectedGroup.Items.
    73.                                      Cast(Of ListViewItem)().
    74.                                      OrderBy(Function(item) item.Index).
    75.                                      First()
    76.         End If
    77.  
    78.         'Change the selection.
    79.         selectedItem.Selected = False
    80.         nextItem.Selected = True
    81.     End Sub
    82.  
    83. End Class
    84.  
    85.  
    86. Public Class ListViewItemSorter
    87.     Implements IComparer
    88.  
    89.     Private ReadOnly columnIndex As Integer
    90.  
    91.     Public Sub New(columnIndex As Integer)
    92.         Me.columnIndex = columnIndex
    93.     End Sub
    94.  
    95.     Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
    96.         Dim itemX = DirectCast(x, ListViewItem)
    97.         Dim itemY = DirectCast(y, ListViewItem)
    98.  
    99.         Select Case columnIndex
    100.             Case 0
    101.                 Return itemX.Text.CompareTo(itemY.Text)
    102.             Case 1
    103.                 Return CInt(itemX.SubItems(1).Text).CompareTo(CInt(itemY.SubItems(1).Text))
    104.         End Select
    105.  
    106.         Return 0
    107.     End Function
    108.  
    109. End Class

  10. #10

    Thread Starter
    New Member
    Join Date
    Jun 2018
    Posts
    5

    Resolved Re: Listview - Next item when not ordered by Index

    Hi jmcilhinney,

    thank you so much for your effort - the code works for my project!

    I do not completely understand the "linq" part, but I see how its working in general and
    I was able to modify the code for another "previous item" sub.


    Thanks again,

    Lunex

  11. #11
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: Listview - Next item when not ordered by Index

    To clarify some of the LINQ parts, this:
    vb.net Code:
    1. Dim nextItem = selectedGroup.Items.
    2.                              Cast(Of ListViewItem)().
    3.                              OrderBy(Function(item) item.Index).
    4.                              FirstOrDefault(Function(item) item.Index > selectedItem.Index)
    would be equivalent to this:
    vb.net Code:
    1. Dim items(selectedGroup.Items.Count - 1) As ListViewItem
    2.  
    3. selectedGroup.Items.CopyTo(items, 0)
    4. Array.Sort(items, AddressOf CompareListViewItemsByIndex)
    5.  
    6. Dim nextItem As ListViewItem = Nothing
    7.  
    8. For Each item As ListViewItem In items
    9.     If item.Index > selectedItem.Index Then
    10.         nextItem = item
    11.         Exit For
    12.     End If
    13. Next
    while this:
    vb.net Code:
    1. nextItem = selectedGroup.Items.
    2.                          Cast(Of ListViewItem)().
    3.                          OrderBy(Function(item) item.Index).
    4.                          First()
    would be equivalent to this:
    vb.net Code:
    1. Dim items(selectedGroup.Items.Count - 1) As ListViewItem
    2.  
    3. selectedGroup.Items.CopyTo(items, 0)
    4. Array.Sort(items, AddressOf CompareListViewItemsByIndex)
    5.  
    6. Dim nextItem As ListViewItem = items(0)
    where the comparison method used to sort the array in each case looks like this:
    vb.net Code:
    1. Private Function CompareListViewItemsByIndex(item1 As ListViewItem, item2 As ListViewItem) As Integer
    2.     Return item1.Index.CompareTo(item2.Index)
    3. End Function
    Note that there are also Last and LastOrDefault methods that may be useful for 'Previous Item' functionality.

  12. #12

    Thread Starter
    New Member
    Join Date
    Jun 2018
    Posts
    5

    Re: Listview - Next item when not ordered by Index

    Thanks very much, the short form was to advanced for my programming experience,
    but I understand the long form.

    Seems like I need to get to learn some Linq ;-)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width