|
Zwei kleine Unsauberkeiten in der Implementierung des TreeView-Steuerelements der Microsoft Common Controls Version 6 sorgen dafür, dass die steuerelementeigenen CheckBoxen (wenn eingeschaltet) nicht unbedingt wie erwartet funktionieren. Zum einen lässt sich der CheckBox-Zustand eines Knotens nicht im NodeCheck-Ereignis gezielt setzen - um etwa eine Änderung durch den Anwender rückgängig machen zu können. Zum anderen verhält sich die CheckBox nicht wie gewohnt beim Überfahren mit niedergedrücktem Mausknopf bzw. beim Loslassen: Wird der Bereich der CheckBox verlassen, sollte sich ihr Zustand wieder zurücksetzen, und es sollte beim Loslassen außerhalb kein NodeCheck-Ereignis ausgelöst werden.
Die Klasse clsTvwNodeCheck verhilft dem TreeView-Steuerelement zu einem sauberen Umgang mit den Knoten-CheckBoxen. Legen Sie dazu eine Objekt-Variable je TreeView-Steuerelement als Ereignisempfänger an. Weisen Sie das gewünschte TreeView-Steuerelement nach dem Instanzieren der Eigenschaft TreeView der Klasse zu zum Beispiel:
Private WithEvents eTvwNodeCheck As clsTvwNodeCheck
Private Sub Form_Load()
Set eTvwNodeCheck = New clsTvwNodeCheck
Set eTvwNodeCheck.TreeView = TreeView1
End Sub
Verwenden Sie nun das NodeCheck-Ereignis dieser Klasse statt des originalen NodeCheck-Ereignisses des TreeView-Steuerelements:
Private Sub eTvwNodeCheck_NodeCheck(Node As MSComctlLib.Node)
' ...
End Sub
Nachdem der Instanz einer Klasse ein TreeView-Steuerelement zugewiesen worden ist, überwacht sie selbsttätig die Maus-Ereignisse des TreeViews. Zur Prüfung, ob die Mausposition im Bereich der CheckBox eines Knotens liegt, wird jeweils die Hilfsfunktion zHitTestCheck aufgerufen. Diese prüft zunächst wie gewohnt über die Methode HitTest des TreeViews, über welchem Knoten sich der Mauszeiger befindet. Befindet sich der Mauszeiger über einem Knoten, wird mittels der API-Nachricht TVM_HITTEST spezifiziert, ob sich der Mauszeiger auch über der CheckBox des Knotens befindet (siehe auch "Differenzierte Trefferprüfung").
Dementsprechend wird beim in MouseDown in der Variablen mNode festgehaltenen Knoten bei Mausbewegungen der CheckBox-Status beim Verlassen des CheckBox-Bereichs zurückgesetzt oder beim Wiedereintritt wieder zugewiesen. Ebenso wird das Ereignis NodeCheck nur dann im MouseUp-Ereignis ausgelöst, wenn der Mausknopf über der CheckBox des gleichen Knotens losgelassen wird. Da hier nun von Seiten des TreeViews die Behandlung des CheckBox-Zustands abgeschlossen ist, können Sie ihn nun bei der Bearbeitung des NodeCheck-Ereignisses der Klasse beliebig manipulieren.
Private Type POINTAPI
x As Long
y As Long
End Type
Private Type TVHITTESTINFO
pt As POINTAPI
flags As Long
hItem 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 WithEvents eTvw As TreeView
Private mDownChecked As Boolean
Private mNode As Node
Public Event NodeCheck(Node As Node)
Public Property Get TreeView() As TreeView
Set TreeView = eTvw
End Property
Public Property Set TreeView(New_TreeView As TreeView)
Set eTvw = New_TreeView
End Property
Private Sub eTvw_MouseDown(Button As Integer, Shift As Integer, _
x As Single, y As Single)
Set mNode = zHitTestCheck(x, y)
If Not (mNode Is Nothing) Then
mDownChecked = mNode.Checked
End If
End Sub
Private Sub eTvw_MouseMove(Button As Integer, Shift As Integer, _
x As Single, y As Single)
If Button Then
If Not (mNode Is Nothing) Then
If zHitTestCheck(x, y) Is mNode Then
zSetChecked Not mDownChecked
Else
zSetChecked mDownChecked
End If
End If
End If
End Sub
Private Sub eTvw_MouseUp(Button As Integer, Shift As Integer, _
x As Single, y As Single)
If Not (mNode Is Nothing) Then
If mNode Is zHitTestCheck(x, y) Then
RaiseEvent NodeCheck(mNode)
End If
End If
End Sub
Private Function zHitTestCheck(ByVal x As Single, _
ByVal y As Single) As Node
Dim nHitTestInfo As TVHITTESTINFO
Dim nHitNode As Node
Const TVM_HITTEST = &H1111
Const TVHT_ONITEMSTATEICON = &H40
Set nHitNode = eTvw.HitTest(x, y)
If Not (nHitNode Is Nothing) Then
With nHitTestInfo
With .pt
.x = x \ Screen.TwipsPerPixelX
.y = y \ Screen.TwipsPerPixelX
End With
SendMessage eTvw.hwnd, TVM_HITTEST, 0, nHitTestInfo
If (.flags And TVHT_ONITEMSTATEICON) = _
TVHT_ONITEMSTATEICON Then
Set zHitTestCheck = nHitNode
End If
End With
End If
End Function
Private Sub zSetChecked(Checked As Boolean)
With mNode
If .Checked <> Checked Then
.Checked = Checked
End If
End With
End Sub
|