| 特性 | Web 控制項 (.cs + DLL) | User Control (.ascx) |
|---|---|---|
| 程式碼與標記分離 | 完全分離 (C# 類別) | 混合在 .ascx 檔案 |
| 預編譯 | 編譯為 DLL,無原始碼部署 | 需部署 .ascx 原始碼 |
| 工具箱整合 | 支援設計工具、屬性視窗 | 不支援 |
| 跨專案重用 | 參考 DLL 即可 | 需複製 .ascx 檔案 |
| 設計時支援 | 完整設計時體驗 | 有限 |
| 版本控制 | 組件版本、強式名稱 | 檔案層級 |
MyNavBar.sln
├── MyNavBar (類別庫專案)
│ ├── NavBar.cs // 核心控制項類別
│ └── Properties/AssemblyInfo.cs
└── WebApplication (測試網站)
├── Default.aspx
└── bin/MyNavBar.dll // 參考輸出
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
namespace MyNavBar
{
///
/// 動態產生 Yahoo 風格麵包屑導航列
///
[ToolboxData("<{0}:NavBar runat=server>{0}:NavBar>")]
public class NavBar : WebControl
{
// === 可自訂屬性 ===
private string _separator = ">";
private int _leftPadding = 1;
private int _rightPadding = 3;
/// 分隔符號 HTML
[Category("Appearance")]
[DefaultValue(">")]
public string Separator
{
get => _separator;
set => _separator = value ?? ">";
}
/// 分隔符號左側空白數
[Category("Appearance")]
[DefaultValue(1)]
public int LeftPadding
{
get => _leftPadding;
set => _leftPadding = Math.Max(0, value);
}
/// 分隔符號右側空白數
[Category("Appearance")]
[DefaultValue(3)]
public int RightPadding
{
get => _rightPadding;
set => _rightPadding = Math.Max(0, value);
}
// === 核心邏輯:建立子控制項 ===
protected override void CreateChildControls()
{
Controls.Clear(); // 確保重新建立時不重複
// 1. 建立分隔符號標籤 (共用實例)
var separatorLabel = new Label
{
Text = new string(' ', _leftPadding) + _separator + new string(' ', _rightPadding)
};
// 2. 首頁連結 (固定)
var homeLink = new HyperLink
{
Text = "Home",
NavigateUrl = "~/"
};
Controls.Add(homeLink);
// 3. 解析目前請求路徑,動態產生中間層級連結
string[] pathSegments = Page.Request.Path.Split('/');
// 範例:/folder1/folder2/page.aspx → ["", "folder1", "folder2", "page.aspx"]
string accumulatedPath = "";
for (int i = 1; i < pathSegments.Length - 1; i++) // 排除首個空字串與檔名
{
string segment = pathSegments[i];
accumulatedPath += "/" + segment;
// 加入分隔符號
Controls.Add(new Label { Text = separatorLabel.Text });
// 加入層級連結
var link = new HyperLink
{
Text = CapitalizeFirst(segment),
NavigateUrl = accumulatedPath + "/" // 目錄需以 / 結尾,依賴 IIS 預設文件
};
Controls.Add(link);
}
// 4. 最後一層:目前頁面標題 (非連結)
if (pathSegments.Length > 1)
{
Controls.Add(new Label { Text = separatorLabel.Text });
string pageName = pathSegments[pathSegments.Length - 1];
// 移除副檔名
int dotIndex = pageName.LastIndexOf('.');
if (dotIndex > 0) pageName = pageName.Substring(0, dotIndex);
Controls.Add(new Label
{
Text = CapitalizeFirst(pageName),
CssClass = "current-page"
});
}
}
private static string CapitalizeFirst(string input)
{
if (string.IsNullOrEmpty(input)) return input;
return char.ToUpper(input[0]) + input.Substring(1).ToLower();
}
// === 渲染輸出 ===
protected override void Render(HtmlTextWriter writer)
{
// 確保子控制項已建立
EnsureChildControls();
// 包裝容器 (可選)
writer.AddAttribute(HtmlTextWriterAttribute.Class, "navbar-container");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
base.Render(writer); // 渲染所有子控制項
writer.RenderEndTag(); // MyNavBar.dllbin 目錄// 擴充屬性
[Category("Behavior")]
[DefaultValue(false)]
public bool UsePageTitle { get; set; } = false;
[Category("Appearance")]
public string CurrentPageCssClass { get; set; } = "current-page";
[Category("Appearance")]
public string LinkCssClass { get; set; } = "nav-link";
[Category("Appearance")]
public string SeparatorCssClass { get; set; } = "nav-separator";
// 在 CreateChildControls 中應用
var link = new HyperLink
{
Text = CapitalizeFirst(segment),
NavigateUrl = accumulatedPath + "/",
CssClass = LinkCssClass
};
// 目前頁面
var currentLabel = new Label
{
Text = UsePageTitle ? Page.Title : CapitalizeFirst(pageName),
CssClass = CurrentPageCssClass
};
// 分隔符號
var sepLabel = new Label
{
Text = separatorLabel.Text,
CssClass = SeparatorCssClass
};
[ToolboxBitmap(typeof(NavBar), "NavBar.bmp")] // 16x16 圖示嵌入資源
public class NavBar : WebControl
{
// 設計工具顯示預覽
public override string DesignModeHtml
{
get
{
return "" +
"[NavBar: Home > Folder > Page] " +
"";
}
}
}
MyNavBar.dll/Products/Books/Detail.aspxC# 自訂 Web 控制項提供:完整封裝、預編譯部署、設計工具整合、屬性化配置、跨專案重用。核心步驟:繼承 WebControl → 定義屬性 → 覆寫 CreateChildControls 建立子控制項樹 → 覆寫 Render 控制輸出 → 編譯 DLL → 註冊標籤前綴使用。掌握此模式,即能建構企業級可重用 UI 元件庫。