12.19 Baumansichten (TreeView und TreeNode)
Im Clientbereich des Microsoft Explorer finden Sie drei Steuerelemente, mit denen wir uns in diesem und den folgenden Abschnitten beschäftigen wollen. Im linken Teil des Fensters werden in einer hierarchischen Struktur alle verfügbaren Laufwerke aufgeführt, die sich bei einem Klick auf das »+«-Zeichen öffnen und die darin enthaltene Ordnerstruktur zeigen. Dieses Verhalten wird durch ein TreeView-Steuerelement bereitgestellt, das auch als Strukturansicht bezeichnet wird. Von einem Ordner werden Dateien verwaltet, die wahlweise in verschiedenen Ansichten im rechten Teil des Explorers ausgegeben werden. .NET bietet uns zu diesem Zweck das Steuerelement ListView an.
Die beiden durch ein TreeView- und ein ListView-Objekt gebildeten Teilfenster haben keine statische Breite, sondern können vom Anwender nach Bedarf unter Beibehaltung der Breite des Fensters mit der Maus vergrößert oder verkleinert werden. Für diese Änderung ist das Steuerelement Splitter zuständig.
12.19.1 Knotenpunkte definieren
Zunächst widmen wir uns dem TreeView-Control, das sich nicht nur zur Anzeige der Laufwerke und deren untergeordneten Verzeichnissen eignet, sondern auch zur Darstellung beliebiger hierarchischer Strukturen wie beispielsweise der einer Datenbank.
Betrachten Sie zunächst Abbildung 12.34, in der in einem TreeView-Steuerelement die fünf Erdteile unterhalb des Stammknotens Erde ausgegeben werden.
Abbildung 12.34 Einfache Anzeige in einem TreeView
Eine TreeView-Strukturansicht ist ein Container für TreeNode-Objekte. Ein Objekt dieses Typs entspricht einem Knoten in der Strukturansicht – unabhängig von der Position innerhalb der hierarchischen Struktur. In Abbildung 12.34 haben wir es mit sechs Knoten zu tun: Erde, Amerika, Asien, Afrika, Australien und Europa.
Sowohl die TreeView als auch jedes TreeNode-Objekt kann beliebig viele TreeNode-Objekte enthalten, die in einer Auflistung vom Typ TreeNodeCollection gespeichert werden. Das Strukturansicht-Steuerelement in der Abbildung enthält in seiner eigenen Auflistung nur ein TreeNode-Objekt, nämlich Erde. Dieser Knoten wird von der TreeNodeCollection des Con-trols verwaltet, während die Elemente Amerika, Asien usw. in der TreeNodeCollection des Knotens Erde enthalten sind.
Die Knoten werden in der Eigenschaft Nodes gespeichert, die wie alle Auflistungen Methoden enthält, um auf die verwalteten TreeNode-Objekte zuzugreifen, neue hinzuzufügen oder zu löschen. Die Anweisungen zur Erzeugung der Kontinente der letzten Abbildung zeigen den Aufbau des Baumes:
Dim tr As New TreeView() Dim kn As String() tr.Dock = DockStyle.Fill tr.Nodes.Add("Erde") kn = New String() {"Amerika", "Asien", "Afrika", "Australien", "Europa"} For Each k As String In kn : tr.Nodes(0).Nodes.Add(k) : Next Me.Controls.Add(tr)
Nach der Instanziierung mit
Dim tr As New TreeView()
sowie der Positionierung und Größenfestlegung wird das Element Erde hinzugefügt, indem zu der TreeNodeCollection der Strukturansicht ein TreeNode-Objekt hinzugefügt wird:
tr.Nodes.Add("Erde")
Erde ist das erste Element in der Auflistung und hat den Index 0. Es wird auch als Stammelement bezeichnet. Durch Übergabe einer Zeichenfolge an Add() wird ein TreeNode-Objekt erzeugt, das Sie auch direkt übergeben können.
Weil auch das TreeNodeCollection-Objekt, wie die meisten anderen Auflistungen, einen Indexer bereitstellt, kann durch Angabe eines Index ein Element angesprochen werden. Über dessen Eigenschaft Nodes haben Sie Zugriff auf die TreeNodeCollection des Elements. Darauf lässt sich erneut die Add-Methode aufrufen, zum Beispiel:
tr.Nodes(0).Nodes.Add(k)
Alle Elemente unterhalb der Stammelemente werden als untergeordnete Elemente bezeichnet, zum Beispiel der Wert Amerika für k.
Alternativ fügen Sie mit der AddRange-Methode ein Array vom Typ TreeNode hinzu.
Dim nodesErde As TreeNode() = New TreeNode() {New TreeNode("Amerika"), _ New TreeNode("Asien"), New TreeNode("Afrika"), _ New TreeNode("Australien"), New TreeNode("Europa")} tr.Nodes(0).Nodes.AddRange(nodesErde)
Ausnahmslos jedes in einer Strukturansicht angezeigte Element ist ein TreeNode-Objekt, das über eine eigene Auflistung ihm untergeordneter TreeNode-Objekte verfügt, die nur dann leer ist, wenn das Element das letzte in der Hierarchie ist. Mit dieser Erkenntnis können wir unser Beispiel beliebig tief strukturieren und jedem Erdteil Staaten zuordnen, die dann selbst in ihrer eigenen Auflistung Städte enthalten.
Das erste Beispiel zum TreeView-Steuerelement wird jetzt in diesem Sinn ergänzt: ein paar Staaten Amerikas und Europas werden ergänzt, zusätzlich Städte in Deutschland und den USA.
Dim tr As New TreeView() Dim kn As String() tr.Dock = DockStyle.Fill tr.Nodes.Add("Erde") kn = New String() {"Amerika", "Asien", "Afrika", "Australien", "Europa"} For Each k As String In kn : tr.Nodes(0).Nodes.Add(k) : Next Dim europa As TreeNode = tr.Nodes(0).Nodes(4) kn = New String() {"England", "Frankreich", "Deutschland", "Italien"} For Each s As String In kn : europa.Nodes.Add(s) : Next Dim deutschland As TreeNode = europa.Nodes(2) kn = New String() {"Bonn", "Aachen", "Hamburg", "Berlin"} For Each s As String In kn : deutschland.Nodes.Add(s) : Next Dim amerika As TreeNode = tr.Nodes(0).Nodes(0) kn = New String() {"USA", "Kanada", "Mexiko"} For Each s As String In kn : amerika.Nodes.Add(s) : Next Dim usa As TreeNode = amerika.Nodes(0) kn = New String() {"Miami", "New York", "San Francisco", "Seattle"} Forr Each s As String In kn : usa.Nodes.Add(s) : Next Me.Controls.Add(tr)
Abbildung 12.35 zeigt das Beispiel, wenn alle Knoten geöffnet sind. Beachten Sie, dass das TreeView-Objekt standardmäßig eine Bildlaufleiste einblendet, sobald die Länge der Liste die Höhe des Steuerelements überschreitet.
Abbildung 12.35 Tiefer strukturiertes TreeView-Steuerelement
Am Programmcode ist deutlich Folgendes zu erkennen: Je tiefer die Hierarchiestruktur ist, desto unübersichtlicher wird der Code. Zur besseren Lesbarkeit wird deshalb eine innere TreeNode-Referenz benutzt, die einen Knoten im Innern der Hierarchie referenziert.
Dim europa As TreeNode = tr.Nodes(0).Nodes(4)
Die Referenz europa verweist hier auf den fünften Knoten des Stammelements, also erde. Damit reduziert sich der Code, den Sie brauchen, um einen europäischen Staat hinzuzufügen, auf:
europa.Nodes.Add(s)
Ansonsten hätte die Anweisung so lauten müssen:
tr.Nodes(0).Nodes(4).Nodes.Add(s)
Steuerelementstruktur anzeigen
Wir wollen das Erlernte in einem kleinen Beispiel umsetzen. Unsere Aufgabe soll es sein, in einem TreeView-Control sämtliche Steuerelemente der aktuellen Form anzuzeigen. Dabei soll die interne Struktur aller Containersteuerelemente sichtbar sein. In der Strukturansicht soll jeder Elementeintrag durch ein steuerelementspezifisches Bildchen ergänzt werden, und der Beschriftungstext soll Objektname und Typ enthalten. Abbildung 12.36 zeigt die Ausgabe des Beispielprogramms.
'...\WinControls\Container\Steuerelementebaum.vb |
Public Class Steuerelementebaum Private Sub btnShowControls_Click(sender As Object, e As EventArgs) _ Handles btnShowControls.Click Me.TreeView1.Nodes.Add(Me.Name) Me.TreeView1.Nodes(0).ImageIndex = 0 Me.TreeView1.Nodes(0).SelectedImageIndex = 0 Me.GetControls(Me.TreeView1.Nodes(0), Me) End Sub Private Sub GetControls(ByVal node As TreeNode, ByVal container As Control) For Each control As Control In container.Controls Dim index As Integer = node.Nodes.Add(New TreeNode(control.Name)) SetImage(node.Nodes(index), control) GetControls(node.Nodes(index), control) Next End Sub Private Shared Sub SetImage(node As TreeNode, control As Control) If TypeOf control Is Form Then : node.ImageIndex = 0 ElseIf TypeOf control Is Button Then : node.ImageIndex = 1 ElseIf TypeOf control Is GroupBox Then : node.ImageIndex = 2 ElseIf TypeOf control Is RadioButton Then : node.ImageIndex = 3 ElseIf TypeOf control Is TextBox Then : node.ImageIndex = 4 ElseIf TypeOf control Is TreeView Then : node.ImageIndex = 5 End If node.Text = control.Name & " (" & control.GetType().Name & ")" node.SelectedImageIndex = node.ImageIndex End Sub End Class
Abbildung 12.36 Die Ausgabe des Beispiels »Steuerelementebaum«
btnShowControls ist der Ereignishandler der Schaltfläche mit der Beschriftung Anzeigen. Hier wird zuerst der Stammknoten erzeugt, der die Form beschreibt. Sie ist ein Container für Steuerelemente. Die Eigenschaft Controls der Form liefert eine ControlCollection der Steuerelemente.
Danach werden in der Methode GetControls() alle Steuerelemente in der Form durchlaufen und der TreeView hinzugefügt. Damit auch Steuerelemente in einem Container erfasst werden, wird die Methode GetControls() rekursiv aufgerufen. Durch die Rekursion dürfen diese wieder Container enthalten und so fort. Damit werden beliebig tief geschachtelte Steuerelemente erfasst.
SetImage hat die Aufgabe, den Typ des gefundenen Steuerelements zu ermitteln und aus der ImageList der Baumansicht das passende Symbol festzulegen. Sie müssen darauf achten, dass keine Symbolumschaltung erfolgt, wenn sich der Zustand eines Strukturelements ändert. Daher wird in SetImage mit
node.Nodes(index).SelectedImageIndex = node.Nodes(index).ImageIndex
der Index des ausgewählten Zustands gleich dem Index des nicht ausgewählten Zustands gesetzt.
12.19.2 Eigenschaften
In der »normalen« Ansicht werden geschlossene Knoten durch ein »+«-Symbol gekennzeichnet, geöffnete durch »-«. Der aktuell markierte Knoten wird farblich invertiert dargestellt. Viele Installationsroutinen bieten Strukturansichten mit Auswahlkästchen an. Diese können Sie auch anbieten, indem Sie der Eigenschaft CheckBoxes den Wert True geben. Die Einzugsbreite der untergeordneten Knoten ist in der Eigenschaft Indent festgelegt und beträgt standardmäßig 19 Pixel.
Für eine einfachere Identifizierung der Knoten sollten Sie für diese Symbole in Betracht ziehen. Die Gesamtverwaltung aller Knotensymbole obliegt dem TreeView, nicht den einzelnen Knoten. Verwaltet werden die Symbole von einem ImageList-Objekt, das Sie der Form hinzufügen und der gleichnamigen Eigenschaft des TreeView im Eigenschaftsfenster zuweisen. Da eine ImageList nicht sehr intuitiv ist, sollten Sie bei der Zusammenstellung der Symbole sofort berücksichtigen, dass ausgewählte und nicht ausgewählte Elemente in der Strukturansicht üblicherweise mit unterschiedlichen Bildchen gekennzeichnet werden, die alle in dieser ImageList enthalten sein müssen. Besser wäre es sicherlich gewesen, wenn uns die .NET-Entwickler für jeden Zustand je eine Bildauflistung spendiert hätten.
Die Eigenschaften ImageIndex/IndexKey (nicht ausgewählt) und SelectedImageIndex/SelectedImageKey (ausgewählt) bestimmen, welche Bildchen standardmäßig für einen ausgewählten bzw. nicht ausgewählten Knoten angezeigt werden. Die Einstellungen kommen nur dann zum Tragen, wenn einem Knoten kein spezielles Symbol zugeordnet wird.
Unter StateImageList dürfen Sie auch eine zweite ImageList angeben. Hier tragen Sie mindestens zwei Bildchen ein, die in den Auswahlkästchen für den Zustand »ausgewählt« und »nicht ausgewählt« stehen. Das erste Symbol kennzeichnet den nicht ausgewählten Zustand, das zweite den ausgewählten. Sie dürfen dieser ImageList natürlich auch noch mehr Symbole hinzufügen, um für jeden Knoten ein ganz individuelles Bildchen im Kontrollkästchen zuzuordnen (dann muss CheckBoxes=False sein). Trotzdem rate ich davon ab, weil zu viele Symbole beim Benutzer leicht zu Irritationen führen.
Das aktuell markierte Element in der Strukturansicht wird farblich in der Breite der Beschriftung hervorgehoben. Mit FullRowSelect=True können Sie die farbliche Hervorhebung über die gesamte Breite der Strukturansicht spannen. Allerdings wirkt sich diese Einstellung nur dann aus, wenn ShowLines=False ist. Mit ShowLines werden die Linien zwischen den neben- und untergeordneten Elementen dargestellt, mit LineColor wird deren Farbe festgelegt.
Wenn Sorted auf True festgelegt ist, werden die TreeNode-Objekte in alphabetischer Reihenfolge nach den Werten ihrer Text-Eigenschaft sortiert. Bei einer großen Anzahl sortierter Elemente sollten Sie immer die Methoden BeginUpdate und EndUpdate aufrufen, um Leistungseinbußen zu verhindern. Haben Sie LabelEdit=True eingestellt, kann der Benutzer zur Laufzeit den Beschriftungstext ändern. Sie müssen dann die Methode Sort aufrufen, um die Elemente neu zu sortieren.
Obwohl dies unüblich ist, können Sie mit ShowPlusMinus=False auf die Plus-/Minusschaltflächen in einer Strukturansicht verzichten. ShowRootLines steuert die Anzeige von Linien zwischen den Stammknoten.
Wenn die HotTracking-Eigenschaft auf True festgelegt ist, wird jede Strukturknotenbezeichnung als blau unterstrichener Hyperlink dargestellt, während der Mauszeiger darüber bewegt wird. Die Darstellung wird nicht durch die Interneteinstellungen im Betriebssystem des Benutzers gesteuert. Sie können das Aussehen nur beeinflussen, wenn Sie den Knoten selbst zeichnen.
Die Eigenschaft SelectedNode ist eine Referenz auf das TreeNode-Objekt, das gerade ausgewählt ist. Durch Zuweisung ändern Sie die Selektion.
Der Eigenschaft PathSeparator eines TreeView-Objekts kommt im Zusammenhang mit der Eigenschaft FullPath eines TreeNode-Objekts besondere Bedeutung zu. FullPath liefert für jeden Knoten eine Zeichenfolge zurück, in der der Bezeichner des Knotens mit allen seinen zum Ursprung zurückführenden Knoten verbunden wird. PathSeparator legt das Trennzeichen fest, das standardmäßig ein Backslash ist.
12.19.3 Ereignisse
Das Aufklappen und Schließen eines Knotens wird von vier Ereignissen begleitet: je einem, das auftritt, bevor die damit verbundene, sichtbare Reaktion des Knotens erfolgt (BeforeExpand bzw. BeforeCollapse), und je einem zum Abschluss des Vorgangs (AfterExpand bzw. AfterCollapse).
In diesem Zusammenhang spielen noch drei weitere Ereignisse eine Rolle. Wird der angeklickte Knoten beim Öffnen oder Schließen gleichzeitig ausgewählt, haben wir es mit dem Ereignispaar BeforeSelect und AfterSelect zu tun. Unabhängig davon, ob ein Knotenelement beim Anklicken ausgewählt ist oder nicht, wird NodeMouseClick ausgelöst. Tabelle 12.17 fasst die Ereignisse zusammen.
Ereignis | Auslösung |
BeforeExpand |
Bevor der Knoten geöffnet wird |
AfterExpand |
Nachdem der Knoten geöffnet worden ist |
BeforeSelect |
Bevor ein Knoten ausgewählt wird |
AfterSelect |
Nachdem ein Knoten ausgewählt wurde |
BeforeCollaps |
Bevor der Knoten geschlossen wird |
AfterCollaps |
Nachdem der Knoten geschlossen worden ist |
NodeMouseClick |
Wenn der Benutzer auf einen Knoten klickt |
BeforeExpand, BeforeSelect und BeforeCollapse übergeben dem Ereignishandler ein Objekt vom Typ TreeViewCancelEventArgs, und die Ereignisse AfterExpand, AfterSelect und AfterCollapse übergeben ein Objekt vom Typ TreeViewEventArgs. Das zuerst aufgeführte Args-Objekt unterscheidet sich nur dadurch, dass der eingeleitete Vorgang mit Cancel abgebrochen werden kann. Die Eigenschaften zeigt die Tabelle 12.18.
Eigenschaft | Beschreibung |
Action |
Gibt an, wie das Ereignis ausgelöst wurde (Maus oder Tastatur) und ob auf- oder zugeklappt wurde (Enumeration TreeViewAction). |
Cancel |
Gibt an, ob das Ereignis abgebrochen werden soll. |
Node |
Strukturknoten, der aktiviert, erweitert, reduziert oder ausgewählt wurde |
Die Action-Eigenschaft ist vom Typ der Enumeration TreeViewAction (siehe Tabelle 12.19) und liefert uns einen Wert, aus dem die Ursache der Ereignisauslösung entnommen werden kann.
Konstante | Grund der Auslösung |
Unknown |
unbekannt |
ByKeyboard |
Tastatureingabe |
ByMouse |
Mausvorgang |
Collapse |
Zusammenklappen eines Knotens |
Expand |
Expandieren eines Knotens |
12.19.4 Knotenexpansion und -reduktion
Die Ansicht des TreeView-Objekts wird jedes Mal aktualisiert, sobald ein Element hinzugefügt wird. Das kann zu trägen Reaktionen und zum Flackern der Anzeige führen, wenn beispielsweise in einer Schleife viele Strukturknoten hinzugefügt werden. Um das zu vermeiden, sollte man vor dem Einfügen die Methode BeginUpdate aufrufen. Damit wird das Zeichnen des Steuerelements so lange unterbunden, bis die EndUpdate-Methode aufgerufen wird. Erstaunlicherweise machen die Methoden ExpandAll und CollapseAll, die auf die gesamte Hierarchiestruktur wirken, keinen Gebrauch von diesem Mechanismus. Die Tabelle 12.20 fasst die Methoden noch einmal zusammen.
Methode | Beschreibung |
BeginUpdate |
Deaktiviert das Neuzeichnen des Steuerelements. |
CollapseAll |
Reduziert alle Strukturknoten. |
EndUpdate |
Aktiviert das Neuzeichnen des Steuerelements. |
ExpandAll |
Expandiert alle Strukturknoten. |
Parallel zur grafischen Änderung der Anzeige durch einen Klick auf das »+«- oder »-«-Zeichen, die durch TreeView verarbeitet werden, stellt die Klasse TreeNode Methoden zur Verfügung, um einen Konten zu erweitern oder zu reduzieren.
12.19.5 Knoten (TreeNode)
Drei Eigenschaften unterstützen Sie bei der Identifikation eines Knotens:
Eigenschaft | Beschreibung | |
Index |
Position des aktuellen Knotens in der Nodes-Liste des Elternknotens. |
R |
Text |
Angezeigter Text des Knotens |
|
TreeView |
TreeView-Objekt, das den Knoten enthält |
R |
Andere Eigenschaften dienen zur Navigation durch die Hierarchie. Mit Parent kann zum Beispiel der übergeordnete Knoten bestimmt werden, mit FirstNode, LastNode, PrevNode und NextNode kann zu den nebengeordneten Knoten auf gleicher Hierarchieebene navigiert werden. Alle geben die Referenz auf das angesteuerte TreeNode-Objekt zurück.
Mit den Eigenschaften IsExpanded und IsSelected können Sie feststellen, ob ein Knoten aktuell reduziert, erweitert oder markiert ist. Auch auf Knotenebene können Sie mit den Methoden Collapse, Expand, ExpandAll und Toggle untergeordnete Knoten expandieren bzw. reduzieren. Anders als im TreeView-Objekt betrifft das nicht immer alle Knoten, sondern nur die Kindknoten des aktuellen Knotens. Die Methode Toggle invertiert den Zustand des Knotens, also entweder von erweitert nach reduziert oder von reduziert nach erweitert.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.