ABOUT Visual Basic Programmieren Programmierung Download Downloads Tips & Tricks Tipps & Tricks Know-How Praxis VB VBA Visual Basic for Applications VBS VBScript Scripting Windows ActiveX COM OLE API ComputerPC Microsoft Office Microsoft Office 97 Office 2000 Access Word Winword Excel Outlook Addins ASP Active Server Pages COMAddIns ActiveX-Controls OCX UserControl UserDocument Komponenten DLL EXE
Diese Seite wurde zuletzt aktualisiert am 10.11.2000

Diese Seite wurde zuletzt aktualisiert am 10.11.2000
Aktuell im ABOUT Visual Basic-MagazinGrundlagenwissen und TechnologienKnow How, Tipps und Tricks rund um Visual BasicActiveX-Komponenten, Controls, Klassen und mehr...AddIns für die Visual Basic-IDE und die VBA-IDEVBA-Programmierung in MS-Office und anderen AnwendungenScripting-Praxis für den Windows Scripting Host und das Scripting-ControlTools, Komponenten und Dienstleistungen des MarktesRessourcen für Programmierer (Bücher, Job-Börse)Dies&Das...

Themen und Stichwörter im ABOUT Visual Basic-Magazin
Code, Beispiele, Komponenten, Tools im Überblick, Shareware, Freeware
Ihre Service-Seite, Termine, Job-Börse
Melden Sie sich an, um in den vollen Genuss des ABOUT Visual Basic-Magazins zu kommen!
Informationen zum ABOUT Visual Basic-Magazin, Kontakt und Impressum

Zurück...

Knoten drunter und drüber

Zurück...


Anzeige

(-hg) mailto:hg_tvwsubnodes@aboutvb.de

Auf einen Rutsch einen Knoten und alle darunter liegenden Knoten eines TreeView-Steuerelements zu bearbeiten, lässt sich am besten mit einer Rekursion erledigen. Rekursion bedeutet, dass Sie ein Prozedur aufrufen, die auf der gegebenen Ebene eine bestimmte Arbeit erledigt und sich dann, solange Unterebenen vorhanden sind, für jede ihrer Unterebenen erneut aufruft. In diesem erneuten Aufruf wird nun die jeweilige Unterebene bearbeitet und die Prozedur wird wiederum erneut für jede der dortigen Unterebenen aufgerufen. Werden auf einer Ebene keine Unterebenen mehr angetroffen, endet dieser Bearbeitungszweig.

Die Knoten (Node-Objekte) eines TreeView-Steuerelements bieten zur Realisierung einer solchen Rekursion die Eigenschaft Children, Child und Next. Die Eigenschaft Children gibt an, ob überhaupt Unterknoten vorhanden sind. Ist dies der Fall, gibt die Child-Eigenschaft den ersten dieser Unterknoten zurück. Die Eigenschaft Next eines Knotens gibt den jeweils nächsten Knoten auf der gleichen Ebene zurück (von einem der Unterknoten aus gesehen). Das Grundgerüst für eine rekursive Prozedur zur Knotenbearbeitung sieht folgendermaßen aus:

Sub NodesRekursive(StartNode As Node)
  Dim nChildNode As Node

  With StartNode
    ' entweder vor den Unterknoten bearbeiten
    ' ...
    If .Children Then
      Set nChildNode = .Child
      Do
        NodesRekursive nChildNode
        Set nChildNode = nChildNode.Next
      Loop Until nChildNode Is Nothing
    End If
    ' oder nach den Unterknoten bearbeiten
    ' ...
  End With
End Sub

Im Prinzip könnten Sie eigentlich auf die Prüfung der Children-Eigenschaft verzichten. Denn wenn keine Unterknoten vorhanden sind, gibt die Child-Eigenschaft sowieso Nothing zurück und Sie könnten am Schleifenbeginn prüfen, ob nChildNode gleich Nothing ist. Die Do...Loop-Schleife würde dann erst gar nicht betreten:

      Set nChildNode = .Child
      Do While Not(nChildNode Is Nothing)
        NodesRekursive nChildNode
        Set nChildNode = nChildNode.Next
      Loop

