16.8 TreeView控件
导航控件TreeView是一种用来表示树状架构的控件,特别适合用来表示复杂的层级分类。它用于以树形结构显示分层数据,如菜单、目录或文件目录等。与Menu控件相比,它可以组织更复杂的数据结构。本节将针对TreeView控件进行讨论,重点讲解它的各种编程技巧。
16.8.1 TreeView结构
TreeView控件是一种用来表示树状架构的控件,树中的每个项都被称为一个节点。因此,一个完整的树由一个或多个节点构成。这些节点的类型如表16-11所示。一个节点可以同时是父节点和子节点,但是不能同时为根节点、父节点和叶节点。
图 16-25 TreeNode UI元素
Tree View控件的每个节点就是一个TreeNode对象,如图16-25所示。TreeNode对象由以下四个用户界面((U)元素组成,
可以自定义或隐藏这些元素。
1)展开节点指示图标:一个可选图像,指示是否可以展开节点以显示子节点。默认情况下,如果节点可以展开,此图像将为加号(“+”),如果此节点可以折叠,则图像为减号(“-”)。
2)可选的节点图像:可以指定要显示在节点文本旁边的节点图像。
3)节点文本:节点文本是在TreeNode对象上显示的实际文本。节点文本的作用类似于导航模式中的超链接或选择模式中的按钮。
4)与节点关联的可选复选框:复选框是可选的,以允许用户选择特定节点。
在TreeNode中,主要在两个属性中存储数据:Text属性和Value属性。Text属性指定在节点显示的文字,Value属性是获取节点的值。它的常用属性如表16-12所示。
TreeView控件的Nodes包含所有节点(即TreeNode)的集合,可以用设计器为TreeView控件添加节点,也可以使用编程的方式动态添加节点。下面的示例展示了一个简单的TreeView,如下面的代码所示:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server">
<Nodes>
<asp:TreeNode Text="顶级节点"Value="0">
<asp:TreeNode Text="节点1"Value="01">
<asp:TreeNode Text="节点1.0"Value="010">
<asp:TreeNode Text="节点1.0.0"Value="0100"/>
<asp:TreeNode Text="节点1.0.1"Value="0101"/>
<asp:TreeNode Text="节点1.0.2"Value="0102"/>
</asp:TreeNode>
<asp:TreeNode Text="节点1.1"
Expanded="false"Value="011">
<asp:TreeNode Text="节点1.1.0"Value="0110"/>
<asp:TreeNode Text="节点1.1.1"Value="0111"/>
<asp:TreeNode Text="节点1.1.2"Value="0112"/>
<asp:TreeNode Text="节点1.1.3"Value="0113"/>
</asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
</Nodes>
</asp:TreeView>
在上面的代码中,将节点“011”的Expanded属性设置为false,即关闭展开。运行结果如图16-26所示。
16.8.2 使用SiteMapDataSource绑定TreeView
对于使用SiteMapDataSource绑定TreeView控件,已经在16.3节做过讲解,这种方法主要用于绑定网站地图文件。绑定方法如图16-27所示。
图 16-26 TreeView示例
图 16-27 使用SiteMapDataSource绑定TreeView
这样,就可以在页面里看见如下代码了:
<asp:SiteMapDataSource ID="SiteMapDataSource1"runat="server"/>
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
DataSourceID="SiteMapDataSource1">
</asp:TreeView>
16.8.3 使用程序动态建立TreeView节点
TreeView控件经常用来表现复杂的层级式数据结构,因而不同于静态菜单。通常还需要通过程序动态将数据传输给TreeView以建立其节点,并且通过取得节点的关联值执行某些特定的动作。建立节点的语法必须根据节点标签进行引用,假设在网页上建立了一个TreeView控件,并且将其命名为TreeView1,如下面的代码所示:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server">
</asp:TreeView>
有了这个TreeView1之后,可以通过Add方法为TreeView1创建一个根节点,如下面的代码所示:
TreeNode netd=new TreeNode(".NET开发");
TreeView1.Nodes.Add(netd);
当然,还可以利用另外一个版本的方法,将其加入到指定的位置中,而所要加入的位置是由以0为起始值的索引值指定的。如下面程序代码将一个名称为".NET开发"的节点如入到树节点里面的第1个节点的位置,即起始位置。
TreeNode netd=new TreeNode(".NET开发");
TreeView1.Nodes.AddAt(0,netd);
如果想要进一步将指定的节点加到某个节点成为其下的子节点,可以通过ChildNodes.Add方法来添加。如下面的代码所示:
TreeNode netd=new TreeNode(".NET开发");
TreeNode language=new TreeNode(".NET编程语言");
TreeView1.Nodes.Add(netd);
netd.ChildNodes.Add(language);
下面的示例展示了一个完整的TreeView动态创建例子:
public partial class WebForm1:System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
TreeNode netd=new TreeNode(".NET开发");
TreeNode language=new TreeNode(".NET编程语言");
TreeNode cs=new TreeNode("C#");
TreeNode c=new TreeNode("C/C++");
TreeNode vb=new TreeNode("VB");
TreeNode asp=new TreeNode("ASP.NET");
TreeNode net=new TreeNode(".NET版本");
TreeNode net11=new TreeNode(".NET1.1");
TreeNode net20=new TreeNode(".NET2.0");
TreeNode net30=new TreeNode(".NET3.0");
TreeNode net35=new TreeNode(".NET3.5");
TreeNode net40=new TreeNode(".NET4.0");
if(!IsPostBack)
{
TreeView1.Nodes.Add(netd);
//.NET开发
netd.ChildNodes.Add(language);
netd.ChildNodes.Add(net);
//.NET编程语言
language.ChildNodes.Add(cs);
language.ChildNodes.Add(c);
language.ChildNodes.Add(vb);
language.ChildNodes.Add(asp);
//.NET版本
net.ChildNodes.Add(net11);
net.ChildNodes.Add(net20);
net.ChildNodes.Add(net30);
net.ChildNodes.Add(net35);
net.ChildNodes.Add(net40);
}
}
}
运行上面的代码,结果如图16-28所示。
我们知道,TreeView控件第一次显示时,所有的节点都会出现。因此,除了可以通过在页面或者编程设置TreeNode.Expanded属性为true或false来打开或折叠节点之外,还可以通过设置TreeView.ExpandDepth属性来控制这一行为。例如,如果ExpandDepth是2,则只有前面三层会被显示(第0层、第1层和第2层)。要控制TreeView总共包含多少层(展开的或折叠的),可以使用MaxDataBindDepth属性。MaxDataBindDepth的默认值为1,并且可以查看整个树。但是,如果使用值2,则只会看到起始节点下的两层。如下面的代码所示:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"ExpandDepth="1"runat="server">
</asp:TreeView>
这样,TreeView控件就只能够显示两层了,即0层与1层,如图16-29所示。
图 16-28 使用程序动态建立TreeView节点示例
图 16-29 设置ExpandDepth="1"的显示结果
16.8.4 使用XML数据源绑定TreeView
TreeView控件可以使用XML文档作为数据源,根据XML文档的层次结构显示节点。而XML文档的访问由XmlDataSource控件来完成,从XmlDataSource控件的DataFile属性中指定XML文档路径,然后在TreeView控件中设置与XML文档中的节点的对应关系。下面将通过一个示例程序来演示这一过程。
要使用XML文件作为数据源,首先就得在项目里创建一个XML文档。在这里,在项目的App_Data目录下创建了一个名为“Test.xml”的XML文档,该文档包含三层结构:联系人、地区和负责人。代码如下所示:
<?xml version="1.0"encoding="utf-8"?>
<contact name="区域联系人">
<genre name="华中地区">
<person Text="负责人">
<name>张华</name>
<tel>13511111111</tel>
<mail>13511111111@163.com</mail>
</person>
</genre>
<genre name="华北地区">
<person Text="负责人">
<name>王刚</name>
<tel>13522222222</tel>
<mail>13522222222@163.com</mail>
</person>
</genre>
<genre name="王勇">
<person Text="负责人">
<name>张华</name>
<tel>13533333333</tel>
<mail>13533333333@163.com</mail>
</person>
</genre>
</contact>
创建好Test.xml文档之后,从工具箱选择TreeView控件和XmlDataSource控件拖入设计页面,把XmlDataSource控件DataFile属性设置成“~/App_Data/Test.xml”,并把TreeView控件的DataSourceID属性设为“XmlDataSource1”。如下面的代码所示:
<asp:XmlDataSource ID="XmlDataSource1"runat="server"
DataFile="~/App_Data/Test.xml">
</asp:XmlDataSource>
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
DataSourceID="XmlDataSource1">
</asp:TreeView>
要想让TreeView控件成功地显示这些XML数据,还需要做这样一个工作,即在首次加载页面的时候,用编程的方式通过TreeNodeBinding对象添加节点与XML文档绑定的对应关系。当然,也可以使用设计器来指定这种对应关系。
TreeNodeBinding类在TreeView控件中定义数据项与该数据项绑定到的节点之间的关系。该类的DataMember属性指定在节点显示的数据源对应XML的节点;ValueField属性对应TreeNode对象的Value属性;Text属性指定向用户显示的文本,如果该属性没有指定,则默认与ValueField属性相同。如下面的代码所示:
public partial class WebForm1:System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
//显示连接虚线
this.TreeView1.ShowLines=true;
//以下是添加节点与数据源绑定的对应关系
//绑定contact
TreeNodeBinding contact=new TreeNodeBinding();
//指定绑定的成员
contact.DataMember="contact";
//取值的字段
contact.ValueField="name";
//添加到树中
this.TreeView1.DataBindings.Add(contact);
//绑定genre
TreeNodeBinding genre=new TreeNodeBinding();
genre.DataMember="genre";
genre.ValueField="name";
this.TreeView1.DataBindings.Add(genre);
//绑定person
TreeNodeBinding person=new TreeNodeBinding();
person.DataMember="person";
person.ValueField="Text";
this.TreeView1.DataBindings.Add(person);
//绑定name
TreeNodeBinding name=new TreeNodeBinding();
name.DataMember="name";
name.ValueField="#InnerText";
this.TreeView1.DataBindings.Add(name);
//绑定tel
TreeNodeBinding tel=new TreeNodeBinding();
tel.DataMember="tel";
tel.ValueField="#InnerText";
this.TreeView1.DataBindings.Add(tel);
//绑定mail
TreeNodeBinding mail=new TreeNodeBinding();
mail.DataMember="mail";
mail.ValueField="#InnerText";
this.TreeView1.DataBindings.Add(mail);
}
}
}
设置好上面的对应关系之后,运行结果如图16-30所示。
16.8.5 使用数据库绑定TreeView
本小节将讨论一个非常实用的示例,即将分类数据从数据库中提取出来,再通过递归技巧根据分类建立TreeView节点。为了实现这个示例,这里仍然使用16.6节中的SiteMap表,如图16-31所示。
图 16-30 使用XML数据源绑定TreeView例子
图 16-31 SiteMap表
准备好数据表之后,需要在页面添加两个控件。如下面的代码所示:
<asp:Label ID="Label1"runat="server"></asp:Label>
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
OnSelectedNodeChanged="TreeView1_SelectedNodeChanged">
</asp:TreeView>
其中,如果在TreeView控件中选择了一个节点,则会引发OnSelectedNodeChanged事件。因此,可以在OnSelectedNodeChanged事件里处理每次选择节点时需要处理的事件(如更新显示的内容等)。在这里,将OnSelectedNodeChanged事件里被选择的节点显示在Label1控件里。详细代码如下面所示:
public partial class WebForm1:System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
string sqlString="select*from SiteMap";
SqlConnection sqlConn=new SqlConnection(
ConfigurationManager.ConnectionStrings
["SiteMapConnectionString"].ToString());
SqlCommand sqlCmd=new SqlCommand(sqlString, sqlConn);
DataSet dataSet=new DataSet();
SqlDataAdapter sqlDataAdapter=
new SqlDataAdapter(sqlCmd);
sqlDataAdapter.Fill(dataSet);
DataTable dt=dataSet.Tables[0];
SetTreeViewNode(dt, null, null);
}
}
private void SetTreeViewNode(DataTable dt, string parentID,
TreeNode treeNode)
{
DataView dv=dt.DefaultView;
dv.Sort="ParentID";
DataRowView[]drvs=dv.FindRows(parentID);
if(drvs.Length>0)
{
foreach(DataRowView drv in drvs)
{
string id=drv["ID"].ToString();
string title=drv["title"].ToString();
TreeNode newTreeNode=new TreeNode(title, id);
SetTreeViewNode(dt, id, newTreeNode);
SetTreeView(treeNode, newTreeNode);
}
}
}
private void SetTreeView(TreeNode treeNode,
TreeNode newTreeNode)
{
if(treeNode==null)
{
TreeView1.Nodes.Add(newTreeNode);
}
else
{
treeNode.ChildNodes.Add(newTreeNode);
}
}
protected void TreeView1_SelectedNodeChanged(object sender,
EventArgs e)
{
Label1.Text="你选择的节点ID是:"
+TreeView1.SelectedNode.Value
+"——节点标题是:"
+TreeView1.SelectedNode.Text;
}
}
在上面的代码中,SetTreeViewNode与SetTreeView这两个函数分别用来设置TreeView控件的组成节点。其中,SetTreeViewNode接受3个参数:dt为整个分类数据表的数据内容;parentID用来取得所有ParentID字段等于此参数的数据;treeNode则是取得的数据所要附加上去的上层节点。SetTreeView则执行节点的创建动作。
现在回到Page_Load方法,这个事件处理程序在网页加载的时候执行,其中首先取得SiteMap数据表的内容,然后引用SetTreeViewNode方法,将取得的DataTable对象及最上层分类项目的ParentID域值(这里为null)当作参数输入。由于是最上层的分类,因此最后一个参数直接设为null。程序的运行结果如图16-32所示。
图 16-32 使用数据库绑定TreeView的示例
通过数据表字段的关联设计及递归式的运用,就可以在页面上轻松地呈现出一个不限层次的树状架构图,这个技术可以让你设计出非常具有弹性的树状导航架构。
16.8.6 按需填充TreeView
有时候,你的TreeView控件可能需要显示大量的数据,在这种情况下如果采用上面的一次填充所有的节点,会大大增加处理第一次请求的时间,而且还会显著增大页面和视图状态的大小。因此,在这种情况下,你可能并不希望一次填充所有的节点,而是按需要填充相关节点。其实,就按需填充功能来讲,TreeView提供了很好的解决方案,它可以让你在节点展开的时候方便地填充树的分支。更妙的是,你随时可以按需填充树的选定部分。
若要动态填充某个节点,请首先将该节点的PopulateOnDemand属性设置为true。然后,为以编程方式填充节点的OnTreeNodePopulate事件定义一个事件处理方法。通常的事件处理方法会从数据源中检索节点数据,将该数据放入一个节点结构中,然后将该节点结构添加到正在被填充的节点的ChildNodes集合中。通过将TreeNode对象添加到父节点的ChildNodes集合中,可以创建一个节点结构。
其实,TreeView支持两种按需填入节点的技术。当PopulateNodesFromClient属性为true的时候(默认),TreeView执行一个客户端的回调来从你的事件获得它需要的节点,而并不需要回发整个页面。如果PopulateNodesFromClient为false,或者它为true,但TreeView侦测到当前浏览器不支持客户端回调,那么TreeView会触发一次正常的回发并获得相同的结果。唯一的区别是整个页面会在浏览器里刷新,产生一个略微不平滑的界面。(它还允许发生另一个页面事件,如控件变化事件。)
下面将通过一个示例来展示如何使用OnTreeNodePopulate事件来实现按需填充的功能。页面的TreeView控件定义如下:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
OnTreeNodePopulate="TreeView1_TreeNodePopulate">
</asp:TreeView>
在Page_Load事件里,只需要加载一个根节点(即id=0的节点),并将该节点的Populate OnDemand属性设置true。这样,就可以通过响应OnTreeNodePopulate事件从而在它展开时填入该根节点的子节点。如下面的代码所示:
public partial class WebForm1:System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
string sqlString=
"select id, title from SiteMap where id=0";
DataTable dt=GetData(sqlString);
foreach(DataRow row in dt.Rows)
{
TreeNode tree=
new TreeNode(row["title"].ToString(),
row["ID"].ToString());
tree.PopulateOnDemand=true;
tree.Collapse();
TreeView1.Nodes.Add(tree);
}
}
}
protected void TreeView1_TreeNodePopulate(object sender,
TreeNodeEventArgs e)
{
string sqlString=
"select id, title from SiteMap where ParentID=
"+e.Node.Value;
DataTable dt=GetData(sqlString);
foreach(DataRow row in dt.Rows)
{
TreeNode tree=new TreeNode(row["title"].ToString(),
row["ID"].ToString());
tree.PopulateOnDemand=true;
e.Node.ChildNodes.Add(tree);
}
}
private DataTable GetData(string sqlString)
{
SqlConnection sqlConn=new SqlConnection(
ConfigurationManager.ConnectionStrings
["SiteMapConnectionString"].ToString());
SqlCommand sqlCmd=new SqlCommand(sqlString, sqlConn);
DataSet dataSet=new DataSet();
SqlDataAdapter sqlDataAdapter=
new SqlDataAdapter(sqlCmd);
sqlDataAdapter.Fill(dataSet);
DataTable dt=dataSet.Tables[0];
return dt;
}
}
运行上面的代码,结果如图16-33所示。
注意 当节点的PopulateOnDemand属性设置为true时,必须动态填充该节点。不能以声明方式将另一节点嵌套在它下面;否则将会在页面上出现一个错误。一个给定的节点只按需填充一次。此后,值保存在客户端,同一个节点再次折叠或展开时不会再执行回调。
图 16-33 按需填充TreeView的示例
16.8.7 TreeView样式
除了自身的属性外,TreeView控件还支持每种节点类型的TreeNodeStyle控件的属性。这些样式属性将重写应用于所有节点类型的NodeStyle属性。
如表16-13所示,TreeNodeStyle类的大多数成员均从Style类继承而来。它通过提供某些属性来扩展Style类,这些属性控制节点中文本周围的间距和相邻节点之间的间距。使用Horizontal-Padding属性控制节点中文本左边和右边的间距。类似地,VerticalPadding属性控制节点中文本上方和下方的间距。通过设置NodeSpacing属性,可以控制应用TreeNodeStyle的节点与相邻节点之间的间距。若要控制父节点与子节点之间的间距,请使用ChildNodesPadding属性。
图 16-34 描述了TreeNodeStyle属性的具体样式控制情况:
图 16-34 TreeNodeStyle属性
其中,NodeIndent属性为所有节点指定缩进量级。节点会从呈现TreeView控件的一侧缩进。对于从左向右呈现的区域设置而言,这是指左侧,而对于从右向左呈现的区域设置而言,这是指右侧。
1.把样式应用到节点类型
TreeView控件允许你分别控制不同类型的节点的样式,如根节点、包含其他节点的节点、选定的节点等,如表16-14所示。
如表16-14所示,如果要对树的所有节点应用样式,可以使用TreeView.NodeStyle属性。如下面的代码所示:
<asp:TreeView ID="TreeView1"NodeStyle-ForeColor="Blue"
NodeStyle-VerticalPadding="0"runat="server">
</asp:TreeView>
当一个节点被选中或鼠标悬停于该节点上时,可对该节点应用不同的样式。当某个节点的Selected属性设置为true时,将应用SelectedNodeStyle属性,对于选中的节点,该属性将重写任何未选择的样式属性。当鼠标悬停于某个节点上时,将应用HoverNodeStyle属性。
这些样式属性按以下优先级顺序应用:
1)NodeStyle.
2)RootNodeStyle、ParentNodeStyle或LeafNodeStyle(根据节点类型应用)。如果定义了LevelStyles集合,则其应用优先级同前,并覆盖其他节点样式属性。
3)SelectedNodeStyle.
4)HoverNodeStyle.
2.LevelStyles集合
LevelStyles集合是单独设置各样式属性(如NodeStyle属性)的替代方法。LevelStyles集合可控制处于树视图中特定级别的节点的样式。集合中的第一个样式对应于树视图第一级中的节点的样式。集合中的第二个样式对应于树视图第二级中的节点的样式,依此类推。此属性最常用于生成目录样式导航菜单,其中处于某个特定级别的节点应具有相同的外观,而无论这些节点是否拥有子节点。
为了更好地演示LevelStyles集合的使用方法,下面将16.8.6节中的例子修改如下:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
OnTreeNodePopulate="TreeView1_TreeNodePopulate">
<LevelStyles>
<asp:TreeNodeStyle ChildNodesPadding="10"
Font-Bold="true"Font-Size="12pt"ForeColor="DarkGreen"/>
<asp:TreeNodeStyle ChildNodesPadding="5"
Font-Bold="true"Font-Size="10pt"/>
<asp:TreeNodeStyle ChildNodesPadding="5"
Font-Underline="true"Font-Size="10pt"/>
<asp:TreeNodeStyle ChildNodesPadding="10"
Font-Size="8pt"/>
</LevelStyles>
</asp:TreeView>
运行程序,运行结果如图16-35所示。
3.ShowLines属性
ShowLines属性指定是否显示将子节点连接到父节点的连线。当此属性设置为true时,将显示一条虚线将子节点连接到父节点。如下面的代码所示:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
OnTreeNodePopulate="TreeView1_TreeNodePopulate"
ShowLines="true">
<LevelStyles>
……
</LevelStyles>
</asp:TreeView>
运行上面的页面,结果如图16-36所示。
图 16-35 LevelStyles集合的使用示例
图 16-36 ShowLines属性的使用示例
当然,也可以定义自己的连线图像,连线图像必需放置在一个文件夹中,然后必须将LineImagesFolder属性设置为指向连线图像所在的文件夹就可以了。
4.在节点中使用图片
在TreeView控件中,除了可以通过TreeViewNode.ImageUrl属性为单个节点设置图片之外,还可以通过3个TreeView属性为整个树设置图片。可以为所有折叠的节点((CllapseImageUrl)、所有展开的节点((EpandImageUrl)和所有没有子节点并因此不能展开的节点((NExpandImageUrl)选择要显示的图片。如果设置了这些属性并通过TreeViewNode.ImageUrl属性为特定节点指定了图片,节点的特定图片将优先使用。
如果不想创建自己自定义的节点图片,TreeView还有自带图片。如果要访问这些图片,就要使用TreeView.ImageSet属性,它接收来自TreeViewImageSet的枚举值。每组都包含折叠、展开和没有分支的节点要使用的图片。使用ImageSet属性时,不需要设置任何其他相关的属性。使用示例如下面的代码所示:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
OnTreeNodePopulate="TreeView1_TreeNodePopulate"
ShowLines="true"
ImageSet="BulletedList4">
<LevelStyles>
……
</LevelStyles>
</asp:TreeView>
运行上面的页面,结果如图16-37所示。
图 16-37 在节点中使用图片的示例
16.8.8 添加复选框
若要在TreeView控件中提供多节点选择支持,可以在节点的图像旁边显示复选框。使用ShowCheckBoxes属性可指定哪些节点类型将显示复选框。表16-15列出了此属性的有效值。
例如,如果ShowCheckBoxes属性设置为All,则会为树中的所有节点显示复选框。如下面的代码所示:
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
OnTreeNodePopulate="TreeView1_TreeNodePopulate"
ShowCheckBoxes="All">
</asp:TreeView>
运行结果如图16-38所示。
图 16-38 ShowCheckBoxes="All"的示例
如果只设置ShowCheckBoxes属性,会存在一个缺陷。如图16-38所示,如果选择“基础资料管理”节点,则它的子节点“员工档案”和“客户档案”不会自动选中。其实,在实际应用中,往往希望选中某个父节点时,它的子节点都被选中,这样可以提高系统的操作效率。
为了改进这个操作缺陷,可以用一段客户端脚本来控制它。如下面的代码所示:
<%@Page Language="C#"AutoEventWireup="true"
CodeBehind="WebForm1.aspx.cs"Inherits="_16_5.WebForm1"%>
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script language="javascript"type="text/javascript">
function client_OnTreeNodeChecked(){
var obj=window.event.srcElement;
var treeNodeFound=false;
var checkedState;
if(obj.tagName=="INPUT"&&obj.type=="checkbox")
{
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>
</head>
<body>
<form id="form1"runat="server">
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
onclick="client_OnTreeNodeChecked();"
OnTreeNodePopulate="TreeView1_TreeNodePopulate"
ShowCheckBoxes="All">
</asp:TreeView>
</form>
</body>
</html>
这样,当选择“基础资料管理”节点时,它的子节点“员工档案”和“客户档案”就会自动选中,如图16-39所示。
图 16-39 节点自动选择的示例
在程序中,若要确定哪些节点的复选框已选定,可以通过循环访问CheckedNodes集合的节点来获取被选中的节点。
下面的示例展示了如何获取被选中节点的值,页面代码如下所示:
<asp:Label ID="Label1"runat="server"></asp:Label>
<asp:TreeView ID="TreeView1"Font-Names="Arial"
ForeColor="Blue"runat="server"
onclick="client_OnTreeNodeChecked();"
OnTreeNodePopulate="TreeView1_TreeNodePopulate"
ShowCheckBoxes="All">
</asp:TreeView>
<asp:Button ID="Button1"runat="server"Text="选择节点"
OnClick="Button1_Click"/>
在Button1_Click事件里,将获取到的选中节点的值与选中节点的父节点的值显示在Label1控件里。如下面的代码所示:
protected void Button1_Click(object sender, EventArgs e)
{
if(TreeView1.CheckedNodes.Count>0)
{
Label1.Text="你选择的节点是:<p>";
foreach(TreeNode node in TreeView1.CheckedNodes)
{
Label1.Text+="节点:"+node.Text;
if(node.Parent!=null)
{
Label1.Text+="——父节点:
"+node.Parent.Text+"<br>";
}
}
}
else
{
Label1.Text="你没有选择任何节点!";
}
}
页面的运行结果如图16-40所示。
注意 用于ShowCheckBoxes属性的枚举类型是标志枚举,它还允许通过按位操作组合值。例如,若要为父节点和叶节点显示复选框,请使用位OR运算符组合TreeNodeType.Parent和TreeNode-Type.Leaf值。
图 16-40 获取选中节点的值的示例