8.04.2011

Redistributing user controls

Ever needed a recipe to build a user control library to use across several web applications? Here's one!
First of all, we should realise that this isn't something you can get out-of-the-box and as such, there are several half-solutions out there, and as far as I know there isn't a perfect one. The one I'm presenting here suits best for what I'm trying to achieve, and it doesn't mean it is the best for other people's situation.

My main motivation, other than user control reusability was to create a project structure that allowed me to migrate a webapplication developed in VB.NET to C#. This has to be done over a long-period and I don't want to keep adding code in VB.NET any time a new feature is needed. Hence I decided to build user controls in C# that I could use in VB.NET pages, thus avoiding any code-behind code in VB.NET.

There is however a big problem with creating user controls outside web applications: We're not supposed to do it! When you build a page (aspx) or a user control (ascx) in a regular web application, the compiler creates an assembly where each of this files produces a class that inherits from our "code-behind" class. This class is where some of the magic happens. When we create a user control outside the web application where the user control is being used the afore-mentioned classes don't exist. So how do we cope with this? We'll create a base class for all our user controls and we inject some magic in it as well! This base class will be responsible for:
  - manually loading the ascx file and parse it to dynamically create the elements/controls present in the ascx.
  - "binding" these elements/controls to the members that represent them in the code-behind class (this members are usually present in the designer file)
Here's the code to such a base class:


And here's the rest of my recipe:

1- Create new asp.net web application project. This makes sure we can create new user controls directly through the "new item" dialog, which would not be possible if we were to create a regular class library project.
  1.1- Clear all the files that visual studio placed inside the new project (leave only the AssemblyInfo.cs)

2- Creating user controls in your new project
  2.1 - Add a new "Web User Control" item to your project
  2.2 - Design your control as you always do
  2.3 - Open the designer files and copy the declaration of controls you'll want to access later on to the code-behind class. (You can also keep the designer files as partial classes to your code-behind class, but there's not much point in it as visual studio won't be able to update it later on)
  2.4 - Delete the designer files (unless you've decided to keep it as a partial class)
  2.5 - The control header (in the ascx file) should be cleaned to keep only the Language attribute, thus leaving only the following header:

  2.6 - Change the ascx file build action from "Content" to "Embedded Resource"
  2.7 - Change the control base class from "UserControl" to our own "BaseUserControl"

3- Using the user controls in your web application
  3.1 - Reference the new project in our old web application
  3.2 - Change the web.config to avoid registering the control in every page you'll use it:


Alternatively you could register the control in in the pages you want to use it by adding the following line:


And we're done! Here's a list of the issues found with this solution (so far):
- Couldn't get the control skins to work with my user controls
- Local and Global Resources are currently in the web application (instead of being properly encapsulated in the user controls that uses them), as asp.net loads them from the page, not the user control. There are at least two workarounds for this. We could create a resource provider and resolve the resources manually or we could use resources like we do in a regular winforms application, instead of using the local/global resources asp.net approach.

That's all folks!