The CodeBeside Model
Inline page parsing parses a single ASPX
markup file into a single class and creates a single assembly from it. The CodeBeside model is a specialization of the inline model, which breaks delegation of page or control operations into two distinct classes. Rather than the single class that Inline pages use, CodeBeside contains two classes: The CodeBeside class contains your user code and the ASP.NET control definitions, and the generated class contains the control tree generation code that inherits from this class. Figure 3
shows an overall view of how the CodeBeside model works.
|Figure 3: The CodeBeside model uses a partial class to implement user code, which merges with a generated partial class that contains control declarations. The combined class then becomes the base class from which the generated ASP.NET control tree class inherits.|
The advantage of this two-class model is that you can separate your user interface (the markup in the ASPX
) and your application logic (your .cs
file) into separate entities that you can edit separately. For example, this makes it easier to hand off ASPX
pages or controls to designers who should see as little as possible about the code that drives the page.
The base CodeBeside class actually contains two partial classes: One contains your user code, while the other is generated by ASP.NET at compile time and contains the control property definitions. The ASP.NET compiler creates the control definition's partial class and compiles it together with your user code class to create the CodeBeside base class.
ASP.NET then creates the control tree class as described earlier. The difference is that the generated class doesn't create the control property definitions but inherits them from the CodeBeside class. Note that in this scenario the controls are defined in the base CodeBeside class, but all the assignments for property values and event hookups are done as part of the control tree class in the various control __BuildXXX
methods. Both classes are tightly coupled together. ASP.NET then compiles both classes into the same assembly.
Pages created for CodeBeside use the CodeFile=
attribute of the @Page
element to tell ASP.NET that it must find and compile a CodeBeside class. Here's the syntax.
<%@ Page Language="C#"
You need to specify the path to the CodeBeside file and the fully-qualified class name. For demonstration purposes, I'll use the simple ASPX
page code defined in Listing 4
The page is super simple but I've added a couple of custom controls to it. One control is defined in this project (CustomControl), and one is an external control in a separate assembly (Westwind.Web.Controls).
Here's the DataEntry.aspx.cs
CodeBeside class for the markup shown in Listing 3
public partial class DataEntry :
protected void Page_Load (object sender,
protected void btnSayHello_Click( object
sender, EventArgs e)
this.ErrorDisplay.ShowMessage("Hello " +
Note that the Page code contains no control definitions and none of the InitializeComponent
code used by ASP.NET 1.x. Instead, you have a simple, clean class that shows you only your specific user code. IntelliSense works in this code while you're typing in Visual Studio, even though there's no second partial class anywhere in your project.
So where does the other half of this partial class come from? ASP.NET generates it at compile time. The IntelliSense works because Visual Studio quietly compiles your ASP.NET page in the background, putting the pieces together at design time to provide you with IntelliSense.
One advantage of generating controls at compile time is that there's a very consistent model for control property generation. In Visual Studio 2003 there were many problems with the designer not properly synching up the control definitions, often resulting in lost event hookups or even missing controls in the CodeBehind class. With control generation and event hookup generation delegated to compile time, these inconsistencies have gone away. I have yet to run into any issues yet with the ASP.NET 2.0 compilation engine missing a control definition or event hookup.
If you open up the assembly created from the DataEntry page in Temporary ASP.NET Files
with Reflector, you'll see something like Figure 4
|Figure 4: A CodeBeside ASP.NET 2.0 page is made up of a user code class and a generated class that contains the page parse tree logic. The user class is the base class inherited by the ASP.NET generated class.|
Notice that the DataEntry
base class contains the control definitions that come courtesy of the generated partial class that ASP.NET created and combined with your CodeBeside user code class. This combined class is the base class. The dataentry_aspx
class is the fully generated classyou can see that it inherits from DataEntry. This class consists purely of the parse tree logic code you can see in the various __BuildXXX
methods for each of the controls and containers on the form. This code is identical to the code you saw in the inline page processing routines, except that the control property definitions are coming from the base class. The __BuildXXX
method sets the control properties and hook up the event handlers. Note that the control property definitions and the control initialization code are split up across the two classes.
The DataEntry page contains a custom control defined in the APP_CODE
directory. You'll recall that the APP_CODE
directory is where any non-page or control code must live, so this creates the custom server control as a separate class in this folder. Notice the References section in the DataEntry class and the APP_CODE.xxxxx
reference, which has been added to the assembly and makes any code from the APP_CODE
directory available to the page, and any pages or controls in this assembly. Along the same lines, the Westwind.Web.Controls assembly has been imported to support the wwErrorDisplay custom control used to display messages. This is driven by the @Register
directive in the HTML markup for the page.
The DataEntry base class inherits from System.Web.UI.Page in this example, but you can override the base class in the partial class definition by inheriting from any other page-derived class. For example, you can create a common base page class for your application and store it in the APP_CODE
folder and have any number of pages in the Web application inherit from this class. Keep in mind that if you do this, the page base class will not have strongly typed access to any controls on the page because the controls are defined and assigned higher up in the hierarchy. That's true even if you define the control properties in this base class. ASP.NET creates the control definitions in the generated CodeBeside partial class with the new keyword so any existing control definitions are ignored.
There's a partial workaround for this problem using the CodeFileBaseClass
attribute on the @Page
directive. When set to a class name, ASP.NET will not override any pre-existing properties on the specified base class. For example:
<%@ Page Language="C#" AutoEventWireup="true"
In this case, ASP.NET uses any control properties defined in PageBaseClass instead of new properties generated in the DataEntry class; therefore, PageBaseClass will be able to reference the control properties it defines.
Unfortunately, this only works if you have a CodeFile
attribute in your page directive. If you want to inherit a page directly from a base class in APP_CODE
or an external assembly, you can't use CodeFileBaseClass
and ASP.NET will continue to blithely generate control definitions in the generated partial class. This means that it will not
work if you use existing control properties in the wwMessageDisplay class.
<%@ Page Language="C#"
The only workaround to get the wwMessageDisplay page to receive control assignments is to use FindControl()
, which is slow and cumbersome.