How to render a ASP.NET User Control within a Web Service and return the generated HTML
My needs:
- Use ASP.NET AJAX PopUpExtender control to provide a mouseover popup with additional content relevant to the GridView row.
- Use DynamicContextKey to pass in GridView record's unique identifier.
My wants:
- Use a User Control to render the HTML response, so I don't need to build the HTML code using a StringBuilder or HtmlTextWriter.
First attempt
My initial code was something similar to the below code:
StringBuilder htmlResponse = new StringBuilder(); using (StringWriter sw = new StringWriter(htmlResponse)) { using (HtmlTextWriter textWriter = new HtmlTextWriter(sw)) { UserControl myControl = new UserControl(); myControl.LoadControl("myUserControl.ascx"); myControl.Attributes["MyDataKey"] = myDataKey; myControl.RenderControl(textWriter); } } return htmlResponse.ToString();
However, my StringBuilder htmlResponse (htmlResponse) and StringWriter (sw) were always empty. So whatever was happening, the user control wasn't being rendered and my response was always empty.
Note, this is within a System.Web.Services.WebService class and not within a System.Web.UI.Page class. Doing this within a System.Web.UI.Page is very simple and you can just create a new instance of your User Control class and render it directly to the page or a string. Since this is within a WebService and not Page class, I discovered that I need to make a mock Page object in order to render the User control as you'll see below.
Second try
After running into a stumbling block of how to render a user control from within a web service, I turned to Google.
First, I found this nice article over at 4GuysFromRolla "Displaying Extended Details in a GridView Using an Ajax Pop-Up". This article was almost what I needed.
This wasn't too helpful, since it was just returning HTML built on the fly. It was exactly what I wanted to do, but without using the Web Control. However, this article did validate my ASP.NET AJAX technique and allowed me to confirm that my ASP.NET code on my web form and GridView was correct.
Finally,... ScottGu to the rescue
After a bunch of trying and searching, I finally found Scott Guthrie's (aka the ASP.NET Guru) blog entry that solved all of my problems. In his article titled "Cool UI Templating Technique to use with ASP.NET AJAX for non-UpdatePanel scenarios", he explains exactly what I wanted to do. With a few minor modifications, I was off and running with a fully functional web service method.
In his ViewManager class (code download available on his web site via the blog link above), I updated the methods so that I can pass in a data object as a public property instead of a public field. I also modified the method to pass in the property name instead of requiring a field named "Data".
Here's ScottGu's modified code:
public static string RenderUserControl(string path, string propertyName, object propertyValue) { Page pageHolder = new Page(); UserControl viewControl = (UserControl)pageHolder.LoadControl(path); if (propertyValue != null) { Type viewControlType = viewControl.GetType(); PropertyInfo property = viewControlType.GetProperty(propertyName); if (property != null) { property.SetValue(viewControl, propertyValue, null); } else { throw new Exception(string.Format( "UserControl: {0} does not have a public {1} property.", path, propertyName)); } } pageHolder.Controls.Add(viewControl); StringWriter output = new StringWriter(); HttpContext.Current.Server.Execute(pageHolder, output, false); return output.ToString(); }
And here is how I'm calling it from my web service:
[WebMethod()] public string GetMyUserControlHtml(string contextKey) { Guid myId = new Guid(contextKey); return htmlResponse = UserControlRenderer.RenderUserControl( "myUserControl.ascx", "MyDataKey", myId); }
Note: I changed ScottGu's class name and method names to UserControlRenderer and RenderUserControl.



September 15th, 2008 - 08:11
Thank you! I’ve being going crazy because of an ascx that contains a datalist. Normal controls are rendered to text using common snippets of code (rendercontrol(..)) but it appears the trick is to use a page as a container when there is a datalist involved.
Cheers
November 28th, 2008 - 03:50
In the UserControl I have a RadChart Telerik control, is possible to render it. Have someone face the same problem?
December 1st, 2008 - 10:10
It seems like you should be able to render it. Are you having a specific problem or getting a specific error message when you try to render it?
December 25th, 2008 - 23:12
Thanks Jim for this
I also wanted to avoid building the html output line-by-line, and your solution saved me after a full-day search.
Johann
December 28th, 2008 - 21:53
I am getting a “Error executing child request for handler ‘System.Web.UI.Page’” error when using the my usercontrol contains a datagrid within it.
Any ideas on what am I doing wrong?
Deepu
December 29th, 2008 - 09:58
You might not be able to use a datagrid in this manner.
January 29th, 2009 - 11:13
Has anyone else noticed that the controls rendered in this way don’t have any of their page lifecycle methods called? For instance OnInit, OnLoad etc will not get called. Thoughts?
-Nate
January 30th, 2009 - 15:04
The single property value is limiting, and should probably be an array, list or dictionary. But that isn’t a big deal–I know this code was mostly “here’s how” and not a library. Either way, it was very useful for me, thanks!
January 30th, 2009 - 15:48
Musafa,
That’s how I ended up coding my final soultion. I might update the above code to show how to use that technique next week.
July 31st, 2009 - 03:15
I did almost exactly the same but instead of the “if (propertyValue != null) {…}” , I created an HtmlForm instance, then added the userControl to the HtmlForm instance, then added the HtmlForm to the page (rather than the control to the page).
After I had to strip out the ViewState, EventValidation and Form tags from the HTML because using the as-is generated html and injecting it into an existing page (with ajax) was causing an:
Sys.WebForms.PageRequestManagerServerErrorException: The state information is invalid for this page and might be corrupted.
Error was thrown when trying to do an partial page update.
Does your technique not generate the ViewState, etc, tags?
July 31st, 2009 - 09:58
The above technique doesn’t generate ASP.NET specific elements such as the hidden form element called ViewState. It only produces typical HTML element output.
August 19th, 2009 - 02:09
Thank you Jim
Its really more helpfull to me.
Thanks a lot.
August 19th, 2009 - 05:02
Is there any way to use Session variables in User control loaded with this Technique
August 19th, 2009 - 08:03
There’s no way to use session variables with this technique.
October 29th, 2009 - 23:57
Hi,
I am getting the foolowing error —-
error executing child request for handler ‘system.web.ui.page’.
I am rendering modified view mananger class.
i have user control with asp.net CheckBox.
How to solve this issue of rendering Asp.net server controls using ajax.
“Why Datalist and Repeater controls can not throw error ,when they are the server side controls”
November 12th, 2009 - 23:33
Hey All,
I’ve been successfully able to render the user controls HTML this way. Thanks all.
Now comes another challenge : How do I access the values of these controls on client side and server side as well? Lets say I need to provide the sum of the values within the user controls added this way.
Any resolution?
Thanks again,
Ishan
December 10th, 2009 - 08:31
Thx. Helped a lot.
March 22nd, 2010 - 15:03
WOOT! I’ve been looking for a solution to this issue for YEARS! THANKS!
June 11th, 2010 - 00:25
Not sure why you cast the item as a UserControl and then used reflection to look up the property. Would’ve been a lot quicker to just cast it to the type you were looking for and set the property.
June 11th, 2010 - 08:02
That might work. The article is a couple of years old, so I don’t have the technical specifics in front of me nor do I remember the code. However, if you were to just cast the object to the UserControl class, then you wouldn’t be able to specify dynamic attributes/properties of the object. You’d have to know both the type of the class and the exact names and data types of the attributes.
December 13th, 2010 - 14:59
Thank you soooooo much. I’ve been breaking my head for the past 2 days to get this solution.
December 15th, 2010 - 02:43
You can use session state, the webservice would have to be on the same box as the website, and you just need to specify:
WebMethod(EnableSession:=True)
for the web method.
April 12th, 2011 - 01:46
Consider the following code (delegation instead of reflection):
public delegate void SetControlProperties(TControl control) where TControl : UserControl, new ();
public class UserControlRenderer where TControl : UserControl, new()
{
private readonly string userControlPath;
private SetControlProperties setControlPropertiesDelegate;
///
///
///
/// Path to the ascx file of the control
public UserControlRenderer(string userControlPath)
{
this.userControlPath = userControlPath;
}
///
/// Use to set the control’s properties before calling RenderHtml
///
///
public void SetControlProperties(SetControlProperties setControlPropertiesCode)
{
setControlPropertiesDelegate = setControlPropertiesCode;
}
public string RenderHtml()
{
var pageHolder = new Page();
var viewControl = (TControl)pageHolder.LoadControl(userControlPath);
setControlPropertiesDelegate(viewControl);
pageHolder.Controls.Add(viewControl);
var output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
}