当前位置: BOLT界面引擎 > 知识库文章 > 模板指南

模板指南

作者:李亚星

                        模板使用指南

一、模板的定义

目前支持三种模板:

a)         Hostwnd模板,节点名为hostwndtemplate

b)         Object模板,节点名为objecttemplate

c)         ObjectTree模板,节点名为objtreetemplate

 

以上三种模板都是xlue根节点下面的二级节点,这里需要注意的是xlue下面的二级节点配置的HostWndObjectObjectTree都是模板,也就是不存在单纯的名为hostwndobjectobjecttree的节点了。例如:

 

<xlue>

         <objtemplate id="xxxx" class="xxxx">

                   ……..

         </objtemplate>

 

         <hostwndtemplate id=”xxx” class=”xxxx”>

                   ……..

         </hostwndtemplate>

 

         <objtreetemplate id=”xxx”>

                   ……..

         </objtreetemplate>

</xlue>

 

关于模板是否需要配置class字段

a)         在配置模板时候,hostwndtemplate一般情况下需要配置class,因为调用CreateInstance来实例化模板时候,需要知道该hostwndclass;但假如该模板不是用来实例化,只是用来被其它模板引用的,那么class可以不用配置,但是同时该hosttemplate就失去了被实例化的能力了。

b)         objtreetemplate 不需要配置class,因为objtree是一系列object组合起来的概念,没有具体的class,所以不需要指定class

c)         objtemplate 可以配置class,也可以不配置,因为在object A引用object模板B时候,并不会去判断模板B的类别是否和当前的object的类别A是否匹配。并且由于objtemplate不可以单独被实例化,只能用作引用,所以class可有可无。一般作为提示作用,可以参照下面的指导:

                                                     i.              假如写了一个objtemplate,并且希望告诉其他使用该模板的用户,该objtemplate期望被相同类型的obj引用,那么可以显示写明class”xxx”,这样使用者在使用该模板时候,就会注意到。

                                                   ii.              假如写了一个objtemplate,并且希望告诉其他使用该模板的用户,该objtemplate可以被任何类型的的obj引用,比如一个textobject可以引用,imageobject也可以引用,那么最好不要配置class,以免误导模板使用者。

二、模板的引用

a)         模板引用的方法:在需要引用模板的其它模板或者object节点的属性里面,使用templateid=”指定模板的id”

b)         模板引用的一个基本原则是模板只可以被同类型的object或者其他模板来引用。

c)         hostwndtemplate 只可以被其它的hostwndtemplate来引用,例如

 

<hostwndtemplate id="BoltFrameWnd" >

                   <attr>

                            …….

                   </attr>

< /hostwndtemplate>

 

<hostwndtemplate id=”Main” class=”FrameHostWnd”  templateid=” BoltFrameWnd”>

         <attr>

                   ……..

         </attr>

</hostwndtemplate>

 

上面例子中,idMain的模板引用了idBoltFrameWnd的模板,并且两个模板的类型都一致,都为hostwndtemplate

 

d)         objtreetemplate只可以被其它的objtreetemplate模板来引用,例如

 

<objtreetemplate id=”GlobalObjTree”>

         <attr>

                   ……

         </attr>

         <obj id=”xxx” templateid=”xxxx”>  //这里的obj也引用了一个模板

                   …….

         </obj>

</objtreetemplate>

 

<objtreetemplate id=”MainObjTree” templateid=” GlobalObjTree”>

         <attr>

                   ……

         </attr>

         <obj id=”yyy” >

                   ……

         </obj>

</objtreetemplate>

 

上面的例子中,idMainObjTree的模板引用了idGlobalObjTree的模板,并且两个模板的类型都一致,都为objtreetemplate

 

e)         objtemplate相比上面两个模板,情况要复杂些。Objtemplate可以被以下三种情况来引用:

 

在说明之前,我们先配置好一个objtemplate模板:

<objtemplate id="AnyObj" >

         <attr>

                   ……

         </attr>

         <eventlist>

                   <event name=”OnBind”>

                            …….

                  </event>                   

         </eventlist>    

</objtemplate>

 

                                       i.              被其它的objtemplate来引用,例如

<objtemplate id="ImageObj" templateid=”AnyObj” >

     <attr>

              ……

     </attr>

</objtemplate>

 

                                     上面又定义了一个objtemplate模板,其中引用了idAnyObj的模板

                                     ii.              objtree里面的obj引用,例如

<objtreetemplate  id="objtree">

         <attr>

                   ……

         </attr>

        

         <obj  id="Bkn" class="ImageObject" templateid=”AnyObj”>

                   <attr>

                            ……

                   </attr>

                   <eventlist>

                            ……

                   </eventlist>

         </obj>

