復合控制項包含多個子控制項,每個子控制項都有自己的樣式屬性(如 ForeColor、BackColor、Font 等)。若不加處理,外部開發者無法直接在復合控制項層級統一設定這些樣式。樣式冒泡就是將子控制項的特定樣式屬性「向上提升」為復合控制項的頂層屬性,讓使用者像操作單一控制項一樣設定外觀。
適用於只需暴露子控制項少數樣式屬性的情境。核心步驟:
CellPadding、Border)。CreateChildControls 中建立子控制項實例。Attributes 集合(鍵/值對),瀏覽器渲染時自動轉為 HTML 屬性。public class MyControl : CompositeControl
{
private int _cellPadding = 0;
private int _border = 1;
public int CellPadding { get { return _cellPadding; } set { _cellPadding = value; } }
public int Border { get { return _border; } set { _border = value; } }
protected override void CreateChildControls()
{
Table t = new Table();
t.Attributes["CellPadding"] = _cellPadding.ToString();
t.Attributes["Border"] = _border.ToString();
Controls.Add(t);
}
}
缺點: 屬性分散、難以維護、多子控制項同名屬性易衝突、無法支援複雜樣式物件(如 TableItemStyle)。
建立繼承自 Style 的專屬樣式類別(如 MyControlStyle : Style),封裝完整樣式邏輯,再將其作為復合控制項的 ControlStyle 型別。此法解決上述所有缺點,且支援視圖狀態自動管理、樣式合併(MergeWith)、複製(CopyFrom)等進階功能。
public class MyPanelStyle : Style
{
private string _backImageUrl;
private HorizontalAlign _horizontalAlign = HorizontalAlign.NotSet;
private bool _wrap = true;
public string BackImageUrl
{
get { return _backImageUrl; }
set { _backImageUrl = value; }
}
public HorizontalAlign HorizontalAlign
{
get { return _horizontalAlign; }
set { _horizontalAlign = value; }
}
public bool Wrap
{
get { return _wrap; }
set { _wrap = value; }
}
protected override void FillStyleAttributes(CssStyleCollection css, IUrlResolutionService urlResolver)
{
base.FillStyleAttributes(css, urlResolver);
if (!string.IsNullOrEmpty(_backImageUrl))
css["background-image"] = "url(" + urlResolver.ResolveClientUrl(_backImageUrl) + ")";
if (_horizontalAlign != HorizontalAlign.NotSet)
css["text-align"] = _horizontalAlign.ToString().ToLower();
if (!_wrap)
css["white-space"] = "nowrap";
}
public override void CopyFrom(Style s)
{
base.CopyFrom(s);
MyPanelStyle mps = s as MyPanelStyle;
if (mps != null)
{
_backImageUrl = mps._backImageUrl;
_horizontalAlign = mps._horizontalAlign;
_wrap = mps._wrap;
}
}
public override void MergeWith(Style s)
{
if (IsEmpty) { CopyFrom(s); return; }
MyPanelStyle mps = s as MyPanelStyle;
if (mps != null && !mps.IsEmpty)
{
if (!string.IsNullOrEmpty(mps._backImageUrl)) _backImageUrl = mps._backImageUrl;
if (mps._horizontalAlign != HorizontalAlign.NotSet) _horizontalAlign = mps._horizontalAlign;
_wrap = mps._wrap;
}
base.MergeWith(s);
}
public override void Reset()
{
_backImageUrl = null;
_horizontalAlign = HorizontalAlign.NotSet;
_wrap = true;
base.Reset();
}
}
[ToolboxData("<{0}:MyPanel runat=server></{0}:MyPanel>")]
public class MyPanel : CompositeControl
{
private MyPanelStyle _controlStyle;
public MyPanelStyle MyPanelStyle
{
get
{
if (_controlStyle == null) _controlStyle = new MyPanelStyle();
return _controlStyle;
}
}
protected override Style CreateControlStyle()
{
return new MyPanelStyle(ViewState);
}
protected override void CreateChildControls()
{
// 這裡可建立內部子控制項,並套用 MyPanelStyle 的屬性
}
protected override void Render(HtmlTextWriter writer)
{
// 將樣式套用至最外層容器
MyPanelStyle.AddAttributesToRender(writer, this);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
RenderContents(writer);
writer.RenderEndTag();
}
}
若復合控制項包含多個同類子控制項(如 HeaderStyle、RowStyle、AlternatingRowStyle),可參考 GridView 模式,定義多個 TableItemStyle 型別屬性,並在 CreateChildControls 或資料繫結階段套用。
[Designer(typeof(MyPanelDesigner))] 提供自訂設計器。ICompositeControlDesignerAccessor.RecreateChildControls 讓設計器即時反映屬性變更。| 方法 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| 屬性映射 | 少數簡單屬性 | 快速、程式碼少 | 擴充性差、無型別安全、難維護 |
| 型別化樣式 | 完整樣式封裝、多子控制項、需設計時支援 | 型別安全、支援視圖狀態、樣式合併/複製、設計時友善 | 初期程式碼較多 |
建議:專案初期若樣式需求簡單可用屬性映射快速原型;正式產品請採用型別化樣式,長期維護成本更低。