复合控件和事件(4)——移花接木

上一篇:复合控件和事件(3)——事件基础
上一篇描述了如何创建一个自己的控件,一切看起来都更加地接近细节,写着写着我自己都觉得是不是应该换个标题,毕竟我们是讲复合控件。那么什么是复合控件呢?
通俗地讲就是控件是以组合其他控件为基础的一种控件。更具体地讲就请看(ASP.NET 控件开发速成教程:生成复合控件)文章中的描述:

None.gif 顾名思义,复合控件是将多个其他控件聚集在某单一顶部和单一 API 下的控件。如果某个自定义控件由一个标签和一个文本框组成,就可以说该控件是一个复合控件。“复合”一词表明该控件本质上是由其他构成组件在运行时组合而成。复合控件所暴露的方法集和属性集通常(但不是必须)由构成组件的方法和属性提供,并加入一些新成员。复合控件也可以引发自定义事件,还可以处理并激起子控件所引起的事件。
复合控件需要我们继承CompositeControl类(关于为什么不是WebControl等,请同样参考( ASP.NET 控件开发速成教程:生成复合控件))
None.gif      public   class  CompositeControl4 : CompositeControl
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif
//CompositeControl4是我们自己的控件名
InBlock.gif
//CompositeControl是指System.Web.UI.WebControls.CompoiteControl类
ExpandedBlockEnd.gif
}
在第一篇 复合控件与事件(1)——基础入门,组合也是一种封装和第二篇 复合控件和事件(2)——属性,页面要回发,属性要保存中我们讨论了复合控件的属性,而这一篇我们将围绕事件话题进行展开。
我们知道既然复合控件是通过组合其他控件进行的一种封装,那么我们在复合控件中实现原来一些控件的事件的时候我们是不是就应该把重点放在原来控件的事件上呢?(如果要有自己的原创事件,请参考本系列第三篇 复合控件和事件(3)——事件基础 )因此我将这篇文章称作移花接木,希望能够引起更好的反响。
以DropDownList为例来展示这一移花接木的招术。
复合控件作为一种封装,有着自己对外对内的一些特性,对外(控件的使用者),复合控件要表现出它的属性、事件,因此它必须要有类似下面的句子:
None.gif public   event  EventHandler EventName;

对内(复合控件类的内部),要能够与子控件的一些属性和事件进行通信。
如何进行通信就成了本文的重点,我们的方法就是移花接木:
我们知道事件本身是需要订阅后才会在事件被触发的时候执行订阅的程序的。那么在复合控件的模型中,事件触发将会是什么过程呢?如果复合控件包含一个按钮,我们按下这个按钮的时候事件被触发了,如果是一个DropDownList,那么我们在下拉项选择发生改变的时候事件也触发了,如果是一个我们自己的事件,那么在原本事件应该触发的条件下事件也触发了。
但是由于复合控件的子控件被我们给封装了,所以对外部来说,一切都是private的,我们在外部则无所适从了。我们定义了自己的事件,并希望我们的事件和子控件的事件能够异曲同工。我们暴露给外部的是我们自己定义的事件,而与所有事件一样,执行事件处理程序是需要事先订阅的,因此我们要给外部的是类似:

None.gif this .CompositeControl4.EventName +=   new  EventHandler(CompositeControl4_××××EventHandler);
None.gif protected   void  CompositeControl4_××××EventHandler( object  sender , ×××EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
ExpandedBlockEnd.gif}

而事实上我们希望订阅的是子控件的相应事件。因此我们需要移花接木。
因此问题的关键就是订阅事件。因此我们可以利用下面的方式进行订阅:

None.gif          public   event  EventHandler ControlTextChanged
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif
//ControlTextChanged就是我们自己控件的事件,它将被转订阅给DropDownList控件的TextChanged。
InBlock.gif
            add
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                EnsureChildControls();
InBlock.gif                
this.ddl.TextChanged += value;
ExpandedSubBlockEnd.gif            }

InBlock.gif            remove
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                EnsureChildControls();
InBlock.gif                
this.ddl.TextChanged -= value;
ExpandedSubBlockEnd.gif            }

ExpandedBlockEnd.gif        }

至此,我们在使用完+=号后,事件就被订阅给了子控件的TextChanged事件,而TextChanged何时激发这样的问题就留给子控件自己去处理,而我们事实上已经将我们的事件订阅给它了,也就不用管它了。