</objtreetemplate>

                                    iii.              control定义里面的objtemplate下面的obj引用,例如

 

<control class="LoginPanel">

                   <attr_def>

                            ……

                   </attr_def>

                   <method_def>

                            ……

                   </method_def>

                   <event_def>

                            ……

                   </event_def>

                  

                   <objtemplate>

                            <children>

                                 <obj id="Icon" class="ImageObject" templateid=”AnyObj”>

                                               ……

                                     </obj>

                            </children>

                   </objtemplate>

</control>

f)          模板的引用不可以出现环状引用,比如模板A引用了模板B,模板B又引用了模板C,而模板C又反过来引用了模板A,这样便形成了一个环状的引用;程序内对环状引用做了处理,不会导致无限递归,但是可能会导致不可预期的结果出现。

顺便说一下我们对于环状引用的处理逻辑:模板展开最多到当前正在开展的节点。比如要展开模板A了,由于A引用了B,此时会先去展开B,而B又导致C的展开,C会不会又导致A展开?答案是不会,因为目前A是正在展开的节点,所以C此时不会展开了,这个结果可能是使用者所预期的,也可能是写错了,但是建议不要这么做,除非你很明确这样做会导致的结果。

 

另外需要注意的是,所有模板只会展开一次,所以一旦A展开了,那么BC也就同时展开了,以后再使用模板BC时候,不会再次展开了,所以哪怕C模板以后再单独使用,也不会再去合并所引用的模板A了。

三、模板的合并merge

我们先事先给出两个称呼:假如模板或者obj A使用了模板B,那么我们称呼A为实体,B为模板,下面在讨论合并过程中,会经常用到“实体和模板”这两个概念,简化描述。

 

模板的合并按照最小合并粒度分割,可以分为以下几个粒度:

a)         属性列表的合并attr节点)

属性列表合并的依据是:属性名,也就是节点名称相同的属性进行合并,比如下面例子中的lefttop

属性列表的合并策略是:对于某个属性,假如实体和模板都存在,那么实体的会覆盖模板的;假如模板的存在但是实体的不存在,那么实体的将继承模板的;

假如只是实体的存在,那么很明显会使用实体的。下面是一个例子:

 

 

<objtemplate id=”A”>

         <attr>

                   <left>10</left>

                   <top>10</top>

         </attr>

</objtemplate>

 

<objtemplate id=”B” templateid=”A”>

         <attr>

                   <left>20</left>

                   <width>1000</width>

                   <height>800</height>

         </attr>

</objtemplate>

 

那么模板B merge之后,将会是如下定义

 

<objtemplate id=”B” >

         <attr>

                   <left>20</left>   // 覆盖模板Aleft属性

                   <top>10</top>         // 继承模板Atop属性

                   <width>1000</width>     // 使用自有的width属性

                   <height>800</height>     // 使用自有的height属性

         </attr>

</objtemplate>

 

b)        事件event的合并

event的合并策略:属性name值相同的进行合并,也就是<event name=”xxx”>

xxx相同的两个event进行合并

 

注意:Event的合并可能会出现多个chunk,例如:

<event name=”OnBind”>

         XLMessagexBox(“OnBindFirst”)

</event>

 

<event name=”OnBind”>

         XLMessageBox(“OnBindSecond”)

</event>

 

                            那么这两个event合并之后,根据不同的合并策略,可能会出现下面的情形

<event name=”OnBind”>

         <chunk >

                   XLMessagexBox(“OnBindFirst”)

                   return true

         </chunk>

<chunk >

                   XLMessagexBox(“OnBindSecond”)

                   return true

         </chunk>

</event>

 

这种chunklist对使用者来是透明的,多个chunk会被当成多个响应函数,挂接到OnBind事件上面,并且根据在xml里面的定义顺序来响应,比如上面的代码运行之后,在出发OnBind事件时候,会先弹出OnBindFirst的提示框,然后弹出OnBindSecond的提示框,这里需要特别注意的是,如果希望在执行完一个chunk代码之后,继续执行下一个chunk代码,需要显示返回true,否则将终止执行,下面的chunk永远没有执行的机会,这也是在第一个chunk里面返回true的缘故,假如不显示写明return true,那么默认的是返回false的。

 

 

event的合并比较复杂,是基于策略的合并,需要在event节点的属性字段里面配置属性mergetype=”xxx” 有如下三种合并策略:

                                       i.              mergetype=”front” 表示当前的event和模板对应的event合并生成chunklist时候,自己的chunk放在chunklist的最前面,模板的相应的chunk需要放在后面,例如

 

<objtemplate id=”src” >

         <eventlist>

                   <event name=”OnBind”>

                                     XLMessagexBox(“OnBindSecond”)

                                     return true

                   </event>

         </eventlist>

