Click here to Skip to main content
Click here to Skip to main content

Loading and Saving XML to and from a TreeView Control

, 4 Jan 2006
Rate this:
Please Sign up or sign in to vote.
Details of how to load XML into a TreeView, and how to save XML to disk from a TreeView.

Introduction

This article will demonstrate how to populate a Windows Forms TreeView control from an XML file, as well as provides two methods to serialize the TreeView back to XML.

Background

There are probably a thousand ways out on the 'net to do this. I know I've downloaded control after control, and I've seen hundreds of different techniques for doing this. This is, to date, the simplest way of doing this I've come across, and it doesn't require adding any controls to your project. You only need a couple of methods, and you're ready to go. Since there tend to be discrepancies on "best practices" with regards to XML serialization, I've included two common, and efficient methods for doing so.

Getting Started

If you download the working sample, you'll be able to immediately run the application and see how the methods work. I've included an examples.xml file for use in testing the application. Keep in mind that I've only done the barest of exception handling in the demo project. You'll want to ensure you include the following references in your application:

using System.Xml;
using System.Text;
using System.IO;

De-Serializing XML

XML is a great format. I mean, it's just text, after all, put together in a hierarchical fashion. .NET has been helpful (albeit a bit over-heady) in providing methods for loading and iterating through XML documents. The following methods will open an XML file, and populate each node into a TreeView control:

//Open the XML file, and start to populate the treeview
private void populateTreeview()
{
    OpenFileDialog dlg = new OpenFileDialog();
    dlg.Title = "Open XML Document";
    dlg.Filter = "XML Files (*.xml)|*.xml";
    dlg.FileName = Application.StartupPath + "\\..\\..\\example.xml";
    if (dlg.ShowDialog() == DialogResult.OK)
    {
        try
        {
            //Just a good practice -- change the cursor to a 
            //wait cursor while the nodes populate
            this.Cursor = Cursors.WaitCursor;
            //First, we'll load the Xml document
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(dlg.FileName);        
            //Now, clear out the treeview, 
            //and add the first (root) node
            treeView1.Nodes.Clear();
            treeView1.Nodes.Add(new 
              TreeNode(xDoc.DocumentElement.Name));
            TreeNode tNode = new TreeNode();
            tNode = (TreeNode)treeView1.Nodes[0];
            //We make a call to addTreeNode, 
            //where we'll add all of our nodes
            addTreeNode(xDoc.DocumentElement, tNode);
            //Expand the treeview to show all nodes
            treeView1.ExpandAll();    
        }
        catch(XmlException xExc) 
          //Exception is thrown is there is an error in the Xml
        {
            MessageBox.Show(xExc.Message);
        }
        catch(Exception ex) //General exception
        {
            MessageBox.Show(ex.Message);
        }
        finally
        {
            this.Cursor = Cursors.Default; //Change the cursor back
        }
    }
}
//This function is called recursively until all nodes are loaded
private void addTreeNode(XmlNode xmlNode, TreeNode treeNode)
{
    XmlNode xNode;
    TreeNode tNode;
    XmlNodeList xNodeList;
    if (xmlNode.HasChildNodes) //The current node has children
    {
        xNodeList = xmlNode.ChildNodes;
        for(int x=0; x<=xNodeList.Count-1; x++) 
          //Loop through the child nodes
        {
            xNode = xmlNode.ChildNodes[x];
            treeNode.Nodes.Add(new TreeNode(xNode.Name));
            tNode = treeNode.Nodes[x];
            addTreeNode(xNode, tNode);
        }
    }
    else //No children, so add the outer xml (trimming off whitespace)
        treeNode.Text = xmlNode.OuterXml.Trim();
}

That's all it takes to load an XML file into a TreeView. The form will look something like this:

While I'd like to take full credit for it, I can't, since MSDN has a similar example that does the same thing. However, the two methods below for serializing the XML back to file format are all mine.

Serializing the TreeView

Using a StreamWriter

There are purists out there who won't want to use this method. .NET provides developers with an XmlTextWriter which has methods inside that will dump formatted XML tags to a file. The bottom line is, it's all text, folks. How it gets there isn't as important as the fact that it got there. So, that in mind, I've provided methods for using a Streamwriter here, and below, methods for using an XmlTextWriter. Honestly, there is probably less actual code required to use the XmlTextWriter, so I suggest using whichever you're most comfortable with.

