ASP.NET 2.0伺服器控制項之複合控制項概述

來源:互聯網
上載者:User
asp.net|伺服器|控制項

  在前面的文章中,主要介紹了伺服器控制項的基本概念、基本理論,這些內容是構建所有自訂伺服器控制項的基石。然而,僅僅依靠這些知識還不足以建立出優秀的伺服器控制項。因為,不同類型的伺服器控制項具有不同的建立方法,開發人員必須在掌握基本概念和理論之後,掌握不同類型伺服器控制項的開發方法。本文及其隨後幾篇文章將詳細介紹與建立複合控制項相關的內容。本文重點介紹有關複合控制項的概念、建立方法等理論,然後,通過一個典型樣本加深讀者對於複合控制項建立方法的理解。

  複合控制項概述

  複合控制項中的“複合”一詞表明該類型控制項本質上由多個組件組合而成。同時,複合控制項對外暴露的成員對象通常由構成組件的方法和屬性提供,並且可能加入一些新的成員。複合控制項也可以實現自訂事件,並處理並引發子控制項所引起的事件。就功能方面而言,複合控制項的功能要比簡單組合幾個控制項的功能要強大的多,而且很多時候具有一定的專項性。例如,ASP.NET 2.0新增的Login控制項就是一個典型的複合控制項。該控制項使用者介面由多個單獨控制群組合而成,並且使用單一的API對控制項進行設定和訪問。另外,Login控制項由於與成員資格等功能整合的原因,因此,其具有快速實現使用者登入的功能。

  可能部分有經驗的讀者在瞭解了複合控制項的基本概念之後會有所疑惑:複合控制項與使用者控制項好像非常相似,那麼它們之間有什麼區別嗎?到底什麼時候建立複合控制項,什麼時候建立使用者控制項呢?回答這個問題,我們必須從使用者控制項的基本概念入手進行研究。

  簡單而言,使用者控制項是指在一個項目中,由於同樣一些功能模組在多處引用,例如,導覽功能表等,可以把這一塊代碼做成一個使用者控制項,然後,在需要引用的頁面中註冊後,直接按控制項使用的方式引用,省去了重複編寫相同代碼的工作。就複合控制項與使用者控制項的區別而言,主要可以總結為以下幾點:

  一、複合控制項創作的最短設計時支援,使用者控制項創作的完全設計時支援。在視覺化設計工具中,創作使用者控制項與創作ASP.NET頁沒有差別。

  二、複合控制項是以目標為公用語言運行庫的物件導向的程式設計語言,如C#,是用編程方式創作的使用者控制項是使用ASP.NET頁文法和指令碼塊聲明性地創作的。

  三、複合控制項是作為程式集(.dll)編譯和保持的。使用者控制項是帶有.ascx副檔名的文字檔。

  四、複合控制項非常適於創作一般的可重新發布的控制項,使用者控制項適合應用程式特定的功能。

  五、可將複合控制項添加到視覺化設計工具的工具箱中並拖放到頁面上,使用時可以在屬性框中設計,使用者控制項只能在HTML中編寫。

  通過以上內容,相信讀者已經能夠基本瞭解了複合控制項。下面介紹一下建立複合控制項的實現方法。在這個過程中,開發人員必須把握以下幾個要點:

  第一、通常情況,複合控制項類必須派生自System.Web.UI.WebControls.CompositeControl類。這一點與ASP.NET 1.x環境下開發複合控制項有些不同。在ASP.NET 1.x中,複合控制項必須實現System.Web.UI.INamingContainer介面。然而,在ASP.NET 2.0下,複合控制項類的基類則發生了變化。下面簡單介紹一下CompositeControl類。

  CompositeControl類是一個抽象類別,該類可為自訂控制項提供命名容器和控制項設計器功能,並且可包含全部子控制項或使用其他控制項功能。CompositeControl類的聲明代碼如下所示:

