在Asp.net中我们知道TreeView控件可以实现站点导航功能,利用模板加上站点地图作为TreeView的DataSource就可以实现轻松的导航功能。我本次所要实现的导航抛开了模板和站点地图,利用框架动态实现导航。
我们知道,TreeView控件可以静态的预定义其结构,支持的数据源控件有XmlDataSource 和 SiteMapDataSource ,并不支持sqldatasource控件。这次我们的数据源正是数据库,所以我们可以动态的填充TreeView的节点。在实现TreeView的动态填充节点后,我们可以利用iframe框架,来实现导航。
一.对TreeView控件本次所要用到的属性及事件的介绍
1.TreeNode.SelectAction属性:获取或设置选择节点时引发的事件,此属性的值为TreeNodeSelectAction 值之一,默认为TreeNodeSelectAction.Select。TreeView的文本节点可以处于两种模式之一,默认情况下,会有一个节点处于选定状态,即TreeNodeSelectAction.Select,此模式下引发SelectedNodeChanged事件,引发后节点只会展开,不会折叠。若要使一个节点处于导航模式,可以把该节点的 NavigateUrl 属性值设置为空字符串 ("") 以外的值。若要使节点处于选择模式,可以把节点的 NavigateUrl 属性设置为空字符串。
2.TreeNode.PopulateOnDemand属性:该属性只为bool类型,获取或设置一个值,该值指示是否动态填充节点,默认为false。当节点的 PopulateOnDemand 属性设置为 true 时,在运行阶段展开节点时通过回发事件填充节点。若要动态填充节点,必须为 TreeNodePopulate 事件定义填充节点的事件处理方法。
3.TreeNode.TreeNodePopulate事件:当节点的 PopulateOnDemand 属性设置为 true 时,可以触发该事件,以编程的方式动态填充节点。
二.实现TreeView的动态填充节点.
1.由于是获取数据里面的数据,于是写了个SqlHelper类,我前面的一篇文章详细叙述过,里面同样介绍了数据库的关系图,感兴趣的朋友可以参考对DataList控件绑定图片的一点小结 里的部分内容,这里为了不影响阅读,把代码还是贴出来了:

DALHelper类
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Text;
5
using System.Configuration;
6
using System.Data;
7
using System.Data.SqlClient;
8
namespace DBHelper
9

{
10
public class DALHelper
11
{
12
private static string constr = ConfigurationManager.ConnectionStrings["MyNBAData"].ConnectionString;
13
private static SqlConnection connection = new SqlConnection(constr);
14
15
/**//// <summary>
16
/// 检查连接是否打开
17
/// </summary>
18
public static bool CheckConnOpen()
19
{
20
if (connection == null || connection.State == ConnectionState.Closed)
21
{
22
connection.Open();
23
return true;
24
}
25
else
26
{
27
return false;
28
}
29
}
30
/**//// <summary>
31
/// 检查连接是否关闭
32
/// </summary>
33
public static bool CheckConnClosed()
34
{
35
if (connection != null || connection.State == ConnectionState.Open)
36
{
37
connection.Close();
38
return true;
39
}
40
else
41
{
42
return false;
43
}
44
}
45
46
/**//// <summary>
47
/// 获取一个DataTable
48
/// </summary>
49
/// <param name="sql"></param>
50
/// <returns></returns>
51
public static DataTable GetDataTable(string sql)
52
{
53
SqlDataAdapter da = null;
54
DataTable dt = null;
55
try
56
{
57
da = new SqlDataAdapter(sql, connection);
58
dt = new DataTable();
59
da.Fill(dt);
60
}
61
catch (Exception ex)
62
{
63
64
throw new Exception(ex.Message);
65
}
66
67
return dt;
68
}
69
70
/**//// <summary>
71
/// 获取DataReader对象
72
/// </summary>
73
/// <param name="sql"></param>
74
/// <param name="parms"></param>
75
/// <returns></returns>
76
public static SqlDataReader ExecuteReader(string sql, SqlParameter[] parms)
77
{
78
SqlCommand command= null;
79
SqlDataReader reader = null;
80
try
81
{
82
command = new SqlCommand(sql, connection);
83
if (parms!=null)
84
{
85
foreach (var item in parms)
86
{
87
command.Parameters.Add(item);
88
}
89
}
90
if( CheckConnOpen())
91
{
92
reader = command.ExecuteReader(CommandBehavior.CloseConnection);
93
}
94
95
96
}
97
catch (Exception ex)
98
{
99
100
throw new Exception(ex.Message);
101
}
102
finally
103
{
104
//CheckConnClosed();
105
}
106
return reader;
107
}
108
109
/**//// <summary>
110
/// 执行数据库的修改,插入,删除
111
/// </summary>
112
/// <param name="sql"></param>
113
/// <param name="parms"></param>
114
/// <returns></returns>
115
public static int ExecuteNonQuery(string sql, SqlParameter[] parms)
116
{
117
SqlCommand command = null;
118
int count = 0;
119
try
120
{
121
command = new SqlCommand(sql, connection);
122
if (parms!=null)
123
{
124
foreach (var item in parms)
125
{
126
command.Parameters.Add(item);
127
}
128
}
129
130
CheckConnOpen();
131
count = command.ExecuteNonQuery();
132
}
133
catch (Exception ex)
134
{
135
136
throw new Exception(ex.ToString());
137
}
138
finally
139
{
140
CheckConnClosed();
141
}
142
return count;
143
}
144
}
145
}
146
2.建立了一个页面DynamicPlayer.aspx,页面内容为:

