Home
Search
 
What's New
Index
Books
Links
Q & A
Newsletter
Banners
 
Feedback
Tip Jar
 
C# Helper...
 
XML RSS Feed
Follow VBHelper on Twitter Follow VBHelper on Twitter
 
 
 
MSDN Visual Basic Community
 
 
 
 
 
TitleDetermine which ListView row and column are under the mouse in Visual Basic .NET
DescriptionThis example shows how to determine which ListView row and column are under the mouse in Visual Basic .NET
KeywordsListView, row, column, mouse, click, Visual Basic .NET, VB.NET
CategoriesControls
 
When you move the mouse over the program's ListView control, it displays the index of the row and column under the mouse. You could modify the program to display the row and column when you click on the control.

There are three interesting pieces to this example. The FindListViewRowColumn function shown in the following code finds the row and column under the mouse.

 
' Find and column at this position.
' Return True if we are successful.
' Return False if the mouse is not under a ListView cell.
Public Function FindListViewRowColumn(ByVal lvw As _
    ListView, ByVal x As Integer, ByVal y As Integer, ByRef _
    row As Integer, ByRef column As Integer) As Boolean
    ' Find the row under the mouse.
    For i As Integer = 0 To lvw.Items.Count - 1
        If lvw.Items(i).Bounds.Contains(x, y) Then
            row = i

            ' See which column is under the mouse.
            column = GetListViewColumn(lvw, x)
            Return True
        End If
    Next i

    Return False
End Function
 
The code loops through the control's items (rows), checking to see whether an item's bounds contain the point. When it finds the row that contains the point, it saves the row number and calls GetListViewColumn to find the column at that point.

The GetListViewColumn function shown in the following code finds the column that contains a specific X position.

 
' Return the column under this X position.
Public Function GetListViewColumn(ByVal lvw As ListView, _
    ByVal x As Integer) As Integer
    ' Get the horizontal scroll bar's position.
    x += GetScrollPos(lvw.Handle, SB_HORZ)

    ' Get the column headers in their current display order.
    Dim headers As List(Of ColumnHeader) = _
        GetListViewColumnsInDisplayOrder(lvw)

    ' Find the column that includes this X position.
    For i As Integer = 0 To headers.Count - 1
        x -= headers(i).Width
        If x <= 0 Then Return headers(i).Index
    Next i

    Return -1
End Function
 
This function does three tricky things. First, it calls the GetScrollPos API function to see where thue ListView's horizontal scroll bar is and adds that amount to the X coordinate. For example, if the control's items are scrolled 100 pixels to the left, then the X coordinate over the items is really 100 pixels greater than it appears on the screen. In other words, the position over the items would be 100 pixels farther to the right if the items were not scrolled.

Second, the code calls GetListViewColumnsInDisplayOrder to get the control's columns in the order in which they currently appear on the screen. If the ListView's AllowColumnReorder property is true, then the user may have dragged the columns into new positions. This step lets the code consider the columns in their current order rather than their original order.

The third thing GetListViewColumn does is it loops through the columns (in their current order) subtracting their widths from the X value. When X <= 0, the code has just passed the column that contains the original X coordinate value.

The GetListViewColumnsInDisplayOrder function shown in the following code returns a List giving the ListView's column headers in their current order.

 
' Return the ListView's columns in their display order.
Public Function GetListViewColumnsInDisplayOrder(ByVal lvw _
    As ListView) As List(Of ColumnHeader)
    Dim headers As New List(Of ColumnHeader)

    ' Find each of the headers in their display order.
    For i As Integer = 0 To lvw.Columns.Count - 1
        ' Find the column with display index i.
        For j As Integer = 0 To lvw.Columns.Count - 1
            If lvw.Columns(j).DisplayIndex = i Then
                ' This is the one. Add it to the list.
                headers.Add(lvw.Columns(j))
                Exit For
            End If
        Next j
    Next i
    Return headers
End Function
 
This function simply loops through the ListView control's Columns collection searching for the column header with DisplayIndex value 0, 1, 2, and so forth, adding the column headers to the result List in order. When it finishes, it returns the result.

Richard Moss pointed out that the ListView control has a HitTest method that does much of this work for you. I've revised the code to use either method. Here's the new code.

 
' Method 2: Use HitTest.
Dim hti As ListViewHitTestInfo = _
    lvwBooks.HitTest(e.Location)
If (hti.Item Is Nothing) Then Return
Dim item As ListViewItem = hti.Item
txtRow.Text = item.Index.ToString()

' See which sub-item this is.
Dim subitem As ListViewItem.ListViewSubItem = hti.SubItem
For i As Integer = 0 To item.SubItems.Count - 1
    If (item.SubItems(i) Is subitem) Then
        txtColumn.Text = i.ToString()
    End If
Next i
 
The code still needs to do some work to find the index of the sub-item under the mouse if you want that sub-item's index. This method is probably better if for no other reason than it avoids using the API GetScrollPos function.

The sub-items also have a Bounds property that you could use to see whether the mouse is under a sub-item. Unfortunately the first sub-item, which represents the item as a whole, has bounds that include the entire row so it doesn't help you determine whether the mouse is under the first column.


Mark Prichard added yet another twist to this example. Instead of digging through the Subitems collection to find the sub-item, you can just use the collection's IndexOf method. Nice!

 
' Method 3: Use HitTest and IndexOf.
Dim hti As ListViewHitTestInfo = _
    lvwBooks.HitTest(e.Location)
If hti.Item Is Nothing Then Return
Dim item As ListViewItem = hti.Item
txtRow.Text = item.Index.ToString()

' See which sub-item this is.
txtColumn.Text = _
    item.SubItems.IndexOf(hti.SubItem).ToString()
 
 
Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.
  Updated