Introduction
The purpose of this article is to demonstrate the saving and loading of System.Windows.Forms.TreeView
control from an XML file. XmlTextReader
and XmlTextWriter
of System.Xml
namespace are used for reading and generating XML files respectively. It also demonstrates a simple XML file viewer using a TreeView
control.
Getting started
The real functionality is enclosed in a class called TreeViewSerializer
. It has two main responsibilities:
- Saving the
TreeView
to a specified XML file.
- Loading the
TreeView
from any specified XML file.
The structure of an XML file used for serializing a TreeView
is quite simple. Following is a sample XML file included with the attached project:
="1.0" ="us-ascii"
<TreeView>
<node text="Asia" imageindex="0">
<node text="China" imageindex="-1" tag="Largest Population">
<node text="Beijing" imageindex="-1" /></node>
<node text="Pakistan" imageindex="4" />
<node text="India" imageindex="5" />
<node text="Srilanka" imageindex="6" />
</node>
<node text="Europe" imageindex="1">
<node text="Germany" imageindex="6" />
</node>
<node text="America" imageindex="2" />
<node text="Africa" imageindex="3" />
</TreeView>
After the XML declaration, all the nodes are enclosed in a TreeView
tag. The TreeView
tag may contain multiple node
tags. The node
tag can also contain other node
tags. Each node
tag can have three attributes:
Text
ImageIndex
Tag
I have serialized the above three attributes of System.Windows.Forms.TreeNode
object, it can be easily extended to include other attributes.
XmlNodeTag
, XmlNodeTextAtt
, XmlNodeTagAtt
and XmlNodeImageIndexAtt
are the constants defined in TreeViewSerializer
class:
private const string XmlNodeTag = "node";
private const string XmlNodeTextAtt = "text";
private const string XmlNodeTagAtt = "tag";
private const string XmlNodeImageIndexAtt = "imageindex";
Loading TreeView from XML � Deserialization
The deserialization is performed by the DeserializeTreeView
method which uses XmlTextReader
to parse through the XML document and fill the TreeView
object. Following is the definition of DeserializeTreeView
method:
public void DeserializeTreeView(TreeView treeView, string fileName)
{
XmlTextReader reader = null;
try
{
treeView.BeginUpdate();
reader = new XmlTextReader(fileName);
TreeNode parentNode = null;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == XmlNodeTag)
{
TreeNode newNode = new TreeNode();
bool isEmptyElement = reader.IsEmptyElement;
int attributeCount = reader.AttributeCount;
if (attributeCount > 0)
{
for (int i = 0; i < attributeCount; i++)
{
reader.MoveToAttribute(i);
SetAttributeValue(newNode,
reader.Name, reader.Value);
}
}
if(parentNode != null)
parentNode.Nodes.Add(newNode);
else
treeView.Nodes.Add(newNode);
if (!isEmptyElement)
{
parentNode = newNode;
}
}
}
else if (reader.NodeType == XmlNodeType.EndElement)
{
if (reader.Name == XmlNodeTag)
{
parentNode = parentNode.Parent;
}
}
else if (reader.NodeType == XmlNodeType.XmlDeclaration)
{
}
else if (reader.NodeType == XmlNodeType.None)
{
return;
}
else if (reader.NodeType == XmlNodeType.Text)
{
parentNode.Nodes.Add(reader.Value);
}
}
}
finally
{
treeView.EndUpdate();
reader.Close();
}
}
As XmlTextReader
parses through the XML document, appropriate actions are taken depending on the NodeType
. If the NodeType
is Element
, a new TreeNode
is created and its properties are set using the XML node attributes. The ParentNode
is set to the new TreeNode
in case of non-empty elements so that its child nodes are deserialized. If an EndElement
is encountered, the ParentNode
is set to the parent of the current parent node indicating that all the child nodes of the current node are deserialized.
For setting the Text
, Tag
and ImageIndex
properties of a TreeNode
, the SetAttributeValue
method is called. It has the following implementation:
private void SetAttributeValue(TreeNode node,
string propertyName, string value)
{
if (propertyName == XmlNodeTextAtt)
{
node.Text = value;
}
else if (propertyName == XmlNodeImageIndexAtt)
{
node.ImageIndex = int.Parse(value);
}
else if (propertyName == XmlNodeTagAtt)
{
node.Tag = value;
}
}
Saving TreeView � Serialization
The SerializeTreeView
saves the System.Windows.Forms.TreeView
to the specified file. Following is the method definition:
public void SerializeTreeView(TreeView treeView, string fileName)
{
XmlTextWriter textWriter = new XmlTextWriter(fileName,
System.Text.Encoding.ASCII);
textWriter.WriteStartDocument();
textWriter.WriteStartElement("TreeView");
SaveNodes(treeView.Nodes, textWriter);
textWriter.WriteEndElement();
textWriter.Close();
}
The above code I guess is pretty much self explanatory. The SerializeTreeView
method:
- Instantiates the
XmlTextWriter
, passing it the provided filename.
- XML declaration (<?xml version="1.0" encoding="us-ascii" ?>) is written to the stream.
- The
TreeView
tag is written to the stream.
SaveNodes
method is called. The TreeNode
collection in a TreeView
is passed to it, it saves all the nodes in a TreeView
to the stream calling itself recursively. Its definition is produced below.
- The end tag of
TreeView
is written.
- And finally the stream is closed.
Following is the definition of SaveNodes
method:
private void SaveNodes(TreeNodeCollection nodesCollection,
XmlTextWriter textWriter)
{
for(int i = 0; i < nodesCollection.Count; i++)
{
TreeNode node = nodesCollection[i];
textWriter.WriteStartElement(XmlNodeTag);
textWriter.WriteAttributeString(XmlNodeTextAtt,
node.Text);
textWriter.WriteAttributeString(
XmlNodeImageIndexAtt, node.ImageIndex.ToString());
if(node.Tag != null)
textWriter.WriteAttributeString(XmlNodeTagAtt,
node.Tag.ToString());
if (node.Nodes.Count > 0)
{
SaveNodes(node.Nodes, textWriter);
}
textWriter.WriteEndElement();
}
}
The SaveNodes
method loops through the TreeNode
collection, and writes the node
tag and its attributes. If a node
has child nodes then the method is called recursively. After the method returns, having written the child nodes to the stream, the node end tag is written.
XML File Viewer
There is another method in the TreeViewSerializer
class called LoadXmlFileInTreeView
. This can be used to view any XML document in the TreeView
. The following screen shot displays our sample XML file loaded into the TreeView
:
The code for loading an XML file is pretty much similar to the SerializeTreeView
method. I am not including the code here for the sake of brevity. You can find it in the attached source code.
Demo application
A demo application is provided with the article as the driver of TreeViewSerializer
functionality. It has three functions:
- Save: save/serialize the
TreeView
to an XML file. A SaveFileDialog
appears for specifying the file.
- Load: load/deserialize the
TreeView
from an XML file.
- View XML File: to view any XML file in the
TreeView
.
Notes
The XML document can also be manipulated using the DOM based XmlDocument
and XmlNode
classes. They are useful if we require searching and editing of XML document. In our case, XmlTextWriter
and XmlTextReader
are more efficient as they operate in a forward only, non-cached manner.
The generated XML file is not formatted by inserting a new line and tabs. Use textWriter.WriteRaw("\r\n");
at appropriate places in SerializeTreeView
for this purpose. Obviously, you may use IE to view the formatted XML file. :)