</objtemplate>

 

<objtemplate id=”dest” templateid=”src” >

         <eventlist>

                   <event name=”OnBind” mergetype=”front”>

                                     XLMessagexBox(“OnBindFirst”)

                                     return true

                   </event>

         </eventlist>

</objtemplate>

 

其中destOnBind Event指明了合并策略为mergetype=”front”,那么模板destsrc合并之后将是下面的样子:

 

 

 

<objtemplate id=”dest” templateid=”src” >

         <eventlist>

                   <event name=”OnBind”>                  

                            <chunk>   //destevent放在前面

                                              XLMessagexBox(“OnBindFirst”)

                                              return true
                                     </chunk>

                            <chunk> //srcevent放在后面

                                              XLMessagexBox(“OnBindSecond”)

                                              return true

                                     </chunk>

                   </event>

         </eventlist>

</objtemplate>

 

                                     ii.              mergetype=”back” 表示当前的event和模板对应的event合并生成chunklist时候,自己的chunk放在chunklist的最后面,模板的相应的chunk需要放在前面,例如

 

<objtemplate id=”src” >

         <eventlist>

                   <event name=”OnBind”>

                                     XLMessagexBox(“OnBindSecond”)

                                     return true

                   </event>

         </eventlist>

</objtemplate>

 

<objtemplate id=”dest” templateid=”src” >

         <eventlist>

                   <event name=”OnBind” mergetype=”back” >

                                     XLMessagexBox(“OnBindFirst”)

                                     return true

                   </event>

         </eventlist>

</objtemplate>

 

其中destOnBind Event指明了合并策略为mergetype=”back”,那么模板destsrc合并之后将是下面的样子:

 

 

 

<objtemplate id=”dest” templateid=”src” >

         <eventlist>

                   <event name=”OnBind”>                  

                            <chunk>   //srcevent放在前面

                                              XLMessagexBox(“OnBindSecond”)

                                              return true

                                     </chunk>

                            <chunk>            //destevent放在后面

                                              XLMessagexBox(“OnBindFirst”)

                                              return true

                            </ chunk >

                   </event>

         </eventlist>

</objtemplate>

 

                                    iii.              mergetype=”overlay” 标识当前的event将覆盖所有模板的对应的event,也就是只保留自己新定义的event默认是overlay,也就是不注明mergetpye=”xxx”时候,就是采用overlay的合并策略,例如

 

<objtemplate id=”src” >

         <eventlist>

                   <event name=”OnBind”>

                                     XLMessagexBox(“OnBindSecond”)

                                     return true

                   </event>

         </eventlist>

</objtemplate>

 

<objtemplate id=”dest” templateid=”src” >

         <eventlist>

                   <event name=”OnBind” mergetype=”overlay”>

                                     XLMessagexBox(“OnBindFirst”)

                                     return true

                   </event>

         </eventlist>

</objtemplate>

 

其中destOnBind Event指明了合并策略为mergetype=”overlay”,那么模板destsrc合并之后将是下面的样子:

 

<objtemplate id=”dest” templateid=”src” mergetype=”front” >

         <eventlist>

                   <event name=”OnBind”>                   //只保留了destevent

                                     XLMessagexBox(“OnBindFirst”)

                                     return true

                   </event>

         </eventlist>

</objtemplate>

c)         事件列表的合并

事件列表的合并策略是:尝试对实体和模板里面每个event进行合并,也就是合并的最小粒度是event

                                       i.              对于实体里面存在但是模板里面不存在的event,保留实体里面的event

                                     ii.              对于实体里面不存在,但是模板里面存在event,继承模板里面的event

                                    iii.              对于实体里面和模板里面都存在的event,则按照event的合并策略对两个event进行合并

 

例如

 

实体template定义如下

<eventlist>

         <event name=”A”>

                   A’s code

         </event>

         <event name=”B”>

                   B’s code

         </event>

</eventlist>

 

模板template定义如下

<eventlist>

         <event name=”A”>

                   A2’s code

         </event>

         <event name=”C”>

                   C’s code

         </event>

</eventlist>

 

那么实体和模板的eventlist合并后如下

 

<eventlist>

         <event name=”A”>

                   <chunk>

                            A’s code

                   </chunk>

                   <chunk>

                            A2’s code

                   </chunk>

         </event>

         <event name=”B”>

                   B’s code

         </event>

         <event name=”C”>

                   C’s code

         </event>

</eventlist>

 

d)         obj 节点的合并,分别对里面的属性列表attr、孩子列表children和事件列表eventlist进行合并

