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

asp.net|服务器|控件

  上一篇文章对控件样式属性的基本概念进行了介绍,同时,还通过示例说明了重写样式属性的方法。本文重点对类型化样式属性的创建方法进行讲解。

  实现类型化样式属性的方法

  继承自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技术,实现控件客户端功能的内容。

时间: 2024-11-01 05:29:25

ASP.NET2.0服务器控件之类型化样式属性的相关文章

ASP.NET2.0服务器控件之创建复杂属性

asp.net|创建|服务器|控件 上一篇文章介绍了实现3种形式复杂属性的具体方法.为了加深读者对于这些实现方法的理解深度,本文详细讲解了一个利用ASP.NET 2.0技术创建复杂属性的示例. 1. 示例应用 本文所实现的示例很简单,其核心是通过实现自定义服务器控件的连字符形式复杂属性来说明复杂属性的实现方法,其中使用了上一篇文章中介绍的内容.示例效果图如图1所示. 图1 如图1所示,页面显示了公司所在城市.姓名.性别和职务信息.这些内容是定义服务器控件呈现的结果,其中公司所在城市由简单属性Ci

ASP.NET2.0服务器控件之创建自定义控件

asp.net|创建|服务器|控件 摘要 本文将详细讲解一个简单的自定义服务器控件的创建过程.通过这些内容,读者将了解利用Visual Studio 2005,创建和测试自定义服务器控件的基本方法. 注意:本文内容基于ASP.NET 2.0技术撰写,示例应用程序使用Visual Studio 2005进行开发. 创建一个简单的自定义服务器控件 创建自定义服务器控件的过程包括: (1)创建一个测试用Web站点应用程序: (2)为新建站点应用程序,新增一个Web控件库项目: (3)编写.编译和测试自

ASP.NET2.0服务器控件之Render方法

asp.net|服务器|控件 控件呈现是指向HTTP输出流中写入标记文本的过程.服务器通过HTTP输出流向客户端发送生成的标记文本,这些文本将会通过客户端浏览器转换为可视化的元素显示出来.使用控件呈现,开发人员可以将HTML标记.脚本代码.CSS样式表等等输入到客户端浏览器.实现服务器控件呈现主要有两种方式:一是Control类的Render方法,二是WebControl的RenderContents方法.本文重点介绍使用Control类的Render方法实现控件呈现的应用. 使用HtmlTex

ASP.NET2.0服务器控件之自定义状态管理

asp.net|服务器|控件 在前面的系列文章中,我们曾经介绍了视图状态和控件状态的基本概念和典型应用,从中可以发现,视图状态和控件状态对于自定义服务器控件实现的重要性.本文将继续这一主题,重点介绍实现视图状态和控件状态自定义管理的方法. 自定义视图状态管理 在介绍视图状态时,我们曾经提到过:对于简单属性,例如,String.Int等类型,.NET执行引擎将自动启用默认视图状态管理机制,以便完成相应的功能.然而,如果开发人员在ViewState中保存的是自定义数据类型,或者需要实现自定义方式优化

ASP.NET2.0服务器控件之捕获回传事件

asp.net|服务器|控件 1. 实现捕获回传事件       如果服务器控件需要捕获来自客户端的回传事件,并想为该回传事件自定义服务器端事件处理逻辑,那么控件必须实现System.Web.UI.IPostBackEventHandler接口.下面列举了该接口定义.      public interface IPostBackEventHandler   {    void RaisePostBackEvent(string eventArgument);   }       如上代码所示,

ASP.NET2.0服务器控件开发之实现事件

asp.net|服务器|控件|控件开发 前面几篇文章讲解了与利用ASP.NET 2.0技术创建自定义服务器控件属性有关的内容.从本文开始,包括随后的几篇文章将探讨创建自定义服务器控件事件的方法.本文重点对实现控件事件的基本概念进行介绍,这些概念对于帮助开发人员为服务器控件创建事件有着重要意义. 1. 事件基本概念 事件是当有动作发生或者状态改变时,类发出的信息或者通知.通常情况下,状态的发生或者改变由用户界面动作初始化,例如,单击按钮,或者由于其他的程序逻辑引起.产生事件的类或者说发送通知的类叫

ASP.NET2.0服务器控件之类型转换器_实用技巧

类型转换器是实现自定义服务器控件属性过程中比较重要的内容.本文将对类型转换器的基本概念和实现方法进行介绍. 1. 类型转换器基本概念 类型转换器是自定义服务器控件的辅助性功能实现.它主要用于执行从字符串表示形式到指定类型之间的双向转换.例如,以文本形式表示属性值,将用户输入的文本转换为相应数据类型等等,都应用了类型转换器. 对于多数基本数据类型(如Int32.Bool.Char.String.枚举类型等),.net框架已经为它们提供了默认的类型转换器,这些类型转换器完成从字符串到相关值的转换并执

一起谈.NET技术,ASP.NET2.0服务器控件之类型转换器

      类型转换器是实现自定义服务器控件属性过程中比较重要的内容.本文将对类型转换器的基本概念和实现方法进行介绍. 1. 类型转换器基本概念 类型转换器是自定义服务器控件的辅助性功能实现.它主要用于执行从字符串表示形式到指定类型之间的双向转换.例如,以文本形式表示属性值,将用户输入的文本转换为相应数据类型等等,都应用了类型转换器. 对于多数基本数据类型(如Int32.Bool.Char.String.枚举类型等),.NET框架已经为它们提供了默认的类型转换器,这些类型转换器完成从字符串到相关

ASP.NET2.0服务器控件之自定义状态管理_实用技巧

     在前面的系列文章中,我们曾经介绍了视图状态和控件状态的基本概念和典型应用,从中可以发现,视图状态和控件状态对于自定义服务器控件实现的重要性.本文将继续这一主题,重点介绍实现视图状态和控件状态自定义管理的方法. 自定义视图状态管理 在介绍视图状态时,我们曾经提到过:对于简单属性,例如,String.Int等类型,.NET执行引擎将自动启用默认视图状态管理机制,以便完成相应的功能.然而,如果开发人员在ViewState中保存的是自定义数据类型,或者需要实现自定义方式优化视图状态管理时,则必