實作類別型化樣式屬性的方法
繼承自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技術,實現控制項用戶端功能的內容。