型別化樣式 是繼承自 System.Web.UI.WebControls.Style 的自訂類別,封裝一組相關的樣式屬性(如 BackImageUrl、HorizontalAlign、Wrap),並能自動參與視圖狀態管理、樣式合併、複製、CSS 渲染。控制項的 ControlStyle 屬性即為此型別,提供強型別、設計時智慧提示、屬性視窗分類顯示等優勢。
| 方法/屬性 | 用途 | 必要性 |
|---|---|---|
| 屬性定義 + ViewState 儲存 | 將樣式值存入 ViewState,跨回傳保持 | 必要 |
| FillStyleAttributes (舊版 AddAttributesToRender) | 輸出 CSS 樣式屬性至 HtmlTextWriter | 必要 |
| CopyFrom | 從另一 Style 物件完整複製屬性值 | 必要 |
| MergeWith | 合併另一 Style 的非預設值(樣式繼承鏈核心) | 必要 |
| Reset | 重設為預設狀態,清除 ViewState 項目 | 必要 |
public class MyPanelStyle : Style
{
public MyPanelStyle() : base() { }
public MyPanelStyle(StateBag bag) : base(bag) { }
private string _backImageUrl;
private HorizontalAlign _horizontalAlign = HorizontalAlign.NotSet;
private bool _wrap = true;
[DefaultValue(""), UrlProperty(), Description("背景圖片 URL")]
public string BackImageUrl
{
get { return _backImageUrl; }
set { _backImageUrl = value; }
}
[DefaultValue(HorizontalAlign.NotSet), Description("水平對齊")]
public HorizontalAlign HorizontalAlign
{
get { return _horizontalAlign; }
set { _horizontalAlign = value; }
}
[DefaultValue(true), Description("是否允許換行")]
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 : Panel
{
private MyPanelStyle _myPanelStyle;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty)]
public MyPanelStyle MyPanelStyle
{
get
{
if (_myPanelStyle == null)
{
_myPanelStyle = new MyPanelStyle();
if (IsTrackingViewState) ((IStateManager)_myPanelStyle).TrackViewState();
}
return _myPanelStyle;
}
}
protected override Style CreateControlStyle()
{
return new MyPanelStyle(ViewState);
}
protected override void Render(HtmlTextWriter writer)
{
// 將 MyPanelStyle 套用至外層 div
MyPanelStyle.AddAttributesToRender(writer, this);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
RenderContents(writer);
writer.RenderEndTag();
}
}
<cc:MyPanel ID="panel1" runat="server">
<MyPanelStyle BackImageUrl="~/images/bg.png" HorizontalAlign="Center" Wrap="false" />
<asp:Label runat="server" Text="標題置中、背景圖、不換行" />
</cc:MyPanel>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]:讓屬性視窗展開子屬性編輯。[NotifyParentProperty(true)]:子屬性變更通知父控制項標記為 Dirty,觸發 ViewState 儲存。[PersistenceMode(PersistenceMode.InnerProperty)]:支援標記內嵌 `[Category("Appearance")]、[Description("...")]、[DefaultValue(...)] 豐富屬性視窗資訊。定義多個 TableItemStyle 型別屬性,各自管理不同 UI 區塊樣式:
[PersistenceMode(PersistenceMode.InnerProperty)]
public TableItemStyle HeaderStyle { get { ... } }
[PersistenceMode(PersistenceMode.InnerProperty)]
public TableItemStyle RowStyle { get { ... } }
[PersistenceMode(PersistenceMode.InnerProperty)]
public TableItemStyle AlternatingRowStyle { get { ... } }
渲染時依資料列索引套用對應 Style。
| 錯誤 | 原因 | 修正 |
|---|---|---|
| 樣式不生效、不輸出 CSS | 未呼叫 AddAttributesToRender、或 FillStyleAttributes 未正確輸出 | 確保 Render 方法呼叫 Style.AddAttributesToRender(writer, this) |
| ViewState 不保存、回傳後樣式消失 | 屬性未存入 ViewState、或未 TrackViewState | 屬性 setter 呼叫 ViewState["Prop"] = value;建構子傳入 ViewState |
| 設計時屬性視窗無法展開 | 缺少 DesignerSerializationVisibility.Content | 加上該屬性於樣式屬性定義上 |
| MergeWith 行為異常 | 未正確判斷 IsEmpty、未呼叫 base.MergeWith | 遵循標準模式:IsEmpty 檢查 → 型別轉換 → 合併非預設值 → base.MergeWith |
型別化樣式是 ASP.NET 2.0 控制項外觀架構的核心。掌握 Style 繼承、ViewState 整合、FillStyleAttributes CSS 輸出、CopyFrom/MergeWith/Reset 樣式鏈操作,並配合設計時屬性,即可打造專業級、可視化編輯、強型別安全的控制項樣式系統。