Referencing Other Pages and Controls
Remember that page and control compilation happens on a per-directory basis! So referencing other pages and controls becomes a little trickier in ASP.NET 2.0, because you cannot assume that a CodeBeside class from another page or control is available in the current assembly. At best, all pages and controls in the same directory end up in the same assembly; at worst, each page or control gets its own assembly and they know nothing about each other.
If you need to reference another page from a control or another page entirely, you need to explicitly import it with the @Reference
directive. In ASP.NET 1.1, all CodeBehind classes are immediately available to your entire Web application. In ASP.NET 2.0, you must explicitly reference an assembly to load it.
Assume for a minute that you have the DataEntry.aspx
page shown earlier and you want to create a second page that uses the same CodeBeside class so you can reuse the page logic, but change the page layout. So you create DataEntry2.aspx
by changing a few colors and moving around the page controls. In essence, you want to have two ASPX
pages reference the same CodeBeside file. Here's how to do this:
<%@ Reference Page="~/DataEntry.aspx" %>
<%@ Page Language="C#" AutoEventWireup="true"
I'm leaving out the CodeFile
attribute reference and the CodeBeside class of the DataEntry page, and adding the @Reference
tag to the page to force the CodeBeside class to import.
The same is true with any user control definitions. To import a user control you need to use the @Register
tag, which imports the assembly that the control lives in. ASP.NET is smart during compilation and figures out exactly where related assemblies live based on how the project compiles. If the control or page resides in the same assembly, ASP.NET does not actually add a reference. But if the user control is externalin another directory, for examplethen ASP.NET adds the assembly reference.
If you can explicitly reference other pages and controls in your markup pages, things work fairly well, but if you load controls or reference pages dynamically in your code, things get a lot more complicated. The most common problem I run into in ASP.NET 2.0 is loading controls dynamically.
In ASP.NET 1.x, you might have run code like this to dynamically load controls into a page.
public partial class DynamicControlLoading :
protected CustomUserControl MessageDisplay;
protected void Page_Load(object sender,
MessageDisplay = this.LoadControl(
protected void btnSay_Click(object sender,
In this case, CustomUserControl is a simple user control that lives in another directory and gets loaded dynamically at run time. Let's also assume that if you dynamically load this control you'll have a choice of several controls, or the end-user might even create a custom control that is dropped into place instead.
If you run the code above in ASP.NET 2.0, it will likely fail. I say likely because there are some inconsistencies that will sometimes pick up control references automatically. For example, this will happen if the user control lives in the same directory and compiles into the same assembly as the page, or if another page has the control referenced. But it shouldand usually willfail. Why? Because ASP.NET compiles on a directory level and the CustomUserControl lives in a separate directory and goes into a separate assembly. It's not visible for the page class to get a strongly-typed reference. IntelliSense will show a big, red exclamation point or nothing at all for the MessageDisplay
control at design time, and when you run the page it will bomb.
You can reference the control generically as a Control type of course, but if you need to access any custom properties on the user control beyond Control properties, you have to resort to using reflection. As far as I know, there's no way to add a reference to another user control or page programmatically at run time.
As an alternative, you can choose to not load controls dynamically or at least provide a mechanism to load user controls beforehand on a page with the appropriate @Register
tags. But that's not always possible. Another option is to create a user control base class in APP_CODE
and expose the public interface there. The main problem with this is that this base class can't see the page's controls unless you use FindControl()
, making this method cumbersome and ugly.
I've run into similar situations with inheritance scenarios, such as inheriting one master page off another's CodeBeside class. There are cases where inherited controls are not recognized properly by the higher level classes, resulting in type mismatches, especially when loading UserControls on master pages. Removing controls on inherited pages will also cause problems, even if the controls are defined explicitly in lower-level classes.
Inconsistencies that reference other types are incredible time wasters because you think that you have things working consistently only to find out later that's not so when you change a completely different page. What's worse is that you must understand the model in order to get your head around the problem.
The overall ASP.NET 2.0 compilation model is internally complex. Most of the time you don't need to understand it, but when you run into these boundary scenarios, you really must
understand what goes on behind the scenes so you can work around the quirks.