前言:
前面讲到了复合控件的视图,今天将进入其复合控件的事件机制世界
本节将讲述:
\n
为什么要事件冒泡(事件上传)
什么是复合控件事件冒泡(事件上传)
复合控件事件的实现原理
实现事件冒泡(事件上传)的步骤。
正文:
\n
(一)为什么要事件上传
\n
复合控件中包含子控件,这就使得复合控件的事件处理变得复杂起来。显而易见,在复合控件的事件实现过程中,需要面临的最大问题是:由于不允许开发人员直接访问子控件(虽然通过Controls集合访问的方法可以实现,但是破坏了程序的封装性,因此是不被允许的),如果子控件的事件不能作为顶级事件引发,那么将无法实现子控件的事件处理。简单的说,即如何实现子控件的事件上传。所谓事件上传是指把子控件的事件暴露为顶级事件,这样父控件可以检查到事件,并按照定义来执行相关事件处理程序.
\n
(二) 什么是复合控件事件冒泡(事件上传)
\n
其核心是使用ASP.NET 2.0框架提供的事件上传机制。这种机制允许子控件将事件沿其包容层次结构向上传播到合适的位置引发,并且允许将事件处理程序附加到原始控件以及公开冒泡的事件的控件上
\n
(三)复合控件的实现原理
\n
页面框架提供了一个事件上传架构,通过它控件可能把某个事件上传(bubble)到控件层次,一个上传事件可能在引发它时或其他时候来处理,更方便的处理时机是在控件树的高层,复合控件可能用这个特征把子控件上传的事件暴露为顶层事件。例如:DataList控件把包含在ItemTemplate 中的Button控件的Command事件暴露为顶层的ItemCommand事件。而command事件(
\n
它的事件数据类派生自CommandEventArgs)由asp.net的内建控件上传的惟一事件,也可以实现初始化上传的其他事件。
\n
事件上传由OnBubbleEvent 和RaiseBubbleEvent方法来启动的,这些方法在Control类中的定义如下:
\n
\n
//定义复合控件的事件是否沿页面的UI 服务器控件层次结构向上传递。
\n
//如果子控件的事件向上传递,则为true;否则为false。默认值为false
\n
protected virtual bool OnBubbleEvent(object source,EventArgs args)
\n
{
\n
return false;
\n
}
\n
// 将所有事件源及其信息分配给控件的父级。
protected void RaiseBubbleEvent(object source, EventArgs args)
\n
{
\n
for (Control control = this.Parent; control != null; control = control.Parent)
\n
{
\n
if (control.OnBubbleEvent(source, args))
\n
{
\n
return;
\n
}
\n
}
\n
}
\n
\n
默认的情况下,初始化上传事件自动被上传通过控件层次,就像RaiseBubbleEvent方法定
\n
义以及OnBubble默义实现中看到的一样。
\n
为了处某个已经上传的事件,应该重载OnBubbleEvent 方法,复合控个把通常包含
\n
多个上传事件的子控件。事件也可以从控件层次的更底层(子控件的子控件)上传。可以用
\n
上传给OnBubbleEvent 方法的参数来确定处理什么事件。在处理完事件之后,如果想要停
\n
止事件进一步上传,那么可以在OnBubbleEvent 方法中返回true来实现。
\n
处理上传事件的方法之一是根据上传的事件来引发新的事件,这样页面开发者可以
\n
把上传事件作为控件的顶层事件处理,通过在控件中定义一个事件,并在 OnBubbleEvent
\n
方法中引发事件作为控个把的顶层事件处理,通过在控件中定义一个事件。并在
\n
OnBubbleEvent方法中引发事件。就可以把一个上传的事件暴露为控件的顶层事件。
\n
示例程序:
\n
通过捕获Button子控件的Command事件,并引发自已的Logon事件:
\n
Protected override bool OnBubbleEvent(object source,EventArgs e){
\n
bool handled=false;
\n
if(e is CommandEventArgs){
\n
CommandEventArgs ce=(CommandEventArgs)e;
\n
If(ce.CommandName=”Login”){
\n
OnLogin(EventArgs.Empty);
\n
Handled=true;
\n
}
\n
}
\n
}
\n
那么我怎么知道OnBubbleEvent被初始化呢。如何把一个button事件上上传到顶层事件
\n
呢。引发事件On<EventName>方法中调用RaiseBubbleEvent方法。
\n
在button中OnCommand的内部实现。
\n
protected virtual void OnCommand(CommandEventArgs e)
\n
{
\n
CommandEventHandler handler = (CommandEventHandler) base.Events[EventCommand];
\n
if (handler != null)
\n
{
\n
handler(this, e);
\n
}
\n
base.RaiseBubbleEvent(this, e); //这里冒泡事件传给顶层事件。
\n
}
\n
而我们知道RaiseBubbleEvent调用了OnBubbleEvent方法(参见前面)。。然后我们就去重写了OnBubbleEvnet 方法,让它去处理我们的逻辑,满足一个条件后。返回true;
\n(四) 实现事件冒泡(事件上传)的步骤。
\n
只要在自定义控件中,重写OnBubbleEvent方法,就能实现事件冒泡。因为在其子控件内部有一个OnCommand方法,它调用了RaiseBubbleEvent方法。而RaiseBubbleEvent调用了OnBubbleEvent方法。
\n
示例:
\n
protected override bool OnBubbleEvent(object sender,EventArgs e){
bool handled = false;
if(e is CommandEventArgs) {
CommandEventArgs ce = (CommandEventArgs)e;
if(ce.CommandName == “ButtonClick“) {
OnButtonClick(EventArgs.Empty);
handled =true;
}
}
return handled;
}
本节用到的示例控件代码:
\n
示例代码
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.ComponentModel.Design;
using System.ComponentModel;
/**//// <summary>
/// Class2 的摘要说明
/// </summary>
///
namespace Cnblogs.Sui
{
[DefaultEvent("Logon"),DefaultProperty("Name")]
\n
public class Class2 : WebControl,INamingContainer
{
\n
private Button _button;
private TextBox _nameTextBox;
private Label _nameLabel;
private Label _passwordLabel;
private TextBox _passwordText;
private RequiredFieldValidator _nameValidator;
private RequiredFieldValidator _passwordValidtor;
private static readonly object EventLogon=new object();
override properties#region override properties
\n
public override ControlCollection Controls
{
get {
\n
EnsureChildControls();
return base.Controls;
}
}
#endregion override properties
prorerties list#region prorerties list
[Bindable(true),Category("Appearance"),DefaultValue(""),Description("The text to display on the button")]
public string ButtonText
{
get{
EnsureChildControls();
return _button.Text;
}
set{
EnsureChildControls();
_button.Text=value;
}
}
[Bindable(true),Category("default"),DefaultValue(""),Description("the user name")]
public string Name{
get{
EnsureChildControls();
return _nameTextBox.Text;
}
set{
EnsureChildControls();
_nameTextBox.Text=value;
}
}
[Bindable(true),Category("Apearance"),DefaultValue(""),Description("Error Message of the validator used for the Name")]
public string NameErrorMessage{
get{
EnsureChildControls();
return _nameValidator.ErrorMessage;
}
set{
EnsureChildControls();
_nameValidator.ErrorMessage=value;
_nameValidator.ToolTip=value;
}
}
[Bindable(true),Category("Apperance"),DefaultValue(""),Description("the text for the name Label")]
public string NameLabel
{
get{
EnsureChildControls();
return _nameLabel.Text;
}
set{
EnsureChildControls();
_nameLabel.Text=value;
}
}
[Bindable(true),DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Password
{
get{
EnsureChildControls();
return _passwordText.Text;
}
}
[Bindable(true),Category("Appearance"),DefaultValue(""),Description("Error Message of the validator used for the Password")]
public string PasswordErrorMessage
{
get{
EnsureChildControls();
return _passwordValidtor.ErrorMessage;
}
set{
EnsureChildControls();
_passwordValidtor.ErrorMessage=value;
_passwordValidtor.ToolTip=value;
}
}
[Bindable(true),Category("Appearance"),DefaultValue(""),Description("the text for the password label")]
public string PasswordLabel
{
get{
EnsureChildControls();
return _passwordLabel.Text;
}
set{
EnsureChildControls();
_passwordLabel.Text=value;
}
}
#endregion
\n
events#region events
[Category("Action"),Description("Raised when the user chicks the login button")]
public event EventHandler Logon
{
add{
Events.AddHandler(EventLogon,value);
}
remove{
Events.RemoveHandler(EventLogon,value);
}
}
public virtual void OnLogon(EventArgs e){
EventHandler logonHandler=(EventHandler)Events[EventLogon];
if(logonHandler !=null)
{
logonHandler(this,e);
}
}
#endregion
\n
Event bubbling#region Event bubbling
//the use of event bubbling in this seenario is somewhat contrived;
//we have implemented it mainly for demostration purposes
//in this case you could instead
//raise the Logon event from an event handler wired to the
//click event or to the command event of the button control
protected override bool OnBubbleEvent(object source, EventArgs e)
{
bool handled=false;
if(e is CommandEventArgs)
{
CommandEventArgs ce=(CommandEventArgs)e;
if(ce.CommandName==”Logon”)
{
OnLogon(EventArgs.Empty);
handled=true;
}
}
return handled;
}
#endregion
override methods#region override methods
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();
\n
_passwordText=new TextBox();
_passwordText.TextMode=TextBoxMode.Password;
_passwordText.ID=”passwordTextBox”;
_passwordValidtor=new RequiredFieldValidator();
_passwordValidtor.ID=”validator2″;
_passwordValidtor.ControlToValidate=_passwordText.ID;
_passwordValidtor.Text=”*”;
_passwordValidtor.Display=ValidatorDisplay.Static;
\n
_button=new Button();
_button.ID=”button1″;
_button.CommandName=”Logon”;
this.Controls.Add(_nameLabel);
this.Controls.Add(_nameTextBox);
this.Controls.Add(_nameValidator);
this.Controls.Add(_passwordLabel);
this.Controls.Add(_passwordText);
this.Controls.Add(_passwordValidtor);
this.Controls.Add(_button);
\n
base.CreateChildControls();
}
#endregion
protected override void Render(HtmlTextWriter writer)
{
AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding,”1″,false);
writer.AddAttribute(HtmlTextWriterAttribute.Border, “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);
_nameValidator.RenderControl(writer);
writer.RenderEndTag();//TD
\n
writer.RenderEndTag();//TR
\n
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
_passwordLabel.RenderControl(writer);
writer.RenderEndTag();//TD
writer.RenderBeginTag(HtmlTextWriterTag.Td);
_passwordText.RenderControl(writer);
_passwordValidtor.RenderControl(writer);
\n
writer.RenderEndTag();//TD
\n
writer.RenderEndTag();//tr
\n
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.AddAttribute(HtmlTextWriterAttribute.Colspan,”2″);
writer.AddAttribute(HtmlTextWriterAttribute.Align,”center”);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
_button.RenderControl(writer);
writer.RenderEndTag();//td
writer.RenderBeginTag(HtmlTextWriterTag.Td);
// writer.Write(“ ”);
writer.RenderEndTag();//td
writer.RenderEndTag();//tr
writer.RenderEndTag();//Table
// base.Render(writer);
}
public Class2()
{
//
// TOD 在此处添加构造函数逻辑
//
}
}
}
\n \n