您的位置:首页 > 编程语言 > ASP

asp.net控件开发基础(14)

2006-09-29 21:09 435 查看
        上一篇讨论了为服务器控件添加客户端功能,这一篇我们所要讲的是控件生成器

1.错误的代码,无法解析

首先来看一段简单的代码


 正确


            <asp:Wizard ID="Wizard1" runat="server">


                <WizardSteps>


                    <asp:WizardStep ID="WizardStep1" runat="server" Title="Step 1">


                        21212</asp:WizardStep>


                    <asp:WizardStep ID="WizardStep2" runat="server" Title="Step 2">


                    </asp:WizardStep>


                </WizardSteps>


            </asp:Wizard>


            错误


            <asp:Wizard ID="Wizard2" runat="server">


                <asp:WizardStep ID="WizardStep1" runat="server" Title="Step 1">


                    21212</asp:WizardStep>


                <asp:WizardStep ID="WizardStep2" runat="server" Title="Step 2">


                </asp:WizardStep>


            </asp:Wizard>


            <br />


            <br />


            <asp:Label ID="Label1" runat="server" Text="Label">


                <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>


            </asp:Label>


            <br />


            <asp:TextBox ID="TextBox3" runat="server">12345</asp:TextBox>


            <br />


            <asp:Label ID="Label2" runat="server" Text="Label">12345</asp:Label>


            <br />


            <br />


            <asp:TextBox ID="TextBox1" runat="server">


        <asp:Label runat="server" Text="Label"></asp:Label>


            </asp:TextBox>

Wizard为asp.net2.0新增的一个控件,这个页面发生两个错误,如下图



运行此页面后则会报错,出现以下提示

分析器错误

这里有一个问题,.net提供我们控件时,我们已经形成一种定向思维,控件就是那样写的,如DropDownList,其中只能是套<asp:ListItem>的,那我为什么就不能套别的属性呢?Wizard控件为什么就要加一个WizardSteps属性才可以正常运行呢?当我们思考到这里,我们就该寻找答案.

2.从ParseChildren元数据属性讲起

从第五篇开始,我们多次用到了ParseChildren此特性.还请大家先看MSDN对其的解释,其有三种情况

(1)ParseChildren(true)     第5篇我们使用集合属性的时候我们曾经这样定义,如下代码


   [ParseChildren(true)]


    public class Custom: Control




    

{


    }

(2)ParseChildren(true,"<Default Property>") 第10篇当我们定义集合属性时,我们曾这样定义

DropItemList为集合属性


   [ParseChildren(true, "DropItemList")]


   public class DropColor:WebControl




    

{


   }

(3)ParseChildren(false)  这个我们没用过,也是我们要讲的内容,当其内部定义为flase时,那么放在此控件内的元素将被解析成控件,应该说是页分析器ControlBuilder 类.这里大家可以看看MSDN文档对ControlBuilder 类的解释,至少要先知道这一点
默认情况下,页上的每个控件都与一个默认的 ControlBuilder 类关联。

下面我们慢慢看下来.

3.控件与集合属性

让我们再次回忆一下ParseChildren的用法,本次的示例代码取自asp.net2.0揭密

(1)ParseChildren(true,"<Default Property>")的使用

此控件实现效果为随机显示一个内部控件内容
RItem为一个继承Control的控件,其内部未实现任何东西,你可以在其控件内部输出呈现,记得上面说的ControlBuilder 类默认关联

示例一


    [ParseChildren(true, "Items")]


    public class ItemRotator : CompositeControl




    

{


        private ArrayList _items = new ArrayList();




        [Browsable(false)]


        public ArrayList Items




        

{




            get 

{ return _items; }


        }




        protected override void CreateChildControls()




        

{


            Random rnd = new Random();


            int index = rnd.Next(_items.Count);


            Control item = (Control)_items[index];


            this.Controls.Add(item);


        }


    }




    public class RItem : Control




    

{




    }

页面代码


    <custom:ItemRotator


        id="ItemRotator1"


        Runat="server">


        <custom:ritem ID="Item1" runat="server">


            First Item


        </custom:ritem>


        <custom:ritem ID="Item2" runat="server">


            Second Item


            <asp:Calendar


                id="Calendar1"


                Runat="server" />


        </custom:ritem>


        <custom:ritem ID="Item3" runat="server">


            Third Item


        </custom:ritem>


    </custom:ItemRotator>


    

效果就不说了,随机显示ritem控件的内容,注意以上控件定义了一个Items集合属性

