TreeView 常用於顯示多層級選單、組織架構、分類目錄。資料庫通常採用 **遞迴關聯** (Self-referencing FK):`ParentId` 指向同表 `Id`。本文示範從資料庫讀取 `DataTable`,遞迴建立 `TreeNode`。
CREATE TABLE MenuTree (
MenuId INT PRIMARY KEY,
ParentId INT NULL REFERENCES MenuTree(MenuId), -- 0 或 NULL 為根
MenuName NVARCHAR(100) NOT NULL,
MenuOrder INT DEFAULT 0,
NavigateUrl NVARCHAR(255) NULL
);
using System;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using System.Web.UI.WebControls;
public partial class TreeViewDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) BindTree();
}
private void BindTree()
{
DataTable dt = GetTreeData();
// 先建立所有節點字典 (Key=MenuId),再建立父子關係,避免遞迴多次查詢
var nodeDict = new System.Collections.Generic.Dictionary();
foreach (DataRow row in dt.Rows)
{
var node = new TreeNode
{
Text = row["MenuName"].ToString(),
Value = row["MenuId"].ToString(),
NavigateUrl = row["NavigateUrl"]?.ToString(),
ToolTip = row["MenuName"].ToString()
};
nodeDict[(int)row["MenuId"]] = node;
}
// 建立父子關係
foreach (DataRow row in dt.Rows)
{
int id = (int)row["MenuId"];
int parentId = row["ParentId"] == DBNull.Value ? 0 : (int)row["ParentId"];
var node = nodeDict[id];
if (parentId == 0 || !nodeDict.ContainsKey(parentId))
{
TreeView1.Nodes.Add(node); // 根節點
}
else
{
nodeDict[parentId].ChildNodes.Add(node); // 掛到父節點
}
}
TreeView1.ExpandAll(); // 或 ExpandDepth(1)
}
private DataTable GetTreeData()
{
string connStr = ConfigurationManager.ConnectionStrings["TreeConn"].ConnectionString;
const string sql = "SELECT MenuId, ParentId, MenuName, MenuOrder, NavigateUrl FROM MenuTree ORDER BY ParentId, MenuOrder";
using (var conn = new SqlConnection(connStr))
using (var da = new SqlDataAdapter(sql, conn))
{
var dt = new DataTable();
da.Fill(dt);
return dt;
}
}
}
<asp:TreeView ID="TreeView1" runat="server"
ShowLines="True"
NodeIndent="15"
ExpandDepth="1">
<NodeStyle Font-Names="微軟正黑體" Font-Size="14px" VerticalPadding="3" />
<HoverNodeStyle ForeColor="Blue" />
<SelectedNodeStyle BackColor="#E0E0E0" ForeColor="Black" />
</asp:TreeView>
<asp:TreeView ID="TreeView1" runat="server" PopulateNodesFromClient="True" OnTreeNodePopulate="TreeView1_TreeNodePopulate" />
// 後台
protected void TreeView1_TreeNodePopulate(object sender, TreeNodeEventArgs e)
{
int parentId = int.Parse(e.Node.Value);
DataTable children = GetChildren(parentId); // 只查該父節點的直屬子節點
foreach (DataRow row in children.Rows)
{
var node = new TreeNode(row["MenuName"].ToString(), row["MenuId"].ToString());
node.PopulateOnDemand = true; // 允許繼續展開
e.Node.ChildNodes.Add(node);
}
}
private DataTable GetChildren(int parentId)
{
// SELECT ... WHERE ParentId = @ParentId
}
新專案建議:後端提供 `/api/tree/nodes?parentId=0` 扁平化 JSON,前端用成熟 JS Tree 元件渲染、搜尋、拖曳、複選,體驗遠優於 WebForms TreeView。