Enthält der Zweig des Startknotens jedoch viele selbst weitverzweigte Unterzweige, ginge dies zu Lasten der Geschwindigkeit. Die Prüfung der Eigenschaft Children ist etwas schneller als die Zuweisung der Eigenschaft Child an eine Variable und einer anschließenden Prüfung, ob diese Nothing enthält.

Beispielsweise können Sie über solche Rekursionen einen Knoten und alle seine Unterknoten auf einen Schlag expandieren oder kollabieren lassen:

Public Sub TreeViewSubNodesExpand(Node As Node, _
 Optional ByVal ExpandAll As Boolean)

  Dim nChildNode As Node
  
  With Node
    If .Children Then
      .Expanded = ExpandAll
      Set nChildNode = .Child
      Do
        TreeViewSubNodesExpand nChildNode, ExpandAll
        Set nChildNode = nChildNode.Next
      Loop Until nChildNode Is Nothing
    End If
  End With
End Sub

Oder Sie setzen die Sorted-Eigenschaft eines Knotens und seiner Unterknoten in einem Rutsch:

Public Sub TreeViewSubNodesSorted(Node As Node, _
 Optional ByVal Sort As Boolean)

  Dim nChildNode As Node
  
  With Node
    .Sorted = Sort
    If .Children Then
      Set nChildNode = .Child
      Do
        TreeViewSubNodesSorted nChildNode, Sort
        Set nChildNode = nChildNode.Next
      Loop Until nChildNode Is Nothing
    End If
  End With
End Sub

Und genau so können Sie auch die ab Version 6 des TreeView-Steuerelements verfügbaren CheckBoxen (Eigenschaft CheckBoxes = True) eines Knotens und aller seiner Unterknoten in einem Durchgang setzen oder löschen:

Public Sub TreeViewSubNodesCheck(Node As Node, _
 Optional ByVal Checked As Boolean = True)

  Dim nChildNode As Node
  
  With Node
    .Checked = Checked
    If .Children Then
      Set nChildNode = .Child
      Do
        TreeViewSubNodesCheck nChildNode, Checked
        Set nChildNode = nChildNode.Next
      Loop Until nChildNode Is Nothing
    End If
  End With
End Sub

Das Prinzip einer Rekursion ist jedoch nicht unbedingt nur auf Abwärtsbewegungen in die Zweige einer Hierarchie beschränkt. Sie können Rekursionen auch für Aufwärtsbewegungen in einer Hierarchie verwenden: Von einem Knoten aufwärts über seinen Elternknoten zu dessen Elternknoten usw., bis kein Elternknoten mehr verfügbar und damit die Spitze (oder die oberste Ebene) der Hierarchie erreicht ist.

Diese Rekursionsrichtung machen wir uns beim folgenden Anwendungszweck zu Nutze. Hier geht es darum, bei einem Knoten immer dann automatisch die CheckBox auf True zu setzen, wenn bei allen seinen Unterknoten die CheckBoxen auf True gesetzt sind. Dies impliziert zum einen, dass dann auch jeweils die Checkboxen der Unterknoten der Unterknoten usw. bis hin ins letzte Glied auf True gesetzt sein müssen. Zum anderen bedeutet das auch, dass sich das Resultat, das sich für einen Knoten aus seinen Unterknoten ergibt, ebenso auf der nächsthöheren Ebene auswirken kann. War nämlich auf dieser übergeordneten Ebene jener Knoten, bei dem die CheckBox als Resultat gesetzt worden ist, der letzte mit zuvor noch nicht gesetzter CheckBox, soll nun auch bei dessen Elternknoten die CheckBox gesetzt werden. Dieses Spiel pflanzt sich aufwärts fort, bis ein Knoten der obersten Ebene bzw. der Stammknoten der gesamten Hierarchie erreicht worden ist.

