Making UserControls And Namespaces Work





3.00/5 (1 vote)
How to make UserControls and Namespaces work
Adding UserControl
s inside of a Web Application project can tend to be a pain. You either have to register the control on each of the pages you want to use them in or add each name and path into the system.web/pages/controls
section of your web.config. Neither of these are very convenient.
However, there is another option – Namespaces
. Simply add an entry in the same location with values for the namespace
, tagPrefix
and assembly
values and your control becomes ‘available’!
<configuration>
<!-- snip -->
<system.web>
<pages>
<controls>
<!-- the assembly should be the same name as the
name generated by the application -->
<add tagPrefix="site"
namespace="WebApp.Controls" assembly="WebApp" />
</controls>
</pages>
</system.web>
</configuration>
Now, everything appears like it is ready to go. If you start to type in your control name, then you'll see it pop up in the intellisense. However, if you run your application, the control won't appear!
If you pay close attention to the AppRelativeVirtualPath
, you'll notice it stays null
the whole time! To make matters worse is that even if you set the value manually, it doesn't make a difference – nothing shows up. You can try the Register
information to add the control to the page and you'll see that the inline control works fine. It appears that you need that ’src
’ attribute before this works.
At this point, we could give up and move on or we could get a little creative in our solution.
[Source Code] Normally, I'd drop a giant block of code in my post right here but from now on, I'm going to start using github for my snippets. You can download the whole snippet at gist on github.
So to summarize our situation…
- UserControls ‘work’ when added by
namespace
but they don't actually appear in the page. (technically, the code still runs it just doesn't show the .ascx content) - We can add
UserControl
s to each page using theRegister
option but it isn't nearly as convenient as thenamespace
. - We could add each
UserControl
to the web.config with the virtual path but it would require us to update the web.config every time we change something.
I've wrestled with this a lot – I've tried the precompilation, I've tried ILMerge
… I even tried kicking my laptop (which admittedly seemed to improve my code… too weird) – nothing seemed to work unless the virtual path to the ascx
file was available. So instead of trying to solve the problem at build time, I opted to solve it as soon as the program starts. Below is the code that I add to the Application_Start
event in my Global.ascx file.
protected void Application_Start(object sender, EventArgs e) {
UserControlScanner scanner = new UserControlScanner();
scanner.ScanAndUpdate();
}
This code starts by finding all of the .ascx files in your site and all of the UserControl
types (and sub types) loaded into the assemblies. It then uses this information to compare against the web.config file and add each control to the controls
section.
What does it compare against? Well – first, it assumes that you added the namespace
to your web.config file to begin with (otherwise, how else are you using it?) If the code finds a match between the UserControl
namespace and a namespace in the web.config then it is added. It is worth noting that the prefix is determined by the tagPrefix
of the web.config entry of the matching namespace
.
So, below is a good example of what to expect:
<!-- Before code executes -->
<controls>
<add tagPrefix="asp" namespace="System.Web.UI"
assembly="System.Web.Extensions (snip)..." />
<add tagPrefix="asp" namespace="System.Web.UI.WebControls"
assembly="System.Web.Extensions (snip)..." />
<add tagPrefix="site" namespace="WebApp.Controls" assembly="WebApp" />
</controls>
<!--After code executes-->
<controls>
<add tagPrefix="asp" namespace="System.Web.UI"
assembly="System.Web.Extensions (snip)..." />
<add tagPrefix="asp" namespace="System.Web.UI.WebControls"
assembly="System.Web.Extensions (snip)..." />
<add tagPrefix="site"
namespace="WebApp.Controls" assembly="WebApp" />
<add tagPrefix="site"
tagName="MyControl" src="~/Controls/MyControl.ascx" />
<add tagPrefix="site" tagName="SomeOtherControl"
src="~/Controls/SomeOtherControl.ascx" />
</controls>
Now, this does cause a bit of a problem… the site just loaded and the web.config just changed – Our updates aren't available! To remedy that, you'll find this bit of code right after it finishes saving the changes.
//get the path to redirect to
string url = HttpContext.Current.Request.Url.AbsolutePath;
//then reset the system
HttpRuntime.UnloadAppDomain();
//and redirect the request to start over
HttpContext.Current.Response.Redirect(url);
So, if any changes are found, then the site is reset and the user is redirected to the same request he originally attempted to reach. Doesn't seem like a big issue to me but it is something you want to keep in mind. Of course, if no changes are detected, then the site isn't reset and the code runs normally.
Now, if you add or remove controls, change paths, then your web.config is automatically kept up to date and your application runs normally!