1. 树形结构的设计
在开发ASP.NET的项目中,有许多项目用到的树形结构,其中有些树形结构相对复杂,也有简单的,但无论是简单的树形结构还是复杂的树形结构,有着好的结构对于性能和稳定性都有着很大的作用。比如你的树结构的数据很大,如果是采用比较死的方式一次性加载进来那么你在加载这棵树的时候就是相当的耗时了,性能就大大的打了折扣。
我在设计这棵树时首先加载的是一级的节点,即想要显示的最外层的节点,然后通过点击展开节点的事件去加载其子节点,即可以根据用户的需要才去加载相关的子节点;可能在节点中我们可能会在节点中设计节点的右键菜单,比如“刷新”节点的菜单等,所以刷新也是重新加载节点的一个过程,下面将分别通过图示和简单的例子去讲解。
下面将以一颗商品的树来简单讲解,比如商品有着电器、文具、食品等类型。
1) 页面代码:
<asp:TreeView ID="treeView" runat="server" ShowLines="true" EnableClientScript="false" Height="100%" OnTreeNodeExpanded="treeNodeExpanded">
<Nodes>
<asp:TreeNode Text="商品" Value="sp01" Expanded="true" ImageUrl="节点图片" NavigateUrl="点击节点的连接地址"></asp:TreeNode> </Nodes>
</asp:TreeView>
2) 后台代码:
l 初始化节点
if (!IsPostBack)
{
InitTreeView();
}
private void InitTreeView()
{
AddCollectNode(treeView.Nodes[0]); //即给“商品”添加子节点
}
l 添加节点
private void AddCollectNode(TreeNode baseNode)
{
//清空父节点下的所有子节点
baseNode.ChildNodes.Clear();
//获取父节点的子节点集合
。。。。。。
//判断子节点集合数是否大于零,即是否存在子节点
。。。。。
//如果存在子节点,循环添加子节点
foreach (遍历子集合)
{
TreeNode node = new TreeNode(子节点);
node.NavigateUrl = 子节点连接 baseNode.ChildNodes.Add(node);//父节点添加子节点
node.Collapse();
}
//如果不存在子节点,则添加一个空节点,给父节点添加空节点是为了能让父节点折起,可以让父节点有展开事件,可以对父节点进行数据刷新。
TreeNode tn = new TreeNode("[ 暂无数据 ]", "0");
tn.NavigateUrl = "javascript:void(0);";
baseNode.ChildNodes.Add(tn);
}
l 节点展开事件
protected void treeNodeExpanded (object sender, TreeNodeEventArgs e)
{
RefreshTreeNode(e.Node); //调用刷新节点方法
}
l 刷新节点
private void RefreshTreeNode(TreeNode baseNode)
{
AddCollectNode(baseNode);
}
//你可能会问为什么还要一个刷新的方法,而不在展开事件时直接调用的是刷新的方法,因为你在做刷新操作时可以判断节点的类型,即在刷新方法中可以有一个逻辑的判断过程,当然了这也是根据你的需要的了。
2. 页面异步刷新树
OK,上面简单分析的是在页面中如果去展现一颗树,且让这棵树有着比较好的性能了,当然树还有很多东西值得去探讨和学习的,比如下面要简单说的是,在框架或不同页面中去刷新这棵树。比如,我使用的下面这样的一个页面框架:
比如当前的操作是我在页面框架的右边的页面做了添加一个商品的操作,那么就得让左边的树能自动去刷新看到添加的商品的节点。
要实现这样的功能,采用的是在页面上添加一个隐藏按钮,在其它页面去触发这个隐藏的按钮,通过隐藏按钮的事件去执行刷新这棵树。流程图如下:
还是来看下代码吧!~
1) 页面代码:
<div style="display: none;">
<asp:Button ID="refreshBt" runat="server" OnClick="refreshBt_onclick" />
</div>
2) 后台代码:
/// <summary>
/// 异步刷新TreeView按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void refreshBt_onclick(object sender, EventArgs e)
{
refresh_TreeNodes(treeView.Nodes[0])
}
//刷新方法
protected void refresh_TreeNodes(TreeNode baseNode)
{
TreeNodeCollection childnNods = baseNode.ChildNodes;
ArrayList expandedNodes = new ArrayList();
SaveExpandedTreeNodes(childnNods, expandedNodes); //记录保存展开的节点
if (baseNode.Expanded == true)
{
baseNode.Expanded = false;
baseNode.Expanded = true;
foreach (object id in expandedNodes) //判断展开节点集合中的节点是否和树节点对应,如果是,则做收缩和展开操作
{
TreeNode node = FindNode(Convert.ToInt32(id), baseNode, "mark");
if (node != null)
{
node.Expanded = false;
node.Expanded = true;
}
}
}
}
/// <summary>
/// 记录保存展开的节点
/// </summary>
/// <param name="childNods"></param>
/// <param name="expandedNodes"></param>
private void SaveExpandedTreeNodes(TreeNodeCollection childNods, ArrayList expandedNodes)
{
while (childNods != null && childNods.Count > 0)
{
for (int ix = 0; ix < childNods.Count; ix++)
{
TreeNode node = childNods[ix];
if (node.Expanded == true)
{
expandedNodes.Add(node.Value);
SaveExpandedTreeNodes(node.ChildNodes, expandedNodes);
}
}
return;
}
}
private TreeNode FindNode(int id, TreeNode baseNode, string mark)
{
TreeNode myNode = null;
foreach (TreeNode tn in baseNode.ChildNodes)
{
int myId = Convert.ToInt32(tn.Value);
if (id != myId)
{
myNode = FindNode(id, tn);
if (myNode != null)
{
break;
}
}
else
{
if (tn.ChildNodes.Count != 0)
{
myNode = tn;
break;
}
else
{
continue;
}
}
}
return myNode;
}
private TreeNode FindNode(int id, TreeNode tn)
{
TreeNode myNode = null;
foreach (TreeNode t in tn.ChildNodes)
{
int myId = Convert.ToInt32(t.Value);
if (id != myId)
{
myNode = FindNode(id, t);
if (myNode != null)
{
break;
}
}
else
{
if (t.ChildNodes.Count != 0)
{
myNode = t;
break;
}
else
{
continue;
}
}
}
return myNode;
}
3. 权限树的设计
权限树结构的设计也可以和上面的树那样了,但在权限树中会更多用到的是节点的选择,即checkbox,当选择一个父节点时还要把其子节点也选择,如果通过JS是可以去实现这个级联操作的,但问题是在treeView中节点的checkbox是不返回服务器端的,即你点击checkbox服务器端是不响应的,所以很难去记录选择的节点。对于记录选择的节点也有多种,一种可以采用JS去实现页面的级联,在后台采用编辑树节点的方法,另一种是没选择一个节点时响应服务器,再去记录节点。还是看看代码吧!
<asp:TreeView ID="tieeView" runat="server" ShowLines="true" EnableClientScript="false"
Height="100%" OnTreeNodeCheckChanged=" NodeCheckChanged">
<Nodes>
<asp:TreeNode Text="商品" Value="unit05" Expanded="true"></asp:TreeNode>
</Nodes>
</asp:TreeView>
1) 后台:
tieeView.ShowCheckBoxes = TreeNodeTypes.All;
tieeView.Attributes.Add("onclick", "postBackByObject()");
2) 用JS去实现级联及服务器响应
//树checkbox选择父节点及子节点,并返回服务器处理
function postBackByObject()
{
var obj = window.event.srcElement;
var treeNodeFound = false;
var checkedState;
if (obj.tagName == "INPUT" && obj.type == "checkbox")
{
//用JS控制选择子节点
var treeNode = obj;
checkedState = treeNode.checked;
do
{
obj = obj.parentElement;
}
while (obj.tagName != "TABLE")
var parentTreeLevel = obj.rows[0].cells.length;
var parentTreeNode = obj.rows[0].cells[0];
var tables = obj.parentElement.getElementsByTagName("TABLE");
var numTables = tables.length
if (numTables >= 1)
{
for (i=0; i < numTables; i++)
{
if (tables[i] == obj)
{
treeNodeFound = true;
i++;
if (i == numTables)
{
return;
}
}
if (treeNodeFound == true)
{
var childTreeLevel = tables[i].rows[0].cells.length;
if (childTreeLevel > parentTreeLevel)
{
var cell = tables[i].rows[0].cells[childTreeLevel - 1];
var inputs = cell.getElementsByTagName("INPUT");
inputs[0].checked = checkedState;
}
else
{
return;
}
}
}
}
}
}
</script>
3) 选择checkbox响应服务器端
function postBackByObject()
{
__doPostBack("","");
}
以上是个人在项目开发中经常用到的一些简单的东东,感兴趣的朋友可以一起探讨学习!~
声明:以上是个人在项目中经常用到的一些东东,在这是想和大家分享,对于我现在来说也是个学习的过程,可能以上的东西对于牛人或从事软件行业多年的人来说算是小菜一碟吧!~但我想上面的思想对于新人来说也是一个好的参考或学习过程吧,简单的难的技术问题都需要来丰富网络资源,给未接触过的人来说也是个支持!~牛人来了,希望你轻轻的离开,大不必留下什么鄙视的话,谢谢!~本人也是在不断的学习,谢绝转载时因为看到许多的商业性的网站转载了还带有不良的东东,谈不上“太自恃过高”!~