ASP.NET服務(wù)器控件開(kāi)發(fā)之復(fù)合控件
ASP.NET服務(wù)器控件:復(fù)合控件概念
所謂復(fù)合控件:簡(jiǎn)單的理解就是將多個(gè)基本的控件組合成一個(gè)控件,從而實(shí)現(xiàn)自己想要的效果。微軟為asp.net2.0中推出的登錄控件等就是一個(gè)復(fù)合控件。從功能的實(shí)現(xiàn)上,復(fù)合式控件有點(diǎn)像用戶控件,只是一個(gè)是.ascx文件,一個(gè)是.dll文件。
呈現(xiàn)簡(jiǎn)單的復(fù)合控件:
要想呈現(xiàn)一個(gè)復(fù)合控件,需要了解以下幾個(gè)方面:
實(shí)現(xiàn)INamingContainer接口
任何實(shí)現(xiàn)該接口的控件都創(chuàng)建一個(gè)新的命名空間,在這個(gè)新的命名空間中,所有子控件 ID 屬性在整個(gè)應(yīng)用程序內(nèi)保證是唯一的。
Control.CreateChildControls 方法
由 asp.net 頁(yè)面框架調(diào)用,以通知使用基于合成的實(shí)現(xiàn)的服務(wù)器控件創(chuàng)建它們包含的任何子控件,以便為回發(fā)或呈現(xiàn)做準(zhǔn)備。 當(dāng)開(kāi)發(fā)復(fù)合服務(wù)器控件或模板服務(wù)器控件時(shí),必須重寫(xiě)此方法。重寫(xiě) CreateChildControls 方法的控件應(yīng)實(shí)現(xiàn) INamingContainer 接口以避免命名沖突。
Control.ChildControlsCreated 屬性
獲取一個(gè)值,該值指示是否已創(chuàng)建服務(wù)器控件的子控件。
Control.EnsureChildControls 方法
確定ASP.NET服務(wù)器控件是否包含子控件。如果不包含,則創(chuàng)建子控件。
下面就通過(guò)實(shí)例來(lái)呈現(xiàn)個(gè)簡(jiǎn)單的復(fù)合登陸控件:創(chuàng)建asp.net服務(wù)器控件工程。complexControl。
先來(lái)看代碼:
- 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();//確定服務(wù)器控件是否包含子控件
- 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("姓名標(biāo)簽")]
- 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("密碼標(biāo)簽")]
- 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
- }
- }
- }
首先我們實(shí)例化了幾個(gè)現(xiàn)有控件的對(duì)象。然后聲明了一大堆的屬性,要注意的:和平時(shí)定義屬性不同,我們?cè)诿恳粋€(gè)屬性中都添加了EnsureChildControls ()方法。其他的沒(méi)有任何變化,和一般的屬性聲明一樣。
接下來(lái)我們從寫(xiě)了重要的CreateChildControls()。將前面聲明好的實(shí)例化控件對(duì)象添加到controlcollection中。融合成一個(gè)控件。
***重寫(xiě)控件顯示的Render()方法。生成登錄窗體的樣式。效果如下:
這樣,我們基本上就完成了復(fù)合控件的基本顯示功能。
復(fù)合控件的事件處理
由于復(fù)合控件中包含子控件,這就使得復(fù)合控件的事件處理變得復(fù)雜起來(lái)。由于不允許開(kāi)發(fā)人員直接訪問(wèn)子控件,如果子控件的事件不能作為***事件引發(fā),那么將無(wú)法實(shí)現(xiàn)子控件的事件處理。
我們可以以兩種形式來(lái)完成事件的處理:一是直接將事件封裝到控件中,顯然靈活性很差。二就是自定義事件,用戶來(lái)完成事件的代碼。
***種情況比較簡(jiǎn)單:就是在創(chuàng)建我們得控件時(shí),將要實(shí)現(xiàn)的效果直接封裝在dll中。這里就不做說(shuō)明了。
但是往往控件觸發(fā)時(shí),我們想做自己的事情,這就是第二種情況的事件處理。這就需要把事件交給主控件,由主控件統(tǒng)一暴露事件,這樣開(kāi)發(fā)人員在使用控件時(shí)僅需要為主控件注冊(cè)事件即可,剩下的由主控件負(fù)責(zé)引發(fā)子控件的事件或執(zhí)行子控件的某些功能,這里就涉及主控件與其子控件的事件銜接問(wèn)題,復(fù)合控件的這種事件處理,主要是實(shí)現(xiàn)子控件事件上傳的過(guò)程。一般分為:包含法和冒泡法兩種處理方式。
包含法:
基本思想是:通過(guò)在子控件的事件處理程序中調(diào)用復(fù)合控件的頂層事件處理程序,以完成子控件的事件上傳。 在CreateChildControls方法中,為子控件添加事件處理程序。
接著上面登陸控件的例子,來(lái)實(shí)現(xiàn)下登錄按鈕的事件。
首先在CreateChildControls()中,為_(kāi)button添加單擊事件。(其他代碼略)
- _button = new Button();
- _button.ID = "button1";
- _button.Click += new EventHandler(_button_Click);
然后創(chuàng)建主控件對(duì)外的處理函數(shù):
- 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)//處理回發(fā)事件
- {
- OnClickLogin(new EventArgs());
- }
事件的詳細(xì)處理請(qǐng)參看上一篇。這里要說(shuō)明的是:在按鈕的單擊事件處理函數(shù)中,將我們?cè)谥骺丶新暶鞯氖录魅脒M(jìn)去:
- void _button_Click(Object source, EventArgs e)
- {
- OnClickLogin(EventArgs.Empty);
- }
這樣,我們就實(shí)現(xiàn)了***種方法。測(cè)試一下:
- protected void LoginControl1_ClickLogin1(object sender, EventArgs e)
- {
- Label1.Text = "sssssssssssssssqwwssss";
- }
單擊按鈕,將觸發(fā)上面的事件。
冒泡法:
基本思想:使用asp.net 2.0框架提供的事件上傳機(jī)制。這種機(jī)制允許子控件將事件沿其包容層次結(jié)構(gòu)向上傳播到合適的位置引發(fā),并且允許將事件處理程序附加到原始控件以及公開(kāi)冒泡的事件的控件上。
冒泡法的實(shí)現(xiàn),使用Control基類中專門用于事件上傳的兩個(gè)方法:OnBubbleEvent和RaiseBubbleEvent。OnBubbleEvent方法用于確定子控件的事件是否沿復(fù)合控件層次結(jié)構(gòu)向上傳遞。在該方法中,參數(shù)source表示事件源,參數(shù)args表示包含事件數(shù)據(jù)的EventArgs對(duì)象。如果子控件的事件向上傳遞,則為true;否則為false。默認(rèn)值為false。RaiseBubbleEvent方法用于將所有事件源及其信息分配給控件的父級(jí),并且不能被重寫(xiě)。盡管無(wú)法重寫(xiě)此方法,但創(chuàng)作的控件可以通過(guò)重寫(xiě) OnBubbleEvent 方法處理或引發(fā)冒泡事件。
還是通過(guò)例子說(shuō)明一下:
首先在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)//處理回發(fā)事件
- {
- OnClickLogin(new EventArgs());
- }
里主要要注意的是:OnBubbleEvent()的使用。通過(guò)CommandName的值,來(lái)相應(yīng)的找到處理事件的控件。
測(cè)試一下:
- protected void LoginControl1_ClickLogin1(object sender, EventArgs e)
- {
- Label1.Text = "sssssssssssssssqwwssss";
- }
單擊按鈕,將觸發(fā)上面的事件。
小結(jié):這樣,ASP.NET服務(wù)器控件中復(fù)合控件的基本使用就介紹完了,不是很難。只要記住特定的一些東西,就可以很容易的創(chuàng)造出復(fù)合控件。值得大家注意的是復(fù)合控件中事件的兩種處理方法。希望對(duì)新手有幫助。
【編輯推薦】