这是一个自定义控件文件的两部分 SelectCategoryControl.ascx <%@ Control Language="C#" AutoEventWireup="true" CodeFile="SelectCategoryControl.ascx.cs" Inherits="App_Control_SelectCategoryControl" %> <div> <asp:Label ID="LabelStatus" runat="server"></asp:Label> <asp:Button ID="Button2" runat="server" Text="更改分类" OnClick="Button2_Click" CausesValidation="False" /> <asp:Panel ID="Panel1" runat="server"> <asp:Label ID="Label1" runat="server" Visible="False"></asp:Label> <br /> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:LocalSqlServer %>" SelectCommand="SELECT [id], [name], [fatherid] FROM [category]"></asp:SqlDataSource> <br /> <asp:TreeView ID="TreeView1" runat="server" MaxDataBindDepth="3" OnTreeNodePopulate="TreeView1_TreeNodePopulate" ExpandDepth="1" OnTreeNodeCollapsed="TreeView1_TreeNodeClicked" OnTreeNodeExpanded="TreeView1_TreeNodeClicked" ShowLines="True" NodeIndent="36" ShowExpandCollapse="False"> <SelectedNodeStyle Font-Bold="False" /> <Nodes> <asp:TreeNode PopulateOnDemand="True" Text="全部分类" Value="0" SelectAction="SelectExpand"></asp:TreeNode> </Nodes> </asp:TreeView> </asp:Panel> </div> SelectCategoryControl.ascx.cs using System; using System.Collections; using System.Configuration; using System.Data.SqlClient; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; public partial class App_Control_SelectCategoryControl : System.Web.UI.UserControl { protected void Page_Load(object sender, EventArgs e) { string categoryPath = Category.GetPathText(); // 取得当前存储在 Cookies 里面的分类路径的文字形式 if (!IsPostBack) // 第一次显示页面的时候 { if (categoryPath == string.Empty) // 如果没有找到存储的分类路径 { Panel1.Visible = true; // 打开下面的选择分类的面板 TreeView1.Nodes[0].Expand(); // 展开第一级分类 } else // 如果找到分类路径 { LabelStatus.Text = "全部分类-->" + categoryPath; // 显示分类路径 Panel1.Visible = false; // 关闭选择分类路径面板 } } } /// <summary> /// 此方法用于展开 TreeView 的节点时,填充子节点的内容,内容是从数据库中读取的 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void TreeView1_TreeNodePopulate(object sender, TreeNodeEventArgs e) { if (e.Node.ChildNodes.Count == 0) // 判断当前节点的子节点数目,如果为0,即没有子节点,才填充 { // 读取子节点的所有内容,ordernumber可以设置值,用于排序节点 string sql = "select id,name,fatherid from category where fatherid=" + e.Node.Value.ToString() + " order by ordernumber"; using (SqlDataReader reader = SqlHelper.ExecuteReader(sql)) { while (reader.Read()) { TreeNode newNode = new TreeNode(reader["name"].ToString(), reader["id"].ToString()); //设置节点在展开的时候自动填充 newNode.PopulateOnDemand = true; // 一个节点在被鼠标点中的时候可以引发四种事件:Expand、Select、SelectExpand、None, // 选择 Expand 可以使节点在单击的时候展开或者折叠,但节点不会被选中 // 选择 SelectExpand 可以使节点在单击的时候被展开,同时被选中,但是已经展开的节点不会被折叠 // 因此,如果想要在单击节点的时候节点会自动展开折叠,必须使用 Expand // 但这时节点不会被选中,那么就只能在节点展开和折叠的事件中,编码使该节点被选中了 // e.Node.Selected = true; newNode.SelectAction = TreeNodeSelectAction.Expand; e.Node.ChildNodes.Add(newNode); } } } } /// <summary> /// 当节点被单击时,会产生展开(对于折叠着的节点)、折叠(对于已经展开的节点)动作 /// 这个方法就是这两个动作发生时的事件处理程序,具体执行的操作是:显示一个从当前节点到跟节点的路径 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void TreeView1_TreeNodeClicked(object sender, TreeNodeEventArgs e) { LabelStatus.Text = e.Node.Text.ToString(); TreeNode node = e.Node; // 因为单击了这个节点,所以把当前节点置为选中 // 但是这里有一个奇怪的问题,在这里如果用 e.Node.Selected = true 的形式将当前节点设置为已选中状态 // 那么在按提交按钮的时候,奇数次则会出现TreeView 控件并没有选中节点,而偶数次就显示 TreeView 控件已经选中节点 // 因此只能用一个替代方法解决这个问题,就是:当点击一个节点的时候,把节点的 value 取出来,存储到一个隐藏的 Label 控件中 e.Node.Select(); Label1.Text = TreeView1.SelectedNode.ValuePath; while (node.Depth != 0) // 取得从当前节点到跟节点的分类路径 { node = node.Parent; LabelStatus.Text = node.Text + "-->" + LabelStatus.Text; } if (e.Node.ChildNodes.Count == 0) // 如果已经是叶节点 { Category.SetPath(Label1.Text); Panel1.Visible = false; // 关闭选择分类面板 } } protected void Button2_Click(object sender, EventArgs e) { Panel1.Visible = true; TreeView1.Nodes[0].Expand(); } }