使用 XmlTextWriter 和 XmlTextReader 将 TreeView 控件加载和保存到 XML 文件






4.74/5 (53投票s)
演示如何使用单向、非缓存的 XmlTextReader 和 XmlTextWriter 从 XML 文件中序列化和反序列化 System.Windows.Forms.TreeView 控件的内容。
引言
本文的目的是演示如何将 System.Windows.Forms.TreeView
控件的内容保存到 XML 文件以及从 XML 文件加载。System.Xml
命名空间中的 XmlTextReader
和 XmlTextWriter
分别用于读取和生成 XML 文件。它还使用 TreeView
控件演示了一个简单的 XML 文件查看器。
入门
实际功能包含在一个名为 TreeViewSerializer
的类中。它有两个主要职责:
- 将
TreeView
保存到指定的 XML 文件。 - 从任何指定的 XML 文件加载
TreeView
。
用于序列化 TreeView
的 XML 文件的结构非常简单。以下是随附项目包含的示例 XML 文件:
<?xml version="1.0" encoding="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>
在 XML 声明之后,所有节点都包含在 TreeView
标签中。TreeView
标签可以包含多个 node
标签。node
标签也可以包含其他 node
标签。每个 node
标签可以有三个属性:
文本
ImageIndex
Tag
我已经序列化了 System.Windows.Forms.TreeNode
对象的上述三个属性,可以轻松扩展以包含其他属性。
XmlNodeTag
、XmlNodeTextAtt
、XmlNodeTagAtt
和 XmlNodeImageIndexAtt
是在 TreeViewSerializer
类中定义的常量。
// Xml tag for node, e.g. 'node' in case of <node></node>
private const string XmlNodeTag = "node";
// Xml attributes for node e.g. <node text="Asia" tag=""
// imageindex="1"></node>
private const string XmlNodeTextAtt = "text";
private const string XmlNodeTagAtt = "tag";
private const string XmlNodeImageIndexAtt = "imageindex";
从 XML 加载 TreeView – 反序列化
反序列化由 DeserializeTreeView
方法执行,该方法使用 XmlTextReader
解析 XML 文档并填充 TreeView
对象。以下是 DeserializeTreeView
方法的定义:
public void DeserializeTreeView(TreeView treeView, string fileName)
{
XmlTextReader reader = null;
try
{
// disabling re-drawing of treeview till all nodes are added
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;
// loading node attributes
int attributeCount = reader.AttributeCount;
if (attributeCount > 0)
{
for (int i = 0; i < attributeCount; i++)
{
reader.MoveToAttribute(i);
SetAttributeValue(newNode,
reader.Name, reader.Value);
}
}
// add new node to Parent Node or TreeView
if(parentNode != null)
parentNode.Nodes.Add(newNode);
else
treeView.Nodes.Add(newNode);
// making current node 'ParentNode' if its not empty
if (!isEmptyElement)
{
parentNode = newNode;
}
}
}
// moving up to in TreeView if end tag is encountered
else if (reader.NodeType == XmlNodeType.EndElement)
{
if (reader.Name == XmlNodeTag)
{
parentNode = parentNode.Parent;
}
}
else if (reader.NodeType == XmlNodeType.XmlDeclaration)
{
//Ignore Xml Declaration
}
else if (reader.NodeType == XmlNodeType.None)
{
return;
}
else if (reader.NodeType == XmlNodeType.Text)
{
parentNode.Nodes.Add(reader.Value);
}
}
}
finally
{
// enabling redrawing of treeview after all nodes are added
treeView.EndUpdate();
reader.Close();
}
}
当 XmlTextReader
解析 XML 文档时,会根据 NodeType
采取适当的操作。如果 NodeType
是 Element
,则会创建一个新的 TreeNode
,并使用 XML 节点属性设置其属性。对于非空元素,将 ParentNode
设置为新的 TreeNode
,以便对其子节点进行反序列化。如果遇到 EndElement
,则将 ParentNode
设置为当前父节点的父节点,表示当前节点的子节点已全部反序列化。
为了设置 TreeNode
的 Text
、Tag
和 ImageIndex
属性,会调用 SetAttributeValue
方法。其实现如下:
/// <summary>
/// Used by Deserialize method for setting properties of
/// TreeNode from xml node attributes
/// </summary>
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;
}
}
保存 TreeView – 序列化
SerializeTreeView
将 System.Windows.Forms.TreeView
保存到指定的文件。以下是方法定义:
public void SerializeTreeView(TreeView treeView, string fileName)
{
XmlTextWriter textWriter = new XmlTextWriter(fileName,
System.Text.Encoding.ASCII);
// writing the xml declaration tag
textWriter.WriteStartDocument();
//textWriter.WriteRaw("\r\n");
// writing the main tag that encloses all node tags
textWriter.WriteStartElement("TreeView");
// save the nodes, recursive method
SaveNodes(treeView.Nodes, textWriter);
textWriter.WriteEndElement();
textWriter.Close();
}
我猜上面的代码是相当不言自明的。SerializeTreeView
方法:
- 实例化
XmlTextWriter
,并将提供的文件名传递给它。 - XML 声明 (<?xml version="1.0" encoding="us-ascii" ?>) 被写入流。
TreeView
标签被写入流。- 调用
SaveNodes
方法。将TreeView
中的TreeNode
集合传递给它,它通过递归调用自身将TreeView
中的所有节点保存到流中。其定义如下所示。 - 写入
TreeView
的结束标签。 - 最后,关闭流。
以下是 SaveNodes
方法的定义:
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());
// add other node properties to serialize here
if (node.Nodes.Count > 0)
{
SaveNodes(node.Nodes, textWriter);
}
textWriter.WriteEndElement();
}
}
SaveNodes
方法循环遍历 TreeNode
集合,并写入 node
标签及其属性。如果一个 node
包含子节点,则递归调用该方法。在方法返回后(已将子节点写入流),写入节点的结束标签。
XML 文件查看器
TreeViewSerializer
类中还有另一个名为 LoadXmlFileInTreeView
的方法。该方法可用于在 TreeView
中查看任何 XML 文档。以下屏幕截图显示了加载到 TreeView
中的示例 XML 文件:
加载 XML 文件的代码与 SerializeTreeView
方法非常相似。为简洁起见,此处不包含代码。您可以在附件的源代码中找到它。
演示应用程序
本文提供了一个演示应用程序,作为 TreeViewSerializer
功能的驱动程序。它有三个功能:
- 保存:将
TreeView
保存/序列化到 XML 文件。会出现一个SaveFileDialog
用于指定文件。 - 加载:从 XML 文件加载/反序列化
TreeView
。 - 查看 XML 文件:在
TreeView
中查看任何 XML 文件。
注释
XML 文档也可以使用基于 DOM 的 XmlDocument
和 XmlNode
类进行操作。如果我们还需要搜索和编辑 XML 文档,它们会很有用。在我们的例子中,XmlTextWriter
和 XmlTextReader
更高效,因为它们以单向、非缓存的方式运行。
生成的 XML 文件**不会**通过插入换行符和制表符进行格式化。为此,请在 SerializeTreeView
的适当位置使用 textWriter.WriteRaw("\r\n);
。当然,您可以使用 IE 查看格式化的 XML 文件。 :)