另外改进的话就是我们第十篇的讲的,为Ritem定义属性,作为一个集合属性,这里就不再列出代码.

(1)ParseChildren(false)的使用

此控件未添加属性,而多了一个方法AddParsedSubObject()

控件有默认的页面分析逻辑,重写AddParsedSubObject方法,可以向控件添加子控件


示例二




   [ParseChildren(false)]


    public class ContentRotator : WebControl




    

{




        protected override void AddParsedSubObject(object obj)




        

{


            if (obj is Content)


                base.AddParsedSubObject(obj);


        }




        protected override void RenderContents(HtmlTextWriter writer)




        

{


            Random rnd = new Random();


            int index = rnd.Next(this.Controls.Count);


            this.Controls[index].RenderControl(writer);


        }


    }


    [


    ToolboxItem(false)


    ]


    public class Content : Control




    

{


    }

页面代码


<custom:ContentRotator


        id="ContentRotator1"


        Runat="server">


        <custom:Content


            id="Content1"


            Runat="server">


            显示的第一项,此不为属性    


        </custom:Content>    


        <custom:Content


            id="Content2"


            Runat="server">


            显示的第二项,此不为属性


            <asp:Calendar


                id="Calendar1"


                Runat="server" />    


        </custom:Content>    


        <custom:Content


            id="Content3"


            Runat="server">


            显示的第三项,此不为属性  


        </custom:Content>  


    </custom:ContentRotator>

注意:ContentRotator无任何属性(其内部添加的为控件),而是用AddParsedSubObject 方法向控件添加了子控件,而不像ItemRotator控件一样,其内部是属性而非控件.

4.修改默认解析逻辑

上面已经说过每个控件都有默认的解析逻辑,其通过ControlBuilder 类来实现,可以通过重写其方法来自定义解析逻辑.下面通过一个例子来说明,它把一个控件以自定义标签所代替

以下列出部分代码

示例三


    //自定义页分析器


    public class ServerTabsBuilder : ControlBuilder




    

{


        public override Type GetChildControlType(string tagName, IDictionary attribs)




        

{


            if (String.Compare(tagName, "tab", true) == 0)


                return typeof(ServerTab);


            else


                return null;


        }


    }




    [ToolboxItem(false)]


    public class ServerTab : Control




    

{


        private string _Text;




        public string Text




        

{




            get 

{ return _Text; }




            set 

{ _Text = value; }


        }


    }

(1)ServerTabsBuilder类重写了ControlBuilder类的
GetChildControlType 方法  获取与子标记对应的控件类型的 Type

在此方法中,其以tab标签代替了ServerTab控件,改写了页分析逻辑
ControlBuilder类常用的还有AllowWhitespaceLiterals 方法 其指定控件之间是否允许存在空白,大家可以重写此方法,然后测试下就明白了

(2)定义一个简单的ServerTab控件.

还须在父控件中重写AddParsedSubObject方法将ServerTab控件添加到子控件中


        protected override void AddParsedSubObject(object obj)




        

{


            if (obj is ServerTab)


                base.AddParsedSubObject(obj);


        }

(3)最后还需要把控件生成器跟控件关联起来,当然还要设置ParseChildren(false)


    [ControlBuilder(typeof(ServerTabsBuilder))]


    [ParseChildren(false)]


    public class ServerTabs : WebControl, IPostBackEventHandler




    

{


    }

好了,这里主要代码就实现了,呈现代码大家可在后面下载,下面看下页面代码




<%

--以上省略css代码--%>


    <custom:ServerTabs


        ID="ServerTabs1"


        Runat="Server">


        <tab Text="First Tab">


        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>


          Contents of the first tab


        </tab>    


        <tab Text="Second Tab">


          Contents of the second tab


        </tab>    


        <tab Text="Third Tab">


          Contents of the third tab


        </tab>    


    </custom:ServerTabs>

以上镶套代码为tab标签,而非<custom:ServerTabs></custom:ServerTabs>,但实现效果是一样的,只是我们改了默认的页分析逻辑,自定义了控件页生成器(分析器)
看下效果(当重新编译后需要重新启动vs2005才能看到效果)



好了,这次的主题也讲完了,这里需要注意的是asp.net2.0中复合控件只需要继承CompositeControl类即可.

这里预祝大家国庆快乐.

示例代码下载

下一篇将对此系列写下来的十四篇文章进行一个小结,并进行补充,当然小结并非是结束,接着我们还会继续讨论模板以及更多.希望大家喜欢.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息