Visual Basic Common Control API Routines
SendMessage: Clicking ListView White Space to De-select Items
     
Posted:   Monday September 16, 2002
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows XP
OS restrictions:   None
Author:   VBnet - Randy Birch
     
 Prerequisites
This code is equally applicable to the VB4-32, VB5 and VB6 Listview controls operating in either "normal" or FullRowSelect mode.

A common complaint with the Listview control is that it always returns a SelectedItem even when no item has been selected, or when you programmatically attempt to deselect everything using the ListView's intrinsic commands.

This pages shows the code required to deselect a selected item when the user clicks on the white space within the control. White space in a Report-mode listview varies dependant on whether FullRowSelect is active or not.  When FullRowSelect is not active (only clicking the main list item selects an item), the white space is defined as :

  • the very narrow area to the left of the control between the control border and the listitem or SmallIcon (shown in red in the illustration)
  • the area immediately to the right of the main listitem text (green area)
  • the area to the right of the last SubItem column (blue area)

When FullRowSelect is active, only the last (blue) item is applicable. In addition, white space never includes the non-textual areas within SubItems in Fullrowselect mode.

When the red or green white space is clicked, or when a main item is clicked, the SubItem index returned from the SendMessage call is 0.  When the blue area is clicked, the SubItem index is -1. Otherwise the SubItem index reflects the SubItem column clicked.  This makes it easy to customize the routine to deselect (or select) items based on the index clicked. In fact, you could code for a double click event and if, say, the SubItem index was -1 (tracked from the MouseDown event), you could programmatically do a select all or select none on the control by calling the code shown at SendMessage: ListView Select / Deselect All .

This example contains all code required to construct the illustration shown.

 BAS Module Code
None.

 Form Code
Add a command button (Command1), a check box (Check1) and four labels (Label1 - Label4), with the following code:

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_SUBITEMHITTEST As Long = (LVM_FIRST + 57)
Private Const LVM_HITTEST As Long = (LVM_FIRST + 18)

Private Const LVHT_ABOVE = &H8
Private Const LVHT_BELOW = &H10
Private Const LVHT_TORIGHT = &H20
Private Const LVHT_TOLEFT = &H40
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 HITTESTINFO
   pt As POINTAPI
   flags As Long
   iItem As Long
   iSubItem  As Long
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 Form_Load()

   Dim itmx As ListItem
   Dim cnt As Long
   Dim tmp As Long
   
   Randomize Timer
   
   With ListView1
   
      .ColumnHeaders.Add , , "Name"
      .ColumnHeaders.Add , , "Size"
      .ColumnHeaders.Add , , "Type"
      .ColumnHeaders.Add , , "Created"
      .View = lvwReport
   
      For cnt = 1 To 100
      
        'create a few random entries
        'to simulate real data
         tmp = Int(Rnd(20) * 20) + 1
     
         Set itmx = .ListItems.Add(, , String(tmp, Chr$(123 - tmp)))       
         itmx.SubItems(1) = tmp & " kb"
         itmx.SubItems(2) = "winzip file"
         itmx.SubItems(3) = DateAdd("d", -Int(Rnd(365) * 365), Date)
      
      Next
   
   End With
   
   With Check1
      .Caption = "Toggle FullRowSelect"
      .Value = vbChecked
   End With

End Sub


Private Sub Command1_Click()

   Unload Me

End Sub


Private Sub Check1_Click()

  'VB6 only. VB4-32 and VB5 users will
  'require the FullRowSelect API code from
  'http://vbnet.mvps.org/code/comctl/lvfullrowselect.htm
   On Local Error Resume Next
   
   ListView1.FullRowSelect = Check1.Value = vbChecked

End Sub


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

   Dim hti As HITTESTINFO
   Dim itemIndex As Long
      
  'Fill a HITTESTINFO structure with
  'information about the point in the
  'listview where the mouse was clicked.
   With hti
      .pt.x = (x / Screen.TwipsPerPixelX)
      .pt.y = (y / Screen.TwipsPerPixelY)
      .flags = LVHT_ABOVE Or LVHT_BELOW Or _
               LVHT_TOLEFT Or LVHT_TORIGHT Or _
               LVHT_ONITEMICON Or LVHT_ONITEMLABEL Or LVHT_NOWHERE
   End With
      
   itemIndex = SendMessage(ListView1.hwnd, LVM_SUBITEMHITTEST, 0, hti)
   
   If itemIndex = -1 And _
      (hti.iSubItem = -1 Or _
       hti.iSubItem = 0) Then
   
      Set ListView1.SelectedItem = Nothing
   
   End If

  'update labels with index info
   Label3.Caption = itemIndex
   Label4.Caption = hti.iSubItem
      
End Sub


Private Sub ListView1_MouseUp(Button As Integer, _
                              Shift As Integer, _
                              x As Single, _
                              y As Single)
                              
  'update labels with string info,
  'first testing to ensure SelectedItem
  'can be referenced without error.
   If Not ListView1.SelectedItem Is Nothing Then
      Label1.Caption = ListView1.SelectedItem.Index
      Label2.Caption = ListView1.SelectedItem.Text
   Else
      Label1.Caption = "(no selected index)"
      Label2.Caption = "(nothing)"
   End If
   
End Sub
 Comments
Note that the code is split between the MouseDown and MouseUp events. This is due to the fact that in the MouseDown event, it is too early to retrieve the selected item information since it has not been set by the control. Similarly, the MouseDown code can not go into the MouseUp event, because by that time its too late to clear the selected item..

 
 

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