top
Loading...
ASP.NET服務器控件之RenderContents
下載本文源代碼

上一篇文章介紹了使用Control類的Render方法實現控件呈現的基本知識和示例應用。本文重點介紹另一種實現控件呈現的常用方法--使用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);
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);
}

分析以上代碼可以得出以下結論:

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

二、重寫AddAttributesToRender、RenderBeginTag、RenderEndTag、RenderContents等方法非常重要(請注意重寫這些方法的條件及注意事項),否則服務器控件可能會出現丟失標簽的情況,這將嚴重影響服務器控件的呈現。

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

上文介紹了WebControl類的一些基本知識。尤其是對于上文所列舉的示意性代碼需要重點理解。這對于實現控件呈現有著重要作用。

應用示例

相信讀者在瀏覽各個網站時,經常會看到"聯系我們"等類似文字。當單擊這些文字時,操作系統將自動打開自身所帶的郵件客戶端軟件,提示用戶發送郵件。本例將實現一個自定義服務器控件RenderContentsControl,其用于通過呈現包含"mailto:郵件地址"的超鏈接。本例的示例效果圖如圖1所示:


圖1

如圖1所示,頁面中呈現了超鏈接文字"我的郵箱地址"。當單擊該鏈接,系統將發送郵件給"[email protected]"。

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

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

using System;
using System.ComponentModel;
using System.Security;
using System.Security.Permissions;
using System.Web;using System.Web.UI;
using System.Web.UI.WebControls;
namespace UsingRenderContentsControl{
[
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="[email protected]">我的郵箱地址</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="[email protected]">我的郵箱地址</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:[email protected]" 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方法。記住這一點是非常重要的。至此,筆者已經利用兩篇文章來介紹實現控件呈現的方法。在以后的文章中,將介紹有關為控件實現屬性等方面的內容。
作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