说明: 这是我刚刚解决的一个工作中的问题. 一种超复杂的动态创建导航树的创建方法.为了更好的说明后面的示例, 这里就从最普通的导航树的动态创建开始吧:
一. 最普通的导航树创建: 一般情况下, 动态创建导航树(我们平时称为资源树)是利用递归完成的, 因为要操作的全部信息只存储在一张表中, 这种情况下, 我们常常会在表中增加一个标识父结点的字段 (暂且称为 parentID吧),同时表中的主键也是必须的 (此处记为 UID), 然后在递归中用当前记录 ( 记为 R1) 中的 parentID 去匹配表中其他记录的 UID, 一旦 parentID==UID 成立 ( 将匹配到的记录记为 R2) , 则将当前记录 ( R1) 挂接在匹配到的结点 ( R2)上. 重复上面的工作直到全部挂接完成.
注: 如果某 parentId为空或-1 ( 具体为空还是-1或其他值人为定义 ) 表示此结点没有父结点, 即为根或第一层结点 ( 一般情况下, 根在前台定义, 这时 parentID为空的结点挂在第一层).
(1)下面附普通导航树的初始化代码:
普通树初始化部分(包括调用递归函数部分)
public void InheritTree(TreeView treeview)
{
tvw.Nodes.Clear(); //清空树
TreeNode node=new TreeNode(); //声明树结点
node.Text = "资源树"
node.Tag = ""
node.Name = ""
tvw.Nodes.Add(node)
string strSql="";
DataTable dt=new DataTable();
strSql = "Select UID, Name, DisPlayLabel decode(parent_id,null,'',parent_id) parent_id
From table*** where parent_id='' or parent_id Is null order by UID"
dt = DB.QueryTable(strSql)
//递归添加子结点.
RecursiveNomalAddChileNode(node, dt)
node.Expand() '展开
}
普通资源树递归添加子结点的代码
普通树递归的实现部分
public void RecursiveNomalAddChileNode(TreeNode node, DataTable dt)
{
if dt =null
return;
for (int i=0; i< dt.Rows.Count-1; i++)
{
if (dt.Rows(i).item("parent_id").ToString.Equals(node.Tag) ) //判断--找到了它的父结点: 开始挂接
{
TreeNode t_node=new TreeNode();
t_node.Text=dt.Rows(i).Item("DisPlayLabel").ToString; //显示文本
t_node.Tag=dt.Rows(i).Item("UID").ToString; //主键, 唯一标识一个结点
t_node.Name=dt.Rows(i).Item("Name).ToString; //结点名
node.Nodes.Add(t_node);
//递归
RecursiveNomalAddChileNode(t_node, dt)
}
}
}
二. 多表关联创建导航树: 现在有这样一种情况: 每一层的结点可能都在不同的表中, 每个表中对标识父结点 parentID 定义也不一样 ( 这是我们平时工作中很容易碰到的情况), 这个时候用递归显然是行不通的, 原因一是你无法递归不同的表, 二是你即使有办法递归不同的表, 也不可能修改标识父结点的 parentID 定义, 因为数据库很可能不是你设计的. 这个时候无法控制匹配的成立 (即无法完成对 parentID==UID 的验证) .
为了解决这个问题, 这里我结合这几天工作中遇到的实际问题加以说明. 有三张表, 地市表 (称为管理域--district ): 显示字段为 南京, 苏州等, 主键为 UID, 因为它是导航树的是第一层结点, 因此无父结点标识字段;第二张表, 用户组表 (customerGroup ): 显示字段为 政府组, 银行组等, 主键为 UID, 因它需要挂接在管理域 (地市表) 下, 因此父结点标识字段为 DistrictID;第三张表, 用户表 (customer), 显示字段为 中国农业银行南京分行, 中国银行苏州分行等信息, 主键为 UID, 因为它需要挂接在用户组结点下, 因此定义父结点标识字段为 CustomerGroupID;
下面建立三张表的关联关系: customerGroup .DistrictID=disrict.UID
customer.CustomerGroupID= customerGroup.UID
然后利用关联关系将三张表虚构为二张表 (说明这二张虚表的构造是必须的, 因为只有构成关系表, 树操作才能取得相应的子结点, 这是TreeNode 类的相关操作决定的, 具体可参考 TreeNode 类)
district_customerGroup关系表的实现: ds.Relations.Add("district_custgroup",
ds.Tables["district"].Columns["uuid"],
ds.Tables["customergroup"].Columns["districtid"], false);
customerGroup_customer 关系表的实现: ds.Relations.Add("group_cust",
ds.Tables["CUSTOMERGROUP"].Columns["uuid"],
ds.Tables["CUSTOMER"].Columns["customergroupid"], false);
然后使用三层forcach 循环. 具体代码如下:
动态创建多表关联导航树的实现
private void InitialRestree()
{
treeView1.Nodes.Clear();
if (ds == null)
return;
if (ds.Tables["district"].Rows.Count == 0)
return;
//添加根结点
TreeNode rootNode = treeView1.Nodes.Add(ViewNode.GetTypeName(ViewNodeTypes.All), "管理域");
ViewNode vroot = new ViewNode(ViewNodeTypes.All, -1);
rootNode.Tag = vroot;
//添加管理域结点
foreach (DataRow districRow in ds.Tables["district"].Rows)
{
if (districRow["parentid"].ToString() == "0" || districRow["parentid"].ToString() == "")
{
ViewNode vDistrict = new ViewNode(ViewNodeTypes.District,
long.Parse(districRow["uuid"].ToString()));
TreeNode districtNode = rootNode.Nodes.Add(districRow["uuid"].ToString(),
districRow["DisplayLabel"] as string);
districtNode.Tag = vDistrict;
//添加用户组结点
DataRow[] custgroupRows = districRow.GetChildRows("district_custgroup");
foreach (DataRow custgroupRow in custgroupRows)
{
ViewNode vCustGroup = new ViewNode(ViewNodeTypes.CustomerGroup,
long.Parse(custgroupRow["uuid"].ToString()));
TreeNode custgroupNode = districtNode.Nodes.Add(custgroupRow["uuid"].ToString(),
custgroupRow["groupName"] as string);
custgroupNode.Tag = vCustGroup;
//添加用户结点
DataRow[] custRows = custgroupRow.GetChildRows("group_cust");
foreach (DataRow custRow in custRows)
{
ViewNode vCust = new ViewNode(ViewNodeTypes.Customer,
long.Parse(custRow["uuid"].ToString()));
TreeNode custNode = custgroupNode.Nodes.Add(custRow["uuid"].ToString(),
custRow["customername"] as string);
custNode.Tag = vCust;
//添加电路结点
DataRow[] lcsRows = custRow.GetChildRows("cust_lcs");
foreach (DataRow lcsRow in lcsRows)
{
ViewNode lcsViewNode = new ViewNode(ViewNodeTypes.LCS,
long.Parse(lcsRow["uuid"].ToString()));
TreeNode lcsTreeNode = custNode.Nodes.Add(lcsRow["uuid"].ToString(),
lcsRow["lcsname"] as string);
lcsTreeNode.Tag = lcsViewNode;
}
}
}
}
}
//展开至客户
rootNode.Expand();
}
注: 自己觉得这种方法不是最佳的, 针对这种实现情况, 如果您有更好的解决方案, 还望多多交流.
三. 更复杂的导航树创建: (这是我这几天碰到的事, 需求出了问题, 做了好几次无用功, 逼着我解决了一些难以解决的技术问题--也算一种收获吧) 情况是这样的: 由于业务逐渐庞大, 需要对上面第二种情况做出改进: 具体要求是对管理域 (district表)进行细化, 如南京下面需要挂自己的子域, 如江宁区, 浦口区等, 再将镇细化为镇或村. 但用户组和用户不再细化, 体质不变, 这样情况就就变成了: 南京市下面有用户组 (如市政府组)和子域 ( 如江宁 ), 而子域下面又有自己的用户组(如江宁区政府组)和子域 (如泰山新村)......
这种情况之所以复杂, 是因为它是上面讲的第一种情况和第二种情况的一种结合, 因为很容易发现对于地市表 (district--管理域) 需要使用递归进行挂接所有的子域, 而对于每一个管理域下的用户组和用户又得使用上面讲的第二种方法 ( foreach 嵌套) 完成挂接.
难点是这里面每一层管理域下不但要挂接子域结点同时还要挂接用户组和用户结点. 因为我的思路是用 递归和 foreach 嵌套结合.
这样分析后解决问题的方法也就不难实现了, 具体实现代码如下 :
调用递归部分:
调用递归部分
private void InitialRestree()
{
treeView1.Nodes.Clear();
if (ds == null)
return;
if (ds.Tables["district"].Rows.Count == 0)
return;
//添加根结点
TreeNode rootNode = treeView1.Nodes.Add(ViewNode.GetTypeName(ViewNodeTypes.All), "管理域");
ViewNode vroot = new ViewNode(ViewNodeTypes.All, -1);
rootNode.Tag = vroot;
//添加管理域结点
foreach (DataRow districRow in ds.Tables["district"].Rows)
{
if (districRow["parentid"].ToString() == "0" || districRow["parentid"].ToString() == "")
{
ViewNode vDistrict = new ViewNode(ViewNodeTypes.District,
long.Parse(districRow["uuid"].ToString()));
TreeNode districtNode = rootNode.Nodes.Add(districRow["uuid"].ToString(),
districRow["DisplayLabel"] as string);
districtNode.Tag = vDistrict;
//添加用户组结点
DataRow[] custgroupRows = districRow.GetChildRows("district_custgroup");
foreach (DataRow custgroupRow in custgroupRows)
{
ViewNode vCustGroup = new ViewNode(ViewNodeTypes.CustomerGroup,
long.Parse(custgroupRow["uuid"].ToString()));
TreeNode custgroupNode = districtNode.Nodes.Add(custgroupRow["uuid"].ToString(),
custgroupRow["groupName"] as string);
custgroupNode.Tag = vCustGroup;
//添加用户结点
DataRow[] custRows = custgroupRow.GetChildRows("group_cust");
foreach (DataRow custRow in custRows)
{
ViewNode vCust = new ViewNode(ViewNodeTypes.Customer,
long.Parse(custRow["uuid"].ToString()));
TreeNode custNode = custgroupNode.Nodes.Add(custRow["uuid"].ToString(),
custRow["customername"] as string);
custNode.Tag = vCust;
//添加电路结点
DataRow[] lcsRows = custRow.GetChildRows("cust_lcs");
foreach (DataRow lcsRow in lcsRows)
{
ViewNode lcsViewNode = new ViewNode(ViewNodeTypes.LCS,
long.Parse(lcsRow["uuid"].ToString()));
TreeNode lcsTreeNode = custNode.Nodes.Add(lcsRow["uuid"].ToString(),
lcsRow["lcsname"] as string);
lcsTreeNode.Tag = lcsViewNode;
}
}
}
RecursiveAddSubGroupOrCustomer(districRow["uuid"].ToString(), districtNode);
}
}
//展开至客户
rootNode.Expand();
}
递归实现部分:
更复杂的导航树的动态创建实现
private void RecursiveAddSubGroupOrCustomer(string uuid, TreeNode Node)
{
if (ds == null)
return;
if (ds.Tables["district"].Rows.Count == 0)
return;
//管理域
foreach (DataRow districRow in ds.Tables["district"].Rows)
{
if (districRow["parentid"].ToString() == uuid && districRow["uuid"].ToString() != uuid)
{
ViewNode vDistrict = new ViewNode(ViewNodeTypes.District,
long.Parse(districRow["uuid"].ToString()));
TreeNode districtNode = Node.Nodes.Add(districRow["uuid"].ToString(),
districRow["DisplayLabel"] as string);
districtNode.Tag = vDistrict;
//添加用户组结点
DataRow[] custgroupRows = districRow.GetChildRows("district_custgroup");
foreach (DataRow custgroupRow in custgroupRows)
{
ViewNode vCustGroup = new ViewNode(ViewNodeTypes.CustomerGroup,
long.Parse(custgroupRow["uuid"].ToString()));
TreeNode custgroupNode = districtNode.Nodes.Add(custgroupRow["uuid"].ToString(),
custgroupRow["groupName"] as string);
custgroupNode.Tag = vCustGroup;
//添加用户结点
DataRow[] custRows = custgroupRow.GetChildRows("group_cust");
foreach (DataRow custRow in custRows)
{
ViewNode vCust = new ViewNode(ViewNodeTypes.Customer,
long.Parse(custRow["uuid"].ToString()));
TreeNode custNode = custgroupNode.Nodes.Add(custRow["uuid"].ToString(),
custRow["customername"] as string);
custNode.Tag = vCust;
//添加电路结点
DataRow[] lcsRows = custRow.GetChildRows("cust_lcs");
foreach (DataRow lcsRow in lcsRows)
{
ViewNode lcsViewNode = new ViewNode(ViewNodeTypes.LCS,
long.Parse(lcsRow["uuid"].ToString()));
TreeNode lcsTreeNode = custNode.Nodes.Add(lcsRow["uuid"].ToString(),
lcsRow["lcsname"] as string);
lcsTreeNode.Tag = lcsViewNode;
}
}
}
RecursiveAddSubGroupOrCustomer(districRow["uuid"].ToString(), districtNode);
}
}
}
注: 同样, 如果您有更好的解决方案, 希望能共享.
附第三种情况下动态创建资源树的完整代码
完整代码
//为构造资源树加载数据--准备阶段
private void LoadDataForRestree()
{
DBInterface di = new DBInterface();
ds = new DataSet();
using (DbConnection conn = di.CreateConnection())
{
ISecurityService ss = ModuleEntry.GetServiceInstance<ISecurityService>();
DbCommand command = di.DbProviderFactory.CreateCommand();
command.Connection = conn;
DbDataAdapter dataAdapter = di.DbProviderFactory.CreateDataAdapter();
dataAdapter.SelectCommand = command;
string sql = "";
//管理域: 权限在管理域中便已定义. 后面的用户组和用户没有必要再加入权限限制.
if (ss.IsSuperUser)
{
sql = "SELECT * FROM res_district d";
}
else
{
sql = string.Format("SELECT * FROM res_district d where uuid in ({0})",
ModuleEntry.GetServiceInstance<ISecurityService>().ManagableDistrictSql());
}
command.CommandText = sql;
dataAdapter.Fill(ds, "district");
//用户组
sql = "SELECT * FROM res_customergroup g";
command.CommandText = sql;
dataAdapter.Fill(ds, "customergroup");
//管理域与用户组关系表
ds.Relations.Add("district_custgroup",
ds.Tables["district"].Columns["uuid"],
ds.Tables["customergroup"].Columns["districtid"], false);
//用户
sql = "SELECT c.uuid, c.customername, c.shortname, c.customergroupid FROM res_customer c";
command.CommandText = sql;
dataAdapter.Fill(ds, "customer");
//用户组与用户关系表
ds.Relations.Add("group_cust",
ds.Tables["CUSTOMERGROUP"].Columns["uuid"],
ds.Tables["CUSTOMER"].Columns["customergroupid"], false);
//电路
sql = "SELECT l.uuid ,l.lcsname, l.customerid FROM res_lcs l ";
command.CommandText = sql;
dataAdapter.Fill(ds, "LCS");
//用户组与电路关系表
ds.Relations.Add("cust_lcs",
ds.Tables["CUSTOMER"].Columns["uuid"],
ds.Tables["LCS"].Columns["customerid"], false);
}
}
//初始化树(包括递归的调用)
private void InitialRestree()
{
treeView1.Nodes.Clear();
if (ds == null)
return;
if (ds.Tables["district"].Rows.Count == 0)
return;
//添加根结点
TreeNode rootNode = treeView1.Nodes.Add(ViewNode.GetTypeName(ViewNodeTypes.All), "管理域");
ViewNode vroot = new ViewNode(ViewNodeTypes.All, -1);
rootNode.Tag = vroot;
//添加管理域结点
foreach (DataRow districRow in ds.Tables["district"].Rows)
{
if (districRow["parentid"].ToString() == "0" || districRow["parentid"].ToString() == "")
{
ViewNode vDistrict = new ViewNode(ViewNodeTypes.District,
long.Parse(districRow["uuid"].ToString()));
TreeNode districtNode = rootNode.Nodes.Add(districRow["uuid"].ToString(),
districRow["DisplayLabel"] as string);
districtNode.Tag = vDistrict;
//添加用户组结点
DataRow[] custgroupRows = districRow.GetChildRows("district_custgroup");
foreach (DataRow custgroupRow in custgroupRows)
{
ViewNode vCustGroup = new ViewNode(ViewNodeTypes.CustomerGroup,
long.Parse(custgroupRow["uuid"].ToString()));
TreeNode custgroupNode = districtNode.Nodes.Add(custgroupRow["uuid"].ToString(),
custgroupRow["groupName"] as string);
custgroupNode.Tag = vCustGroup;
//添加用户结点
DataRow[] custRows = custgroupRow.GetChildRows("group_cust");
foreach (DataRow custRow in custRows)
{
ViewNode vCust = new ViewNode(ViewNodeTypes.Customer,
long.Parse(custRow["uuid"].ToString()));
TreeNode custNode = custgroupNode.Nodes.Add(custRow["uuid"].ToString(),
custRow["customername"] as string);
custNode.Tag = vCust;
//添加电路结点
DataRow[] lcsRows = custRow.GetChildRows("cust_lcs");
foreach (DataRow lcsRow in lcsRows)
{
ViewNode lcsViewNode = new ViewNode(ViewNodeTypes.LCS,
long.Parse(lcsRow["uuid"].ToString()));
TreeNode lcsTreeNode = custNode.Nodes.Add(lcsRow["uuid"].ToString(),
lcsRow["lcsname"] as string);
lcsTreeNode.Tag = lcsViewNode;
}
}
}
//递归调用
RecursiveAddSubGroupOrCustomer(districRow["uuid"].ToString(), districtNode);
}
}
//展开至客户
rootNode.Expand();
}
//递归子结点
private void RecursiveAddSubGroupOrCustomer(string uuid, TreeNode Node)
{
if (ds == null)
return;
if (ds.Tables["district"].Rows.Count == 0)
return;
//管理域
foreach (DataRow districRow in ds.Tables["district"].Rows)
{
if (districRow["parentid"].ToString() == uuid && districRow["uuid"].ToString() != uuid)
{
ViewNode vDistrict = new ViewNode(ViewNodeTypes.District,
long.Parse(districRow["uuid"].ToString()));
TreeNode districtNode = Node.Nodes.Add(districRow["uuid"].ToString(),
districRow["DisplayLabel"] as string);
districtNode.Tag = vDistrict;
//添加用户组结点
DataRow[] custgroupRows = districRow.GetChildRows("district_custgroup");
foreach (DataRow custgroupRow in custgroupRows)
{
ViewNode vCustGroup = new ViewNode(ViewNodeTypes.CustomerGroup,
long.Parse(custgroupRow["uuid"].ToString()));
TreeNode custgroupNode = districtNode.Nodes.Add(custgroupRow["uuid"].ToString(),
custgroupRow["groupName"] as string);
custgroupNode.Tag = vCustGroup;
//添加用户结点
DataRow[] custRows = custgroupRow.GetChildRows("group_cust");
foreach (DataRow custRow in custRows)
{
ViewNode vCust = new ViewNode(ViewNodeTypes.Customer,
long.Parse(custRow["uuid"].ToString()));
TreeNode custNode = custgroupNode.Nodes.Add(custRow["uuid"].ToString(),
custRow["customername"] as string);
custNode.Tag = vCust;
//添加电路结点
DataRow[] lcsRows = custRow.GetChildRows("cust_lcs");
foreach (DataRow lcsRow in lcsRows)
{
ViewNode lcsViewNode = new ViewNode(ViewNodeTypes.LCS,
long.Parse(lcsRow["uuid"].ToString()));
TreeNode lcsTreeNode = custNode.Nodes.Add(lcsRow["uuid"].ToString(),
lcsRow["lcsname"] as string);
lcsTreeNode.Tag = lcsViewNode;
}
}
}
RecursiveAddSubGroupOrCustomer(districRow["uuid"].ToString(), districtNode);
}
}
}