热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ASP.NET2.0服务器控件之复合控件事件

在上面一篇文章中,我们讨论了有关创建复合控件的基本理论,并且通过一个典型应用掌握了复合控件的呈现方法。本文将继续讲解有关创建复合控件的内容,重点是为复合控件实现事件的具体方法。复合控件的事
上面一篇文章中,我们讨论了有关创建复合控件的基本理论,并且通过一个典型应用掌握了复合控件的呈现方法。本文将继续讲解有关创建复合控件的内容,重点是为复合控件实现事件的具体方法。

   复合控件的事件处理简介

  谈到自定义控件的事件处理问题,这在前面的系列文章中已经进行讲解。由前文可知,实现控件事件的核心主要是定义事件属性结构和事件处理 程序等。然而,这些内容是构建所有自定义 服务器控件的基础。仅仅依靠这些方法是无法实现复合控件的事件的。因为,复合控件中包含子控件,这就使得复合控件的事件处理变得复杂起来。显而易见,在复合控件的事件实现过程中,需要面临的最大问题是:由于不允许开发人员直接访问子控件(虽然通过Controls集合访问的方法可以实现,但是破坏了程序的封装性,因此是不被允许的),如果子控件的事件不能作为顶级事件引发,那么将无法实现子控件的事件处理。简单的说,即如何实现子控件的事件上传。所谓事件上传是指把子控件的事件暴露为顶级事件,这样父控件可以检查到事件,并按照定义来执行相关事件处理程序。

  由以上内容可知,复合控件的事件处理,主要是实现子控件事件上传的过程。下面将介绍两种常用的事件上传实现方法:包含法和冒泡法。这两种方法实现机理不同,然而,完成了同样的功能。在下文中,我们将通过理论结合示例的方法展开讲解。

   包含法

  包含法的核心是,通过在子控件的事件处理程序中调用复合控件的顶层事件处理程序,以完成子控件的事件上传。在执行过程中,当引发子控件事件后,子控件的事件处理程序将自动调用相关顶层事件处理程序。

  包含法的关键步骤如下:

  · 在CreateChildControls方法中,为子控件添加事件处理程序。

  · 定义顶层事件及其事件处理程序OnEventName。

  · 在子控件的事件处理程序中调用OnEventName。

  · 定义事件属性结构。

  由以上内容可知,包含法的步骤与前面文章中介绍的实现控件的方法基本类似。关键是多出了一个在CreateChildControls方法中,为子控件添加事件处理程序的步骤。为了读者能够更加清晰的理解包含法,下文列举了一个利用包含法为复合控件实现事件的示例。

  首先,利用上一篇文章中介绍的复合控件呈现方法,创建一个由文本框和按钮组成的复合控件,然后,使用上文所述的包含法,将按钮的Click事件上传为顶层事件Submit。下面列举了该控件的源代码。

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;
namespace WebControlLibrary{
 public class CompositeEvent : CompositeControl {
  //声明变量
  private Button _button;
  private TextBox _textBox;
  private static readonly object EventSubmitKey = new object();
  //定义属性ButtonText,用于指定按钮上的文字
  [
   Bindable(true), Category("Appearance"), DefaultValue(""), Description("获取或设置显示显示在按钮上的文字")
  ]
  public string ButtonText {
   get {
    EnsureChildControls();
    return _button.Text;
   }
   set {
    EnsureChildControls();
    _button.Text = value;
   }
  }
  //定义属性Text,表示文本框的输入

  [
   Bindable(true), Category("Appearance"), DefaultValue(""), Description("获取或设置文本框输入文本")
  ]
  public string Text {
   get {
    EnsureChildControls();
    return _textBox.Text;
   }
   set {
    EnsureChildControls();
    _textBox.Text = value;
   }
  }
  // 实现事件属性结构
  public event EventHandler Submit {
   add {
    Events.AddHandler(EventSubmitKey, value);
   }
   remove {
    Events.RemoveHandler(EventSubmitKey, value);
   }
  }
  // 实现OnSubmit
  protected virtual void OnSubmit(EventArgs e) {
   EventHandler SubmitHandler = (EventHandler)Events[EventSubmitKey];
   if (SubmitHandler != null) {
    SubmitHandler(this, e);
   }
  }
  // 实现Submit事件引发的事件处理程序
  private void _button_Click(Object source, EventArgs e) {
   OnSubmit(EventArgs.Empty);
  }
  // 重写ICompositeControlDesignerAccessor接口的RecreateChildContrls方法
  protected override void RecreateChildControls() {
   EnsureChildControls();
  }