Die folgende Prozedur TreeViewSubTreesCheck rufen Sie im NodeCheck-Ereignis eines TreeView-Steuerelements auf, und übergeben einfach nur den dort als Parameter zur Verfügung gestellten Knoten. Damit wird zum einen über die oben vorgestellte Prozedur TreeViewSubNodesCheck bei allen Unterknoten in allen Zweigen die CheckBox auf den Status des betreffen Knotens gesetzt, zum anderen wird eine Aufwärts-Rekursion (zTreeViewSubTreesCheckUp) für den Elternknoten (falls noch einer vorhanden ist) zur Aktualisierung der übergeordneten Knoten-Ebenen aufgerufen. Sie können diese Prozedur auch dazu verwenden, die CheckBox eines Knotens manuell an beliebiger Stelle in Ihrem Code zu setzen, indem Sie im optionalen Parameter Checked den gewünschten Knoten-Status (True oder False) übergeben.

Public Sub TreeViewSubTreesCheck(Node As Node, _
 Optional Checked As Variant)

  With Node
    If IsMissing(Checked) Then
      TreeViewSubNodesCheck Node, .Checked
    Else
      TreeViewSubNodesCheck Node, Checked
    End If
    If Not (.Parent Is Nothing) Then
      zTreeViewSubTreesCheckUp .Parent
    End If
  End With
End Sub

Der aufwärts rekursiven Prozedur zTreeViewSubTreesCheckUp wird immer bereits ein Elternknoten übergeben. In der Prozedur wird dann ermittelt, ob bei allen Unterknoten ermittelt, ob die CheckBox gesetzt ist, und dementsprechend die CheckBox des Knotens dieser Ebene gesetzt. Dabei ist es aus der Sicht dieser Ebene unerheblich, über welchen ihrer Unterknoten der rekursive Aufruf "herauf"gekommen ist. Existiert für den Knoten dieser Ebene selbst wieder ein Elternknoten, läuft die Rekursion eine weitere Stufe aufwärts.

Private Sub zTreeViewSubTreesCheckUp(Node As Node)
  Dim nChildNode As Node
  Dim nCheck As Boolean
  
  With Node
    Set nChildNode = .Child
    nCheck = nChildNode.Checked
    Do
      With nChildNode
        Set nChildNode = .Next
        nCheck = nCheck And .Checked
      End With
    Loop Until nChildNode Is Nothing
    .Checked = nCheck
    If Not (.Parent Is Nothing) Then
      zTreeViewSubTreesCheckUp .Parent
    End If
  End With
End Sub

Damit Sie nicht immer wieder den gleichen Code darum herum schreiben müssen, wenn ein Knoten neu eingefügt, wenn ein Knoten gelöscht oder wenn ein Knoten (samt Unterknoten) von einem Elternknoten zu unter einen anderen umgehängt werden soll, können Sie die folgenden drei Funktionen bzw. Prozeduren verwenden.

Die Funktion TreeViewAddNodeCheck dient Einfügen eines neuen Knotens entsprechend den gewohnten Parametern. Zusätzlich können Sie angeben, ob beim neu eingefügten Knoten die CheckBox gleich gesetzt werden soll. Unabhängig davon wird jedoch vom Elternknoten des neu eingefügten Knotens für die Aktualisierung der CheckBoxen sowohl in der Hierarchie aufwärts als auch abwärts gesorgt. Im weiteren optionalen Parameter ExpandParent können Sie noch angeben, ob der Knoten, unter dem der neue Knoten eingefügt wurde, gleich expandiert werden soll. Den neu eingefügten Knoten erhalten Sie, ebenfalls wie gewohnt, als Rückgabewert der Funktion.

Public Function TreeViewAddNodeCheck(TreeView As TreeView, _
 Optional Relative As Variant, Optional Relationship As Variant, _
 Optional Key As Variant, Optional Text As Variant, _
 Optional Image As Variant, Optional SelectedImage As Variant, _
 Optional ByVal Checked As Boolean, _
 Optional ByVal ExpandParent As Boolean) As Node

  Dim nNode As Node
  Dim nNodeParent As Node
  
  Set nNode = TreeView.Nodes.Add(Relative, Relationship, Key, _
   Text, Image, SelectedImage)
  TreeViewSubTreesCheck nNode, Checked
  If ExpandParent Then
    Set nNodeParent = nNode.Parent
    If Not (nNodeParent Is Nothing) Then
      nNodeParent.Expanded = True
    End If
  End If
  Set TreeViewAddNodeCheck = nNode
