Forcing ASP.NET Control To Go Through Its Full Life Cycle On Static Methods

Posted by Ahmed Tarek Hasan on 12/20/2013 10:17:00 PM with No comments
A few days ago I needed to use a custom control which I made as a template to generate some HTML and return it as a response for an Ajax call, but, I faced a problem which is that my WebMethod is a static method which cannot access the control instance i dropped on the same page including the WebMethosd on code behind.

To make it more clear, here was the situation. I was working on an ASP.NET tree control which represents some system objects into hierarchical form. When testing the tree on few objects everything went fine, but with increasing the number of objects the tree became heavier and its response was not that appealing.

This made me think to take another approach which is using Ajax calls to hit the database and get the only objects I need per situation and finally attach/bind/update the tree with these objects. This will save me costy round trips back and forth to the server through postbacks and for sure the heavy load of the tree HTML with each request and response.

First, the idea was ideal but then came the difficulties. The tree control I was using is a third party control. So, I didn't actually have the code and pattern on which the nodes were built, so this made me think, how would I write the full HTML of the new nodes I got from the Ajax calls?

After short thinking, an idea hit me...... why don't I use the same tree control to draw me the single nodes I need, so whenever an Ajax call is performed a new instance of the tree control on another page will return me the HTML of the new nodes and then I will just update my main tree control with the new HTML. Luckily, I found that the tree control I was using had a method which would return me the generated HTML by the tree, so I cheered up and said out loud "Found it".

For the second time, this was a promising idea but then came the troubles. I had already implemented a static WebMethod on a page to call by Ajax. This WebMethod should be responsible for getting me the new HTML. So, I created an instance of the tree control inside this method, bound the tree to the new object, called the tree method which returns the generated HTML, but my code blown up with an exception.

After debugging I found that the tree method I was calling to get the generated HTML thrown some serious exceptions as the tree control instance I created inside my WebMethod is not actually fully loaded because the control didn't go through its whole life cycle.

This made me feel bad as the other alternatives are nightmares. So, I tried to search for any way by which I can force a control to go through its life cycle inside a static method. Finally, I found the solution and that is what I am going to show you here.

First, you have to know that the "Page" class has some validation which forces you to add a "Form" tag or you will get a serious exception. Since we will need on our solution to add the control we wish to load inside a static method on a page instance, then it would be nice to know this first.

To create a Page and bypass the Form tag existence we need to override the "VerifyRenderingInServerForm" method on the "Page" class. So, we will create a new class inherited from the "Page" class and override the "VerifyRenderingInServerForm" method as shown below.
public class PageWithoutForm : Page
{
    public override void VerifyRenderingInServerForm(Control control)
    {
    }
}

Now, we get to the last part which is forcing the control to go through its life cycle. This can be done as in the code below.
[WebMethod]
public static string LoadControlInStaticMethod()
{
 CustomControl ctrl = new CustomControl();

 var page = new PageWithoutForm();
 page.Controls.Add(ctrl);
 StringWriter writer = new StringWriter();
 HttpContext.Current.Server.Execute(page, writer, false);
 
 ctrl.CustomProperty = "SomeValue";
 ctrl.FireSomeAction();
}

If you have your control in "ascx" form, you can do the same as in the code above but with just updating the line where you defined the control instance as in the code below.
[WebMethod]
public static string LoadControlInStaticMethod()
{
 CustomControl ctrl = (CustomControl)LoadControl("~/SomeFolder/CustomControl.ascx");

 var page = new PageWithoutForm();
 page.Controls.Add(ctrl);
 StringWriter writer = new StringWriter();
 HttpContext.Current.Server.Execute(page, writer, false);
 
 ctrl.CustomProperty = "SomeValue";
 ctrl.FireSomeAction();
}


That is it all. I hope this will help someone one day.
Good luck.