|
Die Eigenschaft Index eines Knotens in einem TreeView-Steuerelement liefert Ihnen einen Wert, mit dem Sie eigentlich nichts anfangen können. Er stellt lediglich eine lineare Durchnummerierung aller aktuell im TreeView vorhandenen Knoten dar, allerdings in der Reihenfolge des Einfügens und unabhängig von der tatsächlichen Position in der Hierarchie oder Sortierung einer Knoten-Ebene.
Zwei Ansätze sind denkbar, zu aussagekräftigeren und damit nützlicheren Index- und Positionsangaben zu kommen. Ein Ansatz könnte auf einer Verfolgung sämtlicher Geschehnisse beruhen. Bei jedem Hinzufügen oder Entfernen eines Knotens, bei jedem Öffnen oder Schließen und eventuell auch bei jedem Rollen und Blättern müsste die Veränderung analysiert werden. Dieser Ansatz würde daher eine nahezu komplette Kapselung eines TreeView-Steuerelements erfordern, damit alle Methoden und Ereignisse kontrollierbar würden - ein ziemlich aufwändiges Unterfangen. Ein anderer Ansatz - und nur diesen wollen wir hier der Einfachheit halber weiter verfolgen - beruht auf einem Durchzählen, ausgehend von einem gegebenen Knoten aus nach oben hin.
Wir stützen uns dabei im wesentlichen auf die wohl seltener verwendeten und eher weniger bekannten Knoten-Eigenschaften wie FirstSibling, Previous und Next. Diese Eigenschaften spiegeln nämlich immer die aktuelle Reihenfolge wieder, unabhängig von der Reihenfolge der Einfügungen und unabhängig von der Sortierung einer Knotenebene.
Der nächstliegende Bereich eines Knotens ist seine eigene Ebene. Die Funktion TvwNodePosition ermittelt daher die Position eines Knotens auf seiner Ebene. Dabei wird der Vorgänger des Knotens und wiederum dessen Vorgänger usw. ermittelt, bis kein weiterer Vorgänger mehr existiert. Jeder Schritt wird mitgezählt - das Ergebnis der Zählung gibt somit die Position auf der Ebene wieder.
Public Function TvwNodePosition(Node As Node) As Long
Dim nNode As Node
Dim nIndex As Long
nIndex = 1
Set nNode = Node.Previous
Do While Not (nNode Is Nothing)
nIndex = nIndex + 1
Set nNode = nNode.Previous
Loop
TvwNodePosition = nIndex
End Function
Die nächste interessante Zahl wäre der absolute Index eines Knotens. Dies wäre die Position des Knotens, wenn zwischen ihm und dem obersten Knoten im Baum sämtliche Knoten-Zweige expandiert wären. Hierzu werden in der Funktion TvwNodeAbsoluteIndex zunächst auch wieder alle vorhergehenden Knoten auf der gleichen Ebene gezählt. Dazu werden aber auch alle Knoten in sämtlichen Verästelungen der Vorgänger gezählt (mehr zu der rekursiven Funktion zCountNodeChildren weiter unten). Dann ruft sich die Funktion erneut (rekursiv) auf, um auf dasselbe auf der nächsthöheren Eltern-Ebene zu erledigen, bis hin zur obersten Ebene, der Knoten keinen Elternknoten (Parent) mehr aufweisen.
Public Function TvwNodeAbsoluteIndex(Node As Node)
Dim nNode As Node
Dim nCount As Long
nCount = 1
If Not (Node Is Node.FirstSibling) Then
Set nNode = Node.Previous
Do While Not (nNode Is Nothing)
nCount = nCount + zCountNodeChildren(nNode, False, True) + 1
Set nNode = nNode.Previous
Loop
End If
Set nNode = Node.Parent
If Not (nNode Is Nothing) Then
nCount = nCount + TvwNodeAbsoluteIndex(nNode)
End If
TvwNodeAbsoluteIndex = nCount
End Function
Die Hilfsfunktion zCountNodeChildren wollen wir noch anderweitig verwenden. Daher enthält sie die Möglichkeit, ihre Zählung einerseits auf nur sichtbare Knoten zu beschränken (optionaler Parameter VisibleOnly) und andererseits auch ungeöffnete Zweige in die Zählung mit einzubeziehen. Bezüglich sichtbarer Knoten ist die Eigenschaft Visible eines Knotens hilfreich. Sie gibt wieder, ob der gesamten Strang bis zu dem betreffenden Knoten hin geöffnet ist, und ob sich der Knoten dazu tatsächlich gerade auch noch im sichtbaren Ausschnitt des TreeView-Steuerelements befindet. Um alle entsprechenden Knoten zu ermitteln ruft sich die Funktion selbst rekursiv für jeden Knoten auf der gleichen Ebene auf.
Private Function zCountNodeChildren(Node As Node, _
ByVal VisibleOnly As Boolean, ByVal ForceExpanded As Boolean) _
As Long
Dim nCount As Long
Dim nNode As Node
If Node.Expanded Or ForceExpanded Then
Set nNode = Node.Child
Do While Not (nNode Is Nothing)
nCount = nCount + zCountNodeChildren(nNode, _
VisibleOnly, ForceExpanded) + _
Abs(nNode.Visible Or Not VisibleOnly)
Set nNode = nNode.Next
Loop
End If
zCountNodeChildren = nCount
End Function
Die nächsten beiden interessanten Positionswerte ermittelt die Funktion TvwNodeAbsolutePosition. Sie ermittelt zunächst die absolute Position eines Knotens unter Einschränkung auf alle darüber liegenden Knoten und Zweige, soweit sie geöffnet sind. Ist der optionale Parameter VisibleOnly gesetzt, werden auch nur tatsächlich im Steuerelement sichtbare Knoten gezählt - darunter auch die Knoten in Zweigen, die von einem höher gelegenen Zweig so zu sagen "ins Bild hinein hängen", ohne dass deren Zweigwurzeln sichtbare wären. Die Funktion gleicht eigentlich fast genau der vorhergehenden Funktion TvwAbsoluteIndex und müsste sich auch genau so rekursiv aufrufen. Damit jedoch beim allerersten Aufruf die Entscheidung über die Sichtbarkeit des Start-Knotens getroffen werden kann, besteht sie nur aus einer kleinen Vorschalt-Funktion - ihre eigentliche Arbeit ist in die Hilfsfunktion zTvwNodeAbsolutePosition ausgelagert.
Public Function TvwNodeAbsolutePosition(Node As Node, _
Optional ByVal VisibleOnly As Boolean) As Long
If Node.Visible Or Not VisibleOnly Then
TvwNodeAbsolutePosition = zTvwNodeAbsolutePosition(Node, VisibleOnly)
End If
End Function
Private Function zTvwNodeAbsolutePosition(Node As Node, _
ByVal VisibleOnly As Boolean) As Long
Dim nNode As Node
Dim nCount As Long
nCount = Abs(Node.Visible Or Not VisibleOnly)
If Not (Node Is Node.FirstSibling) Then
Set nNode = Node.Previous
Do While Not (nNode Is Nothing)
nCount = nCount _
+ zCountNodeChildren(nNode, VisibleOnly, False) _
+ Abs(nNode.Visible Or Not VisibleOnly)
Set nNode = nNode.Previous
Loop
End If
Set nNode = Node.Parent
If Not (nNode Is Nothing) Then
nCount = nCount + zTvwNodeAbsolutePosition(nNode, VisibleOnly)
End If
zTvwNodeAbsolutePosition = nCount
End Function
|