ASP.NET服務器控件開發之復合控件
ASP.NET服務器控件:復合控件概念
所謂復合控件:簡單的理解就是將多個基本的控件組合成一個控件,從而實現自己想要的效果。微軟為asp.net2.0中推出的登錄控件等就是一個復合控件。從功能的實現上,復合式控件有點像用戶控件,只是一個是.ascx文件,一個是.dll文件。
呈現簡單的復合控件:
要想呈現一個復合控件,需要了解以下幾個方面:
實現INamingContainer接口
任何實現該接口的控件都創建一個新的命名空間,在這個新的命名空間中,所有子控件 ID 屬性在整個應用程序內保證是唯一的。
Control.CreateChildControls 方法
由 asp.net 頁面框架調用,以通知使用基于合成的實現的服務器控件創建它們包含的任何子控件,以便為回發或呈現做準備。 當開發復合服務器控件或模板服務器控件時,必須重寫此方法。重寫 CreateChildControls 方法的控件應實現 INamingContainer 接口以避免命名沖突。
Control.ChildControlsCreated 屬性
獲取一個值,該值指示是否已創建服務器控件的子控件。
Control.EnsureChildControls 方法
確定ASP.NET服務器控件是否包含子控件。如果不包含,則創建子控件。
下面就通過實例來呈現個簡單的復合登陸控件:創建asp.net服務器控件工程。complexControl。
先來看代碼:
- namespace complexControl
- {
- [DefaultProperty("Text")]
- [ToolboxData("< {0}:LoginControl runat=server ButtonText='登錄' NameLabel='用戶名:' PasswordLabel='用戶密碼:'>< /{0}:LoginControl>")]
- public class LoginControl : WebControl, INamingContainer, IPostBackEventHandler
- {
- private Button _button;
- private TextBox _nameTextBox;
- private Label _nameLabel;
- private TextBox _passwordTextBox;
- private Label _passwordLabel;
- private RequiredFieldValidator _nameValidator;
- private RequiredFieldValidator _passwordValidator;
- [Bindable(true),Category("Appearance"),DefaultValue(""),Description("按鈕文本")]
- public string ButtonText
- {
- get
- {
- EnsureChildControls();//確定服務器控件是否包含子控件
- return _button.Text;
- }
- set
- {
- EnsureChildControls();
- _button.Text = value;
- }
- }
- [Bindable(true),Category("Default"),DefaultValue(""),Description("姓名")]
- public string Name
- {
- get
- {
- EnsureChildControls();
- return _nameTextBox.Text;
- }
- set
- {
- EnsureChildControls();
- _nameTextBox.Text = value;
- }
- }
- [Bindable(true),Category("Appearance"),DefaultValue(""),Description("必須輸入姓名")]
- public string NameErrorMessage
- {
- get
- {
- EnsureChildControls();
- return _nameValidator.ErrorMessage;
- }
- set
- {
- EnsureChildControls();
- _nameValidator.ErrorMessage = value;
- _nameValidator.ToolTip = value;
- }
- }
- [Bindable(true),Category("Apperance"),DefaultValue(""),Description("姓名標簽")]
- public string NameLabel
- {
- get
- {
- EnsureChildControls();
- return _nameLabel.Text;
- }
- set
- {
- EnsureChildControls();
- _nameLabel.Text = value;
- }
- }
- [Browsable(false),DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public string Password
- {
- get
- {
- EnsureChildControls();
- return _passwordTextBox.Text;
- }
- }
- [Bindable(true),Category("Appearance"),DefaultValue(""),Description("必須輸入密碼")]
- public string PasswordErrorMessage
- {
- get
- {
- EnsureChildControls();
- return _passwordValidator.ErrorMessage;
- }
- set
- {
- EnsureChildControls();
- _passwordValidator.ErrorMessage = value;
- _passwordValidator.ToolTip = value;
- }
- }
- [Bindable(true),Category("Appearance"),DefaultValue(""),Description("密碼標簽")]
- public string PasswordLabel
- {
- get
- {
- EnsureChildControls();
- return _passwordLabel.Text;
- }
- set
- {
- EnsureChildControls();
- _passwordLabel.Text = value;
- }
- }
- 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 = "*";
- _nameValidator.Display = ValidatorDisplay.Static;
- _passwordLabel = new Label();
- _passwordTextBox = new TextBox();
- _passwordTextBox.TextMode = TextBoxMode.Password;
- _passwordTextBox.ID = "passwordTextBox";
- _passwordValidator = new RequiredFieldValidator();
- _passwordValidator.ID = "validator2";
- _passwordValidator.ControlToValidate = _passwordTextBox.ID;
- _passwordValidator.Text = "*";
- _passwordValidator.Display = ValidatorDisplay.Static;
- _button = new Button();
- _button.ID = "button1";
- //_button.Click += new EventHandler(_button_Click);
- _button.CommandName = "ClickLogin";
- this.Controls.Add(_nameLabel);
- this.Controls.Add(_nameTextBox);
- this.Controls.Add(_nameValidator);
- this.Controls.Add(_passwordLabel);
- this.Controls.Add(_passwordTextBox);
- this.Controls.Add(_passwordValidator);
- this.Controls.Add(_button);
- }
- 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(); // Td
- writer.RenderBeginTag(HtmlTextWriterTag.Td);
- _nameTextBox.RenderControl(writer);
- writer.RenderEndTag(); // Td
- writer.RenderBeginTag(HtmlTextWriterTag.Td);
- _nameValidator.RenderControl(writer);
- writer.RenderEndTag(); // Td
- writer.RenderEndTag(); // Tr
- writer.RenderBeginTag(HtmlTextWriterTag.Tr);
- writer.RenderBeginTag(HtmlTextWriterTag.Td);
- _passwordLabel.RenderControl(writer);
- writer.RenderEndTag(); // Td
- writer.RenderBeginTag(HtmlTextWriterTag.Td);
- _passwordTextBox.RenderControl(writer);
- writer.RenderEndTag(); // Td
- writer.RenderBeginTag(HtmlTextWriterTag.Td);
- _passwordValidator.RenderControl(writer);
- writer.RenderEndTag(); // Td
- writer.RenderEndTag(); // Tr
- writer.RenderBeginTag(HtmlTextWriterTag.Tr);
- writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "2");
- writer.AddAttribute(HtmlTextWriterAttribute.Align, "right");
- writer.RenderBeginTag(HtmlTextWriterTag.Td);
- _button.RenderControl(writer);
- //writer.AddAttribute(HtmlTextWriterAttribute, Page.GetPostBackEventReference(_button));
- writer.RenderEndTag(); // Td
- writer.RenderBeginTag(HtmlTextWriterTag.Td);
- writer.Write(" ");
- writer.RenderEndTag(); // Td
- writer.RenderEndTag(); // Tr
- writer.RenderEndTag(); // Table
- }
- }
- }
首先我們實例化了幾個現有控件的對象。然后聲明了一大堆的屬性,要注意的:和平時定義屬性不同,我們在每一個屬性中都添加了EnsureChildControls ()方法。其他的沒有任何變化,和一般的屬性聲明一樣。
接下來我們從寫了重要的CreateChildControls()。將前面聲明好的實例化控件對象添加到controlcollection中。融合成一個控件。
***重寫控件顯示的Render()方法。生成登錄窗體的樣式。效果如下:
這樣,我們基本上就完成了復合控件的基本顯示功能。
復合控件的事件處理
由于復合控件中包含子控件,這就使得復合控件的事件處理變得復雜起來。由于不允許開發人員直接訪問子控件,如果子控件的事件不能作為***事件引發,那么將無法實現子控件的事件處理。
我們可以以兩種形式來完成事件的處理:一是直接將事件封裝到控件中,顯然靈活性很差。二就是自定義事件,用戶來完成事件的代碼。
***種情況比較簡單:就是在創建我們得控件時,將要實現的效果直接封裝在dll中。這里就不做說明了。
但是往往控件觸發時,我們想做自己的事情,這就是第二種情況的事件處理。這就需要把事件交給主控件,由主控件統一暴露事件,這樣開發人員在使用控件時僅需要為主控件注冊事件即可,剩下的由主控件負責引發子控件的事件或執行子控件的某些功能,這里就涉及主控件與其子控件的事件銜接問題,復合控件的這種事件處理,主要是實現子控件事件上傳的過程。一般分為:包含法和冒泡法兩種處理方式。
包含法:
基本思想是:通過在子控件的事件處理程序中調用復合控件的頂層事件處理程序,以完成子控件的事件上傳。 在CreateChildControls方法中,為子控件添加事件處理程序。
接著上面登陸控件的例子,來實現下登錄按鈕的事件。
首先在CreateChildControls()中,為_button添加單擊事件。(其他代碼略)
- _button = new Button();
- _button.ID = "button1";
- _button.Click += new EventHandler(_button_Click);
然后創建主控件對外的處理函數:
- void _button_Click(Object source, EventArgs e)
- {
- OnClickLogin(EventArgs.Empty);
- }
- private static readonly object EventClickLogin = new object();
- public event EventHandler ClickLogin
- {
- add
- {
- Events.AddHandler(EventClickLogin, value);
- }
- remove
- {
- Events.RemoveHandler(EventClickLogin, value);
- }
- }
- protected virtual void OnClickLogin(EventArgs e)
- {
- EventHandler clickLoginHandler = (EventHandler)Events[EventClickLogin];
- if (clickLoginHandler != null)
- {
- clickLoginHandler(this, e);
- }
- }
- public void RaisePostBackEvent(string eventArgument)//處理回發事件
- {
- OnClickLogin(new EventArgs());
- }
事件的詳細處理請參看上一篇。這里要說明的是:在按鈕的單擊事件處理函數中,將我們在主控件中聲明的事件傳入進去:
- void _button_Click(Object source, EventArgs e)
- {
- OnClickLogin(EventArgs.Empty);
- }
這樣,我們就實現了***種方法。測試一下:
- protected void LoginControl1_ClickLogin1(object sender, EventArgs e)
- {
- Label1.Text = "sssssssssssssssqwwssss";
- }
單擊按鈕,將觸發上面的事件。
冒泡法:
基本思想:使用asp.net 2.0框架提供的事件上傳機制。這種機制允許子控件將事件沿其包容層次結構向上傳播到合適的位置引發,并且允許將事件處理程序附加到原始控件以及公開冒泡的事件的控件上。
冒泡法的實現,使用Control基類中專門用于事件上傳的兩個方法:OnBubbleEvent和RaiseBubbleEvent。OnBubbleEvent方法用于確定子控件的事件是否沿復合控件層次結構向上傳遞。在該方法中,參數source表示事件源,參數args表示包含事件數據的EventArgs對象。如果子控件的事件向上傳遞,則為true;否則為false。默認值為false。RaiseBubbleEvent方法用于將所有事件源及其信息分配給控件的父級,并且不能被重寫。盡管無法重寫此方法,但創作的控件可以通過重寫 OnBubbleEvent 方法處理或引發冒泡事件。
還是通過例子說明一下:
首先在CreateChildControls()中聲明commandname屬性。
_button.CommandName = "ClickLogin";
然后定義事件:
- private static readonly object EventClickLogin = new object();
- public event EventHandler ClickLogin
- {
- add
- {
- Events.AddHandler(EventClickLogin, value);
- }
- remove
- {
- Events.RemoveHandler(EventClickLogin, value);
- }
- }
- protected virtual void OnClickLogin(EventArgs e)
- {
- EventHandler clickLoginHandler = (EventHandler)Events[EventClickLogin];
- if (clickLoginHandler != null)
- {
- clickLoginHandler(this, e);
- }
- }
- protected override bool OnBubbleEvent(object source, EventArgs e)
- {
- bool handled = false;
- if (e is CommandEventArgs)
- {
- CommandEventArgs ce = (CommandEventArgs)e;
- if (ce.CommandName == "ClickLogin")
- {
- OnClickLogin(EventArgs.Empty);
- handled = true;
- }
- }
- return handled;
- }
- public void RaisePostBackEvent(string eventArgument)//處理回發事件
- {
- OnClickLogin(new EventArgs());
- }
里主要要注意的是:OnBubbleEvent()的使用。通過CommandName的值,來相應的找到處理事件的控件。
測試一下:
- protected void LoginControl1_ClickLogin1(object sender, EventArgs e)
- {
- Label1.Text = "sssssssssssssssqwwssss";
- }
單擊按鈕,將觸發上面的事件。
小結:這樣,ASP.NET服務器控件中復合控件的基本使用就介紹完了,不是很難。只要記住特定的一些東西,就可以很容易的創造出復合控件。值得大家注意的是復合控件中事件的兩種處理方法。希望對新手有幫助。
【編輯推薦】