ASP.NET2.0服務器控件之類型化樣式屬性
上一篇文章對控件樣式屬性的基本概念進行了介紹,同時,還通過示例說明了重寫樣式屬性的方法。本文重點對類型化樣式屬性的創建方法進行講解。
實現類型化樣式屬性的方法
繼承自Style類的類稱為類型化樣式。Style類可以由控件開發人員來擴展,創建一個自定義類型化樣式,它重寫或者添加Style類的屬性。服務器控件也可以把自定義類型化樣式作為ControlStyle屬性的類型。例如,Table控件的ControlStyle屬性就是TableStyle類型,該類型是擴展的Style,添加了例如CellPadding、CellSpacing和GridLines屬性等。在初步了解類型化樣式屬性的基本概念之后,下面列舉了實現類型化樣式屬性的方法要點。
(1)創建一個派生自System.Web.UI.WebControls.Style的類;
(2)定義樣式將為控件提供的屬性。在Style的ViewState字典中保存該屬性;
(3)重寫CopyFrom和MergeWith方法,從定義的屬性中復制或者將定義的屬性和一個給定樣式的屬性合并;
(4)重寫Reset方法,刪除添加到ViewState中的屬性;
(5)重寫AddAttributesToRender方法,產生HTML和CSS特性,作為控件呈現過程的一部分。
實際上,創建類型化樣式屬性并不是一個簡單的過程。為此,下面我們將通過典型應用示例來說明創建的具體方法,以便讀者加深對于實現要點的理解。
典型應用
本節通過創建一個MyPanel控件以及相關聯的類型化樣式MyPanelStyle,來講解如何實現并使用自定義類型化樣式。就功能而言,MyPanel與ASP.NET 2.0內置的Panel控件是一致。開發人員可以通過把需要添加的控件嵌套在控件的標簽中,向Controls集合中添加控件。在可視化設計器中,把所需添加的控件拖放到Panel的設計界面上,就可以把控件添加到Controls集合中。然而,MyPanel并不是從Panel類繼承而來,而是自定義實現的結果,同時,該控件還提供了類型化樣式屬性MyPanelStyle,其中設置了3個樣式屬性:
(1)BackImageUrl,用于指定背景圖片的URL;
(2)HorizontalAlign,用于指定所添加內容的水平對其方式;
(3)Wrap,用于指定是否允許對所添加的內容換行。
下面列舉了示例效果圖。
如圖1所示,圖中顯示了一個MyPanel控件,其中包括一行文字,文字的背景圖像已經定義,并且文字處于居中位置。
下面列舉了實現自定義服務器控件的MyPanel.cs源代碼。
在分析之前,為了幫助讀者更好的閱讀以上源代碼,下面列舉了MyPanel類圖。
如上代碼所示,MyPanel繼承自WebControl基類,其中定義了3個屬性BackImageUrl、HorizontalAlign和Wrap。關于這3個屬性的說明,讀者可參考前面的內容。另外,MyPanel重寫了CreateControlStyle方法,返回一個MyPanelStyle對象。這樣返回的MyPanelStyle實例間接的賦值給ControlStyle屬性。這種實現方法的原因是由于ControlStyle屬性是只讀屬性,且它的訪問操作需要調用CreateControlStyle方法時間接進行設置。需要讀者注意的是,CreateControlStyle將MyPanel控件的ViewState傳遞給MyPanelStyle的構造函數。當在CreateControlStyle中為控件創建新樣式時,必須將控件的ViewState傳給Style構造函數,那么樣式對象則使用和控件相同的StateBag。
下面列舉了實現MyPanelStyle類的源代碼,它們來自MyPanelStyle.cs文件。
下面列舉了MyPanelStyle類圖。
可能部分讀者感覺MyPanelStyle類實現有些復雜,然而,還是有據可尋的。該類的實現嚴格按照前文所述的類型化樣式屬性創建方法來進行。
首先,MyPanelStyle類繼承了Style類,這是創建類型化樣式屬性的關鍵,接著定義了3個屬性BackImageUrl、HorizontalAlign和Wrap。這3個屬性支持MyPanel中對應的樣式屬性。然后,代碼重寫了AddAttributesToRender方法,以便當控件呈現時準確生成相關的HTML和CSS代碼。需要注意的是,該方法的第二個參數不可為空,其表示擁有Style對象的具體控件。最后,代碼中重寫了3個來自Style的方法,CopyFrom、MergeWith和Reset。重寫前兩個方法是為了復制給定的MyPanelStyle或者把給定的MyPanelStyle與自身合并。這兩個方法都調用了基類方法,然后執行自身的邏輯。重寫Reset方法主要目的是為了刪除添加到ViewState中的屬性,它的實現思路與前兩個方法差不多,都是調用基類方法,然后執行自身邏輯。
下面列舉了為測試MyPanel控件而創建的Default.aspx文件源代碼。
如上代碼所示,開發人員可以像使用Panel控件一樣,將需要添加的控件設置在MyPanel標簽中。這樣所設置的控件將自動的顯示出來,并且由于MyPanel控件自身的屬性設置,其顯示的外觀和樣式將發生相應變化。
小結
本文針對類型化樣式屬性的實現方法進行了介紹,并且通過一個典型示例加強了讀者對于實現方法的理解。在接下來的文章中,我們將繼續討論利用ASP.NET 2.0技術,實現控件客戶端功能的內容。
實現類型化樣式屬性的方法
繼承自Style類的類稱為類型化樣式。Style類可以由控件開發人員來擴展,創建一個自定義類型化樣式,它重寫或者添加Style類的屬性。服務器控件也可以把自定義類型化樣式作為ControlStyle屬性的類型。例如,Table控件的ControlStyle屬性就是TableStyle類型,該類型是擴展的Style,添加了例如CellPadding、CellSpacing和GridLines屬性等。在初步了解類型化樣式屬性的基本概念之后,下面列舉了實現類型化樣式屬性的方法要點。
(1)創建一個派生自System.Web.UI.WebControls.Style的類;
(2)定義樣式將為控件提供的屬性。在Style的ViewState字典中保存該屬性;
(3)重寫CopyFrom和MergeWith方法,從定義的屬性中復制或者將定義的屬性和一個給定樣式的屬性合并;
(4)重寫Reset方法,刪除添加到ViewState中的屬性;
(5)重寫AddAttributesToRender方法,產生HTML和CSS特性,作為控件呈現過程的一部分。
實際上,創建類型化樣式屬性并不是一個簡單的過程。為此,下面我們將通過典型應用示例來說明創建的具體方法,以便讀者加深對于實現要點的理解。
典型應用
本節通過創建一個MyPanel控件以及相關聯的類型化樣式MyPanelStyle,來講解如何實現并使用自定義類型化樣式。就功能而言,MyPanel與ASP.NET 2.0內置的Panel控件是一致。開發人員可以通過把需要添加的控件嵌套在控件的標簽中,向Controls集合中添加控件。在可視化設計器中,把所需添加的控件拖放到Panel的設計界面上,就可以把控件添加到Controls集合中。然而,MyPanel并不是從Panel類繼承而來,而是自定義實現的結果,同時,該控件還提供了類型化樣式屬性MyPanelStyle,其中設置了3個樣式屬性:
(1)BackImageUrl,用于指定背景圖片的URL;
(2)HorizontalAlign,用于指定所添加內容的水平對其方式;
(3)Wrap,用于指定是否允許對所添加的內容換行。
下面列舉了示例效果圖。
![]() 圖1 |
如圖1所示,圖中顯示了一個MyPanel控件,其中包括一行文字,文字的背景圖像已經定義,并且文字處于居中位置。
下面列舉了實現自定義服務器控件的MyPanel.cs源代碼。
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WebControlLibrary{ [ ParseChildren(false), PersistChildren(true) ] [ToolboxData("<{0}:MyPanel runat=server></{0}:MyPanel>")] public class MyPanel : WebControl { // 定義構造函數 public MyPanel() : base(HtmlTextWriterTag.Div) { } // 實現屬性BackImageUrl [Bindable(true)] [Category("Appearance")] [DefaultValue("")] public virtual string BackImageUrl { get { if (ControlStyleCreated) { return ((MyPanelStyle)ControlStyle).BackImageUrl; } return String.Empty; } set { ((MyPanelStyle)ControlStyle).BackImageUrl = value; } } // 實現屬性HorizontalAlign [Bindable(true)] [Category("Layout")] [DefaultValue("")] public virtual HorizontalAlign HorizontalAlign { get { if (ControlStyleCreated) { return ((MyPanelStyle)ControlStyle).HorizonalAlign; } return HorizontalAlign.NotSet; } set { ((MyPanelStyle)ControlStyle).HorizonalAlign = value; } } // 實現屬性Wrap [Bindable(true)] [Category("Layout")] [DefaultValue("")] public virtual bool Wrap { get { if (ControlStyleCreated) { return ((MyPanelStyle)ControlStyle).Wrap; } return true; } set { ((MyPanelStyle)ControlStyle).Wrap = value; } } protected override Style CreateControlStyle() { return new MyPanelStyle(ViewState); } } } |
在分析之前,為了幫助讀者更好的閱讀以上源代碼,下面列舉了MyPanel類圖。
![]() 圖2 |
如上代碼所示,MyPanel繼承自WebControl基類,其中定義了3個屬性BackImageUrl、HorizontalAlign和Wrap。關于這3個屬性的說明,讀者可參考前面的內容。另外,MyPanel重寫了CreateControlStyle方法,返回一個MyPanelStyle對象。這樣返回的MyPanelStyle實例間接的賦值給ControlStyle屬性。這種實現方法的原因是由于ControlStyle屬性是只讀屬性,且它的訪問操作需要調用CreateControlStyle方法時間接進行設置。需要讀者注意的是,CreateControlStyle將MyPanel控件的ViewState傳遞給MyPanelStyle的構造函數。當在CreateControlStyle中為控件創建新樣式時,必須將控件的ViewState傳給Style構造函數,那么樣式對象則使用和控件相同的StateBag。
下面列舉了實現MyPanelStyle類的源代碼,它們來自MyPanelStyle.cs文件。
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WebControlLibrary{ public class MyPanelStyle : Style { // 定義內部常量 internal const int PROP_BACKIMAGEURL = 1; internal const int PROP_HORIZONTALALIGN = 2; internal const int PROP_WRAP = 3; //構造函數一 public MyPanelStyle() { } // 構造函數二 public MyPanelStyle(StateBag bag) : base(bag) { } // 創建BackImageUrl屬性 [ Bindable(true), Category("Appearance"), DefaultValue(""), Description("背景圖片的URL"), NotifyParentProperty(true) ] public virtual string BackImageUrl { get { if (IsSet(PROP_BACKIMAGEURL)) { return (string)ViewState["BackImageUrl"]; } return String.Empty; } set { ViewState["BackImageUrl"] = value; } } // 實現HorizonalAlign屬性 [ Bindable(true), Category("Layout"), DefaultValue(HorizontalAlign.NotSet), Description("所添加內容的水平對其方式"), NotifyParentProperty(true) ] public virtual HorizontalAlign HorizonalAlign { get { if (IsSet(PROP_HORIZONTALALIGN)) { return (HorizontalAlign)ViewState["HorizontalAlign"]; } return HorizontalAlign.NotSet; } set { if (value < HorizontalAlign.NotSet || value > HorizontalAlign.Justify) { throw new ArgumentOutOfRangeException("value"); } ViewState["HorizontalAlign"] = value; } } // 實現IsEmpty protected new internal bool IsEmpty { get { return base.IsEmpty && !IsSet(PROP_BACKIMAGEURL) && !IsSet(PROP_HORIZONTALALIGN) && !IsSet(PROP_WRAP); } } //實現Wrap屬性 [ Bindable(true), Category("Layout"), DefaultValue(true), Description("是否允許對所添加的內容換行"), NotifyParentProperty(true) ] public virtual bool Wrap { get { if (IsSet(PROP_WRAP)) { return (bool)ViewState["Wrap"]; } return true; } set { ViewState["Wrap"] = value; } } //輔助方法IsSet internal bool IsSet(int propNumber) { string key = null; switch (propNumber) { case PROP_BACKIMAGEURL: key = "BackImageUrl"; break; case PROP_HORIZONTALALIGN: key = "HorizontalAlign"; break; case PROP_WRAP: key = "Wrap"; break; } if (key != null) { return ViewState[key] != null; } return false; } //重寫AddAttributesToRender方法 public override void AddAttributesToRender(HtmlTextWriter writer, WebControl owner) { if (IsSet(PROP_BACKIMAGEURL)) { string s = BackImageUrl; if (s.Length > 0) { if (owner != null) { s = owner.ResolveUrl(s); } writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url(" + s + ")"); } } if (IsSet(PROP_HORIZONTALALIGN)) { System.Web.UI.WebControls.HorizontalAlign hAlign = this.HorizonalAlign; if (hAlign != System.Web.UI.WebControls.HorizontalAlign.NotSet) { TypeConverter hac = TypeDescriptor.GetConverter(typeof(HorizontalAlign)); writer.AddAttribute(HtmlTextWriterAttribute.Align, hac.ConvertToInvariantString(hAlign)); } } if (IsSet(PROP_WRAP)) { bool wrap = Wrap; if (!Wrap) { writer.AddAttribute(HtmlTextWriterAttribute.Nowrap, "nowwrap"); } } base.AddAttributesToRender(writer, owner); } //重寫CopyFrom方法 public override void CopyFrom(Style s) { if (s != null) { base.CopyFrom(s); if (s is MyPanelStyle) { MyPanelStyle mps = (MyPanelStyle)s; if (!mps.IsEmpty) { if (mps.IsSet(PROP_BACKIMAGEURL)) this.BackImageUrl = mps.BackImageUrl; if (mps.IsSet(PROP_HORIZONTALALIGN)) this.HorizonalAlign = mps.HorizonalAlign; if (mps.IsSet(PROP_WRAP)) this.Wrap = mps.Wrap; } } } } // 重寫MergeWith方法 public override void MergeWith(Style s) { if (s != null) { if (IsEmpty) { CopyFrom(s); return; } base.MergeWith(s); if (s is MyPanelStyle) { MyPanelStyle mps = (MyPanelStyle)s; if (!mps.IsEmpty) { if (mps.IsSet(PROP_BACKIMAGEURL) && !this.IsSet(PROP_BACKIMAGEURL)) this.BackImageUrl = mps.BackImageUrl; if (mps.IsSet(PROP_HORIZONTALALIGN) && !this.IsSet(PROP_HORIZONTALALIGN)) this.HorizonalAlign = mps.HorizonalAlign; if (mps.IsSet(PROP_WRAP) && !this.IsSet(PROP_WRAP)) this.Wrap = mps.Wrap; } } } } //重寫Reset方法 public override void Reset() { base.Reset(); if (IsEmpty) return; if (IsSet(PROP_BACKIMAGEURL)) ViewState.Remove("BackImageUrl"); if (IsSet(PROP_HORIZONTALALIGN)) ViewState.Remove("HorizontalAlign"); if (IsSet(PROP_WRAP)) ViewState.Remove("Wrap"); } } } |
下面列舉了MyPanelStyle類圖。
![]() 圖3 |
可能部分讀者感覺MyPanelStyle類實現有些復雜,然而,還是有據可尋的。該類的實現嚴格按照前文所述的類型化樣式屬性創建方法來進行。
首先,MyPanelStyle類繼承了Style類,這是創建類型化樣式屬性的關鍵,接著定義了3個屬性BackImageUrl、HorizontalAlign和Wrap。這3個屬性支持MyPanel中對應的樣式屬性。然后,代碼重寫了AddAttributesToRender方法,以便當控件呈現時準確生成相關的HTML和CSS代碼。需要注意的是,該方法的第二個參數不可為空,其表示擁有Style對象的具體控件。最后,代碼中重寫了3個來自Style的方法,CopyFrom、MergeWith和Reset。重寫前兩個方法是為了復制給定的MyPanelStyle或者把給定的MyPanelStyle與自身合并。這兩個方法都調用了基類方法,然后執行自身的邏輯。重寫Reset方法主要目的是為了刪除添加到ViewState中的屬性,它的實現思路與前兩個方法差不多,都是調用基類方法,然后執行自身邏輯。
下面列舉了為測試MyPanel控件而創建的Default.aspx文件源代碼。
| <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ Register TagPrefix="wcl" Assembly="WebControlLibrary" Namespace="WebControlLibrary" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>類型化樣式屬性</title> </head> <body> <form id="form1" runat="server"> <wcl:MyPanel ID="demo1" runat="server" BackImageUrl="pic1.jpg" HorizontalAlign="Center" Height="145" Width="160"> <br /> <br /> 這是一行位于MyPanel控件中的文字。 </wcl:MyPanel> </form> </body> </html> |
如上代碼所示,開發人員可以像使用Panel控件一樣,將需要添加的控件設置在MyPanel標簽中。這樣所設置的控件將自動的顯示出來,并且由于MyPanel控件自身的屬性設置,其顯示的外觀和樣式將發生相應變化。
小結
本文針對類型化樣式屬性的實現方法進行了介紹,并且通過一個典型示例加強了讀者對于實現方法的理解。在接下來的文章中,我們將繼續討論利用ASP.NET 2.0技術,實現控件客戶端功能的內容。