  //重写CreateChildControls方法,将子控件添加到复合控件中

  protected override void CreateChildControls() {
   Controls.Clear();
   _button = new Button();
   _textBox = new TextBox();
   _button.ID = "btn";
   _button.Click += new EventHandler(_button_Click);
   this.Controls.Add(_button);
   this.Controls.Add(_textBox);
  }

  //重写Render方法,呈现控件中其他的HTML代码

  protected override void Render(HtmlTextWriter output) {
   output.AddAttribute(HtmlTextWriterAttribute.Border, "0px");
   output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "5px");  
   output.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0px");  
   output.RenderBeginTag(HtmlTextWriterTag.Table);
   output.RenderBeginTag(HtmlTextWriterTag.Tr);   
   output.RenderBeginTag(HtmlTextWriterTag.Td);
   _textBox.RenderControl(output);
   output.RenderEndTag();
   output.RenderBeginTag(HtmlTextWriterTag.Td);
   _button.RenderControl(output);
   output.RenderEndTag();
   output.RenderEndTag();
   output.RenderEndTag();
  }
 }
}

  如上代码所示,复合控件CompositeEvent中包含两个属性:Text和ButtonText。前者用于获取或者设置文本框中的文本内容,后者用于获取或者设置按钮的显示文本。另外,复合控件类中还实现了一个Submit事件。相关重要逻辑包括:

  第一、在重写CreateChildControls方法中,为子控件Button添加事件处理程序_button_Click。

  第二、和普通的自定义事件一样,为复合控件定义一个顶层事件Submit。这其中包括定义事件属性结构Submit,定义事件处理程序OnSubmit。

  第三、实现_button_Click事件处理程序,调用顶层事件Submit的事件处理程序OnSubmit。

