Introduction
Visual Studio has a great designer... unfortunately there are some limitations to it. One is particularly annoying: user controls are always rendered, regardless what you do with 'DesignMode'.
Since this issue has been there since 2008, and after some Googling around I still haven't found a solution, I decided to fix it.
Background
Sometimes you just have a lot of clogging in a user control. For example, if you have a nice popup panel that does your login, logout, registration and so forth, and you put that in your master page, you will end up breaking your designer on every page or spending loads of time writing server controls.
This is actually functionality of Visual Studio itself: to make it easy for developers, they simply render child controls if you derive your class from UserControl. This limitation is solely for user controls, server controls don't have this limitation.
However, we don't want this, so what we need is a lazy solution that doesn't involve (re)writing user control code.
This gave me an idea: if we would simply wrap all the stuff we want to hide at design-time in a small server control, we can hide whatever we want, including user control contents.
Building the server control
Our server control is a very simple thing: it needs to be able to render its contents if we're running on the website, and hide the contents if we're in design time.
Hiding contents in design time can be done by overriding the RenderControl
method. If we output nothing, the designer will kick in and give us an ugly message telling that a control is there - which is why I render a space :-)
So here's (all) the code:
public class ContentTemplateContainer : Control, INamingContainer
{
}
[ToolboxData("<{0}:HiddenControlContainer runat="'server'"><ContentTemplate></ContentTemplate></{0}:HiddenControlContainer>")]
[ParseChildren(true), PersistChildren(false)]
public class HiddenControlContainer : WebControl
{
[TemplateContainer(typeof(ContentTemplateContainer))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
public ITemplate ContentTemplate { get; set; }
protected override void OnInit(EventArgs e)
{
if (!DesignMode)
{
if (ContentTemplate != null)
{
var container = new ContentTemplateContainer();
this.Controls.Add(container);
ContentTemplate.InstantiateIn(container);
}
}
base.OnInit(e);
}
public override void RenderControl(HtmlTextWriter writer)
{
if (!DesignMode)
{
base.RenderControl(writer);
}
else
{
writer.Write(" ");
}
}
}
The attributes ensure the designer will also generate code for the template, which basically means you can simply wrap this around your controls without breaking it. The only things that will change are the client ID's, since the contents are wrapped in an INamingContainer
.
Using the control
Using the control is easy: wrap your user control contents (or whatever you want to hide) in the content template, and you're done.
<pz:HiddenControlContainer ID="loginContent" runat="server">
<ContentTemplate>
<!-- Original user control contents -->
</ContentTemplate>
</pz:HiddenControlContainer>
And the result?
Let's render the user control again in the designer:
Nice and white, exactly what we wanted. Yes, that looks great :-)
History
5/14/2014 - Initial article