上一篇复合控件和事件(4)——移花接木
先征求个题目吧,这个名字越来越难起了,暂时就先名为《你Call我应》吧。有好的建议记得留言给我~
上一篇提到了“移花接木”之术,原理就是简单地把自己的订阅过程交给别人,也可以称之为“代购”吧。
我们知道我们经常在事件的写法中包含OnEventName的方法(用On打头算是一个约定吧)。它是一个函数,它被调用后它的内容将被执行。找一个经典的写法进行引述吧:
protected virtual void OnControlTextChanged(ControlEventArgs e)
{
if (ControlTextChanged != null)
{
ControlTextChanged(this, e);
}
}
这段代码是本节示例中我写的关于事件处理程序的部分,其中virtual并不是必须的,ControlTextChanged 是我们定义的事件,看看调用OnControlTextChanged后我们都做了什么呢?
1、首先判断事件是否为空,如果不为空执行步骤2
2、通过事件委托,执行被订阅到事件的事件处理程序(通常这些事件处理程序就是我们在aspx页面中通过属性面板添加的那些事件处理程序(但不表示仅仅aspx中可以如此使用,任何地方显式或隐式使用了事件订阅语法,将事件处理程序订阅给事件的所有此类事件处理程序都将被执行,执行顺序可以类似“先进先出”))
3、事件处理程序被唤醒,依次执行相应事件处理程序(似乎目的达到了!exciting!)
假设我们的控件已经完成了,(想像一下)现在向页面中添加了我们控件,并添加事件处理程序,并订阅它(asp2.0中这个过程已经对您相对更透明了,参见复合控件和事件(3)——事件基础中的相关内容),我们把事件处理程序订阅到我们自己定义的事件上,运行我们的程序,激活事件(本示例中继续以下拉列表框来演示,这里的事件就是“下拉框中的文字变化”),我们实际上触发的是子控件(这里是下拉框,这个下拉框是包含在我们的控件中的一个private DropDownList ddl;)的相应事件,只是这时候它们的名字被我们起的刚好近似罢了。因此这时候激活的是子控件的事件处理程序。聪明的读者一定想到了解决办法:
1、提供一个事件处理程序,并将其订阅给子控件;
2、如果有数据参数的,则将参数进行传递
3、在事件处理程序中执行我们复合控件的事件的相关事件处理程序(上文中一直在提的OnEventName函数)
因此我们有如下代码:
1 /**//// <summary>
2 /// 创建并添加子控件
3 /// </summary> 4 protected virtual void CreateControlHierarchy()
5 {
6 this.ddl = new DropDownList();
7 this.ddl.TextChanged += new EventHandler(ddl_TextChanged);
8 this.Controls.Add(ddl);
9 }
10
11 private void ddl_TextChanged(object sender, EventArgs e)
12 {
13 ControlEventArgs cea = new ControlEventArgs();
14 cea.Message = "ControlTextChanged!";
15 OnControlTextChanged(cea);
16 }
其中第7行做到了步骤1,第13-14行做到了第2步,第15行做到了第3步。
其实以上只是一些必要步骤,你还能做的事更多了,因为程序是因为执行了OnEventName的函数才执行了你在外部写的事件处理程序,这样的话,这里可以被理解成是一个执行程序的Driver,那么你可以在这里写下自己的运算逻辑,比如这里的第14行,当然写死了可能不太好,那你外部定义一个比如作者名之类的再通过配置文件反射进来也是个不错的选择,另外你可以让一个下拉事件同时执行多个事件,这样你在外部如果定义了三个事件处理程序,并希望它们会在事件激发(下拉框值变化)的时候执行多项事件从处理程序,也是可以的,只要在这里写上多个OnEventName就可以了,甚至你可以在这里调用工厂来执行不同的事件处理程序等,不过至于意义有多大,就得看你的应用场景了。
这一些特点在上一篇中所涉及的移花接木中则无法实现,本文中所提的方法不但可以满足需求,还能移花接木之上有所突破。
CompositeControl5.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace ComponentWebControls
{
//添加元数据可以使其显示在工具箱中
[ToolboxData("<{0}:CompositeControl5 runat=\"server\" />")]
public class CompositeControl5 : CompositeControl
{
private DropDownList ddl;
/**//// <summary>
/// 构造函数,为本示例提供了数据源
/// </summary>
public CompositeControl5()
{
EnsureChildControls();
ddl.DataSource = GetData();
ddl.DataBind();
}
public bool AutoPostBack
{
get
{
EnsureChildControls();
return this.ddl.AutoPostBack;
}
set
{
EnsureChildControls();
this.ddl.AutoPostBack = value;
}
}
/**//// <summary>
/// 定义ControlTextChanged事件
/// </summary>
[Category("ControlTextChanged"), Description("ControlTextChanged")]
public event ControlEventHandler ControlTextChanged;
protected virtual void OnControlTextChanged(ControlEventArgs e)
{
if (ControlTextChanged != null)
{
ControlTextChanged(this, e);
}
}
/**//// <summary>
/// 由 ASP.NET 页面框架调用,以通知使用基于合成的实现的服务器控件
/// 创建它们包含的任何子控件,以便为回发或呈现做准备。
/// </summary>
protected override void CreateChildControls()
{
//从当前服务器控件的 ControlCollection 对象中移除所有控件。
Controls.Clear();
CreateControlHierarchy();
//删除服务器控件的所有子控件的视图状态信息。
ClearChildViewState();
}
/**//// <summary>
/// 创建并添加子控件
/// </summary>
protected virtual void CreateControlHierarchy()
{
this.ddl = new DropDownList();
this.ddl.TextChanged += new EventHandler(ddl_TextChanged);
this.Controls.Add(ddl);
}
private void ddl_TextChanged(object sender, EventArgs e)
{
ControlEventArgs cea = new ControlEventArgs();
cea.Message = "ControlTextChanged!";
OnControlTextChanged(cea);
}
private ListItemCollection GetData()
{
ListItemCollection lis = new ListItemCollection();
lis.Add(new ListItem("Hello world!(DDLText)A", "aaa"));
lis.Add(new ListItem("Hello world!(DDLText)B", "bbb"));
lis.Add(new ListItem("Hello world!(DDLText)C", "ccc"));
lis.Add(new ListItem("Hello world!(DDLText)D", "ddd"));
return lis;
}
}
}
CompositeControl5.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CompositeControl5.aspx.cs" Inherits="WebAppTestControls.CompositeControl5" %>
<%@ Register Assembly="ComponentWebControls" Namespace="ComponentWebControls" TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>CompositeControl5</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<cc1:CompositeControl5 ID="CompositeControl5_1" runat="server" OnControlTextChanged="CompositeControl5_1_ControlTextChanged">
</cc1:CompositeControl5>
</div>
</form>
</body>
</html>
CompostieControl5.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
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;
namespace WebAppTestControls
{
public partial class CompositeControl5 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
this.CompositeControl5_1.AutoPostBack = true;
}
}
protected void CompositeControl5_1_ControlTextChanged(object sender, ComponentWebControls.ControlEventArgs e)
{
this.Response.Write(e.Message);
}
}
}
CompositeControl5.aspx.designer.cs
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.1318
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace WebAppTestControls {
/**//// <summary>
/// CompositeControl5 类。
/// </summary>
/// <remarks>
/// 自动生成的类。
/// </remarks>
public partial class CompositeControl5 {
/**//// <summary>
/// form1 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
/**//// <summary>
/// CompositeControl5_1 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::ComponentWebControls.CompositeControl5 CompositeControl5_1;
}
}