I would like to create an custom server control, VersionedContentControl, which will allow me to specify different variations of the final markup.
Example usage:
<custom:VersionedContentControl runat="server" VersionToUse="2">
<ContentVersions>
<Content Version="1">
<asp:HyperLink runat="server" ID="HomeLink" NavigateUrl="~/Default.aspx">Home</asp:HyperLink>
</Content>
<Content Version="2">
<asp:LinkButton runat="server" ID="HomeLink" OnClick="GoHome">Home</asp:LinkButton>
</Content>
<Content Version="3">
<custom:HomeLink runat="server" ID="HomeLink" />
</Content>
</ContentVersions>
</custom:VersionedContentControl>
Using the markup above, I would expect the only LinkButton control to be utilized on the page.
Long Story
I am having great difficulty trying to define this custom control. I haven't even been able to find a good example on the MSDN of using nested controls like this. Instead, I have had to resort to following these blog posts as examples:
- http://blog.spontaneouspublicity.com/child-collections-in-asp-net-custom-controls
- http://www.tomot.de/en-us/article/2/asp.net/how-to-create-an-asp.net-control-that-behaves-as-a-template-container-to-nest-content-via-markup
Unfortunately, everything I have tried has failed miserably. Here is what I have currently:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
namespace CustomControls
{
[ParseChildren(true)]
[PersistChildren(false)]
public class VersionedContentControl : Control, INamingContainer
{
public string VersionToUse { get; set; }
[PersistenceMode(PersistenceMode.InnerProperty)]
public IList<Content> ContentVersions { get; set; }
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
var controlToUse = ContentVersions.Single(x => x.Version == VersionToUse);
Controls.Clear();
controlToUse.InstantiateIn(this);
}
}
public class Content : ITemplate
{
public string Version { get; set; }
public void InstantiateIn(Control container)
{
// I don't know what this method should do
}
}
public class ContentVersionsList : List<Content> {}
}
Even though I haven't implemented InstantiateIn, all 3 versions of my content appear on the page; it shows 3 links.
Also, I can't actually use the control unless I specify different ID property values for each nested control; I can't use "HomeLink" for all of them. I would like to be able to re-use the ID so that I can access the control from the code behind.
I realize that, normally, it is forbidden to specify duplicate ID values for multiple controls on a page. However, in the MSDN documentation for System.Web.UI.MobileControls.DeviceSpecific, the examples use duplicate ID values for nested controls. Infact, the example is very close to what I want to do; it differs content based on a mobile device compatibility filter.
<mobile:Form id="Form1" runat="server">
<mobile:DeviceSpecific Runat="server">
<Choice Filter="isHTML32">
<HeaderTemplate>
<mobile:Label ID="Label1" Runat="server">
Header Template - HTML32</mobile:Label>
<mobile:Command Runat="server">
Submit</mobile:Command>
</HeaderTemplate>
<FooterTemplate>
<mobile:Label ID="Label2" Runat="server">
Footer Template</mobile:Label>
</FooterTemplate>
</Choice>
<Choice>
<HeaderTemplate>
<mobile:Label ID="Label1" Runat="server">
Header Template - Default</mobile:Label>
<mobile:Command ID="Command1" Runat="server">
Submit</mobile:Command>
</HeaderTemplate>
<FooterTemplate>
<mobile:Label ID="Label2" Runat="server">
Footer Template</mobile:Label>
</FooterTemplate>
</Choice>
</mobile:DeviceSpecific>
</mobile:Form>
It would be nice to look at the source of those controls to see how they accomplish this but, unfortunately, it is not open-source.
My Question
How can I create a custom server control which contains a list of nested controls and only renders one of the nested controls based on a property? Ideally re-using IDs among separate nested controls.
MultiView. I didn't even know this control existed! Unfortunately, it does not allow re-use of IDs likeITemplateinstances do. I am going to keep trying.