Link to home
Start Free TrialLog in
Avatar of alexkwok
alexkwok

asked on

Listview "Selection" event that is raised when Items are being selected?

What I'm looking for is a way to know when a selection occurs, much like how in Windows Explorer, the filesize in the status bar is update as you select files.

There isn't an event built-in that I know of, and the Mouse(up/down/move) events do not work as it should.  Anyone know how to do this?
Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

There is a method called OnSelectedIndexChanged for listview controls
Avatar of alexkwok
alexkwok

ASKER

Which version of common controls is it on?  The one I am using does not have it.
Private Sub Form_Load()
    With ListView1
        Call .ListItems.Add(, , "1")
        Call .ListItems.Add(, , "2")
        Call .ListItems.Add(, , "3")
        Call .ListItems.Add(, , "4")
    End With
End Sub
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
    Me.Caption = "Selected: " & Item.Text
End Sub
I do it in a separate helper class - I call CheckSelChange (sub which checks selected count) in KeyUp event and in MouseUp, and also in MouseMove if flag buttonisdown was set in MouseDown.
To check count I use:
     SelCount = SendMessage(ListView.hWnd, LVM_GETSELECTEDCOUNT, 0&, ByVal 0&)
What you want is a Hittest.  

With LV as the ListView control:

Private Sub LV_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim li As ListItem
   
    Set li = LV.HitTest(x, y)
   
    Select Case Button
 
        Case 1 ' Left Click
            If Not li Is Nothing Then
            ' An Item was clicked with the Left Button

        Case 2
            If li Is Nothing Then ' Whitespace was clicked with Right Button
               
            Else
                ' An Item was clicked with the Right Button

End Select

I hope this helps.

RichW
Ooops,  forgot the End If in the Case 2.

I actually want a solution where if you drag the selection rectangle and start selecting things, the status bar will update.  Using MouseMove will not work because it just doesn't raise events when you select like that.  Mouseup/mousedown/itemclick  only occurs when you click.

I know that this is possible by subclassing, but I would like a more elegant solution.

(If you don't understand about the selection thing, open Windows Explorer and drag the selection rectangle.  _As_ you select multiple files, the status bar will update the sum size of the files everytime the selection changes).
ASKER CERTIFIED SOLUTION
Avatar of zzzzzooc
zzzzzooc

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
MouseMove isn't raised, true, but I have a code there, it is executed once after selection is finished.
Here is a small sample which shows selection while user is dragging selection rectangle and selecting items.
It doesn't show selection if user is dragging and DEselecting items, but it might be partial solution until you make something with timer, as zzzzzoc suggested.
(to simplify code, I removed CheckSelChange, it requires module variables oldNumItems and oldSelItem, I simply raise SelChange event instead)

' caList class ---------------------------------
Option Explicit
Event SelChange()
Public WithEvents ListView As ListView
Private idx As Long
Private buttonisdown As Boolean
' APIs
Private Const LVM_FIRST = &H1000
Private Const LVM_GETSELECTEDCOUNT = (LVM_FIRST + 50)
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

' returns collection of selected items
Public Property Get HilitedItems() As Collection
    Dim i As Long, cnt As Long, selcnt As Long
    On Error Resume Next
   
    selcnt = SelCount
    Set HilitedItems = New Collection
   
    For i = 1 To ListView.ListItems.Count
        If ListView.ListItems(i).Selected = True Then
            HilitedItems.Add ListView.ListItems(i)
            cnt = cnt + 1
            If cnt = selcnt Then Exit Property
        End If
    Next
End Property

Public Property Get SelCount() As Long
    SelCount = SendMessage(ListView.hwnd, LVM_GETSELECTEDCOUNT, 0&, ByVal 0&)
End Property

Private Sub ListView_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
    buttonisdown = True
    If ListView.HitTest(x, y) Is Nothing Then
        If Not ListView.SelectedItem Is Nothing Then
            idx = ListView.SelectedItem.Index
            Set ListView.SelectedItem = Nothing
            ListView.ListItems(idx).Selected = False
        End If
    End If
End Sub

Private Sub ListView_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
    If buttonisdown Then
        If Button = 0 Then
            buttonisdown = False
            RaiseEvent SelChange
        End If
    End If
End Sub

Private Sub ListView_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
    buttonisdown = False
    RaiseEvent SelChange
End Sub

Private Sub ListView_KeyUp(KeyCode As Integer, Shift As Integer)
    RaiseEvent SelChange
End Sub

Private Sub ListView_ItemClick(ByVal Item As MSComctlLib.ListItem)
    RaiseEvent SelChange
End Sub


' Form1, add listview ------------------------
Option Explicit
Private WithEvents aList As caList

Private Sub aList_SelChange()
    Dim sum As Long, i As Long, itm As ListItem
   
    Beep
    For Each itm In aList.HilitedItems
        sum = sum + CLng(itm.SubItems(1))
    Next
    Caption = aList.SelCount & " items, sum=" & sum
End Sub

Private Sub Form_Load()
    With ListView1
        With .ColumnHeaders
            .Add 1, , "Name", 1555, 0
            .Add 2, , "Count", 1485, 0
        End With
        .View = lvwReport
        .MultiSelect = True
        .ListItems.Add(, , "item 1").SubItems(1) = 10
        .ListItems.Add(, , "item 2").SubItems(1) = 20
        .ListItems.Add(, , "item 3").SubItems(1) = 30
        .ListItems.Add(, , "item 4").SubItems(1) = 40
    End With
   
    Set aList = New caList
    Set aList.ListView = Me.ListView1
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set aList = Nothing  ' cleanup
End Sub
zzzz's code works well enough... I guess it'll have to do until I decide to subclass.