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

8.2  实现模板控件

了解了ASP.NET框架下实现模板控件如此简单后,我们来实现一个模板控件——BookInfo,它使用模板的方式显示BookData数据。以下是使用BookInfo的一个例子,它有一个ItemTemplate模板,它的DataItem属性为BookData数据类型:

<tc:BookInfo ID="tc0470109491" runat="Server"

DataItem-Author="Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett"

 DataItem-Description="My favorite book" DataItem-ISBN="0470109491"

 DataItem-Publisher="Wrox" DataItem-Title="Professional AJAX">

 <ItemTemplate>

    <div class="bookContainer">

        <asp:Image runat="server" ID="imgBook"  CssClass="bookCover"

            AlternateText='<%# Container.DataItem.Title %>'

            ImageUrl='<%# "~/images/"+Container.DataItem.ISBN+".png"%>'

        />

        <div class="bookContent">

            <h3><%# Container.DataItem.Title %></h3>

            Written by:<%# Container.DataItem.Author %><br />

            ISBN: #<%# Container.DataItem.ISBN %>

            <div class="bookPublisher">

                    <%# Container.DataItem.Publisher %></div>

        </div>

    </div>

 </ItemTemplate>

 </tc:BookInfo>

运行时效果则由模板来决定,如图8-1所示。

BookInfo.gif

图8-1  BookInfo的模板运行效果

8.2.1  BookData

首先实现BookInfo控件要用到的BookData类,它表示一本书的信息,为了在页面回传时,BookData数据能得到持久保存,BookData类需要实现IStateManager接口。

[TypeConverter(typeof(ExpandableObjectConverter))]

[Serializable]

public class BookData:IStateManager

{

    [NotifyParentProperty(true)]

    [Category("Data")]

    public string ISBN

    {

        get

        {

            if (ViewState["ISBN"] == null)

                return "";

            return (string)ViewState["ISBN"];

        }

        set

        {

            ViewState["ISBN"] = value;

        }

    }

    [NotifyParentProperty(true)]

    [Category("Data")]

    public string Title

    {

        get

        {

            if (ViewState["Title"] == null)

                return "";

            return (string)ViewState["Title"];

        }

        set

        {

            ViewState["Title"] = value;

        }

    }

    [NotifyParentProperty(true)]

    [Category("Data")]

    public string Author

    {

        get

        {

            if (ViewState["Author"] == null)

                return "";

            return (string)ViewState["Author"];

        }

        set

        {

            ViewState["Author"] = value;

        }

    }

    [NotifyParentProperty(true)]

    [Category("Data")]

    public string Publisher

    {

        get

        {

            if (ViewState["Publisher"] == null)

                return "";

            return (string)ViewState["Publisher"];

        }

        set

        {

            ViewState["Publisher"] = value;

        }

    }

    [NotifyParentProperty(true)]

    [Category("Data")]

    public string Description

    {

        get

        {

            if (ViewState["Description"] == null)

                return "";

            return (string)ViewState["Description"];

        }

        set

        {

            ViewState["Description"] = value;

        }

    }

    public BookData()

    {

    }

public BookData(string isbn, string title, string author,

                    string publisher, string description)

    {

        ISBN = isbn;

        Title = title;

        Author = author;

        Publisher = publisher;

        Description = description;

    }

    #region IStateManager 成员

    private bool _isTrackViewState;

    private StateBag _viewState;

    [Browsable(false)]

    [DesignerSerializationVisibility(

                    DesignerSerializationVisibility.Hidden)]   

    public StateBag ViewState

    {

        get

        {

            if (_viewState == null)

            {

                _viewState = new StateBag(false);

                if (_isTrackViewState)

                {

                    ((IStateManager)_viewState).TrackViewState();

                }

            }

            return _viewState;

        }

    }

    [Browsable(false)]

    [DesignerSerializationVisibility(

                        DesignerSerializationVisibility.Hidden)]

    public bool IsTrackingViewState

    {

        get

        {

            return _isTrackViewState;

        }

    }

    public void LoadViewState(object state)

    {

        if (state != null)

        {

            ((IStateManager)ViewState).LoadViewState(state);

        }

    }

    public object SaveViewState()

    {

        if (this._viewState != null)

        {

            return ((IStateManager)_viewState).SaveViewState();

        }

        return null;

    }

    public void TrackViewState()

    {

        this._isTrackViewState = true;

        if (_viewState != null)

        {

            ((IStateManager)_viewState).TrackViewState();

        }

    }

    #endregion

}

BookData拥有5个属性,这5个属性都保存在一个叫_viewState的StateBag变量中,_viewState中保存的数据要持久化到页面__VIEWSTATE隐藏域中,这需要参与到页面的视图状态管理过程中,这个过程将在BookInfo控件中实现。

BookData关联ExpandableObjectConverter类型转换器,这样BookData类型的属性在属性窗口中显示为可展开的属性,如图8-2所示,便于用户编辑。

expandable.gif

图8-2  可展开属性

8.2.2  BookInfo控件

BookInfo控件用来显示图书信息,所以它拥有一个DataItem属性,关联一个BookData实例。然后它用ItemTemplate模板显示DataItem的数据。

由于模板的原因,BookInfo控件会拥有多个子控件,所以BookInfo从CompositeControl继承。

