ASP.NET User Controls are pretty useful. They allow functional modules of code and markup to be encapsulated in such a way that reuse is convenient and easy, without sacrificing the power or integration of the ASP.NET model. As we move into an era of AJAX-driven websites, this modularity is still very important. Can the user controls that we all know and (mostly) love still help with this encapsulation, despite being engineered before AJAX techniques emerged? I think they can. But at this point in the ASP.NET timeline, user controls are in need of some help.
The Fundamental Problem
The following example illustrates a basic scenario in which we have a page that uses jQuery to load a user control when a button is clicked. The calling page is pretty simple:
As you can see, all I’ve done in jQuery’s ready event handler is wire up the click event of the button to make an ajax call to a web service. The data result that is returned from the ajax call is then added into the content div on the page. Let’s take a look at the web service that we are calling in that code:
This is a pretty standard WCF Ajax service, which uses a utility class called UserControlUtility by calling its RenderAsString method, which looks like this:
In the helper method above, I’m simply accepting a parameter called path, which allows us to use the LoadControl method in the usual way. If you are worried about the potential baggage of instantiating a Page object for every User Control that is rendered, don’t lose too much sleep over it. A page object that is instantiated like this is pretty lightweight, and doesn’t go through the heavy ASP.NET Page lifecycle that occurs on a normal page load.
jQuery.DynamicLoader is a simple jQuery plugin I wrote that allowed a parent page to dynamically load User Controls and their corresponding script files on demand. Here is the way it works:
- You reference jQuery.DynamicLoader on your parent page.
- Create an ajax service that renders user controls, similar to the example I showed earlier
Let’s jump into the sample project I’ve created as an example:
Here is how the first button is wired up with jQuery:
As you can see, it is calling DynamicLoader’s loadUC function, which takes a few options: ucName is the path to the user control to be loaded, queryString allows you to pass parameters to your UserControl to help render it on the server, and eventBindings allows you to handle events that are fired within the usercontrol.
We have a standard jQuery ready handler, and inside that we call DynamicLoader’s registerUC function. This will only be loaded once, even if multiple TableWidgets are loaded afterwards. Also notice the event triggers. You can create as many different types of events as your heart’s desire, as long as the parent knows the name of the event (and references it in the eventBindings option). I’ve included ready, busy, unbusy, and finished in the default options. The ready event is one that I consider critical, because it is the event that the parent will use to attach the user control to the page.
Here is a screenshot of the demo:
Room for Improvement
DynamicLoader is more of a proof concept than a full-fledged plugin, and there are several areas in which it needs to be improved:
The event chaining needs some work. I haven’t really tested it with events that bubble more than two layers up.
Right now it doesn’t look like jQuery’s $.getScript is caching the scripts. I’d like to rewrite a version of getScript that does.
The registration system is very rigid at this point. It expects you to pass in a user control’s path, and the script needs to register itself with that exact path as its key (without the extension).
So there you have it. This technique allows you to treat your User Controls as neatly encapsulated modules that are loaded and configured on demand. Plus, there is no limit to nesting your user controls, and they will load efficiently and within their own context. Finally, you don’t have to break communication with your user controls. The event binding allows a separation of concerns, while still being able to act on important things that happen within the user control.
I hope you find this technique useful, and please let me know if you have suggestions or improvements!