DynamicPlayer.aspx页面内容
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DynamicPlayer.aspx.cs" Inherits="DynamicPlayer" %>
<!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>动态显示TreeView及利用框架实现导航</title>
</head>
<body>
<form id="form1" runat="server">
<div style="height: 437px; 587px">
<table style=" 100%">
<tr style=" height:437px">
<td style="50%; background-color: #CCCCFF; " align="left"; valign="top">
<asp:TreeView ID="trPlayer" runat="server"
ontreenodepopulate="trPlayer_TreeNodePopulate"
>
<Nodes>
<asp:TreeNode PopulateOnDemand="True" Text="NBA球队" Value="NBA球队"></asp:TreeNode>
</Nodes>
</asp:TreeView>
</td>
<td style="50%" valign="top">
<div style="border-style: dashed; border-color: #FF00FF; height:423px; 273px; position: fixed; margin-left: 50px; margin-top: 50px; top: -31px; left: 257px;">
<iframe id="myframe" style=" 263px; height: 300px" frameborder="1" scrolling="yes" >
</iframe>
</div>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
3.实现TreeNode.TreeNodePopulate事件
1 /// <summary>
2 /// TreeNodePopulate事件,实现动态填充叶节点
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 protected void trPlayer_TreeNodePopulate(object sender, TreeNodeEventArgs e)
7 {
8 if (e.Node.ChildNodes.Count==0)
9 {
10 switch (e.Node.Depth)
11 {
12 case 0:
13 BindTeam(e.Node);//填充第一层节点
14 break;
15 case 1:
16 BindPlayer(e.Node);//填充第二层节点
17 break;
18 default:
19 break;
20 }
21 }
22 }
4.实现TreeView动态填充节点,主要有两个方法:
i: BindTeam(TreeNode node),实现第一层节点绑定
1 //第一层节点绑定到数据库表里的Team的球队名字
2 public void BindTeam(TreeNode node)
3 {
4 string sql = "select TeamID,TeamNameCH from Team";
5 DataTable dt = DBHelper.DALHelper.GetDataTable(sql);
6 if (dt!=null)
7 {
8 foreach (DataRow dr in dt.Rows)
9 {
10 TreeNode tn = new TreeNode(Convert.ToString(dr["TeamNameCH"])
11 , Convert.ToString(dr["TeamID"]));
12 tn.PopulateOnDemand = true;
13 tn.SelectAction = TreeNodeSelectAction.Expand;
14 node.ChildNodes.Add(tn);
15 }
16 }
17 }
ii:BindPlayer(TreeNode node):实现第二层节点绑定:
1 //第二层节点绑定到数据库表里的Player的球员名字
2 public void BindPlayer(TreeNode node)
3 {
4 string sql= "select PlayerID,NameCH from Player where TeamID='{0}'";
5 sql = string.Format(sql, node.Value);
6 DataTable dt =DBHelper.DALHelper.GetDataTable(sql);
7 if (dt != null)
8 {
9 foreach (DataRow dr in dt.Rows)
10 {
11
12 TreeNode tn = new TreeNode(Convert.ToString(dr["NameCH"]), Convert.ToString(dr["PlayerID"]));
13 tn.PopulateOnDemand = false;//设为FALSE时不会在去填充下一级节点
14
15 //设为select模式可以实现导航,需设置NavigateUrl属性,由于本次实现的
16 //是框架内的导航模式,所以NavigateUrl的值为空
17 tn.SelectAction = TreeNodeSelectAction.Select;
18 // tn.NavigateUrl = "ShowPlayerImage.aspx?PlayerID=" + playerID;
19 tn.Target = "myframe";
20 node.ChildNodes.Add(tn);
21 }
22 }
23 }
三.实现当单击叶节点即是点击球员姓名时在iframe中导航至一个已经写好了的专门用来显示球员相片的的页面
1.显示球员相片的的页面为ShowPlayerImage.aspx。
页面内容为:

ShowPlayerImage.aspx页面内容

<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="ShowPlayerImage.aspx.cs" Inherits="ShowPlayerImage" %>

<!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>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
后台代码为:

ShowPlayerImage.aspx后台代码
1
using System;
2
using System.Collections;
3
using System.Configuration;
4
using System.Data;
5
using System.Linq;
6
using System.Web;
7
using System.Web.Security;
8
using System.Web.UI;
9
using System.Web.UI.HtmlControls;
10
using System.Web.UI.WebControls;
11
using System.Web.UI.WebControls.WebParts;
12
using System.Xml.Linq;
13
using System.IO;
14
using System.Data.SqlClient;
15
public partial class ShowPlayerImage : System.Web.UI.Page
16