[Designer(typeof(BookInfoDesigner))]

public class BookInfo:CompositeControl

{

    BookData _dataItem;

    [DesignerSerializationVisibility(

                    DesignerSerializationVisibility.Content)]

    [Category("Data")]

    public virtual BookData DataItem

    {

        get

        {

            if (_dataItem == null)

            {

                _dataItem = new BookData();

                ((IStateManager)_dataItem).TrackViewState();

            }

            return _dataItem;

        }

        set

        {

            _dataItem = value;

        }

    }

    private ITemplate _itemTemplate;

    [Browsable(false)]

    [TemplateContainer(typeof(BookInfo))]

    [PersistenceMode(PersistenceMode.InnerProperty)]

    public virtual ITemplate ItemTemplate

    {

        get { return _itemTemplate; }

        set

        {

            _itemTemplate = value;

            base.ChildControlsCreated = false;

        }

    }

    protected override HtmlTextWriterTag TagKey

    {

        get

        {

            return HtmlTextWriterTag.Div;

        }

    }

    protected override void CreateChildControls()

    {

        Controls.Clear();

        if (_itemTemplate != null)

            _itemTemplate.InstantiateIn(this);

        else

            base.CreateChildControls();

        ChildControlsCreated = true;

    }

    #region ViewState

    protected override object SaveViewState()

    {

        object[] states = new object[2];

        states[0] = base.SaveViewState();

        states[1] = ((IStateManager)DataItem).SaveViewState();

        return states;

    }

    protected override void LoadViewState(object savedState)

    {

        object[] states = (object[])savedState;

        base.LoadViewState(states[0]);

        ((IStateManager)DataItem).LoadViewState(states[1]);

    }

    protected override void TrackViewState()

    {

        base.TrackViewState();

        ((IStateManager)DataItem).TrackViewState();

    }

    #endregion

}

注意对两个属性的处理,DataItem属性在设计时被序列化为Content,所以它的子属性被序列化成DataItem-XXX:

<tc:BookInfo ID="tc0470109491" runat="Server"

 DataItem-Author="Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett"

 DataItem-Description="My favorite book" DataItem-ISBN="0470109491"

 DataItem-Publisher="Wrox" DataItem-Title="Professional AJAX">

… … </tc:BookInfo>

而ItemTemplate显示在属性窗口中却没有任何意义,所以干脆不要让它显示在属性窗口中。

由于BookData需要进行状态管理,所以我们重写了与视图状态管理有关的方法,让控件在处理自己视图状态的同时,BookData也有机会管理它的视图状态。

特别需要注意的是重写的CreateChildControls()。在CreateChildControls()方法中,使用_itemTemplate的InstantiateIn()方法生成控件的子控件。在实现化的过程中,模板内容的容器是实现了INamingContainer接口的BookInfo控件,所以模板实例出的子控件的ID将体现命名容器内部控件ID的规律:

<img id="tc0470109491_imgBook" class="bookCover" src="images/0470109491.png" alt="Professional AJAX" style="border-width:0px;" />

此外,传递给InstantiateIn()方法的参数类型应该与ItemTemplate上应用的TemplateContainerAttribute指定的类型相匹配。

8.2.3  BookInfoDesigner

并不是所有的页面开发人员都习惯在源代码视图设计控件,所以我们需要为控件提供更好的设计时支持,如图4-3所示。

DesignTime.gif

图8-3  模板的设计时支持

public class BookInfoDesigner : CompositeControlDesigner

{

    public override void Initialize(IComponent component)

    {

        base.Initialize(component);

        SetViewFlags(ViewFlags.TemplateEditing, true);

    }

    public override string GetDesignTimeHtml()

    {

        BookInfo control = Component as BookInfo;

        if (control != null)

        {

            if (control.ItemTemplate == null)

            {

                return CreatePlaceHolderDesignTimeHtml(

                    "Please edit ItemTemplate");

            }

        }

        return base.GetDesignTimeHtml();

    }

    TemplateGroupCollection _tempgc;

    public override TemplateGroupCollection TemplateGroups

    {

        get

        {

            if (_tempgc == null)

            {

                _tempgc = base.TemplateGroups;

                //定义模板组和声明模板

                TemplateGroup templates =

                    new TemplateGroup("ItemTemplate");

                templates.AddTemplateDefinition(

                    new TemplateDefinition(this, "ItemTemplate",

                Component, "ItemTemplate",false));

                _tempgc.Add(templates);

            }

            return _tempgc;

        }

    }

}

除了重写TemplateGroups属性,还需要重写Initialize()方法调用SetViewFlags(ViewFlags. TemplateEditing, true)方法才能让设计时支持所见即所得的编辑模板。

此外,我们重写了GetDesignTimeHtml(),这样在用户没有提供模板内容时,控件显示默认的占位块界面,如图8-4所示。

Empty.gif

图8-4  模板为空时的设计时效果

最后,编译控件,将BookInfo添加到页面中,为它设计ItemTemplate模板。需要注意的是,需要调用BookInfo的DataBind()方法才能使模板内容结合控件的数据。

protected void Page_Load(object sender, EventArgs e)

{

    this.tc0470109491.DataBind();

}

查看所有评论(0)条】

最近评论



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