另外有一点需要说明的地方:
根据运行时的需要,声明变量的过程不适合new一个对象,因为new出来之后就占内存了,但是用了移花接木这一招的时候,我们需要在事件转订阅的时候用到类似this.ddl.××××EventName的事件,而在类的内部,它必须保证是已经存在的对象,因此我们需要将其定义为类内部的全局对象。不知大家是否记得有个不成文的规定,就是在CreateChildControls()函数中new对象这一约定。而在现在这种情况似乎不适合。真的不适合么?之前我们在调用子控件属性的时候我们都用了EnsureChildControls();函数,它的真正意义就在于确保子控件的存在,子控件如果不存在则调用CreateChildControls();函数创建子控件对象。因此我们可以在CreateChildControls()中继续new我们的对象,而在类的开始(通常是这样)的时候只需要声明一下而无需实例化。这样可以更合理地利用内存,因为每次PostBack的时候我们的对象都会被重新生成,而如果我们用如上不成文的规定,我们就可以在PostBack的时候检测我们的控件是否已经存在而决定是否new一个新的对象,这有利于我们合理分配我们的宝贵的服务器资源。(因此我们在有用到我们子控件对象的时候,因为我们无法确保子控件是否已经存在,所以我们都需要显式调用EnsureChildControls()已确保我们的控件总是有对象存在的,否则将会意外地出现运行时错误。)【一种误会:多写那么多EnsureChildControls()是一种很没效率的事,事实上我们不应该以代码量为衡量的主要依据,而应该以实际上的效率为优先考虑。可以想象一个页面中有无数的控件,每次PostBack我们都需要重新去new它们,效率其实是不高的,做EnsureChildControls函数的代价应该会比较小(不然微软不会推荐这么做的,是吧?)至于代码的多少,如果仍然存在误会的话请参阅《Code complete 2(代码大全2)》中的相关说明】


代码:
CompositeControl4.cs

ContractedBlock.gif ExpandedBlockStart.gif
None.gifusing System;
None.gif
using System.Web.UI;
None.gif
using System.Web.UI.WebControls;
None.gif
using System.ComponentModel;
None.gif
None.gif
namespace ComponentWebControls
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
//添加元数据可以使其显示在工具箱中
InBlock.gif
    [ToolboxData("<{0}:CompositeControl4 runat=\"server\" />")]
InBlock.gif    
public class CompositeControl4 : CompositeControl
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
private DropDownList ddl;
InBlock.gif                
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 构造函数,为本示例提供了数据源
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public CompositeControl4()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            EnsureChildControls();
InBlock.gif            ddl.DataSource 
= GetData();
InBlock.gif            ddl.DataBind();
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public bool AutoPostBack
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                EnsureChildControls();
InBlock.gif                
return this.ddl.AutoPostBack;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                EnsureChildControls();
InBlock.gif                
this.ddl.AutoPostBack = value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 定义ControlTextChanged事件
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        [Category("ControlTextChanged"), Description("ControlTextChanged")]
InBlock.gif        
public event EventHandler ControlTextChanged
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            add
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                EnsureChildControls();
InBlock.gif                
this.ddl.TextChanged += value;
ExpandedSubBlockEnd.gif            }

InBlock.gif            remove
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                EnsureChildControls();
InBlock.gif                
this.ddl.TextChanged -= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 由 ASP.NET 页面框架调用,以通知使用基于合成的实现的服务器控件
InBlock.gif        
/// 创建它们包含的任何子控件,以便为回发或呈现做准备。 
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        protected override void CreateChildControls()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
//从当前服务器控件的 ControlCollection 对象中移除所有控件。 
InBlock.gif
            Controls.Clear();
InBlock.gif            CreateControlHierarchy();
InBlock.gif            
//删除服务器控件的所有子控件的视图状态信息。 
InBlock.gif
            ClearChildViewState();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 创建并添加子控件
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        protected virtual void CreateControlHierarchy()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
this.ddl = new DropDownList();
InBlock.gif            
this.Controls.Add(ddl);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
private ListItemCollection GetData()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            ListItemCollection lis 
= new ListItemCollection();
InBlock.gif            lis.Add(
new ListItem("Hello world!(DDLText)A""aaa"));
InBlock.gif            lis.Add(
new ListItem("Hello world!(DDLText)B""bbb"));
InBlock.gif            lis.Add(
new ListItem("Hello world!(DDLText)C""ccc"));
InBlock.gif            lis.Add(
new ListItem("Hello world!(DDLText)D""ddd"));
InBlock.gif            
return lis;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif

CompositeControl4.aspx