//We use this in the export and the saveNode 
//functions, though it's only instantiated once.
private StreamWriter sr;

public void exportToXml(TreeView tv, string filename) 
{
    sr = new StreamWriter(filename, false, System.Text.Encoding.UTF8);
    //Write the header
    sr.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
    //Write our root node
    sr.WriteLine("<" + treeView1.Nodes[0].Text + ">");
    foreach (TreeNode node in tv.Nodes)
    {
        saveNode(node.Nodes);
    }
    //Close the root node
    sr.WriteLine("</" + treeView1.Nodes[0].Text + ">");
    sr.Close();
}

private void saveNode(TreeNodeCollection tnc)
{
    foreach (TreeNode node in tnc)
    {
        //If we have child nodes, we'll write 
        //a parent node, then iterrate through
        //the children
        if (node.Nodes.Count > 0)
        {
            sr.WriteLine("<" + node.Text + ">");
            saveNode(node.Nodes);
            sr.WriteLine("</" + node.Text + ">");
        } 
        else //No child nodes, so we just write the text
            sr.WriteLine(node.Text);
    }
}

Using an XmlTextWriter

The following methods do exactly the same thing as above, only using an XmlTextWriter instead of a StreamWriter:

//We use this in the exportToXml2 and the saveNode2 
//functions, though it's only instantiated once.
private XmlTextWriter xr;

public void exportToXml2(TreeView tv, string filename) 
{
    xr = new XmlTextWriter(filename, System.Text.Encoding.UTF8);
    xr.WriteStartDocument();
    //Write our root node
    xr.WriteStartElement(treeView1.Nodes[0].Text);
    foreach (TreeNode node in tv.Nodes)
    {
        saveNode2(node.Nodes);
    }
    //Close the root node
    xr.WriteEndElement();
    xr.Close();
}

private void saveNode2(TreeNodeCollection tnc)
{
    foreach (TreeNode node in tnc)
    {
        //If we have child nodes, we'll write 
        //a parent node, then iterrate through
        //the children
        if (node.Nodes.Count > 0)
        {
            xr.WriteStartElement(node.Text);
            saveNode2(node.Nodes);
            xr.WriteEndElement();    
        } 
        else //No child nodes, so we just write the text
        {
            xr.WriteString(node.Text);
        }
    }
}

That's all there is to it.

Points of Interest

The methods I used above are barebones. What I mean by this is that no attributes will be saved during serialization, only node names and text. This is rather easy to overcome, but you'll need to modify the serialization a bit to allow it to do so. This is probably most easily done using the XmlTextWriter serialization methods.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

UsualDosage
Architect
United States United States
I have been an ASP.NET/C# Programmer/Software Architect for about 10 years, specializing in web architecture, user interface, and user experience. I formerly wrote business applications for mortgage banking front-ends in C++ before switching to the .NET Framework many years ago, and I've never looked back. I'm an evangelist of HTML5 and web standards, and spend the majority of my time working on front end design, performance and scale.

My primary website is located at http://www.usualdosage.com

Comments and Discussions

 
GeneralMy vote of 3 PinmemberVanShiyi10-Oct-12 15:06 
GeneralWorks really good, shows the basics. PinmemberPhix196429-Mar-12 11:08 
SuggestionA little change for Web Pinmemberhectorea9-Aug-11 8:06 
Generalthanks, I made a few changes with given code to make it work for me..check if anyone finds it helpful :) PinmemberBaba Deep19-Jul-09 15:34 
//To save treeview11 state in xml
Properties.Settings.Default.GRAPHS_GROUP_XML = exportToXml(treeView1);
Properties.Settings.Default.Save();
this.Close();
 
//To restore treeview1 state from xml
string xmlStr = Properties.Settings.Default.GRAPHS_GROUP_XML;
if (xmlStr != string.Empty)
{
populateTreeview(treeView1, xmlStr);
}
 

 

#region treeview to xml to treeview
 
