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. |
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
|