|
|
|
|
|
Es gibt nur eine einzige direkte Möglichkeit, einen Knotens in einem TreeView-Steuerelement an eine andere Position zu verschieben: Sie können ihn einem anderen Elternknoten zuweisen. Die Änderung der Reihenfolge der Knoten auf einer Ebene können Sie nachträglich nicht direkt ändern. Genau so wenig können Sie einen Knoten auf einfache Weise von einem TreeView-Steuerelement in ein anderes umhängen.
Es wird Ihnen nichts übrig bleiben, als den betreffenden Knoten aus der Nodes-Sammlung des TreeViews heraus zu nehmen und einen neuen Knoten an der gewünschten Stelle mit den Eigenschaften des alten Knotens wieder einzufügen. Während sämtliche Kindknoten und Verzweigungen eines Knotens beim Umhängen an einen anderen Elternknoten erhalten bleiben, gehen sie beim Herausnehmen eines Knotens unweigerlich verloren. Die Eigenschaften und Positionen sämtlicher Kindknoten müssen also vor dem Umhängen ebenfalls ermittelt und festgehalten werden, damit sie nach dem Umhängen wieder entsprechend eingefügt werden können.
Die im folgenden beschriebene Klasse clsNodeStore dient dazu, die Eigenschaften eines Knotens und die seiner Kindknoten und die Positionen in den Verzweigungen festzuhalten ("den Knoten zu serialisieren") und an einen neuen Knoten nach seinem Einfügen zu übergeben ("deserialisieren").
Die Eigenschaften des Knotens werden zudem als Eigenschaften der Klasse clsNodeStore zur Verfügung gestellt, so dass sie während der Phase der Zwischenspeicherung beliebig ausgelesen und auch manipuliert werden können. Dies gilt auch für die Kindknoten - die unter dem in einer Instanz von clsNodeStore serialisierten Knoten liegende Hierarchie ist daher auch nach dem Entfernen des Anfangsknotens aus der Nodes-Collection des TreeViews zugänglich. Alle Kindknoten sind hier ebenfalls als clsNodeStore-Instanzen abgelegt.
Sie können daher auch eine neue leere clsNodeStore-Instanz erzeugen, mit aus einer beliebigen Quelle stammenden Daten für die Eigenschaften füllen und als neuen Knoten in die Nodes-Collection einfügen. Ebenso können Sie so auch eine komplette, darunter liegende Hierarchie vorbereiten und zugleich einfügen. Dazu verfügt die Klasse clsNodeStore über die für eine Collection üblichen Methoden Add, Remove und Clear, um die jeweilige Kind-Ebene eines Knotens bearbeiten zu können. Zugriff auf die einzelnen Kindknoten bietet die Eigenschaft ChildNode und die Anzahl der Kindknoten liefert die Eigenschaft Children (statt des üblichen "Count" die analoge Bezeichnung wie beim Node-Objekt). Auch die Möglichkeit der Enumeration der Kindknoten in einer For...Each-Schleife steht zur Verfügung.
Die Klasse clsNodeStore ist für Knoten des TreeView-Steuerelements der Version 5 der Microsoft Common Controls geeignet. Zur Verwendung mit der Version 5 ist die Klasse clsNodeStore5 gedacht. Diese ist gegenüber der 6er-Version ein wenig abgespeckt - es fehlen die erweiterten Eigenschaften BackColor, Bold, Checked und ForeColor. Außerdem kann hier die Tag-Eigenschaft keine Objekt-Referenzen aufnehmen. Hier sehen Sie die 6er-Version, die 5er-Version ist im Download-Paket zu diesem Artikel enthalten.
Private pChildNodes As Collection
Private pBackColor As OLE_COLOR
Private pBold As Boolean
Private pChecked As Boolean
Private pExpanded As Boolean
Private pExpandedImage As Variant
Private pForeColor As OLE_COLOR
Private pImage As Variant
Private pKey As String
Private pSelected As Boolean
Private pSelectedImage As Variant
Private pTag As Variant
Private pText As String
Public Property Get BackColor() As OLE_COLOR
BackColor = pBackColor
End Property
Public Property Let BackColor(New_BackColor As OLE_COLOR)
pBackColor = New_BackColor
End Property
Public Property Get Bold() As Boolean
Bold = pBold
End Property
Public Property Let Bold(New_Bold As Boolean)
pBold = New_Bold
End Property
Public Property Get Checked() As Boolean
Checked = pChecked
End Property
Public Property Let Checked(New_Checked As Boolean)
pChecked = New_Checked
End Property
Public Property Get ChildNode(KeyIndex As Variant) As clsNodeStore
If Not (pChildNodes Is Nothing) Then
Set ChildNode = pChildNodes(KeyIndex)
End If
End Property
Public Property Get Children() As Long
If Not (pChildNodes Is Nothing) Then
Children = pChildNodes.Count
End If
End Property
Public Property Get Expanded() As Boolean
Expanded = pExpanded
End Property
Public Property Let Expanded(New_Expanded As Boolean)
pExpanded = New_Expanded
End Property
Public Property Get ExpandedImage() As Variant
ExpandedImage = pExpandedImage
End Property
Public Property Let ExpandedImage(New_ExpandedImage As Variant)
pExpandedImage = New_ExpandedImage
End Property
Public Property Get ForeColor() As OLE_COLOR
ForeColor = pForeColor
End Property
Public Property Let ForeColor(New_ForeColor As OLE_COLOR)
pForeColor = New_ForeColor
End Property
Public Property Get Image() As Variant
Image = pImage
End Property
Public Property Let Image(New_Image As Variant)
pImage = New_Image
End Property
Public Property Get Key() As String
Key = pKey
End Property
Public Property Let Key(New_Key As String)
pKey = New_Key
End Property
Public Property Get Selected() As Boolean
Selected = pSelected
End Property
Public Property Get SelectedImage() As Variant
SelectedImage = pSelectedImage
End Property
Public Property Let SelectedImage(New_SelectedImage As Variant)
pSelectedImage = New_SelectedImage
End Property
Public Property Get Tag() As Variant
If IsObject(pTag) Then
Set Tag = pTag
Else
Tag = pTag
End If
End Property
Public Property Let Tag(New_Tag As Variant)
zSetTag New_Tag
End Property
Public Property Set Tag(New_Tag As Variant)
zSetTag New_Tag
End Property
Private Sub zSetTag(New_Tag As Variant)
If IsObject(New_Tag) Then
Set pTag = New_Tag
Else
pTag = New_Tag
End If
End Sub
Public Property Get Text() As String
Text = pText
End Property
Public Property Let Text(New_Text As String)
pText = New_Text
End Property
Public Function Add(Optional Key As Variant, _
Optional Before As Variant, Optional After As Variant) _
As clsNodeStore
Dim nNodeStore As clsNodeStore
If pChildNodes Is Nothing Then
Set pChildNodes = New Collection
End If
Set nNodeStore = New clsNodeStore
pChildNodes.Add nNodeStore, Key, Before, After
Set Add = nNodeStore
End Function
Public Sub Clear()
Set pChildNodes = Nothing
End Sub
Public Sub Remove(KeyIndex As Variant)
If Not (pChildNodes Is Nothing) Then
pChildNodes.Remove KeyIndex
End If
End Sub
Public Function NewEnum() As IUnknown
Set NewEnum = pChildNodes.[_NewEnum]
End Function
Ehe wir zu den eigentlich interessanten Methoden der Klasse zur Serialisierung und Deserialisierung kommen, möchte ich noch ein weiteres Feature erwähnen: Die Serialisierung in ein PropertyBag-Objekt, das sie allerdings erst ab Visual Basic 6 als eigenständiges Objekt selbst instanzieren können. Auf die direkte Serialisierung der Klasse, wie sie ab Visual Basic 6 ebenfalls möglich ist, wurde hier verzichtet - sie bringt nicht allzu viel und würde die Klasse nur komplizierter gestalten. Wenn Sie wollen, dürfte es aber nicht schwierig sein, die Klasse an eine direkte Serialisierung anzupassen.
Public Sub ReadProperties(PropBag As PropertyBag)
Dim nChildren As Long
Dim nPropBag As PropertyBag
Dim nNodeStore As clsNodeStore
Dim nChildKey As String
Dim l As Long
With PropBag
pBackColor = .ReadProperty("BackColor")
pBold = .ReadProperty("Bold")
pChecked = .ReadProperty("Checked")
pExpanded = .ReadProperty("Expanded")
pExpandedImage = .ReadProperty("ExpandedImage")
pForeColor = .ReadProperty("ForeColor")
pImage = .ReadProperty("Image")
pKey = .ReadProperty("Key")
pSelectedImage = .ReadProperty("SelectedImage")
pTag = .ReadProperty("Tag")
pText = .ReadProperty("Text")
nChildren = .ReadProperty("Children")
If nChildren Then
Set pChildNodes = New Collection
For l = 1 To nChildren
Set nNodeStore = New clsNodeStore
Set nPropBag = New PropertyBag
nPropBag.Contents = .ReadProperty("Child" & l)
nNodeStore.ReadProperties nPropBag
nChildKey = nNodeStore.Key
If Len(nChildKey) Then
pChildNodes.Add nNodeStore, nChildKey
Else
pChildNodes.Add nNodeStore
End If
Next
End If
End With
End Sub
Public Sub WriteProperties(PropBag As PropertyBag)
Dim nNodeStore As clsNodeStore
Dim nPropBag As PropertyBag
Dim l As Long
With PropBag
.WriteProperty "BackColor", pBackColor
.WriteProperty "Bold", pBold
.WriteProperty "Checked", pChecked
.WriteProperty "Expanded", pExpanded
.WriteProperty "ExpandedImage", pExpandedImage
.WriteProperty "ForeColor", pForeColor
.WriteProperty "Image", pImage
.WriteProperty "Key", pKey
.WriteProperty "SelectedImage", pSelectedImage
If Not IsObject(pTag) Then
.WriteProperty "Tag", pTag
End If
.WriteProperty "Text", pText
If pChildNodes Is Nothing Then
.WriteProperty "Children", 0
Else
.WriteProperty "Children", pChildNodes.Count
For Each nNodeStore In pChildNodes
Set nPropBag = New PropertyBag
nNodeStore.WriteProperties nPropBag
l = l + 1
.WriteProperty "Child" & l, nPropBag.Contents
Next
End If
End With
End Sub
Schauen wir uns nun zunächst die Methode Serialize an. Im ersten Parameter geben Sie den zu serialisierenden Knoten an - entweder als Schlüssel (Key) oder Index des Knotens, oder Sie übergeben den betreffenden Knoten direkt. Geben Sie nur den Schlüssel oder Index an, müssen Sie im zweiten Parameter das TreeView-Steuerelement angeben, in dem sich der Knoten befindet. In nächsten optionalen Parameter IncludeChildren legen Sie fest, ob die unter dem Knoten liegende Kindknoten-Hierarchie mit serialisiert werden soll. Setzen Sie den nächsten optionalen Parameter Remove nicht auf True, wird der Knoten so zu sagen nur kopiert und nicht aus der Nodes-Collection des TreeView-Steuerelements entfernt. Setzen Sie ihn auf True, müssen Sie zuvor auch das TreeView-Steuerelement angegeben haben, aus dem der Knoten entfernt werden soll. Im letzten optionalen Parameter ToPropBag können Sie eine Instanz eines PropertyBag-Objekts übergeben, die Sie ab Visual Basic 6 selbst erzeugen können. Übergeben Sie hier ein PropertyBag-Objekt, wird der komplette Inhalt der Klasse einschließlich der Kindknoten-Hierarchie über die intern aufgerufene oben stehende WriteProperties-Methode darin abgelegt.
Public Sub Serialize(Node As Variant, _
Optional TreeView As TreeView, _
Optional ByVal IncludeChildren As Boolean, _
Optional ByVal Remove As Boolean, _
Optional ToPropBag As PropertyBag)
Dim nNode As Node
Dim nNodeStore As clsNodeStore
Dim nChildKey As String
If IsObject(Node) Then
If TypeOf Node Is Node Then
Set nNode = Node
End If
Else
Set nNode = TreeView.Nodes(Node)
End If
If Not (nNode Is Nothing) Then
With nNode
pBackColor = .BackColor
pBold = .Bold
pChecked = .Checked
pExpanded = .Expanded
pExpandedImage = .ExpandedImage
pForeColor = .ForeColor
pImage = .Image
pKey = .Key
pSelected = .Selected
pSelectedImage = .SelectedImage
If IsObject(.Tag) Then
Set pTag = .Tag
Else
pTag = .Tag
End If
pText = .Text
If IncludeChildren Then
If .Children Then
Set pChildNodes = New Collection
Set nNode = .Child
With pChildNodes
Do While Not (nNode Is Nothing)
Set nNodeStore = New clsNodeStore
nNodeStore.Serialize nNode, , True
nChildKey = nNode.Key
If Len(nChildKey) Then
.Add nNodeStore, nChildKey
Else
.Add nNodeStore
End If
Set nNode = nNode.Next
Loop
End With
End If
Else
Set pChildNodes = Nothing
End If
If Remove Then
TreeView.Nodes.Remove .Index
End If
End With
If Not (ToPropBag Is Nothing) Then
Me.WriteProperties ToPropBag
End If
End If
End Sub
Das Einfügen eines in einer clsNodeStore-Instanz serialisierten Knotens erfolgt über die Methode Deserialize. Ihr übergeben Sie im ersten Parameter das TreeView-Steuerelement, in dessen Nodes-Collection der serialiserte Knoten eingefügt werden soll. Die weiteren, optionalen Parameter Relative, Relationship und Key entsprechen denen der Add-Methode der Nodes-Collection. Hiermit bestimmen Sie also die tatsächliche (gegebenenfalls neue) Position des neu eingefügten Knotens. Setzen Sie den nächsten optionalen Parameter UseOriginalKey auf True, wird der ursprüngliche Schlüssel des serialiserten Knotens wieder verwendet (das gilt jeweils auch für seine Kindknoten) - der eventuell zuvor im Parameter Key übergebene Schlüssel wird ignoriert. Im Parameter IncludeChildren geben Sie an, ob auch eine mit serialisierte Kindknoten-Hierarchie eingefügt werden soll. In KeepExpanded und KeepSelection legen Sie fest, ob die eingefügten Knoten so wie serialisiert expandiert werden sollen, und ob der Knoten, der beim Serialisieren gegebenenfalls selektiert war, nun auch wieder selektiert werden soll. Im letzten Parameter können Sie wieder ein PropertyBag-Objekt übergeben aus dem der Inhalt der clsNodeStore-Instanz zunächst ausgelesen wird, ehe der weitere Aufbau des Knotens erfolgt. Die Methode gibt wie die Add-Methode der Nodes-Collection eine Referenz auf den neu eingefügten Knoten zurück.
Public Function Deserialize(TreeView As TreeView, _
Optional Relative As Variant, _
Optional ByVal Relationship As Variant, _
Optional Key As Variant, _
Optional ByVal UseOriginalKey As Boolean, _
Optional ByVal IncludeChildren As Boolean, _
Optional ByVal KeepExpanded As Boolean, _
Optional ByVal KeepSelection As Boolean, _
Optional FromPropBag As PropertyBag) As Node
Dim nNode As Node
Dim nNodeStore As clsNodeStore
If Not (FromPropBag Is Nothing) Then
Me.ReadProperties FromPropBag
End If
If UseOriginalKey Then
Set nNode = _
TreeView.Nodes.Add(Relative, Relationship, pKey, pText)
Else
Set nNode = _
TreeView.Nodes.Add(Relative, Relationship, Key, pText)
End If
With nNode
.BackColor = pBackColor
.Bold = pBold
.Checked = pChecked
.ExpandedImage = pExpandedImage
.ForeColor = pForeColor
.Image = pImage
.SelectedImage = pSelectedImage
If IsObject(pTag) Then
Set .Tag = pTag
Else
.Tag = pTag
End If
.Text = pText
If IncludeChildren Then
If Not (pChildNodes Is Nothing) Then
For Each nNodeStore In pChildNodes
nNodeStore.Deserialize TreeView, nNode, tvwChild, , _
UseOriginalKey, True, KeepExpanded, KeepSelection
Next
End If
End If
.Expanded = pExpanded And KeepExpanded
If KeepSelection And pSelected Then
.Selected = True
Set TreeView.SelectedItem = nNode
End If
End With
Set Deserialize = nNode
End Function
Damit hätten Sie eigentlich schon alles, was Sie zum Serialisieren, Transportieren und wieder Einfügen von Knoten samt einer darunter liegenden Knoten-Hierarchie benötigen. Da zum Serialisieren und zum wieder Einfügen die Methoden Serialize und Deserialize immer paarweise aufgerufen werden, lassen sie sich auch zusammenfassen - in den Methoden CopyNode und MoveNode. Beide rufen intern die private Prozedur zCopyMoveNode auf. Dort wird nur beim Aufruf von MoveNode aus der alte Knoten aus der Nodes-Collection des TreeView-Steuerelements entfernt.
Public Function CopyNode(Node As Variant, _
FromTreeView As TreeView, _
Optional ByVal IncludeChildren As Boolean, _
Optional ByVal KeepExpanded As Boolean, _
Optional ToTreeView As TreeView, _
Optional Relative As Variant, _
Optional ByVal Relationship As Variant, _
Optional Key As Variant, _
Optional ByVal DestNodeExpanded As Boolean) As Node
Set CopyNode = zCopyMoveNode(Node, FromTreeView, IncludeChildren, _
KeepExpanded, ToTreeView, Relative, Relationship, Key, , _
DestNodeExpanded)
End Function
Public Function MoveNode(Node As Variant, _
FromTreeView As TreeView, _
Optional ByVal IncludeChildren As Boolean, _
Optional ByVal KeepExpanded As Boolean, _
Optional ToTreeView As TreeView, _
Optional Relative As Variant, _
Optional ByVal Relationship As Variant, _
Optional Key As Variant, _
Optional ByVal UseOriginalKey As Boolean, _
Optional ByVal DestNodeExpanded As Boolean, _
Optional ByVal KeepSelection As Boolean) As Node
Set MoveNode = zCopyMoveNode(Node, FromTreeView, IncludeChildren, _
KeepExpanded, ToTreeView, Relative, Relationship, Key, _
UseOriginalKey, DestNodeExpanded, KeepSelection, True)
End Function
Private Function zCopyMoveNode(Node As Variant, _
FromTreeView As TreeView, _
Optional ByVal IncludeChildren As Boolean, _
Optional ByVal KeepExpanded As Boolean, _
Optional ToTreeView As TreeView, _
Optional Relative As Variant, _
Optional ByVal Relationship As Variant, _
Optional Key As Variant, _
Optional ByVal UseOriginalKey As Boolean, _
Optional ByVal DestNodeExpanded As Boolean, _
Optional ByVal KeepSelection As Boolean, _
Optional ByVal Remove As Boolean) As Node
Dim nToTreeView As TreeView
Dim nNode As Node
Me.Serialize Node, FromTreeView, IncludeChildren, Remove
If ToTreeView Is Nothing Then
Set nToTreeView = FromTreeView
Else
Set nToTreeView = ToTreeView
End If
Set nNode = Me.Deserialize(nToTreeView, Relative, Relationship, _
Key, UseOriginalKey, IncludeChildren, KeepExpanded, _
KeepSelection)
With nNode
If Not (.Parent Is Nothing) Then
.Expanded = DestNodeExpanded
End If
End With
Set zCopyMoveNode = nNode
End Function
|
|
|