public abstract class CompositeControl : WebControl, INamingContainer, ICompositeControlDesignerAccessor
  如上代碼所示,CompositeControl類基層自WebControl基類,並且實現INamingContainer和ICompositeControlDesignerAccessor介面。INamingContainer是一個沒有方法的標記介面。當控制項在實現INamingContainer時,頁架構可在該控制項下建立新的命名範圍,因此,能夠確保子控制項在控制項的分層樹中具有唯一的名稱。當複合控制項公開模板屬性,提供資料繫結或需要傳送事件到子控制項時,這是非常重要的。ICompositeControlDesignerAccessor介面使複合控制項設計器可以在設計時重新建立其關聯控制項的子控制項。該介面包含一個需要實現的方法RecreateChildControls。該方法使複合控制項的設計器可以在設計時重新建立該控制項的子控制項。

  另外,如果建立的是資料繫結複合控制項,那麼自訂控制項類的基類應該是CompositeDataBoundControl。有關該類的具體內容,請讀者查閱相關資料。

  第二、必須重寫Control基類的CreateChildControls方法,以便對子控制項進行初始化、執行個體化,並將其添加到控制項樹中。CreateChildControls用於通知使用基於合成實現的伺服器控制項,建立它們包含的任何子控制項,以便為回傳或呈現做準備。重寫該方法是實現複合控制項的關鍵所在。這種類撰寫的方法將通知.NET架構有關複合控制項中包含哪些子控制項,以及各個子控制項在控制項樹中的位置和關係等內容。通過這種方法,複合控制項將複用子控制項提供的實現來進行呈現、事件處理、樣式及其他功能。

  在實現複合控制項過程中,除了掌握CompositeControl基類和CreateChildControls方法之外,ASP.NET 2.0還提供了與複合控制項相關的其他方法和屬性,掌握這些成員對於開發複合控制項也很重要。下面列舉了這些常見方法和屬性。

  · protected virtual void EnsureChildControls()

  該方法用於確定伺服器控制項是否包含子控制項。如果不包含,則建立子控制項。該方法首先檢查 ChildControlsCreated 屬性的當前值。如果此值為假,則調用CreateChildControls方法。當需要確保已建立子控制項時,將調用該方法。大多數情況下,自訂伺服器控制項的開發人員無需重寫此方法。如果確實重寫了此方法,請按與其預設行為相似的方式來使用。

  · public virtual Control FindControl(string)

  該方法用於在當前的命名容器中搜尋指定的伺服器控制項。

  · public virtual bool HasControls()

  該方法用於確定伺服器控制項是否包含任何子控制項。如果控制項包含其他控制項,則為true;否則為 false。由於該方法僅確定是否存在任何子控制項,它可以通過允許您避免不必要的Controls.Count屬性調用來改進效能。調用此屬性要求執行個體化ControlCollection對象。如果沒有子級,則建立該對象會浪費伺服器資源。

  · protected virtual void DataBindChildren ()

  該方法是ASP.NET 2.0新增內容,其用於將資料來源綁定到伺服器控制項的子控制項。這為開發資料繫結類型的複合控制項提供了便利。然而,需要注意的是,在伺服器控制項上調用此方法時,此方法不會將資料繫結到控制項。若要綁定伺服器控制項及其所有子控制項,請調用DataBind方法。

  · protected bool HasEvents ()

  這也是一個ASP.NET 2.0新增的方法,其用於返回一個值,該值指示是否為控制項或任何子控制項註冊事件。如果註冊事件,則為true;否則為false。

  · Controls屬性

  該屬性的資料類型為ControlCollection,其用於擷取ControlCollection對象,該對象表示 UI 階層中指定伺服器控制項的子控制項。其屬性值指定伺服器控制項的子控制項集合。

  · NamingContainer屬性

  該屬性的資料類型為Control,其用於擷取對伺服器控制項的命名容器的引用,此引用建立唯一的命名空間,以區分具有相同Control.ID屬性值的伺服器控制項。

  · ChildControlsCreated屬性

  該屬性的資料類型為bool,其用於擷取一個值,該值指示是否已建立伺服器控制項的子控制項。如果已建立子控制項則為true;否則為false。

  典型應用

  上文介紹了有關建立複合控制項的一些基本知識,下面將通過一個典型應用加深讀者對於複合控制項實現方法的理解,其重點放在針對複合控制項的呈現方法上。

  多數控制項呈現通過重寫Render方法實現,然而,在複合控制項中則大有不同。複合控制項由多個子控制群組合而成,其呈現邏輯是由子控制項提供的。

  據此,在少數較為簡單的情況下,複合控制項不用重寫Render方法,例如,建立一個包含文字框和按鈕的複合控制項,這時,只要通過類撰寫的方法在CreateChildControls中添加相關控制項即可,而無需Render方法。但是,在多數情況下,複合控制項中既包含子控制項,又包含用于格式化和布局的HTML。

  針對這種情況,如果只採取類撰寫的實現方法,那麼很容易造成錯誤,並且產生的複合控制項效能受到很大影響。最好的解決方案是重寫CreateChildControls方法,同時也重寫Render方法。在CreateChildControls方法中,為複合控制項添加子控制項;在Render方法中,為HTTP輸出資料流添加用于格式化和布局的HTML。

  下面列舉了呈現複合控制項的關鍵步驟:

  · 控制項基類繼承自CompositeControl基類。這是在ASP.NET 2.0中建立複合控制項的關鍵所在。

  · 重寫CreateChildControls方法,完成執行個體化、初始化子控制項,並且將子控制項添加到控制項集合中。

  · 重寫ICompositeControlDesignerAccessor介面的RecreateChildContrls方法。

  · 如果複合控制項中存在用于格式化和布局的HTML,那麼建議將這些內容寫入Render方法中,而不要在CreateChildControls方法中建立和添加所需的LiteralControl執行個體。另外,在添加相關HTML代碼過程中,為了讓子控制項使用預設產生方法,必須使每個子控制項調用RenderControl方法。

  為了便於讀者更好的理解以上內容,下面舉例說明。在此樣本中,Register控制項使用子控制項建立使用者介面(UI),用於輸入使用者資訊,以向網站註冊。此使用者介面包括兩個TextBox控制項(一個用於輸入使用者名稱,另一個用於輸入使用者的電子郵件地址)和一個用於提交資訊的Button控制項。Register還將RequiredFieldValidator控制項與兩個TextBox控制項關聯起來,以確保使用者輸入了名稱和電子郵件地址。複合控制項Register原始碼如下所示:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebControlLibrary{
[
 DefaultProperty("ButtonText"),
 ToolboxData("<{0}:Register runat=\"server\"> </{0}:Register>"),
]

public class Register : CompositeControl {
// 定義私人欄位
private Button submitButton;
private TextBox nameTextBox;
private Label nameLabel;
private TextBox emailTextBox;
private Label emailLabel;
private RequiredFieldValidator emailValidator;
private RequiredFieldValidator nameValidator;
// 實現屬性ButtonText

[
 Bindable(true),
 Category("Appearance"),
 DefaultValue(""),
 Description("按鈕上的文字內容.")
]

public string ButtonText {
 get {
  EnsureChildControls();
  return submitButton.Text;
 }
 set {
  EnsureChildControls();
  submitButton.Text = value;
 }
}

// 實現屬性Name

[
 Bindable(true),
 Category("Default"),
 DefaultValue(""),
 Description("使用者名稱.")
]

public string Name {
 get {
  EnsureChildControls();
  return nameTextBox.Text;
 }
 set {
  EnsureChildControls();
  nameTextBox.Text = value;
 }
}

// 實現屬性NameErrorMessage

[
 Bindable(true),
 Category("Appearance"),
 DefaultValue(""),
 Description("使用者名稱驗證錯誤資訊.")
]

public string NameErrorMessage {
 get {
  EnsureChildControls();
  return nameValidator.ErrorMessage;
 }
 set {
  EnsureChildControls();
  nameValidator.ErrorMessage = value;
  nameValidator.ToolTip = value;
 }
}

// 實現屬性NameLabelText

[
 Bindable(true),
 Category("Appearance"),
 DefaultValue(""),
 Description("使用者名稱文字框旁的文字.")
]

public string NameLabelText {
 get {
  EnsureChildControls();
  return nameLabel.Text;
 }
 set {
  EnsureChildControls();
  nameLabel.Text = value;
 }
}

// 實現屬性Email

[
 Bindable(true),
 Category("Default"),
 DefaultValue(""),
 Description("郵件地址.")
]

public string Email {
 get {
  EnsureChildControls();
  return emailTextBox.Text;
 }
 set {
  EnsureChildControls();
  emailTextBox.Text = value;
 }
}

// 實現屬性EmailErrorMessage

[
 Bindable(true),
 Category("Appearance"),
 DefaultValue(""),
 Description("郵件地址驗證錯誤資訊.")
]

public string EmailErrorMessage {
 get {
  EnsureChildControls();
  return emailValidator.ErrorMessage;
 }
 set {
  EnsureChildControls();
  emailValidator.ErrorMessage = value;
  emailValidator.ToolTip = value;
 }
}

// 實現屬性EmailLabelText

[
 Bindable(true),
 Category("Appearance"),
 DefaultValue(""),
 Description("電子郵件文字框旁的文字.")
]

public string EmailLabelText {
 get {
  EnsureChildControls();
  return emailLabel.Text;
 }
 set {
  EnsureChildControls();
  emailLabel.Text = value;
 }
}

// 重寫ICompositeControlDesignerAccessor介面的RecreateChildContrls方法

protected override void RecreateChildControls() {
 EnsureChildControls();
}

// 重寫Control基類的CreateChildControls方法

protected override void CreateChildControls() {
 // 清除所有子控制項
 Controls.Clear();
 nameLabel = new Label();
 nameTextBox = new TextBox();
 nameTextBox.ID = "nameTextBox";
 nameValidator = new RequiredFieldValidator();
 nameValidator.ID = "validator1";
 nameValidator.ControlToValidate = nameTextBox.ID;
 nameValidator.Text = NameErrorMessage;
 nameValidator.Display = ValidatorDisplay.Static;
 emailLabel = new Label();
 emailTextBox = new TextBox();
 emailTextBox.ID = "emailTextBox";
 emailValidator = new RequiredFieldValidator();
 emailValidator.ID = "validator2";
 emailValidator.ControlToValidate = emailTextBox.ID;
 emailValidator.Text = EmailErrorMessage;
 emailValidator.Display = ValidatorDisplay.Static;
 submitButton = new Button();
 submitButton.ID = "button1";
 this.Controls.Add(nameLabel);
 this.Controls.Add(nameTextBox);
 this.Controls.Add(nameValidator);
 this.Controls.Add(emailLabel);
 this.Controls.Add(emailTextBox);
 this.Controls.Add(emailValidator);
 this.Controls.Add(submitButton);
}

// 重寫Render方法

protected override void Render(HtmlTextWriter writer) {
 AddAttributesToRender(writer);
 writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "1", false);
 writer.RenderBeginTag(HtmlTextWriterTag.Table);
 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 nameLabel.RenderControl(writer);
 writer.RenderEndTag();
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 nameTextBox.RenderControl(writer);
 writer.RenderEndTag();
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 nameValidator.RenderControl(writer);
 writer.RenderEndTag();
 writer.RenderEndTag();
 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 emailLabel.RenderControl(writer);
 writer.RenderEndTag();
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 emailTextBox.RenderControl(writer);
 writer.RenderEndTag();
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 emailValidator.RenderControl(writer);
 writer.RenderEndTag();
 writer.RenderEndTag();
 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
 writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "2", false);
 writer.AddAttribute(HtmlTextWriterAttribute.Align, "right", false);
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 submitButton.RenderControl(writer);
 writer.RenderEndTag();
 writer.RenderBeginTag(HtmlTextWriterTag.Td);
 writer.Write(" ");
 writer.RenderEndTag();
 writer.RenderEndTag();
 writer.RenderEndTag();
}
}
}
  以上列舉了複合控制項類Register的原始碼。雖然代碼有些冗長,然而還是比較容易理解的。下圖1列舉了Register類結構圖。


