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 the System.Xml namespace are used for reading and generating XML files, respectively. It also demonstrates a simple XML file viewer using a TreeView control.
Background
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 the System.Windows.Forms.TreeNode object, it can be easily extended to include other attributes.
XmlNodeTag, XmlNodeTextAtt, XmlNodeTagAtt, and XmlNodeImageIndexAtt are the constants defined in the TreeViewSerializer class:
private const string XmlNodeTag = "node";
private const string XmlNodeTextAtt = "text";
private const string XmlNodeTagAtt = "tag";
private const string XmlNodeImageIndexAtt = "imageindex";
Using the code
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 the 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;
}
}