[Update 1/31/2011: I just attached a zip that demonstrates the files you'll get at each step. In the process, I verified that the steps still work well with VS 2010]
This article applies to ASP.NET 2.0 and Visual Studio 2005.
Since its early days, ASP.NET has always supported two different ways of authoring server controls:
Looking at those two methods, they are very different, and each has a number of advantages and disadvantages. I won't discuss them all, but will focus on those that are relevant to this article:
The goal of this article is to show how you can have the best of both world by turning an ascx user control into a redistributable custom control, by making use of ASP.NET 2.0's new precompilation features.
The basic steps to make this happen are as follows:
We will look at those steps in more detail in the rest of the article.
To author the User Control, it is best to start with an empty site (not web app!) that contains nothing other than the ascx. While the authoring of the User Control uses 'standard' techniques, there are some restrictions that you need to be aware of in order for it to be successfully turned into a standalone Custom Control.
The main restriction is that the User Control needs to be self contained. That is, it cannot be dependent on app global things like App_Code or global.asax. The reason for this is that since the goal is to turn the UserControl into a standalone DLL, it would break in other apps if it relied on code that is not part of that DLL.
One exception to this rule is that the UserControl can be dependent on assemblies that live in the bin directory (or in the GAC). You just have to make sure that the other assemblies are always available when you use your Custom Control in other apps.
Another tricky thing is the use of static resources, like images. After you turn it into a Custom Control in a standalone assembly, it becomes hard for it to keep such references, and avoiding them simplifies deployment. If you really must have such references, one option is to use absolute URL's if you can guarantee that the resource will always be available on a certain site. Or you can look into machine wide resources (e.g. src="http://blogs.msdn.com/MyImages/welcome.jpg"), though you will then need to make sure the resources are installed on the server machine (e.g. as part of some setup).
Ok, so let's start and actually author the User Control. Visual Studio 2005 gives you the choice to place the code in a separate file, or use inline code. This is a matter of personal preference, and either one will work to create a Custom Control. For this article, we will use inline code. When you create the User Control (say MyTestUC.ascx), VS creates it with the a @control directive that looks like this:
This is fine, except for one thing: we want the class to live within a namespace of our choice (instead of 'ASP', which is what's used by default). To do this, we simply modify the ClassName attribute to include the namespace (this is a new feature in 2.0). e.g.
That's really the only 'special' thing you need to do. Now you can go ahead and implement your User Control as you always would: add some server control, some client HTML, server script, client script, etc...
Before trying to turn the User Control into a Custom Control, it is a good idea to test it in the source app using a simple page. To do this, simply create a new Page in Visual Studio, go to design View, and drag and drop your User Control into it.
The two notable pieces of your page are the Register directive:
and the User Control declaration:
Note that at this point, the Register directive uses the User Control syntax (Src/TagName/TagPrefix) and not the Custom Control syntax (TagPrefix/Namespace/Assembly). This will change after we turn the User Control into a Custom Control.
Run your page (Ctrl-F5) and make sure the User Control works the way you want before moving to the next step.
The next step is to use the new Publish command to precompile your site and turn your User Control into a potential Custom Control. You'll find the command under Build / Publish Web Site. In the Publish dialog, do the following:
Though it is entirely optional, note that the Publish Web Site dialog lets you strong name the generated assemblies. This allows you to sign the assembly so that it cannot be tempered with. Additionally, it allows you to place the assemblies in the Global Assembly Cache (GAC), which makes it easier to use machine wide. More on this in Step 5.
Go ahead and complete the dialog, which will perform the precompilation.
Note: This same step can also be accomplished without using Visual Studio by using the new aspnet_compiler.exe command line tool. The options it supports are basically the same as what you see in the Publish dialog. So if you are more command line inclined, you might prefer that route. e.g. you would invoke it using the command: 'aspnet_compiler -p c:\SourceApp -v myapp -fixednames c:\PrecompiledApp'.
Now, using the Windows Explorer or a command line window, let's go to the directory you specified as the target so we can see what was generated. You will see a number of files in there, but let's focus on the one that is relevant to our goal of turning the User Control into a Custom Control.
In the 'bin' directory, you will find a file named something like App_Web_MyTestUC.ascx.cdcab7d2.dll. You are basically done, as this file is your User Control transformed into a Custom Control! The only thing that's left to do is to actually use it.
Note: In case you're curious, the hex number within the file name (here 'cdcab7d2') is a hash code that represents the directory that the original file was in. So all files at the root of your source app will use 'cdcab7d2', while files in other folders will use different numbers. This is used to avoid naming conflicts in case you have files with the same name in different directories (which is very common for default.aspx!).
Now that we have created our Custom Control, let's go ahead and use it in an app! To do this, let's create a new Web application in Visual Studio. We then need to make our Custom Control available to this application:
Note: as an alternative, you can choose to place the assembly in the GAC instead of the 'bin' directory. In order to do this, you need to choose the Strong Name option in Step 3. You then need to add your assembly in the <assemblies> section of web.config in the Web application that wants to use it (or in machine.config to make to globally usable).
Now let's create a test page that uses the Custom Control. This is similar to Step 2, except that you are now dealing with a Custom Control instead of a User Control.
First, add a Register directive to your page. It should look something like this:
Note how we are using a different set of attributes compared to Step 2. Recall that in Step 1, we made sure that the ClassName attribute included a namespace. This is where it becomes useful, as Custom Control registration is namespace based. Also, you need to specify the assembly name, which is why having a name that is easily recognizable is useful, as discussed in Step 3.
Now, let's declare a tag for the Custom Control. e.g.
That's it, you can now run your app (Ctrl-F5), and you are using your new Custom Control!
This shows how to use your Custom Control declaratively, but note that it can also be used dynamically, just like any other control. To do this, just create the control using 'new'. Here is what the previous sample would look like using dynamic instantiation:
Note: instantiating your Custom Control dynamically as above is basically the equivalent of instantiating your original User Control using the LoadControl API. Note that you can no longer use the standard LoadControl API after converting it to a Custom Control, since Custom Controls don't have a virtual path. However, ASP.NET 2.0 has a new LoadControl override which takes a Type, and that you could use in this case. The one reason I can think of that you might choose to call LoadControl instead of just using 'new' is to take advantage of fragment caching (aka partial caching). If you use 'new', any OutputCache directive in your ascx will be ignored.
This article outlines how to turn a User Control into a Custom Control, in order to take advantage of the strength of each model. While there are some limitations with this hybrid model (see Restrictions in Step 1), there are many scenarios for which it can be useful.
In the future, the ASP.NET team is looking at simplifying this scenario and make it more powerful with some new tools that are in the work. For example, you would be able to take an entire application containing an App_Code directory and multiple User Control, and turn it all into a single assembly that can easily be dropped into the 'bin' directory of other apps. Though it will allow some more powerful scenarios, note that the basic idea behind it is essentially the same as what is shown in this article.