e)         children节点也就是孩子列表的合并,尝试对里面的id相同的obj进行合并,存在下面几种情况:

                                       i.              假如都是有idobj,那么如果实体和模板的obj id相同,则进行合并,如果实体存在而模板不存在,则保留实体的;假如实体不存在而模板存在,则继承自模板。

                                     ii.              对于没有idobj,那么就很简单了,所有实体的没有idobj保留,所有模板的没有idobj继承下来。

 

例如:

 

实体的children节点:

<children>

         <obj id=”A”>

         </obj>

 

         <obj id=”B”>

         </obj>

 

         <obj > // 实体的没有idobj

         </obj>

</children>

 

                                     模板的children节点:

<children>

         <obj id=”A”>

         </obj>

 

         <obj id=”C”>

         </obj>

 

         <obj > // 模板的没有idobj

         </obj>

</children>

 

那么实体和模板合并后的结果是:

<children>

         <obj id=”A”>

                   // 这里是实体的A obj和模板的A obj合并后的结果

         </obj>

 

         <obj id=”B”> //实体里面存在而模板里面不存在的obj,则保留实体的

         </obj>

 

         <obj id=”C”> //模板里面存在而实体里面不存在的obj,则保留模板的

         </obj>

 

         <obj > // 实体的没有idobj,保留

         </obj>

 

         <obj > // 模板的没有idobj,直接继承

         </obj>

 

</children>

 

f)          objtreetemplate节点的合并

分别对下面属性列表attr和根节点obj进行合并,两种节点的合并策略如上所述

g)         hostwndtemplate 节点的合并

分别对下面的属性列表attr和根节点eventlist节点进行合并,两种节点的合并策略如上所述

 

四、模板的展开

 

我们把一个节点和它所有的子节点都依照相应的策略merge的过程叫做展开,假如一个模板展开以后,该模板就是已经是可以直接进行解析的xml,里面不包含任何的templateid=”xxx” 的引用了。需要注意的是,一个节点的开展可能会导致下面多个子节点的展开,比如子节点也有引用了其它的template。我们给出明确的展开策略:自底向上,由外向内的展开策略

 

a)         由外向内:假如模板A引用了模板B,模板B引用了模板C,那么模板A展开时候,模板B也要展开,并且先于A展开;同理,假如模板C也引用了其它的模板,那么模板C也要先于模板B展开,例如

 

<objtemplate id=”C”>

</ objtemplate >

 

<objtemplate id=”B” templateid=”C”>

</ objtemplate >

 

<objtemplate id=”A” templateid=”B”>

</ objtemplate >

 

                            那么模板A展开时候,步骤如下:

                            1C没有引用其它模板,则不用展开,可以直接被B模板使用

                            2B先展开,展开结果如下

                           

<objtemplate id=”B”>

         // BC合并后的结果

</ objtemplate >

 

<objtemplate id=”A” templateid=”B”>

</ objtemplate >

 

3A最后展开,展开结果如下

 

<objtemplate id=”A”>

         // ABC合并后的结果

</ objtemplate >

 

b)         自底向上:假如模板A引用了模板CA的子节点B引用了模板D,那么模板A展开时候,模板B也要展开,并且模板B的展开要先于父节点A的展开。例如:

 

<objtemplate id=”C”>

</ objtemplate >

 

<objtemplate id=”D”>

</ objtemplate >

 

<objtemplate id=”A” templateid=”C”>

         <attr>

         </attr>

         <children>

                   <obj id=”B” templateid=”D”>

                   </obj>

         </children>

</ objtemplate >

 

那么模板A展开时候,步骤如下:

1、模板B先展开,展开结果如下:

        

         <objtemplate id=”C”>

</ objtemplate >

 

<objtemplate id=”A” templateid=”C”>

         <attr>

         </attr>

         <children>

                   <obj id=”B”>

                            // 模板BD合并后的结果

                   </obj>

         </children>

</ objtemplate >

                            2、模板A最后展开,结果如下:

                                    

<objtemplate id=”A”>  // 模板AC合并后的结果。

         <attr>

         </attr>

         <children>

                   <obj id=”B”>

                            // 模板BD合并后的结果

                   </obj>

         </children>

</ objtemplate >

 

c)         对于有多层嵌套和多层引用的模板,都遵循上面两个合并策略。

 

五、模板的展开相对比较复杂,尤其是对于多层嵌套的模板的展开,假如引用的模板很复杂,展开结果可能和预期的结果不一致。所以为了方便大家使用,我会提供一个工具用以在编写时候提前展开,看看展开后的结果和你们所预期的时候一致。

 

 

 

 

迅雷公司 版权所有 Copyright 2003-2010 Thunder Inc.All Rights Reserved. 意见反馈:xl7doc@xunlei.com