|
Über die Methode HitTest
eines TreeView-Steuerelements aus den Microsoft Common Controls
können Sie erfahren, ob sich ein Knoten, und wenn ja, welcher, an
einem gegebenen Punkt liegt, etwa in den Mausereignissen.
Dim nNode As Node
Set nNode = TreeView1.HitTest(x, y)
If Not(nNode Is Nothing) Then
Debug.Print nNode.Text
End If
Liegt der Punkt in den Freiräumen links oder rechts des Knotens,
liefert die Methode HitTest keinen Treffer. Wenn Sie jedoch in
Erfahrung bringen möchten, welcher Knoten gerade auf der Höhe
eines gegebenen Punktes liegt, brauchen Sie eine andere
Testmöglichkeit.
So könnten Sie etwa die gesamte Pixelzeile auf der Höhe eines
gegebenen Punktes Pixel für Pixel durchtesten:
Dim nNode As Node
Dim nX As Single
With TreeView1
For nX = Screen.TwipsPerPixelX To .Width _
Step Screen.TwipsPerPixelX
Set nNode = .HitTest(nX, y)
If Not(nNode Is Nothing) Then
Debug.Print nNode.Text
Exit For
End If
Next
End With
Ist das TreeView-Steuerlement sehr breit, und sind die Knoten
sehr tief gestaffelt, kann das unter Umständen ganz schön lange
dauern, bis Sie so auf einen Knoten treffen. Schließlich verfügen
Sie ja auch über keinerlei Hinweise dazu, wo sich der Knoten in der
Zeile zumindest in etwa befinden könnte. Die Pixelzeile von rechts
her zu testen, würde daher auch nichts nützen.
Eine so genannte "binäre" Suche ist in den meisten
Fällen schneller. Binäre Suche heißt, die zu testende Spannweite,
angefangen von der Gesamtbreite des Steuerelement, immer weiter zu
halbieren und nur jeweils den Punkt in der Mitte zu testen. Grob
getestet ist das durchschnittlich in etwa drei Viertel aller Fälle
effizienter. Der ungünstigste Fall wäre, wenn gerade nur noch das
erste Pixel des Knoten am äußersten rechten Ende sichtbar wäre.
Dann würde erst der allerletzte Halbierungsversuch zum Treffer
führen, wobei die Zahl der Schritte größer wäre, als die Zahl
der Pixel in der Zeile.
Die Funktion TvwHitTestEx erweitert sozusagen die HitTest-Methode
des TreeView-Steuerelements. Sie übergeben Ihr im ersten Parameter
das betreffende TreeView-Steuerelement. In den beiden weiteren
Parametern, X und Y, übergeben Sie die Koordinaten, wie Sie sie
etwa in den Mausereignissen zur Verfügung haben. Die beiden
Koordinaten-Parameter sind optional und übergeben als
Voreinstellung jeweils den Wert -1. Lassen Sie die
X-Koordinate weg, betrachtet die Funktion das als Anweisung, die
ganze Zeile zu testen. Lassen Sie die Y-Koordinate weg, testet die
Funktion die zweite Pixel-Zeile von oben. Auf diese Weise können
Sie den obersten sichtbaren Knoten (sozusagen den "TopNode")
ermitteln.
Die Funktion versucht zunächst auf konventionelle Weise über
die HitTest-Methode mit den übergebenen Koordinaten einen Treffer
zu landen. Klappt das nicht, wird die private Funktion zScanLine des
Moduls aufgerufen. Diese erhält neben der Y-Koordinate den Anfang
(= 0) und das Ende (= Width des
TreeView-Steuerelements) der zu testenden Zeile. Dort wird die Mitte
dazwischen ermittelt und wieder mit der HitTest-Methode geprüft.
Falls der Test erfolglos war, ruft sich die Funktion erneut selbst
wieder auf (das nennt man eine "Rekursion") und übergibt
dabei den linken Begrenzer und die Wert für die Mitte als rechten
Begrenzer. Schlägt auch dieser Versuch fehl, ruft sich die Funktion
noch einmal auf, diesmal mit der Mitte als linkem Begrenzer und dem
ursprünglichen rechten Begrenzer. In jedem dieser untergeordneten
Selbstaufrufe wird nach dem gleichen Prinzip verfahren. Wird der
Abstand zwischen dem auf der aktuellen Ebene übergebenen linken und
rechte Begrenzer kleiner als 1 Pixel (Screen.TwipsPerPixelX),
wird erst gar nicht getestet, sondern diese Ebene des Aufrufs
sogleich wieder verlassen.
Public Function TvwHitTestEx(TreeView As TreeView, _
Optional ByVal x As Single = -1, _
Optional ByVal y As Single = -1) As Node
Dim nScanLine As Single
Dim nNode As Node
With TreeView
If .Nodes.Count Then
If y < 0 Then
nScanLine = 2 * Screen.TwipsPerPixelY
Else
nScanLine = y
End If
If x >= 0 Then
Set nNode = .HitTest(x, nScanLine)
If Not (nNode Is Nothing) Then
Set TvwHitTestEx = nNode
Exit Function
End If
End If
Set TvwHitTestEx = zScanLine(TreeView, nScanLine, 0, .Width)
End If
End With
End Function
Private Function zScanLine(TreeView As TreeView, _
ByVal ScanLine As Single, ByVal Left As Single, _
ByVal Right As Single) As Node
Dim nCenter As Single
Dim nNode As Node
With TreeView
If Right - Left < Screen.TwipsPerPixelX Then
Exit Function
End If
nCenter = (Left + Right) \ 2
Set nNode = TreeView.HitTest(nCenter, ScanLine)
If nNode Is Nothing Then
Set nNode = zScanLine(TreeView, ScanLine, Left, nCenter)
If nNode Is Nothing Then
Set nNode = zScanLine(TreeView, ScanLine, nCenter, Right)
End If
End If
End With
Set zScanLine = nNode
End Function
|