ASP.NET2.0服务器控件之类型化样式属性

    技术2022-05-11  72

    文章对控件样式属性的基本概念进行了介绍,同时,还通过示例说明了重写样式属性的方法。本文重点对类型化样式属性的创建方法进行讲解。    实现类型化样式属性的方法   继承自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技术,实现控件客户端功能的内容。

    最新回复(0)