第3课:ASP.NET(3)——树状结构数据绑定
本课将讨论树状结构数据绑定操作,如菜单和树状视图的数据绑定。
树状数据结构
为了动态绑定树状结构视图,如菜单控件(Menu)、树状视图(TreeView)控件等,我们首先需要约定数据的管理结构,如图1中显示了一个树状结构数据,代码中,这些数据会定义在/app_data/excel/menu.xlsx文件中。
图1
如图1中所示,我们约定的树状结构数据有5个字段,分别是:
- depth,定义数据节点的深度,确定节点是第几级。
- value,节点的数据。其中,第一级数据直接使用数字,其它级别的数据使用圆点(.)进行分级。请注意,除了使用数字,这里还可以设置一些有意义的内容,如USER、USER.PWD、USER.LOGIN等。
- text,节点显示的文本。
- data1,扩展数据一,可以根据需要使用。
- data2,扩展数据二,可以根据需要使用。
下面的代码(/app_code/data/base/tNode.cs),我们使用tNode类处理节点数据。
C# |
using System;
using System.Collections.Generic;
using System.Data;
public class tNode
{
// 构造函数
public tNode()
{
Parent = null;
Children = null;
}
// 基本成员
public int Depth { get; set; }
public string Value { get; set; }
public string Text { get; set; }
public tNode Parent { get; set; }
public List<tNode> Children { get; set; }
// 扩展数据
public string Data1 { get; set; }
public string Data2 { get; set; }
}
|
tNode类中定义了节点所需的一些信息属性,包括:
- Depth,指定节点是第几级。
- Value,指定节点的数据值。
- Text,节点的文本。
- Parent,节点的父节点对象,根节点的此属性应为null。
- Children,节点的子节点,定义为List<tNode>类型。
- Data1,扩展数据一。
- Data2,扩展数据二。
下面的代码(/app_code/data/base/tNode.cs),使用tTree类处理树状数据结构。
C# |
// 树状结构
public class tTree
{
public tNode Root { get; set; }
//
public string DepthField { get; set; }
public string ValueField { get; set; }
public string TextField { get; set; }
public string Data1Field { get; set; }
public string Data2Field { get; set; }
//
public tTree()
{
Root = null;
DepthField = "depth";
ValueField = "value";
TextField = "text";
Data1Field = "data1";
Data2Field = "data2";
}
// 从DataTable对象载入数据
public void ReadDataTable(DataTable tbl)
{
if (tbl == null || tbl.Columns.Contains(DepthField) == false ||
tbl.Columns.Contains(ValueField) == false ||
tbl.Columns.Contains(TextField) == false ||
tbl.Columns.Contains(Data1Field) == false ||
tbl.Columns.Contains(Data2Field) == false)
{
Root = null;
}
else
{
Root = new tNode() { Depth = 0 };
AddChildren(tbl, Root);
}
}
// 添加所有子节点
protected void AddChildren(DataTable tbl,tNode parentNode)
{
string cond;
if (parentNode.Depth == 0)
cond = DepthField + "=1";
else
cond = string.Format("{0}={1} and {2} like '{3}.%'",
DepthField, parentNode.Depth + 1,
ValueField, parentNode.Value);
//
DataRow[] rows = tbl.Select(cond);
if (rows.Length == 0) return;
parentNode.Children = new List<tNode>();
for(int row = 0; row < rows.Length; row++)
{
tNode node = new tNode()
{
Value = tStr.GetValue(rows[row][ValueField]),
Text = tStr.GetValue(rows[row][TextField]),
Depth = tInt.GetValue(rows[row][DepthField]),
Data1 = tStr.GetValue(rows[row][Data1Field]),
Data2 = tStr.GetValue(rows[row][Data2Field])
};
node.Parent = parentNode;
AddChildren(tbl, node);
parentNode.Children.Add(node);
}
}
}
|
tTree类中,首先定义了一些基本的属性和构造函数,并设置了默认值,如:
- Root属性,定义为树状结构数据的根节点,默认值为null。
- DepthField属性,指定深度(级别)数据的字段名,默认值为depth。
- ValueField属性,指定节点数据值的字段名,默认值为value。
- TextField属性,指定节点文本内容的字段名,默认值为text。
- Data1Field属性,指定节点扩展数据一的字段名,默认值为data1。
- Data2Field属性,指定节点扩展数据二的字段名,默认值为data2。
代码中,ReadDataTable()方法用于从DataTable对象中读取节点数据,并生成树状结构对象,这里DataTable对象中的列名应包含DepthField、ValueField、TextField、Data1Field和Data2Field属性指定的字段名。
AddChildren()方法的功能是通过递归调用,从DataTable对象数据中添加节点的所有子节点。这里,所有节点的根节点是tTree的Root属性,其Depth定义为0,Root的第一级子节点Depth定义为1,以此类推。
在DataTable对象中查询节点的子节点数据行时,使用了DataTable对象的Select()方法,参数指定查询条件,查询结果为DataRow数组。当前节点为根节点(Root属性)时,查询DepthField数据为1的节点数据行,实际查询条件类似“depth=1”。如果不是根节点时,查找DepthField字段等于当前节点下一级的节点,并且ValueField字段数据以当前节点Value属性内容加圆点作为前缀,实际查询条件的格式类似“depth=2 and value like '1.'”。
下面,通过菜单控件和树状视图控件的实际演示tTree类的应用。
菜单数据绑定
菜单数据绑定示例页面为/demo/menu/MenuBindDemo.aspx,下面是HTML部分的定义。
HTML |
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="MenuBindDemo.aspx.cs"
Inherits="demo_menu_MenuBindDemo" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:Menu ID="menu1" runat="server"
OnMenuItemClick="menu1_MenuItemClick"
Orientation="Horizontal"></asp:Menu>
<p>
<asp:TextBox ID="txt1" runat="server"></asp:TextBox>
</p>
</form>
</body>
</html>
|
页面中定义了一个Menu控件,稍后会编码绑定其菜单项,TextBox控件txt1用于显示点击的菜单项信息。页面的C#代码部分如下(/demo/menu/MenuBindDemo.aspx.cs)。
C# |
using System;
using System.Data;
using System.Web.UI.WebControls;
public partial class demo_menu_MenuBindDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack == false)
{
BindMenu();
}
}
protected void BindMenu()
{
DataTable data = tExcel.ReadExcel(
tWeb.MapPath("/app_data/excel/menu.xlsx"));
tTree tree = new tTree();
tree.ReadDataTable(data);
if (tree.Root == null) return;
AddNodes(null,tree.Root);
}
protected void AddNodes(MenuItem menuItem, tNode node)
{
for (int i = 0; i < node.Children.Count; i++)
{
MenuItem item = new MenuItem(
node.Children[i].Text, node.Children[i].Value);
//
if (node.Children[i].Children != null)
{
AddNodes(item, node.Children[i]);
}
//
if (menuItem == null) menu1.Items.Add(item);
else menuItem.ChildItems.Add(item);
}
}
protected void menu1_MenuItemClick(object sender, MenuEventArgs e)
{
txt1.Text = e.Item.Text;
}
}
|
BindMenu()方法中,首先从/app_data/excel/menu.xlsx文件中读取节点数据,然后通过tTree.ReadDataTable()方法将DataTable对象中的数据读取到tTree对象中,成功读取节点数据后,AddNodes()方法会通过递归调用,并根据节点数据按层次创建菜单项。图2中显示了鼠标悬停于菜单项“菜单三”>>“菜单三C”时的效果。
图2
点击其中的菜单项时,在文本框txt1中会显示菜单的文本内容。
为简化操作,我们在tWebCtr类中封装了Web控件操作的相关代码,菜单数据绑定使用BindMenu()方法,代码如下(/app_code/aspnet/form/tWebCtr.cs)。
C# |
using System;
using System.Collections.Generic;
using System.Data;
using System.Web.UI.WebControls;
public static class tWebCtr
{
// 将tTree对象结构绑定到Menu控件
public static void BindMenu(Menu menu, tTree tree)
{
if (tree.Root == null) return;
AddMenuItems(menu, null, tree.Root);
}
//
private static void AddMenuItems(Menu menu, MenuItem menuItem,
tNode node)
{
for (int i = 0; i < node.Children.Count; i++)
{
MenuItem item = new MenuItem(
node.Children[i].Text, node.Children[i].Value,
node.Children[i].Data1,node.Children[i].Data2);
//
if (node.Children[i].Children != null)
{
AddMenuItems(menu, item, node.Children[i]);
}
//
if (menuItem == null) menu.Items.Add(item);
else menuItem.ChildItems.Add(item);
}
}
}
|
请注意,代码中MenuItem的Value、Text、ImageUrl和NavigateUrl属性分别由tNode对象的Value、Text、Data1和Data2属性赋值。页面中绑定菜单数据时可以参数如下代码。
C# |
protected void BindMenu()
{
DataTable data = tExcel.ReadExcel(
tWeb.MapPath("/app_data/excel/menu.xlsx"));
tTree tree = new tTree();
tree.ReadDataTable(data);
tWebCtr.BindMenu(menu1, tree);
}
|
从数据源构建了tTree对象后,可以直接调用tWebCtr.BindMenu()方法将tTree对象结构绑定到Menu控件中。
树状视图数据绑定
树状视图(TreeView)的数据结构与菜单(Menu)控件相似,我们可以使用tTree、tNode对象完成相同的工作,并且使用相同结构的数据。
下面使用/demo/treenode/TreeNodeBindDemo.aspx页面演示树状视图数据绑定操作,HTML代码部分如下。
HTML |
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="TreeNodeBindDemo.aspx.cs"
Inherits="demo_treenode_TreeNodeBindDemo" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:TreeView ID="tv1" runat="server"
OnSelectedNodeChanged="tv1_SelectedNodeChanged">
</asp:TreeView>
<p>
<asp:TextBox ID="txt1" runat="server"></asp:TextBox>
</p>
</form>
</body>
</html>
|
代码中定义了一个名为tv1的TreeView控件,文本框txt1则用于显示点击节点的信息。
页面的C#代码部分如下(/demo/treenode/TreeNodeBindDemo.aspx.cs)。
C# |
using System;
using System.Data;
using System.Web.UI.WebControls;
public partial class demo_treenode_TreeNodeBindDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack == false)
{
BindTreeView();
}
}
protected void BindTreeView()
{
DataTable data = tExcel.ReadExcel(
tWeb.MapPath("/app_data/excel/menu.xlsx"));
tTree tree = new tTree();
tree.ReadDataTable(data);
if (tree.Root == null) return;
AddNodes(null, tree.Root);
}
protected void AddNodes(TreeNode treeNode, tNode node)
{
for (int i = 0; i < node.Children.Count; i++)
{
TreeNode tn = new TreeNode(
node.Children[i].Text, node.Children[i].Value);
//
if (node.Children[i].Children != null)
AddNodes(tn, node.Children[i]);
//
if (treeNode == null) tv1.Nodes.Add(tn);
else treeNode.ChildNodes.Add(tn);
}
}
protected void tv1_SelectedNodeChanged(object sender, EventArgs e)
{
txt1.Text = tv1.SelectedNode.Text;
}
}
|
页面显示的默认效果如图3。点击其中一个节点时,会在文本框txt1中显示节点的文本内容。
图3
在tWebCtr类中封装了BindTreeView()方法,其功能是将tTree对象的结构绑定到TreeView控件中,实现代码如下(/app_code/aspnet/form/tWebCtr.cs)。
C# |
// 将tTree对象结构绑定到TreeView控件
public static void BindTreeView(TreeView treeView,tTree tree)
{
if (tree.Root == null) return;
AddTreeNodes(treeView, null, tree.Root);
}
//
private static void AddTreeNodes(TreeView treeView,
TreeNode treeNode, tNode node)
{
for (int i = 0; i < node.Children.Count; i++)
{
TreeNode tn = new TreeNode(
node.Children[i].Text, node.Children[i].Value,
node.Children[i].Data1,node.Children[i].Data2,
"_blank");
//
if (node.Children[i].Children != null)
{
AddTreeNodes(treeView,tn, node.Children[i]);
}
//
if (treeNode == null) treeView.Nodes.Add(tn);
else treeNode.ChildNodes.Add(tn);
}
}
|
代码中,TreeNode的Value、Text、ImageUrl和NavigateUrl属性分别由tNode对象的Value、Text、Data1和Data2属性赋值。页面中可以参考如下代码绑定TreeView控件数据。
C# |
protected void BindTreeView()
{
DataTable data = tExcel.ReadExcel(
tWeb.MapPath("/app_data/excel/menu.xlsx"));
tTree tree = new tTree();
tree.ReadDataTable(data);
tWebCtr.BindTreeView(tv1, tree);
}
|