如何使用与UpdatePanel类似的标记使用内部属性创建ASP.NET用户控件?

时间:2010-11-05 15:51:00

标签: asp.net user-controls

我正在创建一个用户控件,我希望它具有内部属性和普通属性。虽然控件的实际功能与UpdatePanel无关,但我试图创建具有类似ASPX标记的东西。设计页面的开发人员应该能够使用我的控件,如:

<ns:MyControl ID="someID" runat="server" SomeOtherAttribute="true">
  <ContentTemplate>
    <asp:Label ID="someLabel" runat="server" Text="Normal page markup and controls should go here" /><br />
    <p>This should be OK too.</p>
  </ContentTemplate>
  <ControlEvent ControlName="idOfOtherControl" Event="Click" />
  <ControlEvent ControlName="idOfSomeOtherControl" Event="MouseOver" />
</ns:MyControl>

同样可以接受的是将ControlEvent标记包装在其他标记中,更像是使用UpdatePanel时发生的情况:

<ns:MyControl ID="someID" runat="server" SomeOtherAttribute="true">
  <ContentTemplate>
    <asp:Label ID="someLabel" runat="server" Text="Normal page markup and controls should go here" /><br />
    <p>This should be OK too.</p>
  </ContentTemplate>
  <ControlEvents>
    <ns:ControlEvent ControlName="idOfOtherControl" Event="Click" />
    <ns:ControlEvent ControlName="idOfSomeOtherControl" Event="MouseOver" />
  </ControlEvents>
</ns:MyControl>

但是,ControlEvents标记应该只允许这个特定的标记,而不是普通的服务器标记或任何东西。

我了解如何让ContentTemplate内的内容呈现(在我的Page_Init中,我使用ITemplate.InstantiateIn(placeholderControl)方法)。那部分工作正常。我遇到麻烦的地方是让ControlEvent部分工作。我可以在代码隐藏中创建内部属性,如:

[TemplateInstance(TemplateInstance.Multiple)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateContainer(typeof(ControlEvent))]
public List<ControlEvent> ControlEvents { get; set; }

通过这样做,ASP.NET似乎明白我想要一个ControlEvents标记。但是,我无法识别属性,无论是内部属性还是属性。相反,如果我尝试第一种类型的标记,我会得到一个运行时解析器错误:“属性'ControlEvents'没有名为'ControlName'的属性。”如果我尝试第二种类型的标记,我会在运行时获得NullReferenceException作为解析器错误,并在<ns:ControlEvent>行生成错误。

我已经在网上查了一下,但是我没有找到一个很好的例子或解释,说明如何完成我要对ControlEventControlEvents做的事情。我错过了什么?有人能指点我这里的好榜样吗?

谢谢!

修改

事实证明,部分问题是由于我如何使用它将控件注册到页面。如果我在使用:

<%@ Register Src="~/Controls/MyControl.ascx" TagPrefix="ns" TagName="MyControl" %>

似乎控件本身在标记中被识别,但ns:ControlEvent不是。如果我将Register更改为:

<%@ Register Namespace="Namespace.Controls" TagPrefix="ns" Assembly="MyAssembly" %>

只要理解控件本身(假设runat="server"标记中包含ns:ControlEvent),就可以了。但是,MyControl的子控件不再被实例化或呈现,并且Page_InitPage_Load等控件的事件未被触发。

经过多次搞乱后,我发现我似乎需要两个Register指令。这似乎是一个混乱的“解决方案”,它甚至没有真正解决所有问题,因为(因为)我的控件中至少有一些标记没有渲染,尽管模板控件(ContentTemplate)似乎再次工作。

我可以在这里遗漏哪些东西会使这个控件使用起来不那么混乱,这会让这个控件实际上正确地渲染一切?

1 个答案:

答案 0 :(得分:10)

必须将属性标记为Collection,而不是List;除此之外,最重要的是要记住,父控件必须设置 [ParseChildren(true)] 属性,否则集合将为null

以下(服务器控制)示例按预期工作:

   public class ControlEvent
    {
        public string ControlName { get; set; }
        public string Event { get; set; }
    }

    [ParseChildren(true)]
    public class SampleControl : System.Web.UI.Control, INamingContainer
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(SampleControl))]
        public ITemplate ContentTemplate { get; set; }

        [MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty), DefaultValue((string)null)]
        public Collection<ControlEvent> ControlEvents { get; set; }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            if (ContentTemplate != null)
                ContentTemplate.InstantiateIn(this);

            if (null != ControlEvents)
                foreach (ControlEvent e in ControlEvents)
                    Controls.Add(new Label() { Text = string.Format("ControlName:{0}, Event:{1}", e.ControlName, e.Event) });
        }
    }

HTH。