圖1 類結構圖
  如圖1並結合代碼可知,Register類繼承自CompositeControl基類,其實現了7個屬性和3個方法。屬性包括ButtonText、Email、EmailErrorMessage、EmailLabelText、Name、NameErrorMessage和NameLabelText。這些屬性通過命名,相信讀者基本可以瞭解其意義。

  另外,Register類中重寫了來自不同對象的3個方法。

  (1)Render方法隸屬於Control基類,在本例中主要在該方法中定義了一些與控制項布局相關的HTML等內容。

  (2)CreateChildControls方法隸屬於Control基類。在本樣本中通過重寫該方法,實現了將子控制項添加到複合控制項樹中的任務。請讀者牢記:每當需要Controls集合時,例如,在資料繫結期間(如果適用),伺服器控制項結構都會依賴對CreateChildControls的調用。為此,必須在CreateChildControls方法中添加子控制項。

  (3)RecreateChildControls方法來自CompositeControl基類的ICompositeControlDesignerAccessor介面。通過實現這個方法可使複合控制項的設計器可以在設計時重新建立該控制項的子控制項。

  下面列舉了為測試Register控制項而建立的Default.aspx頁面原始碼。

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register TagPrefix="Sample" 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">
<div>
<Sample:Register id="demo1" runat="server" ButtonText="註冊" EmailLabelText="電子郵件" NameLabelText="使用者名稱" EmailErrorMessage="不可為空" NameErrorMessage="不可為空" />
</div>
</form>
</body>
</html>
  樣本效果如圖2所示。


圖2 應用程式效果圖

  如圖2所示,複合控制項Register在頁面中顯示了文本、文字框、按鈕等,同時,還提供了輸入驗證功能。然而,當使用者單擊“註冊”按鈕之後,即使在使用者名稱和電子郵件的輸入都通過驗證的情況下,頁面仍然沒有引發相應的事件處理常式。這是由於在本例中沒有實現按鈕的事件處理內容。有關複合控制項的事件實現將在隨後的文章中進行詳細講解。

  小結

  複合控制項是通過將其他控制項彙總在某一公用API下建立而成的控制項。複合控制項將保留自己子控制項的活動執行個體,並且不僅限於呈現這些執行個體。使用複合控制項可以帶來幾點好處,例如可以簡化對事件和回傳的處理。然而,本文並沒有對複合控制項的事件實現等相關內容進行講解,請讀者繼續關注本系列的隨後文章。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.