具有可变数量模板的Ascx控件

时间:2011-06-06 15:25:08

标签: asp.net templates user-controls

我有一个正在加载模板区域内容的ascx用户控件。例如:

<。>在.ascx文件中:

<%@ Control Language="C#" CodeBehind=....." %>

[some html ....]
<asp:PlaceHolder runat="server" ID="ctlContentHolder" />
[some other html ...]

然后在后面的代码中:

  [Browsable (false)]
  [PersistenceMode (PersistenceMode.InnerProperty)]
  public ITemplate ContentTemplate
  {
    get; set;
  }

  protected override void OnInit(EventArgs e)
  {
     base.OnInit(e);

     ContentTemplate.InstantiateIn (ctlContenuHolder);
  }

我可以像这样使用控件:

<xxx:myControl runat="server" id="myCoolControl">
   <ContentTemplate>
        The content of my cool control here.
   </ContentTemplate>
</xxx:myControl>

我想做的是,内容模板数量不确定,类似于转发器,但每个模板中的内容不同。我可以用这样的东西:

<xxx:myControl runat="server" id="myCoolControl">
   <ContentTemplate>
        Some content here
   </ContentTemplate>
   <ContentTemplate>
        Some completely different content here
   </ContentTemplate>
</xxx:myControl>

有人知道如何实现这样的控制吗?

1 个答案:

答案 0 :(得分:0)

这可能。这是一些代码,YMMV。我不在乎解释它ATM。它是initially based this question,但最终我有了这种方法,因为那时我没有得到真正的 ITemplate对象,而是带有ITemplate接口的存根对象..或者,大多数都没用。

请注意,每个Control只有一个模板,但它自己的集合有许多这样的控件。它有点冗长,但我找不到轻松删除其他级别的方法。

使用示例:

<seis:SmartTemplate runat="server">
    <NamedTemplates>
        <seis:NamedTemplate Name="Name1" runat="server">
        <Content>
           Template1
        </Content>
        </seis:NamedTemplate>
        <seis:NamedTemplate Name="Name2" runat="server">
        <Content>
           Template2
        </Content>
        </seis:NamedTemplate>
        ...

代码:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Web.UI;

namespace Channelmatter.SBCJC.SEI.UISupport
{
    /// <summary>
    /// Provides the ability to create templates in Markup that
    /// can be applied dynamically.
    /// </summary>
    [ToolboxData("<{0}:SmartTemplate runat='server'></{0}>")]
    [ParseChildren(true)]
    [PersistChildren(false)]
    public class SmartTemplate : Control, ITemplate
    {
        public SmartTemplate()
        {
            NamedTemplates = new NamedTemplateCollection();
        }

        public class SelectTemplateEventArgs : EventArgs
        {
            // The control that the template instantiates into
            public Control Container { get; set; }
            public object DataItem { get; set; }
            // If set to a non-null value, represents the template name to use
            public string TemplateName { get; set; }
        }

        public delegate void SelectTemplateEventHandler(object sender, SelectTemplateEventArgs args);

        /// <summary>
        /// Allow the template to be selected based upon the Container and DataItem.
        /// The event should specify the TemplateName to use.
        /// </summary>
        [Browsable(true)]
        public event SelectTemplateEventHandler SelectTemplate;

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public NamedTemplateCollection NamedTemplates { get; set; }

        /// <summary>
        /// Find a template with the given name.
        /// Returns null if there is no such template.
        /// </summary>
        public ITemplate FindTemplate(string name)
        {
            foreach (var namedTemplate in NamedTemplates)
            {
                if (namedTemplate.Name == name)
                {
                    return namedTemplate;
                }
            }
            return null;
        }

        public void InstantiateIn(Control container)
        {
            var dataContainer = (IDataItemContainer)container;
            var dataItem = dataContainer.DataItem;
            if (SelectTemplate != null)
            {
                var args = new SelectTemplateEventArgs
                {
                    Container = container,
                    DataItem = dataItem,
                };
                SelectTemplate(this, args);
                var namedTemplate = FindTemplate(args.TemplateName);
                if (namedTemplate != null)
                {
                    namedTemplate.InstantiateIn(container);
                }
            }
        }
    }

    [ToolboxData("<{0}:NamedTemplate runat='server'></{0}>")]
    [ParseChildren(true)]
    [PersistChildren(false)]
    public class NamedTemplate : Control, INamingContainer, ITemplate
    {
        public class BeforeInstantiateInEventArgs : EventArgs
        {
            public Control Container;
            public object DataItem;
            /// <summary>
            /// If set to a non-null value this will be used instead of DataItem.
            /// </summary>
            public object MappedDataItem;
        }

        public delegate void BeforeInstantiateInEventHandler(object sender, BeforeInstantiateInEventArgs args);

        /// <summary>
        /// Can be used to do programatic things before the template is initiated.
        /// </summary>
        [Browsable(true)]
        public event BeforeInstantiateInEventHandler BeforeInstantiateIn;

        [Browsable(true)]
        public string Name { get; set; }

        [Browsable(false)]
        [TemplateContainer(typeof (NamedTemplateContainer), BindingDirection.TwoWay)]
        [TemplateInstance(TemplateInstance.Multiple)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate Content { get; set; }

        public void InstantiateIn(Control container)
        {
            var dataContainer = (IDataItemContainer)container;
            var dataItem = dataContainer.DataItem;
            if (BeforeInstantiateIn != null)
            {
                var args = new BeforeInstantiateInEventArgs
                {
                    Container = container,
                    DataItem = dataItem,
                };
                BeforeInstantiateIn(this, args);
                if (args.MappedDataItem != null)
                {
                    dataItem = args.MappedDataItem;
                }
            }

            var namedTemplateControl = new NamedTemplateContainer(dataItem);
            Content.InstantiateIn(namedTemplateControl);
            container.Controls.Add(namedTemplateControl);
        }
    }

    public class NamedTemplateCollection : Collection<NamedTemplate>
    {
    }

    public class NamedTemplateContainer: Control, IDataItemContainer
    {
        readonly private object dataItem;

        public NamedTemplateContainer(object dataItem)
        {
            this.dataItem = dataItem;
        }

        public object DataItem
        {
            get { return dataItem; }
        }

        public int DataItemIndex
        {
            get { throw new NotImplementedException(); }
        }

        public int DisplayIndex
        {
            get { throw new NotImplementedException(); }
        }
    }

}