{
17
protected void Page_Load(object sender, EventArgs e)
18
{
19
if (!IsPostBack)
20
{
21
int playerID = Convert.ToInt32(Request.QueryString["PlayerID"]);
22
byte[] bytes = BindImage(playerID);
23
24
Response.Clear();
25
if (bytes != null)
26
{
27
Response.BinaryWrite(bytes);
28
}
29
else
30
{
31
Response.BinaryWrite(LoadUnknowImage());
32
}
33
Response.End();
34
}
35
}
36
37
/**//// <summary>
38
/// 绑定数据库中的球员相片
39
/// </summary>
40
/// <param name="playerID">该参数是用来接收DataList.aspx页面传过来的球员ID</param>
41
/// <returns></returns>
42
public byte[] BindImage(int playerID)
43
{
44
string sql = "select Photo from Player where PlayerID=@PlayerID";
45
SqlDataReader reader = null;
46
byte[] photoBytes=null;
47
try
48
{
49
SqlParameter parm = new SqlParameter("@PlayerID", SqlDbType.Int);
50
parm.Value = playerID;
51
reader = DBHelper.DALHelper.ExecuteReader(sql, new SqlParameter[]
{parm});
52
if (reader!=null)
53
{
54
while (reader.Read())
55
{
56
if (!(reader["Photo"] is DBNull))
57
{
58
photoBytes = (byte[])reader.GetSqlBinary(0);
59
}
60
}
61
}
62
63
64
}
65
finally
66
{
67
if (reader!=null)
68
{
69
reader.Close();
70
}
71
72
73
}
74
return photoBytes;
75
}
76
77
/**//// <summary>
78
/// 当未能从数据中取得相片时我们用一张事先准备好的图片来替代,该图片在网站的App_Themes的文件夹下
79
/// </summary>
80
/// <returns></returns>
81
public byte[] LoadUnknowImage()
82
{
83
byte[] bytes = null;
84
FileStream fs = null;
85
BinaryReader readBytes = null;
86
try
87
{
88
fs = new FileStream(Server.MapPath("~/App_Themes/Pictures/unknow.jpg"), FileMode.Open, FileAccess.Read);
89
readBytes = new BinaryReader(fs);
90
bytes = readBytes.ReadBytes(Convert.ToInt32(fs.Length));
91
}
92
catch (Exception ex)
93
{
94
95
throw new Exception(ex.Message);
96
}
97
finally
98
{
99
readBytes.Close();
100
fs.Close();
101
}
102
return bytes;
103
}
104
}
105
2.由于TreeNode里的两事件TreeNodeCollapsed(当节点折叠后触发),TreeNodeExpanded(节点展开后触发)都没有实现导航功能所想要的事件,于是自己自定义了一个关于TreeView的节点单击事所要触发的事件TreeNodeOnClick,每当单击叶节点时都会触发该事件,并导航到显示相片的页面。
自定义事件的代码为:
1 //自定义TreeNodeOnClickHandler委托
2 public delegate void TreeNodeOnClickHandler(object sender, TreeNodeEventArgs e);
3 //自定义TreeNodeOnClick事件,每当鼠标单击节点时触发该事件
4 public event TreeNodeOnClickHandler TreeNodeOnClick;
5 /// <summary>
6 ///单击叶节点事件, 实现动态导航
7 /// </summary>
8 /// <param name="sender"></param>
9 /// <param name="e"></param>
10 protected void DynamicPlayer_TreeNodeOnClick(object sender, TreeNodeEventArgs e)
11 {
12 string src = "";
13 string playerID = "";
14 if (e.Node.ChildNodes.Count == 0 && e.Node.Depth == 2)
15 {
16 playerID = e.Node.Value;
17 src = "ShowPlayerImage.aspx?PlayerID=" + playerID;
18 //把iframe的属性src会发给客户端时赋给一个hidden
19 Page.ClientScript.RegisterHiddenField("srcHidden", src);
20 //注册客户端脚本,给iframe的属性src赋值,这样就可以在框架内导航到另外一个页面
21 Page.ClientScript.RegisterStartupScript(this.GetType(), "registFrameAttribute",
22 "var frame=document.getElementById('myframe');frame.src=document.getElementById('srcHidden').value;",true);
23 }
24 }
3.每次我们单击叶节点会向服务器回发,此时就可以实现导航,代码为:
1 protected void Page_Load(object sender, EventArgs e)
2 {
3 //每次单击节点时回发
4 if (IsPostBack)
5 {
6 //注册事件
7 this.TreeNodeOnClick += new TreeNodeOnClickHandler(DynamicPlayer_TreeNodeOnClick);
8 //如果是叶节点并且被选中事,触发自定义的TreeView的Click事件
9 if (trPlayer.SelectedNode.Selected&&trPlayer.SelectedNode.ChildNodes.Count==0)
10 {
11 TreeNodeOnClick(this, new TreeNodeEventArgs(trPlayer.SelectedNode));
12 }
13 }
14
15 }
这样就简单实现了我说的全部功能,运行后效果为:
