首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

6.2  设计器

DesignerAttribute可以为控件指定一个设计器,设计器是指System.Web.UI.Design. ControlDesigner类或其子类。ASP.NET框架中有五个基本的设计器及一些特定于某个控件的设计器,这五个基本设计器是:ControlDesigner,ContainerControlDesigner,CompositeControlDesigner, BaseDataBoundControlDesigner,TemplatedControlDesigner,其中ControlDesigner是ASP.NET 1.X就有的设计器,但在2.0版以后得到了极大的加强(也更复杂),ContainerControlDesigner适用于容器控件,比如Panel,它支持拖放子控件,并直接在设计视图中编辑子控件。

ASP.NET 2.0中的设计器功能强大,它们负责提供控件设计时效果的HTML代码,提供“自动套用格式”功能,提供智能任务列表功能,提供对模板编辑的支持,提供控件设计器区域功能,分别如图6-2~图6-6所示。

DisignTimeRender.jpg                   AutoFormat.jpg

图6-2  设计时呈现                 图6-3  GridView控件属性窗口中的“自动套用格式”功能

ActionList.jpg

图6-4  智能任务列表

Template.jpg        Region.jpg

图6-5  对模板编辑的支持                                                                  图6-6  设计时区域与事件支持

6.2.1  设计时呈现

设计器最根本的功能是设计时呈现,即设计时显示在设计视图中的控件效果。Visual Studio中的ASP.NET设计视图应该说也是基于Windows中的IE实现的,它支持HTML和样式,但禁用了脚本。

和设计时呈现相关的方法有:

—  GetDesignTimeHtml()——提供设计时显示的HTML代码。

—  GetEmptyDesignTimeHtml()——提供当控件内容为空时显示的HTML代码。

—  GetErrorDesignTimeHtml()——提供出现错误时呈现的HTML代码。

—  UpdateDesignTimeHtml()——刷新运行时呈现的HTML代码

—  CreatePlaceHolderDesignTimeHtml()——提供一个包括控件类型和ID的占位块HTML代码,比如ObjectDataSource控件的GetDesignTimeHtml()方法就直接使用此方法生成呈现效果。

接下来我们利用IE的滤镜功能实现一个阴影效果的DropShadowLiteral控件,需要注意的是滤镜是IE特有的,所以实际使用时应尽量避免使用滤镜。不过DropShadowLiteral控件只在设计时提供阴影效果,如图6-7所示。并不是在运行时真正往浏览器呈现滤镜代码,所以我们不能通过改变控件的呈现行为来提供阴影功能,而通过它的设计器来提供:

图6-7  DropShadowLiteral设计时效果

[assembly:WebResource("ControlDesignTimeLabrary.alert.gif","image/gif")]

namespace ControlDesignTimeLabrary

{

    [Designer(typeof(DropShadowLitralDesigner))]

    public class DropShadowLiteral:Literal

    {

    }

    public class DropShadowLitralDesigner : ControlDesigner

    {

        public override string GetDesignTimeHtml()

        {

            string html =  base.GetDesignTimeHtml();

            html = "<div style='filter:dropShadow(

                    color=#AAAAAA,offX=3,offY=3);padding:1em;width:"

                    +((DropShadowLiteral)Component).Text.Length.ToString()

                    +"em;'>" + html + "</div>";

            return html;

        }

        protected override string GetEmptyDesignTimeHtml()

        {

            string imgSrc = ((DropShadowLiteral)Component).Page

                    .ClientScript.GetWebResourceUrl(

                    typeof(DropShadowLiteral),

                "ControlDesignTimeLabrary.alert.gif");

            return string.Format("<img

                   src='{0}'/>PleaseInputControlText",imgSrc);

        }

    }

}

DropShadowLiteral控件本身并没有什么特殊的逻辑,但是它通过DesignerAttribute关联了自定义的设计器——DropShadowLiteralDesigner。

DropShadowLiteralDesigner重写了GetDesignTimeHtml()方法,在默认的设计时HTML代码外围加了一个带阴影滤镜的DIV,所以控件的设计时效果会带有阴影效果。GetDesignTimeHtml()是控件未出现异常时获取设计时HTML代码的总入口,包括GetEmptyDesignTImeHtml()方法也是在GetDesignTimeHtml()方法判断控件内容为空后由GetDesignTimeHtml()方法调用的。

默认的GetEmptyDesignTimeHtml()方法会返回一个ControlType[ControlID]形式的字符串,此处我们对它进行重写,返回一个图片和一个字符串。从上述代码中可以看出,设计时对内嵌资源也是支持的。

6.2.2  自动格式设置

复杂的服务端控件通常都提供了自动格式设置的功能,以“模板”的方式一次对控件的多个属性(多数是样式属性)进行设置,比如Calendar,GridView,如图6-8所示。

图6-8  自动套用格式

ASP.NET 2.0中的控件设计器对自动格式设置提供了简明的接口。

ControlDesigner类的AutoFormats属性代表自动格式的集合,通过重写此属性可以自定义控件的自动格式。AutoFormats返回的是DesignerAutoFormatCollection类型,为DesignerAutoFormat类型的集合,也就是说AutoFormats返回的是一组DesignerAutoFormat子类的实例。DesignerAutoFormat类为自动格式的真正执行者,它是一个抽象类,它定义抽象方法Apply(),它的子类必须实现该方法以提供设置控件属性的逻辑,此外,它还提供一个GetPreviewControl()方法,用于在预览应用自动格式的效果时提供一个当前控件的拷贝。

接下来我们简单地扩展一下TextBox,也给它提供几种自动格式选择,以演示自动化格式功能:

[Designer(typeof(AutoFormatTextBoxDesigner))]

public class AutoFormatTextBox:TextBox

{

}

[SupportsPreviewControl(true)]

public class AutoFormatTextBoxDesigner : ControlDesigner

{

    static DesignerAutoFormatCollection _autoFormats;

    public override DesignerAutoFormatCollection AutoFormats

    {

        get

        {

            if (_autoFormats == null)

            {

                _autoFormats = new DesignerAutoFormatCollection();

                _autoFormats.Add(new TextBoxFormat("MultiLine"));

                _autoFormats.Add(new TextBoxFormat("Red"));

                _autoFormats.Add(new TextBoxFormat("Default"));

            }

            return _autoFormats;

        }

    }

    class TextBoxFormat : DesignerAutoFormat

    {

        public TextBoxFormat(string name)

            : base(name)

        {

        }

        public override void Apply(System.Web.UI.Control control)

        {

            AutoFormatTextBox textbox = control as AutoFormatTextBox;

            if (textbox != null)

            {

                switch (this.Name)

                {

                    case "MultiLine":

                        textbox.TextMode = TextBoxMode.MultiLine;

                        textbox.BorderColor = Color.Empty;

                        textbox.ForeColor = Color.Empty;

                        textbox.Rows = 20;

                        textbox.Columns = 20;

                        break;

                    case "Red":

                        textbox.TextMode = TextBoxMode.SingleLine;

                        textbox.BorderColor = Color.Red;

                        textbox.BorderStyle = BorderStyle.Solid;

                        textbox.BorderWidth = new Unit(1);

                        textbox.ForeColor = Color.Red;

                        break;

                    default:

                        textbox.TextMode = TextBoxMode.SingleLine;

                        textbox.BorderColor = Color.Empty;

                        textbox.ForeColor = Color.Empty;

                        break;

                }

            }

        }

    }

}

每个DesignerAutoFormat项都有一个名字,在Apply()方法中根据Name决定如何设置控件的属性,可以看到DesignerAutoFormat并不局限于设置控件的样式属性。

此外AutoFormatTextBoxDesigner类设置SupportPreviewControlAttribute以支持预览控件应用自动格式的效果,如图6-9所示。

图6-9  AutoFormatTextBox控件自动套用格式

6.2.3  智能任务列表

像GridView这样的控件非常复杂,你需要设置它的样式、模板、编辑它的列,等等,但这些操作中有一些是使用最频繁的,比如编辑列和模板,为了简化操作,提供开发效率,GridView的设计器提供了智能任务列表,辅助控件用户,如图6-10所示。

文本框:  
图6-10  GridView控件的智能任务列表

智能任务列表突出重点,并能利用一些设计器窗体简化操作(比如点击编辑列时弹出的字段编辑器),极大丰富了控件的设计时。

智能任务列表由ControlDesigner类的ActionLists属性指定,ActionLists属性为DesignerActionListCollection类型,通过重写ActionLists属性可以为设计器提供一个或多个任务列表,任务列表为DesignerActionList或其子类的实例。

DesignerActionList类最关键的方法为GetSortedActionItems()方法。该方法返回DesignerActionItemCollection集合。通过重写GetSortedActionItems(),可以组织任意多个智能任务,智能任务由DesignerActionItem类表示。

DesignerActionItem类的Category属性用来对智能任务进行分类,Category属性相同的任务将被放在一起,不同分类的任务之间有分隔线进行区别。此外,DisplayName属性将显示在智能任务列表面板上,作为任务的名字。这些属性都可以通过构造函数参数指定。

DesignerActionItem类为抽象类,它有四个派生类:DesignerActionTextItem用于在智能任务列表面板上显示文本;DesignerActionHeaderItem在面板上显示标题;DesignerActionPropertyItem用于设置设计器的一个属性(注意不是控件的属性),需要注意的是,面板对属性任务的表现方式受属性关联的TypeConverter影响;此外,DesignerActionMethodItem用于调用一个设计器方法。

接下来,我们扩展上一章完成的TabControl控件,为其提供丰富的设计时支持,如图6-11所示。

我们在智能任务列表中加入了两个标题:“选中选项页”、“操作选项页”,两个操作TabControl的SelectedTabIndex属性的属性任务,以及一个“添加选项页”的函数任务。

TabControlActionList.jpg  TabControlActionList2.jpg

图6-11  TabControl设计时智能任务列表

public class TabControlDesigner:ControlDesigner

{

    TabControl _control;

    public override void Initialize(

                    System.ComponentModel.IComponent component)

    {

        base.Initialize(component);

        _control = component as TabControl;

    }

    public override bool AllowResize

    {

        get

        {

            return true;

        }

    }

    #region ActionList

    public override DesignerActionListCollection ActionLists

    {

        get

        {

            DesignerActionListCollection lists =

                    new DesignerActionListCollection();

            lists.AddRange(base.ActionLists);

            lists.Add(new TabControlActionList(Component));

            return lists;

        }

    }

    //定义智能任务列表

    internal class TabControlActionList:DesignerActionList

    {

        DesignerActionItemCollection _items;

        public TabControlActionList(IComponent component)

            : base(component)

        {

        }

        public override DesignerActionItemCollection

                                                GetSortedActionItems()

        {

            if (_items == null)

            {

                _items = new DesignerActionItemCollection();

                _items.Add(new DesignerActionHeaderItem("选中选项页",

                        "SelectedIndex"));

                _items.Add(new DesignerActionPropertyItem(

                    "SelectedIndex", "输入",

                    "SelectedIndex", "输入选中页"));

                _items.Add(new DesignerActionPropertyItem(

                    "SelectedTabIndex", "选择",

                    "SelectedIndex", "选择选中页"));

                _items.Add(new DesignerActionHeaderItem(

                    "操作选项页", "Behavior"));

                _items.Add(new DesignerActionMethodItem(

                    this, "AddNewTab", "添加选项页","Behavior"));

            }

            return _items;

        }

//应用转换器将界面由输入框变为下拉选择框

        [TypeConverter(typeof(SelectedIndexConverter))]

        public int SelectedTabIndex

        {

            get

            {

                return ((TabControl)Component).SelectedTabIndex;

            }

            set

            {

                TypeDescriptor.GetProperties(Component)

                     ["SelectedTabIndex"].SetValue(Component, value);

            }

        }

        //使用默认的输入框作为界面

        public int SelectedIndex

        {

            get

            {

                return ((TabControl)Component).SelectedTabIndex;

            }

            set

            {

                TypeDescriptor.GetProperties(Component)

                     ["SelectedTabIndex"].SetValue(Component, value);

            }

        }

        //智能任务执行的方法,需要使用事务化的执行方式

        public void AddNewTab()

        {

            TransactedChangeCallback addNewCallback =

                    new TransactedChangeCallback(DoAddNewTab);

            ControlDesigner.InvokeTransactedChange(Component,

                    addNewCallback, "AddNewTab", "添加新的选项卡");

        }

        public bool DoAddNewTab(object args)

        {

            TabControl control = Component as TabControl;

            TabPage newPage = new TabPage();

            newPage.Title = "NewTab";

            newPage.ID = "Tab" + control.Controls.Count.ToString();

            control.Controls.Add(newPage);

            return true;

        }

    }

    #endregion

}

我们为TabControl专门设计一个设计器——TabControlDesigner,继承自ControlDesigner类,重写的AllowResize属性允许控件用户在设计视图中拖动设置控件的大小。重写的ActionLists属性添加了一个TabControlActionList类的实例。

TabControlActionList类是一个TabControlDesigner类的内部类,之所以把它放在TabControlDesigner类的内部是因为它仅为TabControlDesigner类服务,减少它对程序结构的影响。

TabControlActionList类继承自DesignerActionList类,其重写的GetSortedActionItems()方法组织了我们前面提到的智能任务。

五个任务项被分成SelectedIndex和Behavior两类。DesignerActionPropertyItem构造函数的第一个参数指定任务对应的设计器属性。此处的两个属性任务分别对应SelectedIndex和SelectedTabIndex两个属性,这两个属性同样操作TabControl控件的SelectedTabIndex属性,但SelectedTabIndex属性上应用了自定义SelectedIndexConverter类型转换器,改变了任务列表面板中的属性显示方式。类型转换器我们稍后再讨论。

DesignerActionMethodItem代表一个设计器方法,所代表的方法名由构造函数的第二个参数传递。此处函数任务代码设计器中的AddNewTab()方法。AddNewTab()方法使用TransactedChangeCallback委托和ControlDesigner.InvokeTransactedChange()方法调用DoAddNewTab()方法,这样做的好处在于,DoAddNewTab()方法会以事务的方式执行,并获得设计器的操作回退能力。

最后,为TabControl应用DesignerAttribute,把TabControlDesigner指定为TabControl的设计器。编译后在IDE中测试TabControl,将得到图6-11所示效果。

注:测试设计时效果,最好是在调试控件类库项目时启动另一个IDE实例,在启动的IDE实例中打开网站项目,把控件添加到页面中进行调试。详细方法请参见序中附录。

6.2.4  设计器区域

前面说到设计视图虽然会模拟控件的HTML和CSS效果,但却不支持控件的行为,脚本不支持,包括链接的导航这样的默认行为也不会有响应。

但有些控件却支持通过点击来产生一些效果,比如Wizard控件在设计时点击其左侧的步骤列表能切换WizardStep,便于针对每个Step进行编辑。

这样的功能在组合多个子控件或有多个模板的控件中非常有用,比如TabControl,Accordion等控件。

首先,要明白的是这种功能在设计器中叫设计器区域。通过重写ControlDesigner基类的GetDesignTimeHtml(DesignerRegionCollection regions)方法可以提供设计器区域,方法很简单,在返回的设计时HTML代码中的HTML标签上添加名为DesignerRegion.DesignerRegion AttributeName的属性,则该HTML标签就成为了一个设计器区域,这个属性的值应设为一个数值字符串,代表这个区域对应设计器的区域集合中的第几个。让HTML标签成为一个设计器区域还不够,还必须添加一个对应的DesignerRegion实例到设计器区域集合中。DesignerRegion的子类EditableDesignerRegion代表可编辑的设计器区域。

当用户点击控件设计视图中的设计区域时,会触发设计器的Click事件,OnClick()方法将得到执行,可以通过重写设计器的OnClick()方法利用设计区域。

EditableDesignerRegion是一个可编辑的区域,编辑区域通过设计器的GetEditableDesignerRegionContent()方法得到被设计对象的原始内容,而编辑结果通过SetEditableDesignerRegionContent()方法返回被设计对象。

回顾一下前一章实现的TabControl,虽然它的运行时功能已经比较完美,但设计时功能基本不可用——设计时呈现的结果不能反映最终的呈现结果,对子控件的组织也极为不力。所以我们接下来的任务是继续改进其设计时功能。

public class TabControlDesigner:ControlDesigner

{

    //… …

    #region DesignerRegion

    public override string GetDesignTimeHtml(

                        DesignerRegionCollection regions)

    {           

        if (_control.Controls.Count < 1)

        {

            return GetEmptyDesignTimeHtml();

        }

        StringBuilder stringBuilder = new StringBuilder();

        StringWriter stringWriter = new StringWriter(stringBuilder);

        HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);

        //得到一个用于生成设计时效果的控件副本

        TabControl contorl = ViewControl as TabControl;

        //简单的设置一些样式

        if (contorl.ControlStyleCreated)

            contorl.ControlStyle.AddAttributesToRender(htmlWriter);

        htmlWriter.AddStyleAttribute("border", "solid 1px blue");

        htmlWriter.RenderBeginTag(HtmlTextWriterTag.Div);

        htmlWriter.AddStyleAttribute(

                        HtmlTextWriterStyle.BackgroundColor, "activecaption");

        htmlWriter.RenderBeginTag(HtmlTextWriterTag.Div);

        for (int i = 0; i < contorl.Controls.Count; i++)

        {

            TabPage tab = contorl.Controls[i] as TabPage;

            //为每个TagPage建一个Region

            DesignerRegion region = new DesignerRegion(

                                this, "tab" + i.ToString());

            region.DisplayName = tab.Title;

            regions.Add(region);

            if (i == contorl.SelectedTabIndex)

            {

                region.Highlight = true;

            }

            else

            {

                htmlWriter.AddStyleAttribute(

                    HtmlTextWriterStyle.BackgroundColor, "navy");

                htmlWriter.AddStyleAttribute(

                    HtmlTextWriterStyle.Color, "white");

            }

            htmlWriter.AddStyleAttribute(

                    HtmlTextWriterStyle.Margin, "4px");

            htmlWriter.AddStyleAttribute("border", "solid 1px blue");

            //添加Region所需的属性标记,让这个HTML元素成为设计时区域

            htmlWriter.AddAttribute(

                    DesignerRegion.DesignerRegionAttributeName,

                    i.ToString());

            htmlWriter.AddAttribute(HtmlTextWriterAttribute.Value,

                                    tab.Title);

            htmlWriter.AddAttribute(HtmlTextWriterAttribute.Type,

                                    "button");

            htmlWriter.RenderBeginTag(HtmlTextWriterTag.Input);

            htmlWriter.RenderEndTag();

        }

        htmlWriter.RenderEndTag();

        htmlWriter.AddStyleAttribute(HtmlTextWriterStyle.Position,

                        "relative");

        htmlWriter.AddStyleAttribute(HtmlTextWriterStyle.Top,

                        "-6px");

        htmlWriter.AddAttribute(

                DesignerRegion.DesignerRegionAttributeName,

                contorl.Controls.Count.ToString());

        htmlWriter.RenderBeginTag(HtmlTextWriterTag.Div);

        //提供一个编辑区域编辑选择的TabPage的内容

        EditableDesignerRegion editRegion =

                new EditableDesignerRegion(this, "edit" +

                contorl.SelectedTabIndex.ToString(), false);

        regions.Add(editRegion);

        htmlWriter.RenderEndTag();

        htmlWriter.RenderEndTag();

   

        return stringWriter.ToString();          

    }

    public override string GetDesignTimeHtml()

    {

        if (_control.Controls.Count < 1)

        {

            return GetEmptyDesignTimeHtml();

        }

        return base.GetDesignTimeHtml();

    }

    protected override string GetEmptyDesignTimeHtml()

    {

        return CreatePlaceHolderDesignTimeHtml("Please add TabPage");

    }

    //处理设计时事件

    protected override void OnClick(DesignerRegionMouseEventArgs e)

    {

        if (e.Region == null)

        {

            return;

        }

        if (!e.Region.Name.StartsWith("tab"))

        {

            return;

        }

        int selectedIndex = int.Parse(e.Region.Name.Substring(3));

        if (selectedIndex != _control.SelectedTabIndex)

        {

            _control.SelectedTabIndex = selectedIndex;

            UpdateDesignTimeHtml();

        }

    }

    //得到被编辑TabPage的原始内容

    public override string GetEditableDesignerRegionContent(

                        EditableDesignerRegion region)

    {

        IDesignerHost host = (IDesignerHost)Component.Site

                        .GetService(typeof(IDesignerHost));

        if (host != null)

        {

            int selectedIndex = int.Parse(region.Name.Substring(4));

            Control c = _control.Controls[selectedIndex];

            //将控件实例持久化为页面源代码格式的字符串

            return ControlPersister.PersistControl(c, host);

        }

        return String.Empty;

}

//将编辑结果应用到当前选择的TabPage上

    public override void SetEditableDesignerRegionContent(

                EditableDesignerRegion region, string content)

    {

        if (content == null)

        {

            return;

        }

        IDesignerHost host = (IDesignerHost)Component.Site

                    .GetService(typeof(IDesignerHost));

        if (host != null)

        {

            int selectedIndex = int.Parse(region.Name.Substring(4));

            _control.Controls.RemoveAt(selectedIndex);

            _control.Controls.AddAt(selectedIndex,

                //将编辑产生的页面源码转换为控件实例

                ControlParser.ParseControl(host, content));

        }

    }

   

    #endregion

}

首先我们重写了设计器的GetDesignTimeHtml()方法,当TabControl不包含任何TabPage时,我们返回GetEmptyDesignTimeHtml(),GetDesignTimeHtml(regions)方法则模拟TabControl的运行时效果呈现HTML内容,并在相应的标签上添加了设计器区域的标识,与此同时,创建相应的设计器区域(见代码中的带底色部分)。

重写的OnClick()方法根据所点击的设计器区域更新TabControl的SelectedTabIndex属性,并更新设计时HTML代码。

GetEditableDesignerRegionContent()方法为编辑区域提供原始内容,它根据区域名得到对应的被编辑对象,并用ControlPersister.PersistControl()静态方法将对象内容转换为控件持久化格式的字符串(即设计时源代码视图中的那种字符串)。

ControlPersister是一个非常有用的工具类,它提供的方法能将Control或ITemplate接口对象序列化为aspx代码字符串。与ControlPersister类对应的类是ControlParser类,它提供的静态类可以把aspx代码字符串解析为控件对象或模板内容。

SetEditableDesignerRegionContent()方法正是利用ControlParser.ParseControl()方法将编辑结果解析成控件树,并更新到TabControl.Controls集合中。

最后,TabPage作为内容容器,我们设置它的Designer为ContainerControlDesigner,以让它支持拖放子控件:

[Designer(typeof(TabPageDesigner))]

public class TabPage:WebControl

{

}

编译控件,添加到页面中测试,TabControl已经可以非常方便地编辑其TabPage的内容了,如图6-12所示。

tabpage1.jpgtab2.jpg

图6-12  直接编辑不同的TabPage

其实你可以把设计时效果弄得和运行时效果一样,因为内嵌资源文件在设计时也可以引用,此处为了让代码更简洁一点,我并没有尝试这样做,好在现在的设计时功能连最挑剔的用户也会感到满意了。

6.2.5  支持模板

ASP.NET 2.0的设计器和Visual Studio 2005为模板的设计提供了非常强大的支持。

模板控件中的模板通常类似于TabControl控件中的TabPage,本身是一个控件容器,而且其内容需要由用户编辑来完成。

文本框:  
图6-13  GridView动态变化的模板组

麻烦不在于此,而在于通常模板控件会有一组模板需要编辑,而GridView这样控件的模板集合还会根据用户的设置(比如使用了模板列)而动态地变化,如图6-13所示。

新的控件设计器为模板提供了专门的设计界面,如图6-13所示。对于控件的多个模板组,新的设计器提供TemplateGroups属性,以管理模板的编辑。通过重写ControlDesigner. TemplateGroups属性可为控件的每一个模板对应一个模板定义,可谓兵来将挡,水来土淹。

TemplateGroups属性返回TemplateGroupCollection,每一个模板可以对应一个TemplateGroup实例,TemplateGroup没有什么特殊用途,纯粹用来对模板进行分组。控件的每一个模板用一个TemplateDefinition实例来表示。TemplateDefinition的构造函数接受多个参数:designer,表示父设计器对象;name,模板的名字,将显示在设计器的模板设计面板中;templatedObject,表示模板控件;templatePropertyName,模板控件中的ITemplate属性,即对应的模板;style,应用到模板上的Style对象,通常不用;serverControlOnly,模板是不是只接受Web服务端控件。TemplateDefinitioin仅用来提供控件中的模板项的一个声明,模板设计功能则还是由IDE提供。

接下来我们演示一个模板控件和它的设计器,这个控件源自MSDN帮助中的示例,实际的模板控件例子我们将在后面的章节中提供。

[Designer(typeof(MyTemplateControlDesigner)),

    ToolboxData("<{0}:MyTemplateControl runat=server></{0}:MyTemplateControl>")]

public sealed class MyTemplateControl : WebControl, INamingContainer

{

    private ITemplate[] _templates;

    public MyTemplateControl()

    {

        _templates = new ITemplate[4];

    }

    [Browsable(false),

        PersistenceMode(PersistenceMode.InnerProperty)]

    public ITemplate Template1

    {

        get { return _templates[0]; }

        set { _templates[0] = value; }

    }

    [Browsable(false),

        PersistenceMode(PersistenceMode.InnerProperty)]

    public ITemplate Template2

    {

        get { return _templates[1]; }

        set { _templates[1] = value; }

    }

    [Browsable(false),

        PersistenceMode(PersistenceMode.InnerProperty)]

    public ITemplate Template3

    {

        get { return _templates[2]; }

        set { _templates[2] = value; }

    }

    [Browsable(false),

        PersistenceMode(PersistenceMode.InnerProperty)]

    public ITemplate Template4

    {

        get { return _templates[3]; }

        set { _templates[3] = value; }

    }

    protected override void CreateChildControls()

    {

        for (int i = 0; i < 4; i++)

        {

            Panel pan = new Panel();

            _templates[i].InstantiateIn(pan);

            this.Controls.Add(pan);

        }

    }

}

public class MyTemplateControlDesigner : ControlDesigner

{

    TemplateGroupCollection col = null;

    public override void Initialize(IComponent component)

    {

        base.Initialize(component);

        SetViewFlags(ViewFlags.TemplateEditing, true);

    }

    public override string GetDesignTimeHtml()

    {

        MyTemplateControl control = (MyTemplateControl)Component;

        string message =

                    "Click here and use the task menu to edit the templates.";

        if (control.Template1 != null)

            message += "<br/>Template1 finished";

        if (control.Template2 != null)

            message += "<br/>Template2 finished";

        if (control.Template3 != null)

            message += "<br/>Template3 finished";

        if (control.Template4 != null)

            message += "<br/>Template4 finished";

        return CreatePlaceHolderDesignTimeHtml(message);

       

    }

    public override TemplateGroupCollection TemplateGroups

    {

        get

        {

            if (col == null)

            {

                col = base.TemplateGroups;

             

                TemplateDefinition tempDef;

                MyTemplateControl ctl = (MyTemplateControl)Component;

                TemplateGroup tempGroup =

                    new TemplateGroup("Template Set A");

                tempDef = new TemplateDefinition(this, "Template A1",

                    ctl, "Template1", true);

                tempGroup.AddTemplateDefinition(tempDef);

                tempDef = new TemplateDefinition(this, "Template A2",

                    ctl, "Template2", true);

                tempGroup.AddTemplateDefinition(tempDef);

                col.Add(tempGroup);

                tempGroup = new TemplateGroup("Template Set B");

                tempDef = new TemplateDefinition(this, "Template B1",

                    ctl, "Template3", true);

                tempGroup.AddTemplateDefinition(tempDef);

                tempDef = new TemplateDefinition(this, "Template B2",

                    ctl, "Template4", true);

                tempGroup.AddTemplateDefinition(tempDef);

                col.Add(tempGroup);

            }

            return col;

        }

    }

    public override bool AllowResize

    {

        get

        {

            if (this.InTemplateMode)

                return true;

            else

                return false;

        }

    }

}

示例中的模板控件拥有四个模板,每个模板在最终呈现为一个Panel。模板控件的设计器则重写了TemplateGroups属性,定义了模板组和模板项,如图6-14所示。需要注意的是Initialize()方法中的SetViewFlags(ViewFlags.TemplateEditing, true)语句指示模板处于编辑状态。

TemplateControl.jpg

图6-14  设计MyTemplateControl控件的模板

可以看到实现有模板编辑功能的设计是非常简单的事情,需要做的只有声明模板。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论