|
Ist doch nett, dass Sie die ListItems der Microsoft Common Controls ab Version 6 mit CheckBoxen versehen können, oder nicht? Nicht so nett ist, dass Sie die ListSubItems nicht ebenso mit CheckBoxen versehen können. Da Sie aber jedem ListSubItem ein eigenes Symbol zuweisen können (ReportIcon), sollte das gar nicht so schwierig sein, ein ListSubItem mit zwei umschaltbaren CheckBox-Symbolen zu versehen.
Die erste und größere Hürde, die Sie dazu nehmen müssen, ist die Ermittlung, wann der Anwender auf ein ReportIcon geklickt hat. Die HitTest-Methode des ListView-Steuerelements hilft uns hier überhaupt nicht weiter - sie meldet nur einen Klick auf das eigentliche ListItem, und dazu noch völlig undifferenziert. Das API liefert dagegen viel mehr an Informationen - Sie müssen es nur entsprechend fragen. Im Artikel "Spalten-Treffer" haben wir beschrieben, wie Sie an die differenzierteren Informationen herankommen.
Nun bleibt nur noch, den jeweiligen Schaltzustand des ReportIcons festzuhalten und für die Anzeige des jeweiligen Symbols zu sorgen. Zur Speicherung des Schaltzustands könnten Sie zwar die Tag-Eigenschaft des ListSubItems verwenden. Doch das wäre einfach Verschwendung, da Sie diese vielleicht für ganz andere Zwecke benötigen. Es reicht vollauf, die Schlüssel (Keys) der Symbole im der ListView zugeordneten ImageListe entsprechend zu kodieren. Sie vergeben einfach wie gewohnt einen Schlüssel, setzen jedoch die Kennung des Schaltzustandes davor: "0" für "unchecked" und "1" für "checked" - also etwa "0CheckBox" und "1CheckBox". Natürlich können Sie für die beiden Schaltzustände Symbole nach Belieben wählen - es muss nicht unbedingt eine schlichte CheckBox-Grafik sein.
Im Falle eines Klicks auf ein ListSubItem lesen Sie nun den Symbol-Schlüssel aus der ReportIcon-Eigenschaft aus, und tauschen das eine, links stehende Zeichen gegen sein Gegenstück aus. Dann schreiben den geänderten Schlüssel wieder zurück - die CheckBox ist "umgeschaltet".
Genau dies erledigt die folgende Funktion ListSubItemCheckToggle. Wenn das betreffende ListSubItem bereits greifbar ist, könne Sie als es als ersten (und einzigen) Parameter übergeben. Kennen Sie wenigstens das übergeordnete ListItem, übergeben Sie statt dessen dieses im zweiten, optionalen Parameter und fügen im dritten, ebenfalls optionalen Parameter den Schlüssel des ListSubItems hinzu. Fehlt Ihnen auch das ListItem, können Sie als letzte Möglichkeit auch das ListView selbst übergeben, zusammen mit den entsprechenden Schlüsseln für das ListItem und dessen ListSubItem.
Die Hilfsfunktion zGetListSubItem ermittelt aus den übergebenen Parametern das gewünschte ListSubItem. Ich habe diese Ermittlung in eine eigene Funktion ausgelagert, da sie auch noch weitere Verwendung finden wird (siehe weiter unten).
Public Function ListSubItemCheckToggle( _
Optional ListSubItem As ListSubItem, _
Optional ListItem As ListItem, _
Optional ListSubItemKey As Variant, _
Optional ListView As ListView, _
Optional ListItemKey As Variant) As Boolean
Dim nListSubItem As ListSubItem
Dim nValue As Boolean
Dim nReportIcon As String
Set nListSubItem = zGetListSubItem(ListSubItem, ListItem, _
ListSubItemKey, ListView, ListItemKey)
If nListSubItem Is Nothing Then
Err.Raise 380
Else
With nListSubItem
nReportIcon = .ReportIcon
nValue = Not CBool(Left$(nReportIcon, 1))
.ReportIcon = Abs(nValue) & Mid$(nReportIcon, 2)
ListSubItemCheckToggle = nValue
End With
End If
End Function
Private Function zGetListSubItem( _
ListSubItem As ListSubItem, ListItem As ListItem, _
ListSubItemKey As Variant, ListView As ListView, _
ListItemKey As Variant) As ListSubItem
If ListSubItem Is Nothing Then
If ListItem Is Nothing Then
If Not (ListView Is Nothing) Then
Set zGetListSubItem = _
ListView.ListItems(ListItemKey).ListSubItems(ListSubItemKey)
End If
Else
Set zGetListSubItem = ListItem.ListSubItems(ListSubItemKey)
End If
Else
Set zGetListSubItem = ListSubItem
End If
End Function
Die Prüfung, ob das Symbol eines ListSubItems angeklickt wurde, und die Umschaltung des Symbols können Sie zur Vereinfachung auch noch über eine Funktion abwickeln. In der Funktion LvwListSubItemCheck wird die Funktion ListItemHitTest des Moduls aus dem oben erwähnten Artikel aufgerufen. Ergibt der Aufruf, dass ein ListSubItem angeklickt wurde, wird die oben stehende Funktion ListSubItemCheckToggle mit diesem ListSubItem (das wie auch das dazugehörige ListItem von ListItemHitTest praktischerweise gleich geliefert wird) als Parameter aufgerufen. Dazu gibt LvwListSubItemCheck True als Erfolgsmeldung zurück.
Public Function LvwListSubItemCheck(ListView As ListView, _
ByVal X As Single, ByVal Y As Single, _
Optional ListSubItem As ListSubItem, _
Optional ListItem As ListItem) As Boolean
Dim nPart As ListItemHitTestPartConstants
Select Case ListItemHitTest(ListView, X, Y, ListItem, ListSubItem, _
nPart)
Case lhsListSubItem
Select Case nPart
Case lhpIcon
ListSubItemCheckToggle ListSubItem
LvwListSubItemCheck = True
End Select
End Select
End Function
Der Aufruf im MouseDown-Ereignis des ListViews ist sich nun unvergleichlich einfach:
Private Sub ListView1_MouseDown(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
LvwListSubItemCheck ListView1, X, Y
End Sub
Einen bestimmten Zustand des CheckBox-Symbols eines ListSubItems können Sie natürlich auch per Code direkt setzen. Sie können aber auch die folgenden Property-Prozeduren verwenden, um das gewisse Eigenschaften-Feeling auch für ein "gechecktes" ListSubItem zu erhalten. Auch hier können Sie wieder als Parameter die ganze Palette von einem vorhandenen ListSubItem bis hin zum ListView und den Schlüsseln verwenden - für das Heraussuchen des ListSubItems sorgt wieder die Hilfsfunktion zGetListSubItem.
Public Property Get ListSubItemChecked( _
Optional ListSubItem As ListSubItem, _
Optional ListItem As ListItem, _
Optional ListSubItemKey As Variant, _
Optional ListView As ListView, _
Optional ListItemKey As Variant) As Boolean
Dim nListSubItem As ListSubItem
Set nListSubItem = zGetListSubItem(ListSubItem, ListItem, ListSubItemKey, _
ListView, ListItemKey)
If nListSubItem Is Nothing Then
Err.Raise 380
Else
ListSubItemChecked = CBool(Left$(ListSubItem.ReportIcon, 1))
End If
End Property
Public Property Let ListSubItemChecked( _
Optional ListSubItem As ListSubItem, _
Optional ListItem As ListItem, _
Optional ListSubItemKey As Variant, _
Optional ListView As ListView, _
Optional ListItemKey As Variant, Checked As Boolean)
Dim nListSubItem As ListSubItem
Set nListSubItem = zGetListSubItem(ListSubItem, ListItem, _
ListSubItemKey, ListView, ListItemKey)
If nListSubItem Is Nothing Then
Err.Raise 380
Else
With nListSubItem
.ReportIcon = Abs(Checked) & Mid$(.ReportIcon, 2)
End With
End If
End Property
Und hier nun nahezu das gewohnte Eigenschaften-Feeling:
Debug.Print ListSubItemChecked(ListSubItemXY)
oder
ListSubItemChecked(ListSubItemABC) = True
Ihnen hat schon die CheckBox des ListItems selbst nicht gefallen? Auch kein Problem - was einem ListSubItem recht ist, soll einem ListItem doch auch billig sein. Sie wählen einfach beliebige Symbole als Symbol für das ListItem (SmallIcon). Die Funktionen für ListSubItems lassen sich beinahe 1:1 übertragen - lediglich der Parameter-Zoo hat sich selbstverständlich ein wenig reduziert. Einziger Haken an der Sache: Sie haben nicht mehr das Extra-Symbol neben der CheckBox zur Verfügung.
Public Function LvwListItemCheck(ListView As ListView, _
ByVal X As Single, ByVal Y As Single, _
Optional ListItem As ListItem) As Boolean
Dim nPart As ListItemHitTestPartConstants
Select Case ListItemHitTest(ListView, X, Y, ListItem, _
Nothing, nPart)
Case lhsListItem
Select Case nPart
Case lhpIcon
ListItemCheckToggle ListItem
LvwListItemCheck = True
End Select
End Select
End Function
Public Function ListItemCheckToggle( _
Optional ListItem As ListItem, _
Optional ListView As ListView, _
Optional ListItemKey As Variant) As Boolean
Dim nListItem As ListItem
Dim nValue As Boolean
Dim nSmallIcon As String
Set nListItem = zGetListItem(ListItem, ListView, ListItemKey)
If nListItem Is Nothing Then
Err.Raise 380
Else
With nListItem
nSmallIcon = .SmallIcon
nValue = Not CBool(Left$(nSmallIcon, 1))
.SmallIcon = Abs(nValue) & Mid$(nSmallIcon, 2)
ListItemCheckToggle = nValue
End With
End If
End Function
Public Property Get ListItemChecked( _
Optional ListItem As ListItem, _
Optional ListView As ListView, _
Optional ListItemKey As Variant) As Boolean
Dim nListItem As ListItem
Set nListItem = zGetListItem(ListItem, ListView, ListItemKey)
If nListItem Is Nothing Then
Err.Raise 380
Else
ListItemChecked = CBool(Left$(ListItem.SmallIcon, 1))
End If
End Property
Public Property Let ListItemChecked( _
Optional ListItem As ListItem, _
Optional ListView As ListView, _
Optional ListItemKey As Variant, Checked As Boolean)
Dim nListItem As ListItem
Set nListItem = zGetListItem(ListItem, ListView, ListItemKey)
If nListItem Is Nothing Then
Err.Raise 380
Else
With nListItem
.SmallIcon = Abs(Checked) & Mid$(.SmallIcon, 2)
End With
End If
End Property
Private Function zGetListItem(ListItem As ListItem, _
ListView As ListView, ListItemKey As Variant) As ListItem
If ListItem Is Nothing Then
If Not (ListView Is Nothing) Then
Set zGetListItem = ListView.ListItems(ListItemKey)
End If
Else
Set zGetListItem = ListItem
End If
End Function
Und nun wird vielleicht auch klar, warum die im MouseDown-Ereignis verwendeten Funktionen LvwListSubItemCheck und LvwListItemCheck eine Erfolgsmeldung zurückgeben - falls die eine schon einen Treffer erzielt, braucht die andere nicht mehr bemüht zu werden:
Private Sub ListView1_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
If Not LvwListItemCheck(ListView1, X, Y) Then
LvwListSubItemCheck ListView1, X, Y
End If
End Sub
|