-
Jun 13th, 2018, 02:39 AM
#1
Thread Starter
New Member
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.
How can i find the next item/row here?
thanks
Lunex
-
Jun 13th, 2018, 03:06 PM
#2
Re: Listview - Next item when not ordered by Index
You are nearly there.
Code:
SelectedItems(0).SubItems(....)
-
Jun 13th, 2018, 10:54 PM
#3
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?
-
Jun 14th, 2018, 02:29 AM
#4
Thread Starter
New Member
Re: Listview - Next item when not ordered by Index
Hi,
thank you for your help!
Originally Posted by ident
Code:
SelectedItems(0).SubItems(....)
I'm looking for the next row, so the next item vertically not horizontally, but thanks!
Originally Posted by jmcilhinney
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...
-
Jun 14th, 2018, 03:05 AM
#5
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:
Private Sub SelectNextItem() Dim selectedItem = ListView1.SelectedItems(0) Dim selectedGroup = selectedItem.Group Dim selectedItemIndex = selectedGroup.Items.IndexOf(selectedItem) selectedItem.Selected = False If selectedItemIndex = selectedGroup.Items.Count - 1 Then 'The last item in a group is selected so select the first item in the next group. Dim selectedGroupIndex = ListView1.Groups.IndexOf(selectedGroup) Do If selectedGroupIndex = ListView1.Groups.Count - 1 Then 'This is the last group so wrap to the first. selectedGroupIndex = 0 Else 'Select the next group. selectedGroupIndex += 1 End If selectedGroup = ListView1.Groups(selectedGroupIndex) Loop Until selectedGroup.Items.Count > 0 'Skip empty groups. 'Select the first item in the next group. selectedItemIndex = 0 Else 'Select the next item in the same group. selectedItemIndex += 1 End If selectedItem = selectedGroup.Items(selectedItemIndex) selectedItem.Selected = True End Sub
-
Jun 14th, 2018, 04:23 AM
#6
Thread Starter
New Member
-
Jun 14th, 2018, 10:33 AM
#7
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
-
Jun 14th, 2018, 07:40 PM
#8
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:
Public Class Form1 Private sortColumnIndex As Integer Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load With ListView1 .View = View.Details .ShowGroups = True .HideSelection = False .Columns.Add("Description", "Description", 100) .Columns.Add("Index", "Index") Dim oddGroup = .Groups.Add("Odds", "Odds") Dim evenGroup = .Groups.Add("Evens", "Evens") AddItem("First", oddGroup) AddItem("Second", evenGroup) AddItem("Third", oddGroup) AddItem("Fourth", evenGroup) AddItem("Fifth", oddGroup) AddItem("Sixth", evenGroup) AddItem("Seventh", oddGroup) AddItem("Eighth", evenGroup) AddItem("Ninth", oddGroup) AddItem("Tenth", evenGroup) End With End Sub Private Sub AddItem(text As String, group As ListViewGroup) Dim item As New ListViewItem With {.Name = text, .Text = text, .Group = group} ListView1.Items.Add(item).SubItems.Add(item.Index.ToString()) End Sub Private Sub ListView1_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles ListView1.ColumnClick sortColumnIndex = e.Column ListView1.ListViewItemSorter = New ListViewItemSorter(e.Column) For Each item As ListViewItem In ListView1.Items item.SubItems(1).Text = item.Index.ToString() Next End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click SelectNextItem() End Sub Private Sub SelectNextItem() Dim selectedItem = ListView1.SelectedItems(0) Dim selectedGroup = selectedItem.Group Dim groupItems = selectedGroup.Items.Cast(Of ListViewItem)().ToArray() 'Sort the group items in the same way as the ListView. Array.Sort(groupItems, New ListViewItemSorter(sortColumnIndex)) 'Get the displayed index of the selected item. Dim selectedIndex = Array.IndexOf(groupItems, selectedItem) 'Deselect the currently selected item. selectedItem.Selected = False If selectedIndex = groupItems.GetUpperBound(0) Then 'The last item in the group is selected so select the first item in the next populated group. Dim selectedGroupIndex = ListView1.Groups.IndexOf(selectedGroup) Do selectedGroupIndex += 1 If selectedGroupIndex = ListView1.Groups.Count Then 'Wrap to the first group after the last. selectedGroupIndex = 0 End If selectedGroup = ListView1.Groups(selectedGroupIndex) Loop Until selectedGroup.Items.Count > 0 groupItems = selectedGroup.Items.Cast(Of ListViewItem)().ToArray() Array.Sort(groupItems, New ListViewItemSorter(sortColumnIndex)) selectedItem = groupItems(0) Else 'Get the next item in the same group. selectedIndex += 1 selectedItem = groupItems(selectedIndex) End If 'Select the new item. selectedItem.Selected = True End Sub End Class Public Class ListViewItemSorter Implements IComparer Private ReadOnly columnIndex As Integer Public Sub New(columnIndex As Integer) Me.columnIndex = columnIndex End Sub Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare Dim itemX = DirectCast(x, ListViewItem) Dim itemY = DirectCast(y, ListViewItem) Select Case columnIndex Case 0 Return itemX.Text.CompareTo(itemY.Text) Case 1 Return CInt(itemX.SubItems(1).Text).CompareTo(CInt(itemY.SubItems(1).Text)) End Select Return 0 End Function 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.
Last edited by jmcilhinney; Jun 14th, 2018 at 09:51 PM.
-
Jun 14th, 2018, 09:22 PM
#9
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:
Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load With ListView1 .View = View.Details .ShowGroups = True .HideSelection = False .Columns.Add("Description", "Description", 100) .Columns.Add("Index", "Index") Dim oddGroup = .Groups.Add("Odds", "Odds") Dim evenGroup = .Groups.Add("Evens", "Evens") AddItem("First", oddGroup) AddItem("Second", evenGroup) AddItem("Third", oddGroup) AddItem("Fourth", evenGroup) AddItem("Fifth", oddGroup) AddItem("Sixth", evenGroup) AddItem("Seventh", oddGroup) AddItem("Eighth", evenGroup) AddItem("Ninth", oddGroup) AddItem("Tenth", evenGroup) End With End Sub Private Sub AddItem(text As String, group As ListViewGroup) Dim item As New ListViewItem With {.Name = text, .Text = text, .Group = group} ListView1.Items.Add(item).SubItems.Add(item.Index.ToString()) End Sub Private Sub ListView1_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles ListView1.ColumnClick ListView1.ListViewItemSorter = New ListViewItemSorter(e.Column) For Each item As ListViewItem In ListView1.Items item.SubItems(1).Text = item.Index.ToString() Next End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click SelectNextItem() End Sub Private Sub SelectNextItem() Dim selectedItem = ListView1.SelectedItems(0) Dim selectedGroup = selectedItem.Group 'Get the item in the same group with the next highest index. Dim nextItem = selectedGroup.Items. Cast(Of ListViewItem)(). OrderBy(Function(item) item.Index). FirstOrDefault(Function(item) item.Index > selectedItem.Index) If nextItem Is Nothing Then 'The selected item is the last in its group so find the next group that contains items. Dim selectedGroupIndex = ListView1.Groups.IndexOf(selectedGroup) Do selectedGroupIndex += 1 If selectedGroupIndex = ListView1.Groups.Count Then 'Wrap to the first group after the last. selectedGroupIndex = 0 End If selectedGroup = ListView1.Groups(selectedGroupIndex) Loop Until selectedGroup.Items.Count > 0 'Get the first item in the next group. nextItem = selectedGroup.Items. Cast(Of ListViewItem)(). OrderBy(Function(item) item.Index). First() End If 'Change the selection. selectedItem.Selected = False nextItem.Selected = True End Sub End Class Public Class ListViewItemSorter Implements IComparer Private ReadOnly columnIndex As Integer Public Sub New(columnIndex As Integer) Me.columnIndex = columnIndex End Sub Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare Dim itemX = DirectCast(x, ListViewItem) Dim itemY = DirectCast(y, ListViewItem) Select Case columnIndex Case 0 Return itemX.Text.CompareTo(itemY.Text) Case 1 Return CInt(itemX.SubItems(1).Text).CompareTo(CInt(itemY.SubItems(1).Text)) End Select Return 0 End Function End Class
Last edited by jmcilhinney; Jun 14th, 2018 at 09:48 PM.
-
Jun 18th, 2018, 03:15 AM
#10
Thread Starter
New Member
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
-
Jun 18th, 2018, 04:17 AM
#11
Re: Listview - Next item when not ordered by Index
To clarify some of the LINQ parts, this:
vb.net Code:
Dim nextItem = selectedGroup.Items. Cast(Of ListViewItem)(). OrderBy(Function(item) item.Index). FirstOrDefault(Function(item) item.Index > selectedItem.Index)
would be equivalent to this:
vb.net Code:
Dim items(selectedGroup.Items.Count - 1) As ListViewItem selectedGroup.Items.CopyTo(items, 0) Array.Sort(items, AddressOf CompareListViewItemsByIndex) Dim nextItem As ListViewItem = Nothing For Each item As ListViewItem In items If item.Index > selectedItem.Index Then nextItem = item Exit For End If Next
while this:
vb.net Code:
nextItem = selectedGroup.Items. Cast(Of ListViewItem)(). OrderBy(Function(item) item.Index). First()
would be equivalent to this:
vb.net Code:
Dim items(selectedGroup.Items.Count - 1) As ListViewItem selectedGroup.Items.CopyTo(items, 0) Array.Sort(items, AddressOf CompareListViewItemsByIndex) Dim nextItem As ListViewItem = items(0)
where the comparison method used to sort the array in each case looks like this:
vb.net Code:
Private Function CompareListViewItemsByIndex(item1 As ListViewItem, item2 As ListViewItem) As Integer Return item1.Index.CompareTo(item2.Index) End Function
Note that there are also Last and LastOrDefault methods that may be useful for 'Previous Item' functionality.
Last edited by jmcilhinney; Jun 18th, 2018 at 04:22 AM.
-
Jun 18th, 2018, 07:13 AM
#12
Thread Starter
New Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|