ContractedBlock.gif ExpandedBlockStart.gif
None.gif<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CompositeControl4.aspx.cs" Inherits="WebAppTestControls.CompositeControl4" %>
None.gif
None.gif
<%@ Register Assembly="ComponentWebControls" Namespace="ComponentWebControls" TagPrefix="cc1" %>
None.gif
None.gif
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
None.gif
None.gif
<html xmlns="http://www.w3.org/1999/xhtml" >
None.gif
<head runat="server">
None.gif    
<title>CompositeControl4</title>
None.gif
</head>
None.gif
<body>
None.gif    
<form id="form1" runat="server">
None.gif    
<div>
None.gif        
<cc1:CompositeControl4 ID="CompositeControl4_1" runat="server" OnControlTextChanged="CompositeControl4_1_ControlTextChanged" />
None.gif    
None.gif    
</div>
None.gif    
</form>
None.gif
</body>
None.gif
</html>
None.gif

CompositeControl4.aspx.cs

ContractedBlock.gif ExpandedBlockStart.gif
None.gifusing System;
None.gif
using System.Data;
None.gif
using System.Configuration;
None.gif
using System.Collections;
None.gif
using System.Web;
None.gif
using System.Web.Security;
None.gif
using System.Web.UI;
None.gif
using System.Web.UI.WebControls;
None.gif
using System.Web.UI.WebControls.WebParts;
None.gif
using System.Web.UI.HtmlControls;
None.gif
using ComponentWebControls;
None.gif
None.gif
namespace WebAppTestControls
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
public partial class CompositeControl2 : System.Web.UI.Page
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
private void WriteLine(string str)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (str != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
this.Response.Write(str + "<br>");
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif        
protected void Page_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (!IsPostBack)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
//在页面第一次加载的时候才进行赋值,页面回发将不重新赋值。
InBlock.gif
                this.CompositeControl2_1.Text1 = "Hello world!(Text1)";
InBlock.gif                
this.CompositeControl2_1.Text2 = "Hello world!(Text2)";
InBlock.gif                
this.CompositeControl2_1.DDLText = "Hello world!(DDLText)C";    //这样写是因为Text属性要求值已经存在于下拉列表框中
ExpandedSubBlockEnd.gif
            }

InBlock.gif            
//输出当前控件属性中的值
InBlock.gif
            WriteLine(this.CompositeControl2_1.Text1);
InBlock.gif            WriteLine(
this.CompositeControl2_1.Text2); 
InBlock.gif            WriteLine(
this.CompositeControl2_1.DDLText);
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/**//*DDLText的状态也将在我们的复合控件中被保存:)*/
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif

CompositeControl4.aspx.designer.cs

ContractedBlock.gif ExpandedBlockStart.gif
None.gif//------------------------------------------------------------------------------
None.gif
// <auto-generated>
None.gif
//     此代码由工具生成。
None.gif
//     运行库版本:2.0.50727.1318
None.gif
//
None.gif
//     对此文件的更改可能会导致不正确的行为,并且如果
None.gif
//     重新生成代码,这些更改将会丢失。
None.gif
// </auto-generated>
None.gif
//------------------------------------------------------------------------------
None.gif

ExpandedBlockStart.gifContractedBlock.gif
namespace WebAppTestControls dot.gif{
InBlock.gif    
InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// CompositeControl4 类。
InBlock.gif    
/// </summary>
InBlock.gif    
/// <remarks>
InBlock.gif    
/// 自动生成的类。
ExpandedSubBlockEnd.gif    
/// </remarks>

ExpandedSubBlockStart.gifContractedSubBlock.gif    public partial class CompositeControl4 dot.gif{
InBlock.gif        
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// form1 控件。
InBlock.gif        
/// </summary>
InBlock.gif        
/// <remarks>
InBlock.gif        
/// 自动生成的字段。
InBlock.gif        
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
ExpandedSubBlockEnd.gif        
/// </remarks>

InBlock.gif        protected global::System.Web.UI.HtmlControls.HtmlForm form1;
InBlock.gif        
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// CompositeControl4_1 控件。
InBlock.gif        
/// </summary>
InBlock.gif        
/// <remarks>
InBlock.gif        
/// 自动生成的字段。
InBlock.gif        
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
ExpandedSubBlockEnd.gif        
/// </remarks>

InBlock.gif        protected global::ComponentWebControls.CompositeControl4 CompositeControl4_1;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif



(关于事件的一些其他做法由于与移花接木的标题不太符合因此我决定另起篇章进行描述,移花接木事实上已经解决了问题,因此会移花接木基本上就可以搞定复合控件的事件了。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值