復合控制項 由多個現有控制項(子控制項)組合而成,對外暴露統一的屬性、方法、事件介面,封裝內部複雜度。經典例子:Login 控制項內含 Label、TextBox、Button、Validator,開發者只需設定幾個屬性即可擁有完整登入功能。
| 維度 | 復合控制項 | 使用者控制項 (.ascx) |
|---|---|---|
| 建立方式 | C#/VB 程式碼(繼承 CompositeControl) | 宣告式標記 + 程式碼後置 |
| 部署單位 | 組件 (.dll),可跨專案重用 | 檔案 (.ascx),通常專案內部使用 |
| 設計時支援 | 完整工具箱拖曳、屬性視窗、智慧標籤 | 僅支援原始碼檢視拖曳,屬性視窗有限 |
| 版本控制 | 組件版本、強名稱、GAC 部署 | 隨專案原始碼版控 |
| 適用場景 | 通用元件庫、商業控制項、跨專案共用 | 專案專用 UI 區塊(頁首、選單、頁尾) |
public abstract class CompositeControl : WebControl, INamingContainer, ICompositeControlDesignerAccessor
ctl00$MyComposite$txtName),支援資料繫結、事件路由、樣板。RecreateChildControls)。CompositeDataBoundControl 針對資料繫結)。[ToolboxData("<{0}:SimpleLogin runat=server></{0}:SimpleLogin>")]
public class SimpleLogin : CompositeControl
{
private TextBox _txtUser, _txtPwd;
private Button _btnLogin;
private Label _lblMsg;
private RequiredFieldValidator _valUser, _valPwd;
protected override void CreateChildControls()
{
Controls.Clear();
_txtUser = new TextBox { ID = "txtUser" };
_txtPwd = new TextBox { ID = "txtPwd", TextMode = TextMode.Password };
_btnLogin = new Button { ID = "btnLogin", Text = "登入" };
_lblMsg = new Label { ID = "lblMsg", ForeColor = Color.Red, Visible = false };
_valUser = new RequiredFieldValidator { ID = "valUser", ControlToValidate = "txtUser", ErrorMessage = "請輸入帳號", Display = ValidatorDisplay.Dynamic };
_valPwd = new RequiredFieldValidator { ID = "valPwd", ControlToValidate = "txtPwd", ErrorMessage = "請輸入密碼", Display = ValidatorDisplay.Dynamic };
_btnLogin.Click += new EventHandler(btnLogin_Click);
// 佈局:使用 Table 或 Panel 包裝
Table layout = new Table();
layout.Rows.Add(new TableRow { Cells = { new TableCell { Text = "帳號:" }, new TableCell { Controls = { _txtUser, _valUser } } } });
layout.Rows.Add(new TableRow { Cells = { new TableCell { Text = "密碼:" }, new TableCell { Controls = { _txtPwd, _valPwd } } } });
layout.Rows.Add(new TableRow { Cells = { new TableCell { ColumnSpan = 2, Controls = { _btnLogin } } } });
layout.Rows.Add(new TableRow { Cells = { new TableCell { ColumnSpan = 2, Controls = { _lblMsg } } } });
Controls.Add(layout);
}
public string UserName { get { EnsureChildControls(); return _txtUser.Text; } }
public string Password { get { EnsureChildControls(); return _txtPwd.Text; } }
public event EventHandler LoginClick;
protected virtual void OnLoginClick(EventArgs e) { if (LoginClick != null) LoginClick(this, e); }
void btnLogin_Click(object sender, EventArgs e)
{
if (Page.IsValid) OnLoginClick(EventArgs.Empty);
}
public void ShowMessage(string msg, bool isError)
{
EnsureChildControls();
_lblMsg.Text = msg;
_lblMsg.ForeColor = isError ? Color.Red : Color.Green;
_lblMsg.Visible = true;
}
}
<%@ Register Assembly="MyControls" Namespace="MyControls" TagPrefix="cc" %>
<cc:SimpleLogin ID="loginCtrl" runat="server" OnLoginClick="loginCtrl_LoginClick" />
protected void loginCtrl_LoginClick(object sender, EventArgs e)
{
if (Membership.ValidateUser(loginCtrl.UserName, loginCtrl.Password))
{
FormsAuthentication.RedirectFromLoginPage(loginCtrl.UserName, false);
}
else
{
loginCtrl.ShowMessage("帳號或密碼錯誤", true);
}
}
定義 ITemplate 屬性(如 LayoutTemplate、LoggedInTemplate),在 CreateChildControls 中用 InstantiateIn 具現化,實現完全客製化外觀(參考 Login、ListView)。
繼承 CompositeDataBoundControl,覆寫 CreateDataItem、DataBind,支援 DataSourceID、DataMember 等標準資料繫結模式。
CompositeControlDesigner,覆寫 GetDesignTimeHtml 提供設計時預覽。[ToolboxBitmap(typeof(SimpleLogin), "SimpleLogin.bmp")] 設定工具箱圖示。ICompositeControlDesignerAccessor.RecreateChildControls 讓屬性變更即時生效。| 問題 | 原因 | 解決 |
|---|---|---|
| 子控制項事件不觸發 | 未在 CreateChildControls 掛接事件、或未呼叫 EnsureChildControls | 確保事件掛接在 CreateChildControls,屬性存取前呼叫 EnsureChildControls |
| ViewState 遺失 | 子控制項未正確加入 Controls 集合、或 ID 重複 | INamingContainer 確保唯一 ID,子控制項必須加入 Controls |
| 設計時錯誤「創建控制項時出錯」 | 標記語法錯誤、缺少 runat="server"、組件未正確參考 | 檢查註冊指令、組件參考、建置輸出路徑 |
復合控制項是 ASP.NET 2.0 打造可重用、封裝良好、設計時友善 UI 元件的利器。掌握 CompositeControl 基類機制、CreateChildControls 生命週期、頂層介面暴露技巧與設計時擴充,即可將複雜 UI 邏輯封裝為「拖曳即用」的專業級控制項,大幅提升團隊開發效率與程式碼品質。