2011年5月31日 星期二

如何建立具有樣板(Template)的伺服器控制項

先看一下我們期望做出的效果

在網頁中,如果有使用SuccessTemplate樣板的話,顯示原來網頁上的內容。

<cc1:TheControl ID="TheControl1" runat="server" >
    <SuccessTemplate runat="server" >
    This is the success template.
    </SuccessTemplate>
</cc1:TheControl >

呈現出來的效果如下:


image


在網頁中,如果"沒有"使用SuccessTemplate樣板的話,使用內定的DefaultSuccessTemplate樣板內容。


<cc1:TheControl ID="TheControl2" runat="server" />
呈現出來的效果如下:
image


控制項間的結構如下:


image


控制項間的結構與網頁上標籤結構上有一些不同,在於我們並沒有在網頁上看到SuccessContainer這個容器,而是直接看到SuccessTemplate。


MSDN上說『宣告其容器控制項的基底型別』就是這個意思。


image

核心的步驟可以簡化如下:

CreateChildControls =>
    CreateSuccessViewControls =>
        建立空樣板 =>
        建立樣板容器 =>
        決定使用哪一種樣板 =>
        將樣板放入容器內,並建立子控制項 =>
        將容器放入控制項中

 

程式碼已改善MSDN範例不易擴充的問題,重新改寫如下:

 

[ToolboxData("<{0}:TheControl runat=server></{0}:TheControl>")]
[ParseChildren(true)]
public class TheControl : Control, INamingContainer
{
    private ITemplate _successTemplate;
    //InnerProperty:指定該屬性保存在 ASP.NET 伺服器控制項中做為巢狀標記。
    //TemplateContainer:宣告其容器控制項的基底型別
    //注意:預設的樣板行為由TemplateBuilder來決定
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(TheControl))]
    public virtual ITemplate SuccessTemplate
        {
            get { return this._successTemplate; }
            set
            {
                this._successTemplate = value;
                base.ChildControlsCreated = false;
            }
        }
    private SuccessContainer _successContainer;
    //注意:樣板容器(SuccessContainer)只有定義樣板使用的"資料",樣板行為由樣板(DefaultSuccessTemplate)決定
    internal sealed class SuccessContainer : Control,INamingContainer
    {            
        private Literal _title;
        internal Literal Title
        {
            get { return this._title; }
            set { this._title = value; }
        }
        public SuccessContainer(TheControl owner) { }
    }
    //如果使用者沒有使用樣板的話,使用這個內定的樣板
    //注意:樣板行為由樣板(DefaultSuccessTemplate)決定,樣板容器(SuccessContainer)只有定義樣板使用的"資料"
    private sealed class DefaultSuccessTemplate : ITemplate
    {
        private TheControl _owner;
        public DefaultSuccessTemplate(TheControl owner)
            {
                this._owner = owner;
            }
        
        public void InstantiateIn(Control container)
            {
                SuccessContainer successContainer = container as SuccessContainer;
                this.CreatControls(successContainer);
                this.LayoutControls(successContainer);
            }
        private void CreatControls(TheControl.SuccessContainer container)
            {
                container.Title = new Literal();
                container.Title.Text = "This is the \"default\" success template.";
            }
        private void LayoutControls(TheControl.SuccessContainer container)
            {
                Table table = new Table();
                TableRow tr = new TableRow();
                TableCell tc = new TableCell();
                tc.Controls.Add(container.Title);
                tr.Cells.Add(tc);
                table.Rows.Add(tr);
                container.Controls.Add(table);
            }
    }        
    private void CreateSuccessViewControls()
    {
        ITemplate template = null;
        this._successContainer = new SuccessContainer(this);
        this._successContainer.ID = "SuccessContainerID";
        //網頁上有樣板,型態為System.Web.UI.CompiledTemplateBuilder
        if (this.SuccessTemplate != null)
        {
            template = this.SuccessTemplate;
        }
        else //網頁上沒有樣板,使用內定的模板
        {
            template = new DefaultSuccessTemplate(this);
        }
        //定義子控制項和樣板所屬的控制項物件
        template.InstantiateIn(this._successContainer);
        this.Controls.Add(this._successContainer);
    }
    protected override void  CreateChildControls()
    {
        CreateSuccessViewControls();
    }
}
參考資料:

 

沒有留言:

張貼留言