專案中採用的 dtree JavaScript 樹狀元件載入緩慢,經分析發現其核心流程為:
C# 查詢 DB → 拼接 JavaScript 字串 → 傳輸至瀏覽器 → JS 解析執行 → 字串拼接生成 HTML → document.write/innerHTML 填充
三大瓶頸:
資料庫查詢、C# 執行、區域網路傳輸均非瓶頸。
C# 查詢 DB → StringBuilder 生成 HTML → 傳輸 HTML → 瀏覽器直接渲染
C# 查詢 DB → 生成 XML → 傳輸 XML + JS → 瀏覽器呼叫 MSXML COM → XSLT 轉換 → HTML
C# 查詢 DB → XML 序列化 → 傳輸 XML → 瀏覽器內建 MSXML → XSLT 轉換 → HTML
每個節點佔一 <tr>,兩欄:
id=NodeID_expend)id=NodeID_icon) + 文字連結 (id=NodeID_text) + 子節點 <table id=NodeID_table>
<table cellspacing="0" cellpadding="0" border="1" bordercolor="black" width="187">
<tr>
<td valign="top" align="left" width="16" background="SkyTreeViewControl_line.gif" height="16">
<img id="NodeID_expend" src="SkyTreeViewControl_expend.gif">
</td>
<td valign="top">
<img id="NodeID_icon" height="16" src="SkyTreeViewControl_open.bmp">
<a id="NodeID_text">10359 成先生</a>
<table id="NodeID_table" cellspacing="0" cellpadding="0" border="1" bordercolor="black">
<tr><td width="16"><img src="SkyTreeViewControl_child.gif"></td>
<td><img id="IDAIL4QC_icon" src="SkyTreeViewControl_default.bmp"><a id="IDAIL4QC_text">餅乾</a></td></tr>
<tr><td width="16"><img src="SkyTreeViewControl_child.gif"></td>
<td><img id="IDARL4QC_icon" src="SkyTreeViewControl_default.bmp"><a id="IDARL4QC_text">花奶酪</a></td></tr>
<tr><td width="16"><img src="SkyTreeViewControl_lastchild.gif"></td>
<td><img id="IDAWL4QC_icon" src="SkyTreeViewControl_default.bmp"><a id="IDAWL4QC_text">溫馨奶酪</a></td></tr>
</table>
</td>
</tr>
</table>
| 功能 | 實作方式 | 關鍵 API |
|---|---|---|
| 展開/收合 | 切換 table.style.display ('none'/'') | element.style.display |
| 圖示切換 | 交換 src 與自訂 SrcBack 屬性 | getAttribute/setAttribute |
| 高亮選中 | 切換 className (TreeNode/SelectedNode) | element.className |
| 動態載入 | 讀取 XMLSource → 設定 XML Data Island src | xmlIsland.src |
刻意使用 getAttribute/setAttribute 而非 element.SrcBack,確保符合 W3C DOM 標準,理論上支援非 IE 瀏覽器 (需替換 XML Data Island 為 XMLHttpRequest)。
<xml id="XMLIslandID">
<METADATA>
<AUTHOR>John Smith</AUTHOR>
<GENERATOR>Visual Notepad</GENERATOR>
</METADATA>
</xml>
src 載入外部 XMLxmlIsland.XMLDocument 取得 DOMDocumentonreadystatechange 監聽載入狀態<?xml version="1.0" encoding="utf-8"?>
<RootNodes>
<Node>
<Icon>customer.bmp</Icon>
<Text>艾德高科技</Text>
<Nodes>
<Node>
<Icon>order.bmp</Icon>
<Text>10359 成先生</Text>
<Nodes>
<Node>
<Icon>product.bmp</Icon>
<Text>餅乾</Text>
<OnClick>alert('餅乾')</OnClick>
<Nodes/>
</Node>
</Nodes>
</Node>
</Nodes>
</Node>
</RootNodes>
元素定義:Icon 圖示、Text 文字、OnClick 點擊腳本、Link 超連結、XMLSource 動態載入 URL、Nodes 子節點容器。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="https://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="no"/>
<!-- 主模板:轉換入口 -->
<xsl:template match="/*"> ... </xsl:template>
<!-- 遞迴節點模板 -->
<xsl:template name="TreeNode"> ... </xsl:template>
</xsl:stylesheet>
xsl:output method="html" indent="no":輸出精簡 HTML<table>,對每個 Node 呼叫 TreeNode 模板TreeNode 模板接受 Level 參數,遞迴生成巢狀結構| 檔案 | 用途 |
|---|---|
| Default.aspx | 示範頁面,雙 TreeView 對比 |
| SkyTreeNode.cs | 節點實體類別,可 XML 序列化 |
| SkyTreeNodeList.cs | 強型別節點集合 |
| TreeViewNodeXml.aspx | 動態 XML 供應端點 |
| SkyTreeViewControl.bmp | 工具箱圖示 |
| SkyTreeViewControl.cs | 控制項核心邏輯 |
| SkyTreeViewControl.xslt | XSLT 模板 (嵌入資源) |
| SkyTreeViewControl_*.gif/bmp | 樹狀結構圖示 |
[Serializable]
[XmlType("Node")]
public class SkyTreeNode
{
public string ID { get; set; } // 節點唯一 ID,建議 Guid.NewGuid()
public string Text { get; set; } // 顯示文字
public string Icon { get; set; } // 圖示 URL
public string Link { get; set; } // 超連結
public string OnClick { get; set; } // 點擊 JS 代碼
public string XMLSource { get; set; } // 動態載入 URL
[XmlIgnore] // 避免循環引用
public SkyTreeNode Parent { get; set; }
[XmlArrayItem("Node", typeof(SkyTreeNode))]
public SkyTreeNodeList Nodes { get; } = new SkyTreeNodeList(this);
}
[Serializable]
public class SkyTreeNodeList : CollectionBase
{
private SkyTreeNode _owner;
public SkyTreeNodeList(SkyTreeNode owner = null) { _owner = owner; }
public SkyTreeNode this[int index] => (SkyTreeNode)List[index];
public int Add(SkyTreeNode node)
{
if (node == null) throw new ArgumentNullException(nameof(node));
if (_owner != null) node.Parent = _owner;
return List.Add(node);
}
// AddRange, Remove, IndexOf, Contains ...
}
| 屬性 | 類型 | 說明 |
|---|---|---|
| AutoScroll | bool | 自動顯示捲軸 |
| GenerateAtServer | bool | true=伺服器端 XSLT,false=客戶端 XSLT |
| DynamicLoadChildNodes | bool | 啟用 XMLSource 動態載入 |
| IndentXML | bool | XML 輸出縮排 (除錯用) |
| TreeNodeStyleString | string | 節點 CSS 樣式 |
| SelectedNodeStyleString | string | 選中節點 CSS 樣式 |
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string css = $".SkyTreeViewControl_TreeNode {{{TreeNodeStyleString}}} " +
$".SkyTreeViewControl_SelectedNode {{{SelectedNodeStyleString}}}";
if (Page.Header != null) // ASP.NET 2.0+ <head runat=server>
{
if (!Page.Header.Controls.Cast().Any(c => c.ID == "SkyTreeViewControl_Style"))
{
var style = new HtmlGenericControl("style") { ID = "SkyTreeViewControl_Style", InnerHtml = css };
Page.Header.Controls.Add(style);
}
}
else // ASP.NET 1.x 相容
{
Page.ClientScript.RegisterClientScriptBlock(
GetType(), "SkyTreeViewControl_Style", $"");
}
}
protected override void Render(HtmlTextWriter writer)
{
if (AutoScroll) Style["overflow"] = "auto";
base.Render(writer); // 輸出 <div> 標籤 (TagKey = HtmlTextWriterTag.Div)
}
protected override void RenderContents(HtmlTextWriter writer)
{
if (DesignMode) // VS 設計器
{
writer.Write($"{ID}
Type: {GetType().FullName}
GenerateAtServer: {GenerateAtServer}...");
return;
}
// 真實執行:輸出三段 HTML + 節點資料 (見中篇)
}

這個 2008 年的控制項,以 XML Data Island + XSLT + JavaScript 實現了現代前端框架的核心範式:聲明式模板、資料驅動視圖、虛擬化渲染、伺服器/客戶端同構。雖然 IE 專屬技術已成歷史,但其架構思想在 React Server Components、Astro Islands、Qwik Resumability 中依然熠熠生輝。