一、简介
对于winform中如何加载xml生成目录树,在前边一篇文章“c#如何实现从xml中加载树目录,并且显示完整的Text”中我已经写了详细的过程。但是有些时候我们不可能将大量的数据存储到xml中,原因是,xml一般用于小数据量的传递。而大数据的存储与传递我们一般用专门的数据库管理工具作为传递的介质。在此,声明一点是,对于大数据量的传递,我们在这里不考虑速率传递快慢问题,我们举例只是实现最基本的功能,这样也方便和我一样的初学者理解。
好了,说了这么多废话,开始正式进入我们这篇文章的正题:
“ winform如何实现将数据库数据加载到树上”?
为了更好的展示我们想要的结果,我先把我之前做过的一个系统的这个功能实现图展示给大家,效果图如下所示:
好了,效果图已经展示!如果这是你想要的效果,那么接下来我将以代码的形式告诉你如何制作这种树结构。
二、实现过程详解
为了实现以上的效果,我们需要做的工作可以分为以下两步:
- 构建数据库
对于构建的数据库,我们需要特别进行制作。因为我们要展示成一级的目录形式,因此在构建数据库数据时,需要将各数据之间的关系说明清楚–编码规则。
在这里我不妨说一下我构建此树的编码规则,大家也可以相互学习一下:
1.1. 编码规则
对于不同的项目而言,编码的位数是具体情况具体看待的,这里我的编码用的方式是–前缀的方式。
我在定义编码位数时考虑到不同类别,不同级别,具体哪个三部分。
编码规则: 1+12+4
**1:首位,区分类别。这里我分了:经济组织、社会组织、还是行政区
**12:行政区编码。
它具体又是这样分的:4+2+3+3
4:前四位是市级
2:这两位是县(市/区)级
3:这三位是镇(乡/街道/办事处)级
3:这三位是村(城市社区/农村社区)级
**4:具体哪种类别,哪个级别下的哪个组织
我将这个字段定义为:orgID.
我们仅有了这个字段还是无法进行构建树的,我们还需要name字段(用于显示text),还需要parent字段(用于进行构建各数据之间的联系–继承关系)。当然,你可以根据你的需要构建你所需要的字段,这里我就不再多说了。我构建好的该表如下图所示:
这里我们主要看的就是它parent如何。通过此图,我相信对于个数据之间的继承关系,大家已经熟悉了。好了,我们创建好数据库后,接下来就是代码的舞台了。
- 构建TreeView
如何构建一个TreeView?这是另一个比较关键的问题。
我这里用了visual studio中自带的控件:TreeView控件。这里我不妨啰嗦一下,将我的设计图展示给大家,方便初学者理解:
是不是看起来很简单呢?好了接下来我们就要构建这个TreeView树了。
2.1. 获得数据
对于如何从数据库中获取数据这个我这里就不做说明了,我这里是以xml形式从服务器端传递给客户端的,用DataTable对象来存储。这里我做的是DataGridView某个单元格点击时,弹出这个Organizations树,因此我们还需要用一个·DataRow来记录是DataGridView的哪行数据。
具体从DataGridView中到DataRow中可以用这段代码实现:
dataRowView = gridX.Rows[rowIndex].DataBoundItem as DataRowView;
dataRow = dataRowView.Row;
这样我们就将获取的数据存在了DataTable对象中,将显示的行数据存在了dataRow中。
通过对构造函数的重载,我们将这两个参数传过来,构造函数如下(另外两个参数,我是针对不同条件进行判断的,可以不用去管):
public Organizations(DataTable transTable, DataRow transRow, String parent, String cloumnName)
{
InitializeComponent();
orgTable = new DataTable();
orgTable.TableName = "record";//DataTable表名,客户端和服务器端一致,建立联系
orgTable = transTable;
getRow = transRow;
ParentOrgID = Global.userParentOrg;//Global是我声明的静态类,用户获取各种静态变量。
userOrgID = Global.userOrgID;
name = cloumnName;
functionsParent = parent;
}
好了接下来我们需要的将数据加载到TreeView控件中,具体代码我们可以卸载Load事件中.具体代码如下:
private void Organizations_Load(object sender, EventArgs e)
{
#region 定义变量
List<TreeNode> treeNodes;
// DataRow dataRow;
DataRow[] dataRows;
TreeNode treeNode, tempNode;
NameValueCollection parameter;
String str;
TreeNode[] nodes;
String orgID,parentOrgID;
#endregion
#region 创建树
treeNodes = new List<TreeNode>();
#region 添加查询根节点
dataRows = orgTable.Select("[orgID]=\‘" + userOrgID + "\‘");
treeNode = new TreeNode();
tag = "orgID={0}&orgName={1}&parent={2}&fullName={3}";
tag = String.Format(tag, dataRows[0]["orgID"], dataRows[0]["orgName"], dataRows[0]["parent"], dataRows[0]["fullName"]);
treeNode.Name = Convert.ToString(dataRows[0]["orgID"]);
treeNode.Text = Convert.ToString(dataRows[0]["orgName"]);
treeNode.Tag = tag;
treeNodes.Add(treeNode);//将根节点加入到treeNodes列表中
treeViewX.Nodes.Add(treeNode);//这句话就是加载TreeView中的根节点。
#endregion
#region 添加子节点
while (treeNodes.Count > 0)
{
treeNode = treeNodes[0];
//每次循环都去掉第一个结点,下次循环就是第二个结点
treeNodes.Remove(treeNode);
tag = treeNode.Tag as String;
parameter = System.Web.HttpUtility.ParseQueryString(tag);
str = "[parent]=‘{0}‘";
str = String.Format(str, parameter["orgID"]);
dataRows = orgTable.Select(str);
for (int i = 0; i < dataRows.Length; i++)
{
tag = "orgID={0}&orgName={1}&parent={2}&fullName={3}";
tag = String.Format(tag, dataRows[i]["orgID"], dataRows[i]["orgName"], dataRows[i]["parent"], dataRows[i]["fullName"]);
tempNode = new TreeNode();
tempNode.Tag = tag;
tempNode.Name = Convert.ToString(dataRows[i]["orgID"]);
tempNode.Text = Convert.ToString(dataRows[i]["orgName"]);
treeNodes.Add(tempNode);
//添加上边所找父节点的子节点
treeNode.Nodes.Add(tempNode);//将treeNode下的所有子节点点tempNode加入到它的“麾下”
}
}
#endregion
#endregion
#region 将记录的org选中
//这段代码是判断的根据条件,目的是为了根据条件进行默认的选择。
if (functionsParent == "F005100")
{
if (name == "orgName")
{
parentOrgID = Convert.ToString(getRow["parentOrg"]);
nodes = treeViewX.Nodes.Find(parentOrgID, true);//查询具体的node
if (nodes.Length > 0)
{
nodes[0].Checked = true;
}
}
else if (name == "branchName")
{
orgID = Convert.ToString(getRow["orgID"]);
nodes = treeViewX.Nodes.Find(orgID, true);
if (nodes.Length > 0)
{ nodes[0].Checked = true; }
}
}
else
{
orgID = Convert.ToString(getRow["orgID"]);
nodes = treeViewX.Nodes.Find(orgID, true);
if (nodes.Length > 0)
{ nodes[0].Checked = true; }
}
#endregion
}
其实到这里,加载树已经生成了,接下来我多写一下,点击“OK”,后如何将将数据放到控件中。
private void okButton_Click(object sender, EventArgs e)
{
#region 定义变量
TreeNode selectedNode;
List<TreeNode> nodes;
TreeNode treeNode;
NameValueCollection paramters;
#endregion
#region
selectedNode = null;
nodes = new List<TreeNode>();
//注意TreeView.Nodes是获取的控件中的根节点。
for (int i = 0; i < treeViewX.Nodes.Count; i++)
{
nodes.Add(treeViewX.Nodes[i]);
}
//循环遍历,从根节点开始,然后每个节点查看是否被选中。
while (nodes.Count > 0)
{
treeNode = nodes[0];
nodes.Remove(treeNode);
if (treeNode.Checked == true)
{
selectedNode = treeNode;
break;
}
for (int i = 0; i < treeNode.Nodes.Count; i++)
{
nodes.Add(treeNode.Nodes[i]);
}
}
#endregion
#region 读取信息,并改写数据
if (selectedNode != null)
{
tag = selectedNode.Tag as String;
paramters = System.Web.HttpUtility.ParseQueryString(tag);
getRow["orgID"] = paramters["orgID"];
getRow["orgName"] = paramters["orgName"];
}
else
{
getRow["orgID"] = "";
getRow["orgName"] = "";
}
this.Close();
#endregion
}
至此我们的树基本构建完毕!之所以是基本构建完毕,原因是,当我们点击目录树时会出现如下图所示的问题:
就是我们可以进行多选,但是当我们点击确定时获得的是首个选择项。我们想要的结果是单选,而且可以进行循环的在这个窗体中修改选择项。如何实现呢?
通过msdn查找,我们会发现有个叫“BeforeCheck”的事件(在选中树节点复选框前发生),它的委托是TreeViewCancelEventHandler (BeforeCheck、BeforeCollapse、BeforeExpand 或 BeforeSelect 委托事件方法)。其中备注中有这样一句话: “除非移除了该委托,否则每当发生该事件时就调用事件处理程序。”
也就是说我们其实调用了BeforCheck的默认形式,可以多次调用BeforeCheck事件,也就是我们可以多次点击选择。(其实我们根据备注这句话就可以获取了解答的方法。)
那么我们不禁产生了如下问题:
问题1:如何解决多选?
我们可以通过重新书写BeforeCheck事件,先让其移除,即不可以选择,然后在最后在调用修改的BeforeCheck。
问题2:如何进行在此对话框中修改选择
递归调用
好了,明白了解决方式我们就可以进行重新修改BeforeCheck事件了,具体代码如下:
private void treeViewX_BeforeCheck(object sender, TreeViewCancelEventArgs e)
{
List<TreeNode> nodes;
Int32 intX;
TreeNode treeNode;
//准备手工修改check,先关闭
treeViewX.BeforeCheck -= new TreeViewCancelEventHandler(treeViewX_BeforeCheck);
nodes = new List<TreeNode>();
for (intX = 0; intX < treeViewX.Nodes.Count; intX = intX + 1)
{ nodes.Add(treeViewX.Nodes[intX]); }
//遍历查找
while (nodes.Count > 0)
{
treeNode = nodes[0];
nodes.Remove(treeNode);
treeNode.Checked = false;
for (intX = 0; intX < treeNode.Nodes.Count; intX = intX + 1)
{ nodes.Add(treeNode.Nodes[intX]); }
}
//e.Node.Checked = true;
treeViewX.BeforeCheck += new TreeViewCancelEventHandler(treeViewX_BeforeCheck);//递归调用
}
在Load事件最后添加:
treeViewX.BeforeCheck += new TreeViewCancelEventHandler(treeViewX_BeforeCheck);
到此为止,我们的构建树已经完成!
----祝好运!
2015/05/07