首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET 伺服器控件開發基礎 (12):複合控件樣式屬性完整實作與視圖狀態管理

     編輯:關於ASP.NET

前言:複合控件樣式管理的挑戰

複合控件 (如 GridView, Login, Wizard) 包含多個子控件,每個子控件可能需要獨立樣式。若將所有樣式屬性平鋪為頂層屬性,屬性視窗會極度混亂。ASP.NET 採用 Style 物件 + 內屬性持久化 模式,將樣式封裝為子物件,透過 DesignerSerializationVisibility.Content 實現設計時編輯與視圖狀態自動持久化。

Style 類別核心機制


public class Style : Component, IStateManager
{
    // 核心樣式屬性
    public Color BackColor { get; set; }
    public Color ForeColor { get; set; }
    public FontInfo Font { get; }
    public Unit Height { get; set; }
    public Unit Width { get; set; }
    public string CssClass { get; set; }
    // ... BorderColor, BorderWidth, BorderStyle 等
    
    // 視圖狀態管理 (IStateManager)
    private StateBag _viewState;
    public StateBag ViewState => _viewState ??= new StateBag();
    
    public virtual void LoadViewState(object savedState) { ... }
    public virtual object SaveViewState() { ... }
    public virtual void TrackViewState() { ... }
    
    // 套用至 WebControl
    public virtual void ApplyTo(WebControl control) { ... }
    public virtual void CopyFrom(Style s) { ... }
    public virtual void MergeWith(Style s) { ... }
}

複合控件樣式屬性標準實作模式

1. 定義樣式屬性 (以登入控件為例)


public class LoginCompositeControl : CompositeControl
{
    // 樣式欄位
    private Style _textBoxStyle;
    private Style _buttonStyle;
    private Style _labelStyle;
    private Style _validatorStyle;
    private Style _containerStyle;
    