#region method to populate treeview from given xml string
//Open the XML file, and start to populate the treeview
private void populateTreeview(TreeView treeview, string xmlStr)
{
try
{
//Just a good practice -- change the cursor to a
//wait cursor while the nodes populate
this.Cursor = Cursors.WaitCursor;
//First, we'll load the Xml document
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xmlStr);
//Now, clear out the treeview,
//and add the first (root) node
treeview.Nodes.Clear();
foreach (XmlNode xmlNode in xDoc.DocumentElement.ChildNodes)
{
TreeNode tn = new TreeNode(restoreSpace(xmlNode.Name));
addTreeNode(xmlNode, tn);
treeview.Nodes.Add(tn);
}
 
treeview.ExpandAll();
}
catch (XmlException xExc)
//Exception is thrown is there is an error in the Xml
{
MessageBox.Show(xExc.Message);
}
catch (Exception ex) //General exception
{
MessageBox.Show(ex.Message);
}
finally
{
this.Cursor = Cursors.Default; //Change the cursor back
}
}
//This function is called recursively until all nodes are loaded
private void addTreeNode(XmlNode xmlNode, TreeNode treeNode)
{
XmlNode xNode;
TreeNode tNode;
XmlNodeList xNodeList;
if (xmlNode.HasChildNodes) //The current node has children
{
xNodeList = xmlNode.ChildNodes;
for (int x = 0; x <= xNodeList.Count - 1; x++)
//Loop through the child nodes
{
xNode = xmlNode.ChildNodes[x];
treeNode.Nodes.Add(new TreeNode(restoreSpace(xNode.Name)));
tNode = treeNode.Nodes[x];
addTreeNode(xNode, tNode);
}
}
}
 
#endregion
 
#region method to export treeview to xml string
 
public string exportToXml(TreeView tv)
{
StringWriter sr = new StringWriter();
sr.Flush();
//Write the header
sr.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
//Write our root node
sr.WriteLine("<root>");
foreach (TreeNode tn in treeView1.Nodes)
{
sr.WriteLine("<" + trimSpace(tn.Text) + ">");
saveNode(tn.Nodes, sr);
sr.WriteLine("</" + trimSpace(tn.Text) + ">");
}
//Close the root node
sr.WriteLine("</root>");
sr.Close();
return sr.ToString();
}
 
private void saveNode(TreeNodeCollection tnc, StringWriter sr)
{
foreach (TreeNode node in tnc)
{
//If we have child nodes, we'll write
//a parent node, then iterrate through
//the children
if (node.Nodes.Count > 0)
{
sr.WriteLine("<" + trimSpace(node.Text) + ">");
saveNode(node.Nodes, sr);
sr.WriteLine("</" + trimSpace(node.Text) + ">");
}
else //No child nodes, so we just write the text
sr.WriteLine("<" + trimSpace(node.Text) + "/>");
}
}
#endregion
 
private string trimSpace(string str)
{
return str.Replace(' ', '_');
}
private string restoreSpace(string str)
{
return str.Replace('_', ' ');
}
 
#endregion
GeneralA question... if I have attribute in parent, the tree cannot show the attribute Pinmemberkwokkiu23-Mar-09 18:45 
Generaldon't display "state" Pinmembermquiz1-Mar-09 21:34 
GeneralNice PinmemberSteve_Harris17-Feb-08 23:44 
QuestionXML help *simple PinmemberKanedogg0831-Jan-08 12:33 
GeneralRe: XML help *simple PinmemberUsualDosage31-Jan-08 16:11 
GeneralClean and Nice PinmemberJames S. Taylor12-Dec-07 2:48 
QuestionAttributes? Pinmemberlizard king6921-Nov-07 14:41 
GeneralHelp a lot Pinmemberfelix990200216-Oct-07 21:15 
Questionhow do I list this xml? Pinmemberkris kumar9-Oct-07 12:29 
GeneralPerformance improvement... Pinmembertopherino30-Jul-07 2:46 
GeneralRe: Performance improvement... PinmemberSanjeevg774-Jun-09 23:45 
GeneralRe: Performance improvement... PinmemberPhix196429-Mar-12 11:13 
Generalin case of empty element [modified] Pinmemberdiees19-Apr-07 19:05 
GeneralRe: in case of empty element PinmemberUsualDosage23-Apr-07 3:02 
GeneralRe: Example code PinmemberOzitraveller21-Mar-06 13:40 
GeneralGood work PinmemberAlessio Saltarin8-Feb-06 2:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 4 Jan 2006
Article Copyright 2006 by UsualDosage
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid