Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Serialization to Persist TreeView Control (VB.NET)

0.00/5 (No votes)
27 Oct 2004 11  
Utilising serialization to persist TreeView control hierarchy.

Contents

Introduction

A recent post on a MS group regarding persisting TreeView data to file and apparent lack of VB.Net example lead me to write a solution as it was one of the first problems I faced as a developer and thought it would make for an interesting article for beginners. Additionally, the utilisation of serialisation to persist objects as Xml provides an introduction to a great ‘free’ way of storing data.

The Problem

The TreeView and TreeNode types have no intrinsic methods for persisting their data, neither are they serializable, the solution therefore is to write classes that can meditate between TreeView and file.

TreeView Structures

Firstly we need a couple of structures, one to represent the TreeView and another to represent a TreeNode. These structures need to duplicate the properties within these types that need to be persisted and provide methods to convert from the source objects and these structures.

TreeViewData Structure

This structure is used to represent the TreeView type and its properties in their simplest form:

<Serializable()>Public Structure TreeViewData

    Public Nodes() As TreeNodeData

    Public Sub New(ByVal treeview As TreeView)
        'Check to see if there are any root nodes in the TreeView
        If treeview.Nodes.Count = 0 Then Exit Sub

        'Populate the Nodes array with child nodes
         ReDim Nodes(treeview.Nodes.Count - 1)
        For i As Integer = 0 To treeview.Nodes.Count - 1
            Nodes(i) = New TreeNodeData(treeview.Nodes(i))
        Next

    End Sub

    Public Sub PopulateTree(ByVal treeView As TreeView)
        'Check to see if there are any root nodes in the TreeViewData
        If Me.Nodes Is Nothing OrElse Me.Nodes.Length = 0 Then Exit Sub

        'Populate the TreeView with child nodes
        treeView.BeginUpdate()
        For i As Integer = 0 To Me.Nodes.Length - 1
            treeView.Nodes.Add(Me.Nodes(i).ToTreeNode)
        Next
        treeView.EndUpdate()
    End Sub

End Structure

The only property within the TreeView that is represented by this structure is the Nodes collection as this is the only property that really needs to be persisted. Notice the constructer has a TreeView parameter, this allows the TreeViewData structure to be populated from the source TreeView in a single line of code:

Dim treeViewData1 as New TreeViewData(MyTreeView)

Providing the TreeView Nodes collection contains one or more TreeNode object the constructer will adjust the size of the Nodes() array to match the size of the TreeView.Nodes collection and populate it with TreeNodeData representation of the root TreeNodes. The TreeNodeData structure will take care of populating its own hierarchy (

The PopulateTree method performs the reverse action to the constructor, by populating the specified TreeView with the contents of the TreeViewData instance. Notice in how the For loop is contained within BeginUpdate and EndUpdate statements, this prevents the TreeView from being redrawn each time a new Node is added, providing a smoother visual experience for the user, particularly on larger node hierarchies.

TreeNodeData Structure

This structure is used to represent the TreeNode class and its properties in their simplest form:

<Serializable()> Public Structure TreeNodeData

    Public Text As String
    Public ImageIndex As Integer
    Public SelectedImageIndex As Integer
    Public Checked As Boolean
    Public Expanded As Boolean
    Public Tag As Object
    Public Nodes() As TreeNodeData

    Public Sub New(ByVal node As TreeNode)
        'Set the basic TreeNode properties
        Me.Text = node.Text
        Me.ImageIndex = node.ImageIndex
        Me.SelectedImageIndex = node.SelectedImageIndex
        Me.Checked = node.Checked
        Me.Expanded = node.IsExpanded

        'See if there is an object in the tag property 
        'and if it is serializable
        If (Not node.Tag Is Nothing) AndAlso 
          node.Tag.GetType.IsSerializable Then Me.Tag = node.Tag

        'Check to see if there are any child nodes
        If node.Nodes.Count = 0 Then Exit Sub

        'Recurse through child nodes and add to Nodes array
        ReDim Nodes(node.Nodes.Count - 1)
        For i As Integer = 0 To node.Nodes.Count - 1
            Nodes(i) = New TreeNodeData(node.Nodes(i))
        Next
    End Sub

    Public Function ToTreeNode() As TreeNode
        'Create TreeNode based on instance of 
        'TreeNodeData and set basic properties
        ToTreeNode = New TreeNode(Me.Text, Me.ImageIndex, 
               Me.SelectedImageIndex)
        ToTreeNode.Checked = Me.Checked
        ToTreeNode.Tag = Me.Tag
        If Me.Expanded Then ToTreeNode.Expand()

        'Recurse through child nodes adding to Nodes collection
        If Me.Nodes Is Nothing OrElse Me.Nodes.Length = 0 Then Exit Function

        For i As Integer = 0 To Me.Nodes.Length - 1
            ToTreeNode.Nodes.Add(Me.Nodes(i).ToTreeNode)
        Next
    End Function

End Structure

The TreeNode has a few more properties that need to be persisted compared to the TreeView, again the structure of the TreeNodeData instance is generated from the source object by passing it as a parameter in the constructor:

Dim treeNodeData1 as New TreeNodeData(MyTreeNode)

If (Not node.Tag Is Nothing) AndAlso node.Tag.GetType.IsSerializable 
  Then Me.Tag = node.Tag

Because the Tag property can be any object, and this solution uses serialization (

Finally the structure contains the ToTreeNode function that returns a TreeNode object with its hierarchy populated from the TreeNodeData structure.

Properties to be persisted are directly mapped from the source TreeNode to the TreeNodeData instance with the exception of the Tag property:see below) to persist data we need to check that the object in the Tag can also be serialized, therefore we only map the Tag property if it is of a serializable type.see below).

Storing and Retrieving

These structures are all very well, but we’ve still not got a way of storing the TreeView to file. To achieve this goal we’ll use serialisation which basically converts objects that support it to and from structured Xml.

Notice that both the TreeViewData and TreeNodeData structures have the attribute <Serializable()> preceding their declaration, indicating the class can be serialized. All we have to do is provide a couple of methods to serialize and desterilize the structures, the first saves the structure to file:

Public Shared Sub SaveTreeViewData(ByVal treeView As TreeView, 
ByVal path As String)
    'Create a serializer and file to save TreeViewData
    Dim ser As New System.Xml.Serialization.XmlSerializer(
        GetType(TreeViewData))
    Dim file As New System.IO.FileStream(path, IO.FileMode.Create)
    Dim writer As New System.Xml.XmlTextWriter(file, Nothing)

    'Generate TreeViewData from TreeView and serialize the file.
    ser.Serialize(writer, New TreeViewData(treeView))

    'Tidy up
    writer.Close()
    file.Close()
    file = Nothing
End Sub

This method accepts a couple of parameters; the TreeView to persist and the path to store the data. Notice the method is declared as a Shared, this means that an instance of its containing class does not need to be initiated before the method can be used (

Firstly we create an XmlSerializer that expects to be dealing with object of TreeViewData type, we create a file to save the Xml and an XmlTextWriter to write the serialized object to the file.

The line that does all the work:

ser.Serialize(writer, New TreeViewData(treeView))

Secondly we need to load previously stored TreeViewData structures:

Public Shared Sub LoadTreeViewData(ByVal treeView As TreeView, 
ByVal path As String)

    'Create as serializer and get the file to deserialize
    Dim ser As New System.Xml.Serialization.XmlSerializer(
       GetType(TreeViewData))
    Dim file As New System.IO.FileStream(path, IO.FileMode.Open)
    Dim reader As New System.Xml.XmlTextReader(file)

    'Deserialize the file and populate the treeview
    Dim treeData As TreeViewData = CType(ser.Deserialize(reader), 
        TreeViewData)
    treeData.PopulateTree(treeView)

    'Tidy up
    reader.Close()
    file.Close()
    file = Nothing

End Sub

This method performs the reverse to the SaveTreeViewData, again creating an XmlSerializer to do all the work, only this time it reads the file using an XmlTextReader:

Dim treeData As TreeViewData = CType(ser.Deserialize(reader), TreeViewData)
treeData.PopulateTree(treeView)

Here we create an instance of the TreeViewData structure and populate it from the desterilized Xml file, finally we call the PopulateTreeView method of the TreeViewData object to populate the specified TreeView. This takes the specified TreeView object, creates and populates a new instance of the TreeViewData class and writes it all to file. All we need to do then tidy up by closing the XmlWriter and file.

see Bringing it all Together below).

Bringing it all Together

Finally all this code can be brought into one containing class that can be used to persist the data of any TreeView, exposing the two Shared methods for simple reusability throughout your code. This TreeViewDataAccess class simply contains all the code listed above, and can be downloaded from the links at the top of this article.

To use the class anywhere in your code all you need is one line to save the TreeView data:

TreeViewDataAccess.SaveTreeViewData(TreeView1, “C:\MyFolder\MyTreeView.xml”)

And another to load it:

TreeViewDataAccess.LoadTreeViewData(TreeView1, “C:\MyFolder\MyTreeView.xml”)

The demo project also available for download at the top of this article shows an example of this class in action.

Conclusion

I hope this article provides an interesting solution to persisting TreeView controls in VB. It should also introduce newcomers to the joys of serialization and shared methods.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here