下面是为测试复合控件CompositeEvent而创建的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">
<script runat="server">
void demo1_Submit(object sender, EventArgs e) {
  lbMessage.Text = "您刚才输入的是:" + demo1.Text;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>为复合控件实现事件-包含法</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<Sample:CompositeEvent ID="demo1" runat="server" ButtOnText="提交" OnSubmit="demo1_Submit" />
<br />
<asp:Label ID="lbMessage" runat="server"></asp:Label>
</div>
</form>
</body>
</html>

  示例效果如图1所示。


图1 效果图

  在以上应用中,当用户单击"提交"按钮之后,将引发demo1_Submit处理程序的执行,由此显示文本框输入内容。

  需要读者注意的是其内部执行过程。控件定义的顶层事件是Submit,其对应事件处理程序是OnSubmit,而不是_button_Click。_button_Click是复合控件的子控件的Click事件处理程序。由于在控件实现的代码中定义了子控件的事件处理程序_button_Click,所以,当用户单击按钮后,将首先执行_button_Click,该方法要求调用顶层事件Submit的事件处理程序OnSubmit。从外部来看,子控件的事件即暴露为顶层事件。

  从以上实现过程来看,包含法使用的是程序代码上的小技巧来实现事件上传功能。下面介绍的冒泡法则与此不同,它使用.NET框架提供的事件上传机制来完成子控件的事件上传

冒泡法

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

  冒泡法的实现,使用Control基类中专门用于事件上传的两个方法:OnBubbleEvent和RaiseBubbleEvent。它们的声明如下所示。

// OnBubbleEvent方法定义
protected virtual bool OnBubbleEvent(object source,EventArgs args){ return false;}
// RaiseBubbleEvent方法定义
protected void RaiseBubbleEvent(object source,EventArgs args){
 Control currentTarget = _parent;
 while(currentTarget != null) {
  if(currentTarget.OnBubbleEvent(source,args) { return; }
  currentTarget = currentTarget.Parent;
 }
}

  OnBubbleEvent方法用于确定子控件的事件是否沿复合控件层次结构向上传递。在该方法中,参数source表示事件源,参数args表示包含事件数据的EventArgs对象。如果子控件的事件向上传递,则为true;否则为false。默认值为false。RaiseBubbleEvent方法用于将所有事件源及其信息分配给控件的父级,并且不能被重写。尽管无法重写此方法,但创作的控件可以通过重写 OnBubbleEvent 方法处理或引发冒泡事件。

  复合控件的事件冒泡主要存在以下两种情况:

  情况一:控件停止事件冒泡并引发和/或处理该事件。引发事件需要调用将事件调度给侦听器的方法。若要引发冒泡的事件,控件必须重写OnBubbleEvent以调用引发此冒泡的事件的OnEventName方法。引发冒泡的事件的控件通常将冒泡的事件公开为顶级事件。以下代码引发一个冒泡的事件。

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

  情况二:控件进行一些处理并继续使事件冒泡。若要实现这一点,控件必须重写OnBubbleEvent,并从OnBubbleEvent调用RaiseBubbleEvent。以下代码在检查事件参数的类型后使事件冒泡。

protected override bool OnBubbleEvent(object sender,EventArgs e){
 if(e is CommandEventArgs) {
  CommandEventArgs ce = (CommandEventArgs)e;
  RaiseBubbleEvent(this,ce);
  return true;
 }
 return false;
}

  为了使读者能够更好的理解冒泡法,下面利用冒泡法对上一小节示例进行了重新实现。控件类的源代码如下所示,其中没有改变的部分使用省略号表示。

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;
namespace WebControlLibrary{
 public class CompositeEvent : CompositeControl {
  //声明变量
  private Button _button;
  private TextBox _textBox;
  private static readonly object EventSubmitKey = new object();
  //定义属性ButtonText,用于指定按钮上的文字
  [
   Bindable(true), Category("Appearance"), DefaultValue(""), Description("获取或设置显示显示在按钮上的文字")
  ]
  public string ButtonText { ...... }
  //定义属性Text,表示文本框的输入

  [
   Bindable(true), Category("Appearance"), DefaultValue(""), Description("获取或设置文本框输入文本")
  ]

  public string Text { ...... } // 实现事件属性结构
  public event EventHandler Submit
  {
   add {
    Events.AddHandler(EventSubmitKey, value);
   }
   remove {
    Events.RemoveHandler(EventSubmitKey, value);
   }
  }
  // 实现OnSubmit

  protected virtual void OnSubmit(EventArgs e) {
   EventHandler SubmitHandler = (EventHandler)Events[EventSubmitKey];
   if (SubmitHandler != null) { SubmitHandler(this, e); }
  }
  // 删除_button_Click
  // 重写ICompositeControlDesignerAccessor接口的RecreateChildContrls方法
  protected override void RecreateChildControls() { ...... }
  //重写CreateChildControls方法,将子控件添加到复合控件中
  protected override void CreateChildControls() {
   Controls.Clear();
   _button = new Button();
   _textBox = new TextBox();
   _button.ID = "btn";
   _button.CommandName = "Submit";
   this.Controls.Add(_button);
   this.Controls.Add(_textBox);
  }
  // 重写OnBubbleEvent方法,执行事件冒泡
  protected override bool OnBubbleEvent(object source, EventArgs e) {
   bool handled = false;
   if (e is CommandEventArgs) {
    CommandEventArgs ce = (CommandEventArgs)e;
    if (ce.CommandName == "Submit") {
     OnSubmit(EventArgs.Empty);
     handled = true;
    }
   }
   return handled;
  }
  //重写Render方法,呈现控件中其他的HTML代码
  protected override void Render(HtmlTextWriter output) { ...... }
 }
}

  本例的CompositeEvent类与上小节中的CompositeEvent类实现了同一功能。就控件呈现方面,两个类没有任何差别,差别主要表现在对于复合控件的事件实现方面。差别一:在本例的CreateChildControl方法中,为子控件_button设置了CommandName属性,其属性值为Submit。差别二:删除了_button_Click事件处理程序。差别三:重写了Control基类的OnBubbleEvent方法,检查事件参数是否是CommandEventArgs类的实例。如果是,使用事件参数的CommandName成员确定是否需要引发事件处理程序OnSubmit,并返回true。

   小结

  本文重点介绍了复合控件的事件实现方法,并通过典型示例说明了这些实现方法的具体应用。总体而言,为复合控件实现事件并不是特别困难的事情。关键是开发人员必须在领会为普通控件实现事件的基础之上,掌握包含法和冒泡法的实现要点。
推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
author-avatar
手机用户2602940163
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有