Visual Basic Common Control API Routines
SendMessage: Perform Hit Testing in ListViews
     
Posted:   Thursday January 20, 2000
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows NT4
OS restrictions:   None
Author:   VBnet - Randy Birch
     
 Prerequisites
The hit test code works with both the VB6 mscomctl.ocx and VB5 comctl32.dll controls.

The FullRowSelect property is available only on the VB6 control. VB5 users should see the VBnet FullRowSelect API method in the Common Controls Library. When using the VB5 control, enhanced Comctl32 functionality is only available to users with comctl32.dll version 4.70 or greater installed. This dll is typically installed with IE3.x or greater.


This page demonstrates using the LVHITTESTINFO type along with the LVM_SUBITEMHITTEST message to determine where on the control the mouse was clicked, regardless of the view mode or whether the FullRowSelect feature has been enabled (Label1). The code is provided in the MouseDown event to both take advantage of the mouse coordinates passed, as well as providing a mechanism to perform an action before the corresponding ItemClick event is fired.   The MouseDown event also contains a line that will select the primary list item whenever a SubItem is clicked, even in non-FullRowSelect mode.

Note that the illustration shows the selected list index as its 0-based API value.

This page contains all the code required to build the demonstration app shown. It also works when an imagelist has been assigned to the control; in that case it will accurately identify and report where the control was clicked - overtop the text or overtop the icon (Label2).

 BAS Module Code
None.

 Form Code
Create a new project, adding a VB5 or VB6 ListView control (ListView1), along with a check box (Check1) and two labels (Label1, Label2). You can optionally add an ImageList populated with at least a single 16x16 icon, assigning it to the SmallIcon property of the ListView. Add the following code to the form:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Const LVM_FIRST As Long = &H1000
Private Const LVM_HITTEST As Long = (LVM_FIRST + 18)
Private Const LVM_SUBITEMHITTEST As Long = (LVM_FIRST + 57)
Private Const LVHT_NOWHERE As Long = &H1
Private Const LVHT_ONITEMICON As Long = &H2
Private Const LVHT_ONITEMLABEL As Long = &H4
Private Const LVHT_ONITEMSTATEICON As Long = &H8
Private Const LVHT_ONITEM As Long = (LVHT_ONITEMICON Or _
                                     LVHT_ONITEMLABEL Or _
                                     LVHT_ONITEMSTATEICON)
Private Type POINTAPI
   x As Long
   y As Long
End Type

Private Type LVHITTESTINFO
    pt As POINTAPI
    flags As Long
    iItem As Long
    iSubItem  As Long  'ie3+ only .. was NOT in win95.
                       'Valid only for LVM_SUBITEMHITTEST
End Type

Private Declare Function SendMessage Lib "user32" _
   Alias "SendMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long


Private Sub Check1_Click()

   ListView1.FullRowSelect = Not ListView1.FullRowSelect

End Sub


Private Sub Form_Load()

   Dim itmX As ListItem
   Dim itmH As ColumnHeader
   Dim n As Long
     
  'Crete dummy data in the ListView
   With ListView1
   
      .View = lvwReport
      .FullRowSelect = False
      .LabelEdit = lvwManual
      
      For n = 1 To 4
         Set itmH = .ColumnHeaders.Add(, , "Column " & CStr(n))
         itmH.Width = 1000
      Next

      For n = 1 To 20
         Set itmX = .ListItems.Add(, , "Item " & CStr(n))
         itmX.SubItems(1) = CStr(n)
         itmX.SubItems(2) = CStr(n ^ 2)
         itmX.SubItems(3) = CStr(n ^ 3)
      Next
   
   End With
   
End Sub


Private Sub ListView1_MouseDown(Button As Integer, _
                                Shift As Integer, _
                                x As Single, y As Single)

   Dim HTI As LVHITTESTINFO
   Dim itmX As ListItem
   Dim msg1 As String
   Dim msg2 As String

  '----------------------------------------
  'this gets the hittest info using
  'the mouse co-ordinates
   With HTI
      .pt.x = (x \ Screen.TwipsPerPixelX)
      .pt.y = (y \ Screen.TwipsPerPixelY)
      .flags = LVHT_ONITEM
   End With
      
   Call SendMessage(ListView1.hwnd, LVM_SUBITEMHITTEST, 0, HTI)
      
  '----------------------------------------
  'this determines whether the hit test
  'returned a main or sub item
   If HTI.iSubItem = 0 Then
      
      If HTI.iItem > -1 Then
         msg1 = "User clicked over main item " & HTI.iItem
      Else
         msg1 = "User clicked a main item's white space"
      End If
                
   ElseIf HTI.iSubItem > 0 Then
   
     msg1 = "User clicked main item " & HTI.iItem & _
            " by clicking SubItem " & HTI.iSubItem
                
   End If
   
  '----------------------------------------
  'this determines what part of
  'the item or SubItem was clicked
   If HTI.flags And LVHT_ONITEM Then
   
      If HTI.flags And LVHT_ONITEMICON Then
         msg2 = "(click occurred over the item's icon area)"
      End If
      
      If HTI.flags And LVHT_ONITEMLABEL Then
   
         Select Case HTI.iSubItem
            Case 0:    msg2 = "(click occurred over the item's main text)"
            Case Else: msg2 = "(click occurred over the SubItem text)"
         End Select
      End If
   
   End If
   
  '----------------------------------------
  'this selects the current item if the
  'control's FullRowSelect is False, and
  'a SubItem was clicked. (One is added
  'because the API is 0-based, and the
  'ListItems collection is 1-based).
   If HTI.iSubItem > 0 Then
      ListView1.ListItems(HTI.iItem + 1).Selected = True
   End If
   
   Label1.Caption = msg1
   Label2.Caption = msg2
   
End Sub
 Comments

 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter