Rendering a Usercontrol as a String for Webservices
I am currently working a project at work that requires us to use progressive enhancement so that our site is accessible to all users regardless of whether they have javascript enabled. One of the issues that we came across was keeping only one version of the html that we needed to use, regardless whether we were rendering our HTML client side or server side.
That way, we only ever had one version of the HTML and this also allowed us to use the same usercontrol server side. This also makes it easier for us to maintain the code.We decided to use usercontrols and just render them as a string to the client side, it is a sneaky approach but it seems to be working nicely.
In order to render your user control as a string, simply do the following.
public string RenderViewDdl(string path, List<string\> data)
{
Page pageHolder = new Page();
ctlUserControl viewControl = (ctlUserControl)pageHolder.LoadControl(path);
viewControl.Data = data;
pageHolder.Controls.Add(viewControl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
There is also a public variable on the usercontrol that allows you to set the data to display using these lines.
public partial class ctlUserControl : System.Web.UI.UserControl
{
public List<string> Data;
protected void Page_Load(object sender, EventArgs e)
{
ddlItems.DataSource = Data;
ddlItems.DataBind();
}
}
While working on this, one of my colleagues (Mr. Robin Osborne - thanks Robin!), came across an error while trying to render a usercontrol with an asp.net server side control (For example a drop down list).
This is the following error he received:
System.Web.HttpException: Error executing child request for handler System.Web.UI.Page'. ---> System.Web.HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.Web.HttpException: Control 'ctl00_ddlCities' of type 'DropDownList' must be placed inside a form tag with runat=server.
1. In order to solve this problem, generate a new “page” class that inherits System.Web.UI.Page and overrides VerifyRenderingInServerForm to do nothing:
public class PageOverride : System.Web.UI.Page
{
public override void VerifyRenderingInServerForm(System.Web.UI.Control control)
{
}
}
2. When creating your Page holder, use the new page (PageOverride) instead of Page in your rendering:
Page pageHolder = new ctlUserControl.PageOverride();
This is sneaky and it works like a charm! I've included a small project that show this in action.
Further reading
http://en.wikipedia.org/wiki/Unobtrusive_JavaScript
http://stevesmithblog.com/blog/render-control-as-string/
http://encosia.com/2008/02/05/boost-aspnet-performance-with-deferred-content-loading/