關於自定義WEB服務器控件的知識與實例

關於自定義WEB服務器控件的知識與實例  

 

本文重點介紹一種實現控件呈現的經常使用方法--使用WebControl類的RenderContents方法實現控件呈現。

   基礎知識

    就服務器控件而言只存在兩種狀況:一種是具備外觀可視化元素的控件,還有一種是不具備外觀可視化元素的控件。若是須要開發的服務器控件包含可視化元素, 那麼多數狀況下,建議開發人員建立繼承自System.Web.UI.WebControls.WebControl基類的控件類。這種作法的主要緣由是 基於便捷性考慮。由於,WebControl類可提供服務器控件的部分與外觀有關的公共屬性、方法和事件等。經過該類定義的屬性,能夠控制服務器控件的外 觀和行爲。例如,使用BackColor和ForeColor屬性,能夠分別控制服務器控件的背景色和前景顏色;在能夠顯示邊框的控件上,能夠經過設置 BorderWidth、BorderStyle和BorderColor屬性,控制邊框寬度、邊框樣式和邊框顏色;服務器控件的大小能夠經過 Height和Width屬性來指定等等。若是控件基類是Control類,那麼實現這些相似內容則很是繁瑣。

   在使用WebControl基類實現控件呈現的過程當中,必然要使用該類所提供的屬性和方法等成員對象。這是讀者須要重點掌握的內容。另外,對於該基類的構造函數也是不容忽視的。下面首先從WebControl的構造函數開始入手進行講解,隨後將說明常見的成員對象。

   WebControl類包括三個構造函數,它們都用於初始化WebControl類的新實例,然而它們之間還存在一些細小的差別。

   (1)protected WebControl ()

   該構造函數用於初始化表示Span HTML元素的WebControl類的新實例。一般狀況下,開發人員並不直接調用此構造函數。相反,它一般由派生類的構造函數調用以將TagKey屬性初始化爲Span枚舉值。在隨後的示例中,將重寫TagKey屬性,從而調用此構造函數。

 (2)public WebControl (HtmlTextWriterTag tag)

   開發人員可以使用此構造函數建立並初始化使用指定的System.Web.UI.HtmlTextWriterTag值的WebControl類的新實例。其中的參數tag表示HtmlTextWriterTag枚舉值之一。可能讀者對於HtmlTextWriterTag還不太熟悉。它是一個枚舉類型,其枚舉值多爲HTML標記,例如,A、B、Bold、Button等等。

   (3)protected WebControl (string tag)

   使用此構造函數可建立並初始化使用指定的HTML標記的WebControl類的新實例。其中參數tag表示HTML標記。當使用該構造函數時必定要注意:不能直接調用此構造函數。相反,它一般由派生類的構造函數調用以初始化TagKey和TagName屬性

   在瞭解了WebControl類的構造函數以後,讀者還必須瞭解WebControl類的一些經常使用屬性和方法。下面列舉了這些經常使用成員對象,它們對於實現控件呈現有着重要意義。

   (1)Attributes屬性

   該屬性用於獲取與控件的屬性不對應的任意特性(只用於呈現)的集合,其屬性類型爲AttributeCollection。

   (2)ControlStyle屬性

   該屬性用於獲取服務器控件的樣式,它是Style類型。ControlStyle屬性封裝WebControl類的全部外觀屬性,如BorderColor和Font。

   (3)TagKey屬性

    該屬性用於獲取與此服務器控件相對應的System.Web.UI.HtmlTextWriterTag值,其屬性類型爲HtmlTextWriterTag枚舉。

   (4)protected virtual void AddAttributeToRender(HtmlTextWriter writer);

    該方法將須要呈現的HTML屬性和樣式添加到指定的System.Web.UI.HtmlTextWriter中。注意在重寫過程當中,必定要調用基類中相應的方法

   (5)public void ApplyStyle(Style s);

         該方法將指定樣式的全部非空白元素複製到控件,改寫控件的全部現有的樣式元素。

   (6)public void MergeStyle(Style s);

    該方法將指定樣式的全部非空白元素複製到控件,但不改寫該控件現有的任何樣式元素。

   (7)protected override void Render(HtmlTextWriter writer);方法

    該方法重寫了Control.Render。

   (8)protected virtual void RenderContents(HtmlTextWriter writer);

   該方法將控件的內容呈現到指定的編寫器中。若是要在控件的標籤中寫入文本或其餘內容,則須要重寫該方法;若是要使用默認邏輯來呈現子控件,那麼必定要調用基類中相應的方法。

    可能讀者已經注意到WebControl基類中包括的兩個方法:Render和RenderContents。根據上文所介紹的內容可 知,Control基類中包括Render方法。因爲WebControl類繼承自Control類,所以,WebControl類中包含Render方 法是無可非議的。然而,WebControl類中卻有一個RenderContents方法,而且該方法與Render方法在功能、參數等方面都很是相 似。那麼在呈現控件過程當中到底應該使用哪個呢?

   實際上,在一般狀況下,若是服務器控件自WebControl基類派生,那麼其中的Render方法不多使用,而主要使用RenderContents方法實現控件呈現。爲了說明其中的緣由,咱們必須瞭解WebControl基類中Render方法的實現邏輯。

   在WebControl基類中的Render方法的實現示意性代碼以下所示:

 protected override void Render(HtmlTextWriter output)
 {
  RenderBeginTag(output);
  RenderContents(output);
  RenderEndTag(output);
 }

   在WebControl基類中的RenderBeginTag方法的實現示意性代碼以下:

 public virtual void RenderBeginTag(HtmlTextWriter output)
 {
  AddAttributesToRender(output);              //將要呈現的控件的屬性添加到HtmlTextWriter對象(流)中
  HtmlTextWriterTag tagKey = TagKey;
  if(tagKey != HtmlTextWriterTag.Unknown)
  {
 output.RenderBeginTag(tagKey);
  } else {
 output.RenderBeginTag(this.TagName);
  }
 }


   在WebControl基類中的RenderContents方法的實現示意性代碼以下:

 protected override void RenderContents(HtmlTextWriter output){
  //使用默認邏輯來呈現子控件,那麼必定要調用基類中的方法。
  base.Render(output);
 }
   分析以上代碼能夠得出如下結論:

   1、爲了在由WebControl派生的類中實現控件呈現,必須重寫AddAttributesToRender、RenderBeginTag、RenderEndTag、RenderContents等方法中的一個或者多個,而沒必要重寫Render方法

    2、重寫AddAttributesToRender、RenderBeginTag、RenderEndTag、RenderContents等方法 很是重要(請注意重寫這些方法的條件及注意事項),不然服務器控件可能會出現丟失標籤的狀況,這將嚴重影響服務器控件的呈現。

   3、當呈現服務器控件標籤中的內容時,必須重寫RenderContents方法

   上文介紹了WebControl類的一些基本知識。尤爲是對於上文所列舉的示意性代碼須要重點理解。這對於實現控件呈現有着重要做用。

   應用示例

    相信讀者在瀏覽各個網站時,常常會看到"聯繫咱們"等相似文字。當單擊這些文字時,操做系統將自動打開自身所帶的郵件客戶端軟件,提示用戶發送郵件。本 例將實現一個自定義服務器控件RenderContentsControl,用於經過呈現包含"mailto:郵件地址"的超連接。超連接文字是"個人郵 箱地址"。當單擊該連接,系統將發送郵件給"my@mysample.com"。

   在實現以上控件以前,首先應分析以下:本控件包含外觀元素,例如,文字大小、顏色、是否粗體等。爲此,控件基類不該從Control類繼承,而應從WebControl類繼承。這樣,開發人員將沒必要自行實現這些外觀樣式屬性等內容。

   下面列舉了實現自定義服務器控件的RenderContentsControl.cs文件源代碼。

 using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CustomControlTest2.App_Code
{
    [
       AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal),
       AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
        DefaultProperty("Email"),
        ParseChildren(true, "Text"),
       ToolboxData("<{0}:RenderContentsControl runat=server></{0}:RenderContentsControl>")
    ]
    public class RenderContentsControl : WebControl
    {
        // 實現Email屬性
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("")]
        [Localizable(true)]
        public string Email
        {
            get
            {
                String s = (String)ViewState["Email"];
                return ((s == null) ? String.Empty : s);
            }
            set
            {
                ViewState["Email"] = value;
            }
        }
        // 實現Text屬性
        [
         Bindable(true), Category("Appearance"), DefaultValue(""), Localizable(true),
         PersistenceMode(PersistenceMode.InnerDefaultProperty)
        ]
        public virtual string Text
        {
            get
            {
                string s = (string)ViewState["Text"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["Text"] = value;
            }
        }
        // 重寫TagKey屬性
        protected override HtmlTextWriterTag TagKey
        {
            get
            {
                return HtmlTextWriterTag.A;
            }
        }
        // 重寫AddAttributesToRender方法
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);
            writer.AddAttribute(HtmlTextWriterAttribute.Href, "mailto:" + Email);
        }
        // 重寫RenderContents方法
        protected override void RenderContents(HtmlTextWriter writer)
        {
            if (Text == String.Empty)
            {
                Text = Email;
            }
            writer.WriteEncodedText(Text);
        }
    }
}


 如 上代碼所示,RenderContentsControl類繼承自WebControl基類,其緣由在前文已經說明。另外,在 RenderContentsControl類的實現過程當中,還包括了元數據屬性標記、3個屬性(Email、Text和TagKey)和2兩個方法 (AddAttributesToRender和RenderContents)實現等內容。下面逐一對這些內容進行分析。

   代碼說明之3個屬性:

   在上文代碼中主要包括了3個屬性:Email、Text和TagKey。Email屬性用於獲取或者設置具體的電子郵件地址,Text屬性用於獲取或者設置控件顯示的文本內容。在這兩個屬性實現中,都使用了控件視圖狀態ViewState。服務器控件的視圖狀態爲其全部屬性值的累計。對於簡單屬性的實現,將常用ViewState。TagKey屬性是重寫屬性,其繼承自WebControl基類,這是讀者須要理解的重點內容。重寫TagKey屬性主要是爲了呈現HTML標記中的a元素,這樣就不會呈現WebControl類所默認呈現的span元素此處,也暗示了本控件使用的構造函數是繼承自WebControl的protected WebControl (),讀者可返回上文再看看有關這個構造函數的說明。須要讀者牢記的是:若是要呈現的元素是HtmlTextWriterTag枚舉的成員,則應重寫TagKey屬性。 許多常見的HTML元素標記被映射爲HtmlTextWriterTag枚舉的值。例 如,System.Web.UI.HtmlTextWriterTag.A與a元素對應,而 System.Web.UI.HtmlTextWriterTag.Table與table元素對應。若是要呈現的元素不是由HtmlTextWriterTag枚舉的成員表示,那麼建議重寫TagName屬性,並返回要做爲元素呈現的字符串。

   代碼說明之2個方法:

   在RenderContentsControl服務器控件中重寫了2個重要方法:一個是AddAttributesToRender、另外一個是RenderContents。

   (1)AddAttributesToRender

 該方法用於爲控件添加一個Href屬性,並將該屬性值設置爲"mailto:Email",其中Email是上文所述的表示郵件地址的屬性。當重寫AddAttributesToRender方法時,應始終按照控件源代碼演示的方式:首先,調用基類方法,而後進行相關設置。這樣才能實現爲服務器控件添加樣式和其餘屬性的功能。實際上,根據前文所述的RenderBeginTag方法的實現示意性代碼可知,AddAttributesToRender方法是由WebControl的RenderBeginTag方法調用。

   (2)RenderContents

   該方法是本示例的核心內容,其用於在控件的標記中寫入由Text屬性指定的超連接文本。如代碼所示,服務器控件調用了HtmlTextWriter實例的WriteEncodedText方法,以對開發人員輸入的文本進行HTML編碼。通常狀況下,爲了安全起見,應該對用戶提供的文本進行HTML編碼。

   代碼說明之元數據屬性標記:

   元數據屬性標記在類前和屬性實現前都有所應用。有關這些元數據屬性標記的說明,在之前的文章中已經進行了具體說明。讀者可查閱有關講解如何建立一個簡單的服務器控件的文章。下面重點說明一下有關內部文本持久性的問題

   在類前的元數據屬性標記中,設置了ParseChildren(true, "Text")。在Text屬性前的元數據屬性標記中,設置了PersistenceMode(PersistenceMode.InnerDefaultProperty)。經過以上兩個設置,則爲控件添加了內部文本持久性設置。如此設置,可以使得開發人員可以在控件標記代碼內設置Text屬性。例如:

 <Sample:RenderContentsControl runat="server" ID="CustomerControl" Email="my@mysample.com">個人郵箱地址</Sample:RenderContentsControl>



   在上面的代碼中,原來文本"個人郵箱地址"應由Text屬性設置(這就是默認持久性),然而,因爲內部文本持久性設置,所以,能夠在控件標記內設置控件文本。



 實現內部持久性,應使用ParseChildren(true, "Text")來標記RenderContentsControl控件。ParseChildren的第一個參數true,其用於指定頁分析器應將控件標記內的內容分析爲屬性,而不是子控件。第二個參數提供控件的內部默認屬性名稱爲Text。使用這兩個參數調用ParseChildren構造函數時,控件標記內的內容必須與內部默認屬性對應。Text屬性的PersistenceMode(PersistenceMode.InnerDefaultProperty),用於指定可視化設計器應將此屬性做爲控件標記中的內部內容進行序列化。

   一般,WebControl類使用PersistChildren(false)和ParseChildren(true)控制設計時和分析時屬性的持久性。這兩個屬性將被控件繼承,且僅在要更改繼承的設置時須要應用。PersistChildren告知設計器是否應將服務器控件的子控件做爲嵌套的內部控件保存false參數指示內部內容與屬性對應,而不是與子控件對應。ParseChildren已在上一段中加以說明。若是WebControl類的設計時和分析時持久性適用於您的控件,則沒必要重寫從WebControl繼承的PersistChildren和ParseChildren。

   下面列舉了用於測試服務器控件的Default.aspx文件源代碼:

 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
 <%@ Register TagPrefix="Sample" Assembly="UsingRenderContentsControl" Namespace="UsingRenderContentsControl" %>
 <!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 id="Head1" runat="server">
 <title>使用RenderContents方法實現控件呈現</title>
 </head>
 <body>
 <form id="form1" runat="server">
 <div>
 <Sample:RenderContentsControl runat="server" ID="CustomerControl" Font-Bold="true" Font-Size="small" ForeColor="Blue" Email="my@mysample.com">個人郵箱地址</Sample:RenderContentsControl>
 </div>
 </form>
 </body>
 </html>



 如 上粗體代碼所示,RenderContentsControl控件中設置了Font-Bold、Font-Size、ForeColor、Email等屬 性,同時,還在控件標記之間設置了文本內容。固然,若是開發人員將Text屬性值設置爲相同的文本內容也是能夠的。以上代碼比較簡單,在此再也不說明了。

    經過這個示例,咱們須要重點掌握RenderContents和AddAttributesToRender方法,以及TagKey屬性的使用。提請讀 者注意的是雖然服務器控件的代碼比較複雜,可是結構很簡單。切不可被複雜的樣式設置和客戶端行爲代碼弄得不知所措,而是要注意體會兩個重點方法的使用過 程。

   在服務器控件開發成功以後,最好可以查看其HTML代碼,並將服務器代碼和HTML代碼做以比較,搞清楚每一條服務器控件代碼 呈現了什麼樣的HTML代碼。經過這個方法,相信讀者可以對服務器控件的呈現方法有更加深入的體會。下面列舉了當用戶在瀏覽器中運行以上頁面,並查看相關 的Html源文件時可獲得的代碼:

 <!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 id="Head1">
 <title> 使用RenderContents方法實現控件呈現</title>
 </head>
 <body>
 <form name="form1" method="post" action="Default.aspx" id="form1">
 <div>
 <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTEyNTQwNjQyMDJkZOlJ3PyMGs2hmzn9MU6Ogt9V+5ag" />
 </div>
 <div>
 <a id="CustomerControl" href="mailto:my@mysample.com" style="color:Blue;font-size:Small;font-weight:bold;">個人郵箱地址</a>
 </div>
 </form>
 </body>
 </html>





    經過觀察以上代碼可知,自定義服務器控件RenderContentsControl實際呈現的結果是粗體所示部分的代碼,其最終呈現爲一個表示超連接 的<a>標記,其中包括href、Style和文本等屬性值。它們的值與Default.aspx文件源代碼 中,RenderContentsControl控件的屬性設置有着密切關係。例如,Email屬性值最終呈現爲href屬性值等等。讀者可自行對照查 看,這對於理解控件呈現頗有益處。

   小結

   本文主要介紹了WebControl類的一些基本知識,而且利用這些 基本知識建立了一個簡單的控件呈現實例。實際上,從中讀者應該可以總結出來,建立繼承自WebControl基類的自定義服務器控件,其中最爲重要的重寫 RenderContents方法。記住這一點是很是重要的。至此,筆者已經利用兩篇文章來介紹實現控件呈現的方法。在之後的文章中,將介紹有關爲控件實 現屬性等方面的內容。html

相關文章
相關標籤/搜索