Asp.net控件开发(九)之复合控件(3)事件机制

本文详细解析了复合控件中的事件冒泡机制,包括为何需要事件上传、复合控件事件冒泡的概念、实现原理及具体步骤。通过示例代码展示了如何在自定义控件中实现事件冒泡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:
前面讲到了复合控件的视图,今天将进入其复合控件的事件机制世界
本节将讲述:

  1. 为什么要事件冒泡(事件上传)
  2. 什么是复合控件事件冒泡(事件上传)
  3. 复合控件事件的实现原理
  4. 实现事件冒泡(事件上传)的步骤。

正文:

(一)为什么要事件上传

       复合控件中包含子控件,这就使得复合控件的事件处理变得复杂起来。显而易见,在复合控件的事件实现过程中,需要面临的最大问题是:由于不允许开发人员直接访问子控件(虽然通过Controls集合访问的方法可以实现,但是破坏了程序的封装性,因此是不被允许的),如果子控件的事件不能作为顶级事件引发,那么将无法实现子控件的事件处理。简单的说,即如何实现子控件的事件上传。所谓事件上传是指把子控件的事件暴露为顶级事件,这样父控件可以检查到事件,并按照定义来执行相关事件处理程序.

 

(二) 什么是复合控件事件冒泡(事件上传)

       其核心是使用ASP.NET 2.0框架提供的事件上传机制。这种机制允许子控件将事件沿其包容层次结构向上传播到合适的位置引发,并且允许将事件处理程序附加到原始控件以及公开冒泡的事件的控件上

 

 

(三)复合控件的实现原理

      页面框架提供了一个事件上传架构,通过它控件可能把某个事件上传(bubble)到控件层次,一个上传事件可能在引发它时或其他时候来处理,更方便的处理时机是在控件树的高层,复合控件可能用这个特征把子控件上传的事件暴露为顶层事件。例如:DataList控件把包含在ItemTemplate 中的Button控件的Command事件暴露为顶层的ItemCommand事件。而command事件(

     它的事件数据类派生自CommandEventArgs)由asp.net的内建控件上传的惟一事件,也可以实现初始化上传的其他事件。

事件上传由OnBubbleEvent 和RaiseBubbleEvent方法来启动的,这些方法在Control类中的定义如下:

 

// 定义复合控件的事件是否沿页面的UI 服务器控件层次结构向上传递。 

// 如果子控件的事件向上传递,则为true;否则为false。默认值为false 

protected   virtual   bool  OnBubbleEvent( object  source,EventArgs args) 



return false

}
 

 

 

 

// 将所有事件源及其信息分配给控件的父级。
protected   void  RaiseBubbleEvent( object  source, EventArgs args) 



for (Control control = this.Parent; control != null; control = control.Parent) 



if (control.OnBubbleEvent(source, args)) 



return

}
 

}
 

}

 

   默认的情况下,初始化上传事件自动被上传通过控件层次,就像RaiseBubbleEvent方法定

义以及OnBubble默义实现中看到的一样。

    为了处某个已经上传的事件,应该重载OnBubbleEvent 方法,复合控个把通常包含

多个上传事件的子控件。事件也可以从控件层次的更底层(子控件的子控件)上传。可以用

上传给OnBubbleEvent 方法的参数来确定处理什么事件。在处理完事件之后,如果想要停

止事件进一步上传,那么可以在OnBubbleEvent 方法中返回true来实现。

      处理上传事件的方法之一是根据上传的事件来引发新的事件,这样页面开发者可以

把上传事件作为控件的顶层事件处理,通过在控件中定义一个事件,并在 OnBubbleEvent

方法中引发事件作为控个把的顶层事件处理,通过在控件中定义一个事件。并在

OnBubbleEvent方法中引发事件。就可以把一个上传的事件暴露为控件的顶层事件。

示例程序:

通过捕获Button子控件的Command事件,并引发自已的Logon事件:

Protected  override   bool  OnBubbleEvent( object  source,EventArgs e)

bool handled=false

if(e is CommandEventArgs)

CommandEventArgs ce
=(CommandEventArgs)e; 

If(ce.CommandName
="Login")

OnLogin(EventArgs.Empty); 

Handled
=true

}
 

}
 

}
 

那么我怎么知道OnBubbleEvent被初始化呢。如何把一个button事件上上传到顶层事件

呢。引发事件On<EventName>方法中调用RaiseBubbleEvent方法。

在button中OnCommand的内部实现。

 

protected   virtual   void  OnCommand(CommandEventArgs e) 



CommandEventHandler handler 
= (CommandEventHandler) base.Events[EventCommand]; 

if (handler != null



handler(
this, e); 

}
 

base.RaiseBubbleEvent(this, e); //这里冒泡事件传给顶层事件。 

}
 

而我们知道RaiseBubbleEvent调用了OnBubbleEvent方法(参见前面)。。然后我们就去重写了OnBubbleEvnet 方法,让它去处理我们的逻辑,满足一个条件后。返回true;

 

(四) 实现事件冒泡(事件上传)的步骤。

     只要在自定义控件中,重写OnBubbleEvent方法,就能实现事件冒泡。因为在其子控件内部有一个OnCommand方法,它调用了RaiseBubbleEvent方法。而RaiseBubbleEvent调用了OnBubbleEvent方法。

示例:

 

 

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;
}
 

本节用到的示例控件代码:

示例代码
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")]

    
public class Class2 : WebControl,INamingContainer
    
{

        
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


        
public override ControlCollection  Controls
  
{
        
get 

        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


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


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();

            _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;

            _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);

           
            
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

            writer.RenderEndTag();
//TR


            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _passwordLabel.RenderControl(writer);
            writer.RenderEndTag();
//TD
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _passwordText.RenderControl(writer);
       
            _passwordValidtor.RenderControl(writer);

            writer.RenderEndTag();
//TD

            writer.RenderEndTag();
//tr

            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("&nbsp");
            writer.RenderEndTag();//td
            writer.RenderEndTag();//tr
            writer.RenderEndTag();//Table
        
//    base.Render(writer);
}

        
public Class2()
        
{
            
//
            
// TODO: 在此处添加构造函数逻辑
            
//
        }

    }

}

 

 后记:

1.net组件开发系列(—)之武术系列-----------马步功   之基本功

2.net组件开发系列(—)之武术系列--------太极拳  开发ajax控件

3..net组件开发系列之武术系列 武术招数 控件生命周期与控件事件机制

4..net控件开发系列 事件处理机制 三个接口两个方法

5..net控件开发(五)之 深入理解控件的呈现原理

6..net控件开发(六)之深究控件样式

7..net控件开发(七)之复合控件(1)概念基础篇

8.Asp.net 控件开发(八)之复合控件(二) 复合控件视图幕后机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值