屯昌旅游网

服务器控件开发——组合控件(5)

       组合控件, 顾名思义就是指由2 个或2 个以上的已存在的控件组合在一起, 协同工作从而完成新功能的新的服务器控件组合控件由于

够重用已经存在控件的功能, 能够最大限度的提升我们的开发效率。组合控件就像一台精密的机器, 而起所引用的子控件就像是机械的零件,

通力合作,共同完成新的功能。下面就让我们看看这台机器是如何将各个零件完美的组装在一起的。
     提到组合控件不得不重新来审视我们第一篇所提到的Control基类,Control基类为我们开发组合控件提供了很好的支持。我们要做的只需

要根据需求填充Control 基类为我们提供的模板。 下面我们来看看这个模板的内容。

 

1.  实现INamingContainer  接口。 这个接口没有任何方法, 纳闷呢, 没有任何方法的接口有什么用啊! 当然有用了在Control类的实现中,

 Control类通过 if(this is INamingContainer) 来判断控件是否继承INamingContainer 接口。 并且根据是否为True 来决定采用何种方式

给子控件的ID赋值。 事实上, INamingContainer 的主要作用就是为了保障子控件UniqueID在整个页面的唯一性。让我们来做一个简单的试验:

我们来做一个类似与上一篇的SimpleTextBoxControl 的SimpleCompositeControl 控件。

 

Code

我想这应该够简单的, 只是将一个TextBox 与 Button 控件在页面上呈现出来。 在测试页面拖入一个的SimpleCompositeControl控件,

 并且查看HTML代码,我们将看到类似于这样的源代码。
    <span id="SampleCompositeControl1" style="display:inline-block;width:197px;">
        <input name="tbInput" type="text" id="tbInput" />
        <input type="submit" name="btnSubmit" value="提交" id="btnSubmit" />
    </span>
    
我们注意到2 个Input 标签的ID 属性 正是我们给控件所分配的ID。 这看起来没有什么问题。 但是如果我们在页面
当中存在2 个SimpleCompositeControl 呢,那岂不是在同一个页面里面存在2 个ID 为 tbInput的Input呢。 假设控件PostBack 数据,

我们又如何知道到底是那个Input 里面的数据呢?       显然这样是行不通的,我们需要INamingContainer 接口来保障我们控件的 ID 的唯

一性。将上面的控件改为实现INamingContainer 接口。
    public class SimpleCompositeControl : System.Web.UI.WebControls.WebControl,INamingContainer
同样查看源代码。 控件HTML 变为
     <span id="SampleCompositeControl1" style="display:inline-block;width:197px;">
      <input name="SampleCompositeControl1$tbInput" type="text" id="SampleCompositeControl1_tbInput" />
     <input type="submit" name="SampleCompositeControl1$btnSubmit" value="提交"   id="SampleCompositeControl1_btnSubmit" />
    </span>
控件的ID 变为 父控件ID +子控件ID的形式, INameContainer 改变了组合控件子控件Render 自身ID 的行为, 从而保障了子控件ID 在页
面的

全局唯一性。事实上, 开发组合控件必须继承INamingContainer 接口, 否则将会导致各种奇怪的错误,比如说无法保持子控件视图状态,

 无法触发Button 事件等。

2.  重载CreateChildControls 方法
    这个比较简单,如前例所示, 只要将我们的子控件的属性一个一个的定义好, 然后加入到组合控件的子控件集合中。 为了保障子控件不被多次创建
(可能由控件的继承者不当调用CreateChildControls导致)我们将我们的CreateChildControls 稍微改良一下

   protected override void CreateChildControls()
        
{
            Controls.Clear();           
// 清除子控件
            if (HasChildViewState)       //HasChildViewState 标识是否存在子控件视图信息。
                ClearChildViewState();   //清除子控件视图信息,看过我第一篇的朋友都知道, 在移除子控件的时候
                                            
//视图信息并不会被一次性移除。                                         
                                             
// 而会 是保存在一个 一个HashTable 中, ClearChildViewState方
                                             
//法将清空这个HashTable。
            tbInput = new TextBox();
            tbInput.EnableViewState 
= false;
            tbInput.ID 
= "tbInput";
            
this.Controls.Add(tbInput);

            btSubmit 
= new Button();
            btSubmit.ID 
= "btnSubmit";
            btSubmit.Text 
= "提交";
            btSubmit.CommandName 
= "submit";
            btSubmit.CommandArgument 
= "arg";
            
this.Controls.Add(btSubmit);
        }

3. EnsureChildControls 的调用,
  这个方法实在是太重要了, 尽管我在第一篇的时候已经把代码贴出来过, 我想我有必要再贴出来让大家看看:

Code

该方法的本质就是调用CreateChildControls() 创建子控件集合, 只不过他做了必要的限制, 保障子控件不会被重复创建。 在Control 基类中,

有2 处地方调用了这个方法。第一个地方在Control 生命周期的Load 事件之后, PreRender 之前。 另一个就是在用户调用FindControl 的时候,

显然, 为了保障用户在Load 事件以及之前的事件当中的调用能够获取正确的结果,首先必须要调用  EnsureChildControls() 创建子控件。
根据以上的分析,我们可以得出这样的结论, 组合控件中任何与子控件发生交互的属性或方法, 只要我们不能确定用户将在什么时候调用。

都需要首先调用EnsureChildControls方法。比如我们为控件增加一个Text 属性。 这个属性的值与子控件中的textbox 值保持一致, 我们可以这样写:

 

Code

4. 重载Render 方法。 控制控件的显示样式和布局。
   子控件是有了, 但是所有的子控件简单的叠加在一起显然不是我们所希望的, 我们可以通过重载Render 方法来控制子控件的布局,比如我们

可以将子控件放到一个table 里面来控制子控件的位置。

 

Code

5 事件冒泡机制,
所谓事件冒泡机制就是将子控件的事件Mapping 为组合控件事件的一种机制。 首先我们利用冒泡机制来为我们的控件添加一个submit 事件,

然后再深入的探讨一下其中的机制。首先还是老方法为控件声明submit 事件

 

Code

声明之后我们该在什么地方引发事件呢?Control 基类为我们提供了一个OnBubbleEvent 的事件冒泡事件。

 

Code

设置一下断点, 启动调试,可以发现Submit 事件完美的被激发了. 看起来相当神奇,.NET 是如何做到这一点的呢?我尝试在Control 基类能找到了点
蛛丝马迹, 果然让我找到了RaiseBubbleEvent 方法。

 

Code

对照Button 控件的相关实现:

 

Code
不用我说了吧,神话就此终止。

控件开发系列, 写到这里, 感觉有点写不下去了, 一方面工作又紧张起来了,时间越来越不够了, 另一方面是由于有些东西自己也没有深入理解,

要花很多的时间去Research。
但是无论如何我都会尽自己最大的努力坚持下去, 在这里给自己打一下气。下一篇 服务器控件开发之复杂属性与视图状态管理 可能要等上一阵子才能
出来, 透透气先。
 


 

posted on 2008-07-28 16:05  welkin  阅读(943)  评论(3编辑  收藏  举报

导航

屯昌旅游网