简单看了下如何手工将XML文件中的数据填充到TreeView控件的操作,虽然整个过程比较简单,但还是很有必要记录一下。另外,这部分内容是通过手工填充的方式来完成数据和控件的绑定,也是将来利用数据源绑定数据的基础。
首先明确我们要解决的问题:XML文件中保存的数据存在很明显的树形结构,而这些数据用WinForm中的树控件来显示再合适不过,那么如何才能在控件中显示这些数据呢?
其次,还需要明确下我们的目标,我的想法是,
1. 指定XML文件,在树控件显示数据并保持它们的层级关系。
2. 右键单击树结点,弹出右键菜单,允许用户增加兄弟结点、子结点并允许删除结点(对兄弟结点、子结点这些名词感到陌生的话,建议先去熟悉下树的结构知识)
按照惯例,先上代码:
class CustomTreeView: TreeView
{
//items of context menu strip
#region private enumberator
private enum ItemsString
{
AddSibling=0,
AddChild,
Delete
//AddAttribute
}
#endregion
#region constructor
public CustomTreeView()
{
//add items to context menu strip control attached to this control
ContextMenuStrip cms = new ContextMenuStrip();
foreach (String szItemsString in System.Enum.GetNames(typeof(ItemsString)))
{
cms.Items.Add(szItemsString);
}
cms.Items[0].MouseDown+=new MouseEventHandler(AddSiblingNode);
cms.Items[1].MouseDown += new MouseEventHandler(AddChildNode);
cms.Items[2].MouseDown += new MouseEventHandler(DeleteNode);
//set context menu strip
ContextMenuStrip = cms;
}
#endregion
#region public function
/// <summary>
/// load a xmlfile represented by a stream into a treeView
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public bool load(Stream stream)
{
if (stream == null) return false;
try
{
XmlDocument document = new XmlDocument();
document.Load(stream);
XmlNode rootXmlNode = document.DocumentElement;
TreeNode rootTreeNode = new TreeNode(rootXmlNode.Name);
this.Nodes.Add(rootTreeNode);
//we attach a xml file to a treeview control by recusively add xmlnode to treenode
attachNode(rootTreeNode, rootXmlNode);
this.ExpandAll();
return true;
}
catch (System.Exception ex)
{
Debug.WriteLine(ex.Message);
return false;
}
}
#endregion
#region private function
private void attachNode(TreeNode parentTreeNode, XmlNode parentXmlNode)
{
if (parentTreeNode == null || parentXmlNode == null)
return;
foreach (XmlNode childXmlNode in parentXmlNode.ChildNodes)
{
if (childXmlNode is XmlText)
{
TreeNode childTreeNode = new TreeNode((childXmlNode as XmlText).InnerText);
parentTreeNode.Nodes.Add(childTreeNode);
}
else
{
TreeNode childTreeNode = new TreeNode(childXmlNode.Name);
//go through its attribute and generate attribute string
String szAttribute=" <";
int nCount = childXmlNode.Attributes.Count;
foreach (XmlAttribute attr in childXmlNode.Attributes)
{
szAttribute += attr.Name + " = " + attr.Value;
if (attr != childXmlNode.Attributes[nCount - 1])
szAttribute += ", ";
}
szAttribute += ">";
if (nCount == 0)
szAttribute = String.Empty;
childTreeNode.Text += szAttribute;
//append to the childnodes list of its parent
parentTreeNode.Nodes.Add(childTreeNode);
attachNode(childTreeNode, childXmlNode);
}
}
}
// Summary:
// Represents the method that will handle the MouseDown, MouseUp, or MouseMove
// event of a form, control, or other component.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// A System.Windows.Forms.MouseEventArgs that contains the event data.
private void AddSiblingNode(object sender, MouseEventArgs e)
{
TreeNode parentNode = SelectedNode.Parent;
if (parentNode == null)
{
return;
}
TreeNode nodeToInsert = new TreeNode();
int nIndex = parentNode.Nodes.IndexOf(SelectedNode);
parentNode.Nodes.Insert(nIndex + 1, nodeToInsert);
SelectedNode = nodeToInsert;
ContextMenuStrip.Hide();
this.Invalidate();
}
// Summary:
// Represents the method that will handle the MouseDown, MouseUp, or MouseMove
// event of a form, control, or other component.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// A System.Windows.Forms.MouseEventArgs that contains the event data.
private void AddChildNode(object sender, MouseEventArgs e)
{
TreeNode childNode = new TreeNode();
SelectedNode.Nodes.Add(childNode);
SelectedNode = childNode;
ContextMenuStrip.Hide();
this.Invalidate();
}
private void deleteNodeAndItsDescendants(TreeNode node)
{
}
// Summary:
// Represents the method that will handle the MouseDown, MouseUp, or MouseMove
// event of a form, control, or other component.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// A System.Windows.Forms.MouseEventArgs that contains the event data.
private void DeleteNode(object sender, MouseEventArgs e)
{
if(DialogResult.Yes==
MessageBox.Show(this, "All descendants of this node will be deleted! Are you sure?", "Dangerous", MessageBoxButtons.YesNo))
{
this.BeginUpdate();
TreeNode node = SelectedNode;
if (node.Parent != null)
node.Parent.Nodes.Remove(node);
else //root node
this.Nodes.Remove(node);
this.EndUpdate();
ContextMenuStrip.Hide();
}
}
#endregion
#region override function
/// <summary>
/// get a context menu strip when right-button is clicked, ans allow user to add sibling node and childnode
/// </summary>
/// <param name="e"></param>
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
base.OnNodeMouseClick(e);
if (e.Button == MouseButtons.Right && e.Node!=null)
{
SelectedNode = e.Node;
if (e.Node.Parent == null)
ContextMenuStrip.Items[(int)ItemsString.AddSibling].Visible = false;
else
ContextMenuStrip.Items[(int)ItemsString.AddSibling].Visible = true;
ContextMenuStrip.Show(e.Location);
}
//else //left button click
//{
// e.Node.BeginEdit();
//}
}
#endregion
}
在这里,我们定制了一个树控件,该控件派生自TreeView控件,并且完成了如下操作:
1. 在构造函数中添加了右键菜单,并订阅相应的点击事件
2. 订阅了树结点的鼠标右键点击事件处理函数,在该函数显示右键菜单供用户选择
3. 编写右键菜单的点击事件处理函数,包括增加子结点、兄弟结点以及删除结点功能,其中删除结点会将它所有的后代结点一并删除,因此在删除之前,弹窗确认。
4. 从流中加载XML文件,通过递归的方式,不断地往树中增加结点;另外,如果XML中的结点有属性,也会在树结点中以<name1=value1, name2=value2>的形式显示出来。
个人认为,整个代码还是很简单的,如果可能存在难度的地方,就是第4点中,递归地增加树结点的地方,可能需要有一点点递归的概念,在这里就不多做阐述。好了,关于TreeView的第一部分的内容就到此为止了,这部分主要是通过手工绑定的方式实现XML文件在树控件中的显示,可以看到,这种方法比较原始,需要自己去控件显示,另外,在生成整个树之后,显示的树结点和XML数据之间没有关系,对树结点所有的操作也不会被自动更新到后台的数据,如果用户希望能够动态地更新数据,需要自己来维护,因此使用起来还是不够方便,所以在下篇文章中,将继续介绍如何动态地使用绑定功能,实现数据和树控件之间的绑定,实现自动更新。另外,本文中的代码可以在这里下到。