复合控件是包含两个或多个已存在控件的控件,它复用了子控件提供 的实现来进行生成、回传处理及其他功能。复合简化了控件的开发,因为通过复合,可以把许多任务委托到子控件.例如,当控件包含处理回传数据的标准控件TextBox时,就可以不必实现IPostBackDataHandler接口.同样,可以使用已有的Button控件来铺获表单的回传,而不用实现IPostBackEventHandler接口.
复合控件可以派生自Control类或WebControl类。当实现某个复合控件时,必须执行的两个关键任务是:
重载CreateChildControls方法来对子控件进行实例化、初始化、并把这些子控件添加到控件树中.但一定不能在构造器或OnInit方法中执行该逻辑.
实现System.Web.UI.INamingContainer接口,该接口在控件下创建了一个新的命名范围.
下面来分析为什么要这样实现.必须在CreateChildControls方法中创建子控件,而不是在诸如实例化或初始化这样的特定阶段创建,这样可以在控件生命周期中任何需要的时候来创建子控件.当创建一个复合控件且由其子控件来处理回传数据时,这样做特别重要.
为了确保子控件在代码访问它们之前就已经创建好了,Control类定义了EnsureChildControls保护方法.该方法检查子控件是否已经创建了,如果还没有创建,那么调用CreateChildControls方法来创建子控件.控件实现中的任何需要访问子控件的代码必须先调用EnsureChildControls方法.例如,页面用来定位子控件的FindControl方法的默认实现,就先调用了EnsureChildControls方法.注意,如果子控件没有在控件生命周期中的预先成阶段之前被创建,那么它们会在这个时候根据要求创建.这是因为在PreRender方法的默认实现中调用了所有其Visible属性值为true的控件的EnsureChildcontrols方法.
下面分析为什么必须实现INamingContainer接口.INamingContainer是一个没有任何方法的标记接口,但是它会使页面在控件下创建一个新的命名范围.当实现该接口时,控件包含的任何子控件的表示符(用UniqueID属性来表示)都要保证在页面中确实是唯一的.例如,如果页面开发者在页面上放两个复合控件的实例,第一个实例中的子控件和第二个实例中的子控件会有不同的唯一表示符,即使两个子控件集合有相同的ID属性值.如果页面需要找到某个控件来传递回传数据或回传事件时,这一点特别重要.