End Function

Für den Rückweg ist die Prozedur TreeViewRemoveNodeCheck zuständig. Ihr können Sie im Parameter Node nicht nur den Index oder Schlüssel eines Knotens übergeben, sondern auch den zu löschenden Knoten selbst. Auch hier wird nach dem Entfernen des Knotens vom Elternknoten ausgehend wieder aufwärts der Status der CheckBoxen der übergeordneten Hierarchie-Knoten aktualisiert.

Außerdem wird die Expanded-Eigenschaft des Elternknotens automatisch auf False gesetzt, wenn gerade sein letzter Unterknoten entfernt worden ist. Das ist ein kleines Feature, das ich beim TreeView vermisst habe. Es ergibt in meinen Augen keinen Sinn, einen Knoten expandiert zu lassen, wenn er gar keine Unterknoten mehr enthält. Vergessen sie nämlich den Zustand der Expanded-Eigenschaft, wird beim späteren Einfügen eines neuen Knotens, eventuell entgegen Ihrer Absicht derselbige gleich sichtbar, wenn beim Elternknoten nach wie vor die Expanded-Eigenschaft gesetzt gewesen ist. Mir ist für solche Fälle ein kontrolliertes Expandieren von Knoten lieber.

Public Sub TreeViewRemoveNodeCheck(TreeView As TreeView, _
 Node As Variant)

  Dim nNodeParent As Node
  
  With TreeView
    If IsObject(Node) Then
      If TypeOf Node Is Node Then
        Set nNodeParent = Node.Parent
        .Nodes.Remove Node.Key
      End If
    Else
      On Error Resume Next
      Set nNodeParent = .Nodes(Node).Parent
      .Nodes.Remove Node
      On Error GoTo 0
    End If
  End With
  If Not (nNodeParent Is Nothing) Then
    With nNodeParent
      .Expanded = CBool(.Children) And .Expanded
    End With
    If nNodeParent.Children Then
      zTreeViewSubTreesCheckUp nNodeParent
    End If
  End If
End Sub

Auch beim Umhängen eines Knotens von einem Elternknoten unter einen anderen muss natürlich der CheckBox-Status aller übergeordneten Ebenen aktualisiert werden - dies erledigt schließlich noch die Prozedur TreeViewInsertNodeCheck ebenfalls wieder über den Aufruf der Aufwärts-Rekursion.

Public Sub TreeViewInsertNodeCheck(ParentNode As Node, _
 Node As Node, Optional ByVal ExpandParent As Boolean)

  Set Node.Parent = ParentNode
  zTreeViewSubTreesCheckUp ParentNode
  If ExpandParent Then
    ParentNode.Expanded = True
  End If
End Sub

Modul SubNodes und Beispiel-Projekt (tvwsubnodes.zip - ca. 4,2 KB)


Artikel
Zum Download-Bereich dieses Artikel
Mail an den Autor dieses Artikels

KnowHow
Zur KnowHow-Übersicht

KnowHow-Themen
Themen - Allgemeines
Themen - Entwicklungsumgebung (VB-IDE)
Themen - Forms
Themen - Steuerelemente (Controls)
Themen - Grafik
Themen - Dateien
Themen - UserControls
Themen - Einsteiger-Tipps
Themen - Wussten Sie...?

Übersicht nach Titeln in alphabetischer Reihenfolge
Übersicht nach Erscheinungsdatum

Schnellsuche



Zum Seitenanfang

Copyright © 1999 - 2017 Harald M. Genauck, ip-pro gmbh  /  Impressum

Zum Seitenanfang

Zurück...

Zurück...

Download Internet Explorer