    // 1. TextBox 樣式
    [Category("Styles")]
    [DefaultValue(null)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [Description("輸入框樣式")]
    public Style TextBoxStyle
    {
        get
        {
            if (_textBoxStyle == null)
            {
                _textBoxStyle = new Style();
                if (IsTrackingViewState)
                    ((IStateManager)_textBoxStyle).TrackViewState();
            }
            return _textBoxStyle;
        }
    }
    
    // 2. Button 樣式
    [Category("Styles")]
    [DefaultValue(null)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [Description("按鈕樣式")]
    public Style ButtonStyle
    {
        get
        {
            if (_buttonStyle == null)
            {
                _buttonStyle = new Style();
                if (IsTrackingViewState)
                    ((IStateManager)_buttonStyle).TrackViewState();
            }
            return _buttonStyle;
        }
    }
    
    // 3. Label 樣式
    [Category("Styles")]
    [DefaultValue(null)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [Description("訊息標籤樣式")]
    public Style LabelStyle
    {
        get
        {
            if (_labelStyle == null)
            {
                _labelStyle = new Style();
                if (IsTrackingViewState)
                    ((IStateManager)_labelStyle).TrackViewState();
            }
            return _labelStyle;
        }
    }
    
    // 4. 驗證器樣式
    [Category("Styles")]
    [DefaultValue(null)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [Description("驗證錯誤訊息樣式")]
    public Style ValidatorStyle
    {
        get
        {
            if (_validatorStyle == null)
            {
                _validatorStyle = new Style();
                if (IsTrackingViewState)
                    ((IStateManager)_validatorStyle).TrackViewState();
            }
            return _validatorStyle;
        }
    }
    
    // 5. 容器樣式 (整體外框)
    [Category("Styles")]
    [DefaultValue(null)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [Description("整體容器樣式")]
    public Style ContainerStyle
    {
        get
        {
            if (_containerStyle == null)
            {
                _containerStyle = new Style();
                if (IsTrackingViewState)
                    ((IStateManager)_containerStyle).TrackViewState();
            }
            return _containerStyle;
        }
    }
}

2. 在 CreateChildControls 套用樣式


protected override void CreateChildControls()
{
    Controls.Clear();
    
    _txtUsername = new TextBox { ID = "txtUsername" };
    _txtPassword = new TextBox { ID = "txtPassword", TextMode = TextBoxMode.Password };
    _btnLogin = new Button { ID = "btnLogin", Text = LoginButtonText };
    _lblMessage = new Label { ID = "lblMessage" };
    _valUsername = new RequiredFieldValidator { ID = "valUsername", ControlToValidate = "txtUsername" };
    _valPassword = new RequiredFieldValidator { ID = "valPassword", ControlToValidate = "txtPassword" };
    
    // 套用樣式 (複製非空屬性)
    TextBoxStyle.ApplyTo(_txtUsername);
    TextBoxStyle.ApplyTo(_txtPassword);
    ButtonStyle.ApplyTo(_btnLogin);
    LabelStyle.ApplyTo(_lblMessage);
    ValidatorStyle.ApplyTo(_valUsername);
    ValidatorStyle.ApplyTo(_valPassword);
    
    // 容器樣式套用至最外層 Table
    Table layout = new Table();
    ContainerStyle.ApplyTo(layout);
    
    // ... 建立佈局結構 ...
    Controls.Add(layout);
    ChildControlsCreated = true;
}

3. 頁面宣告式使用



    
    
    
    
    

GridView 風格進階樣式架構

GridView 定義多種項目類型樣式,對應不同渲染狀態:

GridView 樣式屬性

public class GridView : CompositeDataBoundControl
{
    // 項目類型樣式
    public TableItemStyle RowStyle { get; }
    public TableItemStyle AlternatingRowStyle { get; }
    public TableItemStyle HeaderStyle { get; }
    public TableItemStyle FooterStyle { get; }
    public TableItemStyle EditRowStyle { get; }
    public TableItemStyle SelectedRowStyle { get; }
    public TableItemStyle PagerStyle { get; }
    public TableItemStyle EmptyDataRowStyle { get; }
    
    // 欄位樣式
    public DataControlFieldHeaderStyle HeaderStyle { get; }
    // ...
}

渲染時動態套用樣式


protected override void RenderContents(HtmlTextWriter writer)
{
    // 為每一列決定樣式
    for (int i = 0; i < Rows.Count; i++)
    {
        GridViewRow row = Rows[i];
        Style rowStyle = GetRowStyle(row);
        rowStyle.ApplyTo(row);
        row.RenderControl(writer);
    }
}

private Style GetRowStyle(GridViewRow row)
{
    switch (row.RowType)
    {
        case DataControlRowType.Header: return HeaderStyle;
        case DataControlRowType.Footer: return FooterStyle;
        case DataControlRowType.Pager: return PagerStyle;
        case DataControlRowType.DataRow:
            if ((row.RowState & DataControlRowState.Edit) != 0)
                return EditRowStyle;
            if ((row.RowState & DataControlRowState.Selected) != 0)
                return SelectedRowStyle;
            if ((row.RowState & DataControlRowState.Alternate) != 0)
                return AlternatingRowStyle;
            return RowStyle;
        default: return new Style();
    }
}

視圖狀態管理深度解析

Style 物件自動追蹤機制


// 在屬性 getter 中自動啟動追蹤
public Style TextBoxStyle
{
    get
    {
        if (_textBoxStyle == null)
        {
            _textBoxStyle = new Style();
            // 關鍵:若父控件已進入追蹤狀態,子 Style 必須同步追蹤
            if (IsTrackingViewState)
                ((IStateManager)_textBoxStyle).TrackViewState();
        }
        return _textBoxStyle;
    }
}

父控件視圖狀態覆寫


protected override object SaveViewState()
{
    object baseState = base.SaveViewState();
    object textBoxStyleState = _textBoxStyle != null ? ((IStateManager)_textBoxStyle).SaveViewState() : null;
    object buttonStyleState = _buttonStyle != null ? ((IStateManager)_buttonStyle).SaveViewState() : null;
    object labelStyleState = _labelStyle != null ? ((IStateManager)_labelStyle).SaveViewState() : null;
    object validatorStyleState = _validatorStyle != null ? ((IStateManager)_validatorStyle).SaveViewState() : null;
    object containerStyleState = _containerStyle != null ? ((IStateManager)_containerStyle).SaveViewState() : null;
    
    return new object[] { baseState, textBoxStyleState, buttonStyleState, labelStyleState, validatorStyleState, containerStyleState };
}

protected override void LoadViewState(object savedState)
{
    if (savedState == null) return;
    
    object[] states = (object[])savedState;
    base.LoadViewState(states[0]);
    
    if (states[1] != null) ((IStateManager)TextBoxStyle).LoadViewState(states[1]);
    if (states[2] != null) ((IStateManager)ButtonStyle).LoadViewState(states[2]);
    if (states[3] != null) ((IStateManager)LabelStyle).LoadViewState(states[3]);
    if (states[4] != null) ((IStateManager)ValidatorStyle).LoadViewState(states[4]);
    if (states[5] != null) ((IStateManager)ContainerStyle).LoadViewState(states[5]);
}

樣式繼承與層疊規則

層級優先序說明
內建預設樣式最低控件預設外觀
ControlStyle (Control.Style)低控件層級基本樣式
CssClass中CSS 類別樣式
Style 物件屬性 (BackColor 等)高Style 物件直接設定屬性
內嵌 style 屬性最高渲染時直接輸出 style="..."

MergeWith 實現層疊合併


// 合併多個 Style 來源
Style finalStyle = new Style();
finalStyle.MergeWith(ControlStyle);        // 基礎控件樣式
finalStyle.MergeWith(RowStyle);            // 列類型樣式
finalStyle.MergeWith(itemStyle);           // 項目特定樣式
finalStyle.ApplyTo(targetControl);         // 一次套用

設計時支援:樣式編輯器


[Editor(typeof(StyleEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Style TextBoxStyle { get; ... }

VS 內建 StyleEditor 提供完整的樣式屬性網格編輯介面。

總結

複合控件樣式管理核心在於:

  1. Style 物件封裝:將相關樣式屬性群組為邏輯單元
  2. 內屬性持久化:DesignerSerializationVisibility.Content + PersistenceMode.InnerProperty
  3. 自動視圖狀態追蹤:IsTrackingViewState 同步啟動子物件追蹤
  4. 渲染時動態套用:ApplyTo/MergeWith 實現層疊規則
  5. 設計時編輯器支援:StyleEditor 提供專業編輯體驗

此架構不僅解決屬性管理混亂,更提供了企業級的樣式客製化與維護能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved