How it Works
Prior to ASP.NET I always had a function like this in my toolbox because in so many places I needed a consistent way to dump a quick message to the user. In those days I generated some code on the fly from within the source code and eventually called Response.Write()
or its equivalent to dump the hand-generated HTML to the HTTP output stream. ASP.NET makes this process much cleaner by providing a clear page processing model, the ability to transfer control to another page, and pass context information to this page. Because of this, it's now much easier to create a new subclass that includes a template describing the display formatting (the ASPX template).
|Figure 2: The DisplayMessage() method accepts simple parameters and uses the HttpContext object to pass the values to the ASP.NET page handler for processing via the Server.Execute() method.|
I've based the implementation on a base class that provides all the processing functionality and a subclass you implement to provide the display template plus any additional logic you might need to fire when the page runs.
Figure 2 shows the flow of this process. You simply call the static DisplayMessage() method on your subclass from anywhere in your application with at least two parameters of Header and Body. This method then handles calling your page by using the Server.Transfer() mechanism and the Context object's state mechanism to pass it the input parameters.
The ASPX page is then run as a live instance of your subclass. Your only task is to call this.DisplayPage() which it inherits from the base class. This method contains the logic to retrieve the data stored in the Context object, populate properties on the form, and then render the ASPX page.
The key concept is ASP.NET's ability to transfer control from one request to another using the Server.Transfer() mechanism. If you're not familiar with Server.Transfer() it provides the ability to stop execution of the current request and jump over to another page in the same application and process it. All Request information (except the URL which is adjusted for the Transfer's new URL) are kept intact so when you jump over to the new page, your Request.Form and Request.ServerVariables properties still contain the same content as they had on the original page.
What ties the original page or request and the transferred page together is the Context object. The Context objectas the name implieshas the lifetime of the current request. All of ASP.NET's intrinsic objects such as Request, Response, Session, and others are actually members of the Context object. Think of the Context object as the main object that passes through the current request from beginning to end. The Context object also includes an Items state bag that can be used to store values into so they can be picked up at a later point in processing, which is exactly what you need to pass information over a Server.Transfer() call. Using Context.Items["key"] values essentially serves as the parameter interface for Server.Transfer().
To provide this functionality I used a subclass of the Page class that implements both a static and public interface to handle the generic tasks of message creation. The static methods are called from your application. They're static so you can easily call the methods without any setup. The instance properties and methods are called only from within a running Web form. This way the two interfaces have clear separation as external for the static and internal for the instance.
|The HttpContext object makes it possible to pass the parameters from the static method to the Page instance.|
Message display is a two-step process: Initiate the request with a simple static method call, then transfer control to your custom class and let ASP.NET render the template.
Let's start with the static call interface. Listing 3 shows the full parameter list DisplayMessage method implementation and its overloads that you can call from anywhere in your ASP.NET application.
The DisplayMessage() method simply accepts a set of parameters and stuffs all of them except the TemplatePageName into the Context object. The full-function version includes a first parameter of the TemplatePageName in case you don't want to use the default name specified in the Pagename static property. This means if you implement your page as MessageDisplay.aspx you can use one of the other overloads that omits the page name. Why pass the page name? A static method has no way to determine the name of the currently accessed class and so the name of the page/class needs to be stored somewhere (Pagename) or be passed in as a parameter so that we can transfer to the correct page. If you have more than one message template page in your application you can use the TemplatePageName property to specify it explicitly.
Note the use of the HttpContext.Current reference to get a reference to the ASP.NET intrinsic objects, including Context and Request. HttpContext.Current is always available to an ASP.NET request (except in design mode) and provides a great way to access all ASP.NET objects in generic methods like this one without having to explicitly pass in either Request, Response, or the Context object.
The wwDisplayMessage class contains additional non-static methods and properties that provide the page execution behavior. The non-static interface shown in Listing 4 shows the internal interface used to fill the controls of the Web form.
This part of the interface consists of a few properties that simply pull their values out of the Context object for easier access inside the ASPX page code, and the DisplayPage method which is responsible for parsing the context values into the appropriate placeholder Label controls of your page.
Notice that the DisplayPage() method has two overloadsone with no parameters and one with a set of control references for the controls that are to be updated on the page. The parameterless version uses Page.FindControl() to create references to the common names of the controls. Header.Text and Message.Text are simply assigned from the Header and Message properties, which return the corresponding Context collection values.
Most of the DisplayPage code deals with fixing up URLs to display properly. Dealing with the URLs is a bit tricky, because this page can be loaded from different locations in your application, including from the root of the application or one of its subdirectories, which makes it difficult to determine relative paths of links, stylesheets, images, and more. For this reason, the HTML template includes a <BASE> tag in the page to force all related links relative to this base URL. Things are further complicated because some URLs are bound by this base tag and others are not. For example, redirect tag and style sheet references are in the header since <BASE> doesn't apply to the header.
There are two paths: the path for the <base> tag which is a full URL including protocol and port, and the path that is used for the redirection link if provided which must be server relative. Creation of the base path involves taking the Request.ApplicationPath and prepending protocol, server, and port information. The redirection URL is more work because you have to deal with absolute URLs, relative links, or links that use ASP.NET's 'root' convention (using the tilde (~) character to signify the application root). The Control.ResolveURL() method can easily take care of '~' parsing and converting that into an application-relative URL. If the URL is absolute and includes protocol prefix and a port, you don't need to do anything else and just use it as is. If the path is relative, you have to start with the page's current path and then add the relative path to it. Unfortunately, ASP.NET has no mechanism to return you just the path of the current request so you have to parse it out of the full script path using the Request.FilePath property. All of this takes care of parsing the URL for redirection and the <base> tag.