Scrigroup - Documente si articole

Username / Parola inexistente      

Home Documente Upload Resurse Alte limbi doc  

 
CATEGORII DOCUMENTE
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml

AspAutocadCDot netExcelFox proHtmlJava
LinuxMathcadPhotoshopPhpSqlVisual studioWindowsXml

Authoring ASP.NET Server Control Adapters - An Introduction

dot net



+ Font mai mare | - Font mai mic



DOCUMENTE SIMILARE

Building a Rich Windows Form

Building a mobile Web Form

Experience the Extensibility of Windows Forms

Microsoft Visual Studio .NET Guided Tour

Building a Web Form and Test for Scalability

Build an XML Web Service

Setup Instructions

XML Web Services at Work

Authoring ASP.NET Server Control Adapters - An Introduction

Authoring ASP.NET Server Control Adapters - An Introduction

Vandana Datye
October 2003

Applies to:
    Microsoft ASP.NET
    Microsoft .NET
    Microsoft Visual Studio .NET

Summary: Learn about the new adaptive rendering functionality of ASP.NET server controls in the next version of the .NET Framework, code-named 'Whidbey.' Learn how server controls use pluggable adapter classes to render markup to many different browsers for desktop and mobile devices. This article also demonstrates the main steps in implementing a control adapter and shows how to use the tag rendering capabilities of markup writer classes such as XhtmlTextWriter and WmlTextWriter. (42 printed pages)

Download CreatingAdaptersSample.msi.

Contents

Introduction
Architectural Overview of Adaptive Rendering
Adapter Hierarchy
Walkthrough: Implementing an Adapter and Mapping It to a Server Control
Adapter Participation in the Control Life Cycle
Adaptive Rendering and Markup Writer Classes
Browser Definition File Syntax
Browser Definition Tree and Browser Capabilities Object

Introduction

In the Microsoft pre-release version of the .NET Framework code-named 'Whidbey,' Microsoft ASP.NET provides a unified control architecture that enables a single set of server controls to work with many different browsers for desktop and mobile devices. This functionality is made possible by pluggable adapter classes, which render markup based on the capabilities of the browser that accesses a page. For example, the ASP.NET Calendar control has an accompanying CalendarAdapter class that adapts the rendering of the Calendar control for the small screen size and limited memory of a cell phone or a personal digital assistant (PDA).

In the .NET Framework versions 1.0 and 1.1, ASP.NET mobile Web Forms pages were created using mobile controls. These controls implemented rendering for mobile-device browsers and were distinct from the set of standard ASP.NET server controls. In the new ASP.NET adaptive rendering architecture, a separate set of mobile controls does not exist. ASP.NET server controls encapsulate core user interface logic and provide default rendering, which is HTML for desktop browsers. Rendering for other browsers is provided by separate adapter classes, which are not controls. Adapters bind to controls at run time and adapt the output of controls to the requesting browser. Adapters call markup writer classes for creating WML, XHTML, cHTML, and other kinds of markup. Mappings between controls and adapters are specified in configuration files.

An important feature of adapters is that they operate behind the scenes without any work on the part of the page developer. A page developer can use a control without any knowledge of adapters, assuming that the control itself adapts its rendering for different browsers. The ASP.NET page framework enables page developers to use filters to customize control properties for different browsers and devices. For example, in the tag <asp:Label Text='Enter your name' Nokia:Text='Name' runat='server' />, the prefix 'Nokia' is a filter (identifier) applied by the page developer to the Text property of the Label control. When a Nokia cell phone browser requests a page, ASP.NET uses the 'Nokia' filter to assign the string 'Name' to the label's Text property. Property filtering is built into ASP.NET and does not require any work on the part of the control or adapter developer. A page developer can use property filtering with the standard ASP.NET controls as well as with custom controls. This article describes how that filtering works. The documentation for Microsoft Visual Studio 'Whidbey' shows how to use property filtering in a page.

To enable your custom controls to render to different browsers, in general you will have to implement adapters for them. Commonly, server controls need adapters when the requesting browser does not interpret HTML or when the requesting device requires a layout different from that created by the control. It is likely that most adapters you implement will be for rendering to mobile-device browsers. However, it is not only for mobile browsers that adapters are needed. If browsers that accept non-HTML markup (such as VoiceXML, for example) become available for desktop computers, your controls might need adapters for those browsers too.

This article describes the adaptive rendering architecture of ASP.NET and provides an end-to-end walkthrough that shows how to implement a control adapter and map it to a control. It demonstrates how to use the built-in markup writer classes, such as WmlTextWriter and XhtmlTextWriter. In addition, it describes how to create configuration files that contain control/adapter mappings and browser and device capabilities.

Architectural Overview of Adaptive Rendering

Before examining the adaptive rendering architecture, let's look at the effect of adaptive rendering in a simple page. The following page (MyPage.aspx) uses the ASP.NET Login control.

<%@ page language='C#'%>

<html>

<head runat='server'>

<title>Simple Page</title>

</head>

<body>

<form runat='server'>

<asp:Login id='login1' runat='server'

backcolor='#ffcc66' bordercolor='#333366' />

</form>

</body>

</html>

Figure 1 shows the page as viewed in Microsoft Internet Explorer 6.0.

Figure 1. MyPage.aspx as viewed in Internet Explorer 6.0

Figure 2 shows the page as viewed in an Openwave 5.1 device emulator. The WmlLoginAdapter class, which the ASP.NET page framework associates with the Login control, adapts the appearance and behavior of the Login control for an Openwave 5.1 browser.

Figure 2. MyPage.aspx as viewed in an Openwave 5.1 device emulator. Image courtesy Openwave Systems Inc.

When Internet Explorer requests the page, the Login control renders HTML markup within (and including) the <table> tags shown in the following excerpt.

<html>

<table bordercolor='#333366' border='0' id='login1' style='background-

color:#FFCC66;border-color:#333366;'>

<tr>

<td align='center' colspan='2'>Log In</td>

</tr><tr>

<td align='right'>User Name:</td>

<td><input name='login1$UserName' type='text'

id='login1$UserName' /></td></tr>

</table>

</html>

When an Openwave 5.1 browser requests the page, the WmlLoginAdapter adapter renders WML markup within (and including) the <p> tags shown in the following excerpt.

<wml>

<p>

Log In<br/>

User Name:<input name='mcsv0' />

<br/>

Password:<input name='mcsvepckf1' type='password' />

</p>

</wml>

Adaptive Rendering Architecture

To understand the ASP.NET adaptive rendering architecture, it is useful to walk through the processing stages of a page. I will examine how the ASP.NET page framework processes requests made to the MyPage.aspx page by two different browsers-Openwave 5.1 and Internet Explorer 6.0.

Processing a request from an Openwave WML browser

Figure 3 illustrates the ASP.NET adaptive rendering architecture and shows how a page processes a request from an Openwave 5.1 browser. While the figure shows processing for a specific page, the steps performed are general and occur when the ASP.NET page framework responds to any page request. When a block shows two classes, such as PageAdapter (WmlMyPageAdapter), the first class is the base type and the second is the derived type instantiated during the specific request. The elements primarily involved in adaptive rendering are shown in color.

Figure 3. Processing a request from an Openwave 5.1 browser

The numbers in the figure represent the following steps:

1.                   An Openwave 5.1 browser makes a request to the MyPage.aspx page. If the page is not already compiled, the page framework dynamically parses the page into the MyPage_aspx class and compiles the class.

2.                   The ASP.NET page framework instantiates the MyPage_aspx page. The page in turn creates its control tree and creates Login and other controls.

3.                   When the page accesses its Adapter property it calls Request.Browser under the hood. This call causes ASP.NET to make a request to the ASP.NET configuration system for a browser capabilities (System.Web.HttpBrowserCapabilities) object corresponding to an Openwave 5.1 browser. A browser capabilities object is an in-memory representation of browser data stored in .browser configuration files. (At the end of this section you will see how browser capabilities objects are created and cached.)

4.                   The configuration system returns a browser capabilities object that represents browser data for an Openwave 5.1 browser. This object contains data about the browser and device and can be accessed by the page and its controls. The browser capabilities object also contains mappings between control and adapter types, which tell ASP.NET which adapter (if any) to instantiate for each control when the request is from an Openwave 5.1 browser.

5.                   ASP.NET creates the WmlPageAdapter for the page and the WmlLoginAdapter for the Login control, using the adapter mappings in the browser capabilities object.

6.                   The page, its adapter, the controls, and their adapters execute life-cycle phases up to Render, such as Init, Load, and PreRender. You will see how an adapter participates in the control's life cycle in the Adapter Participation in the Control Life Cycle section of this article.

7.                   In the Render phase, the page creates an instance of the WmlTextWriter class, using the type of the System.Web.UI.MarkupTextWriter specified in the browser capabilities object for an Openwave 5.1 browser. WmlTextWriter derives from the System.Web.UI.MarkupTextWriter class and is capable of generating WML markup. The page passes the WmlTextWriter object to its adapter, WmlPageAdapter, and invokes the adapter's rendering methods. Similarly, the Login control passes the WmlTextWriter object to its adapter, WmlLoginAdapter, and invokes its rendering methods. You will see markup writers in more detail in the Adaptive Rendering and Markup Writer Classes section later in this article.

8.                   The WmlTextWriter instance writes WML markup to the response stream.

Processing a request from Internet Explorer

Figure 4 shows the processing of MyPage.aspx when it is requested by Internet Explorer 6.0. Compare this figure with Figure 3 to see the difference in request processing for Openwave 5.1 and Internet Explorer 6.0 browsers.

Figure 4. Processing a request from Internet Explorer 6.0

Steps 4, 5, and 7 show that different objects are instantiated when the request comes from Internet Explorer 6.0 rather than from the mobile-device browser shown in Figure 3. In step 4, The HttpBrowserCapabilities object corresponds to the browser definition for Internet Explorer 6.0, specified in the file Ie.browser. The default rendering of Login is adequate for Internet Explorer, and-based on the mapping in the browser capabilities object-the ASP.NET page framework does not create an adapter for the Login control in step 5. In step 7, the page creates an HtmlTextWriter instance using the markup text writer type specified in the HttpBrowserCapabilities object for Internet Explorer 6.0. The page and its controls render HTML, invoking the HtmlTextWriter instance. The .NET Framework creates the HtmlPageAdapter as the page adapter in step 5, but this adapter does not play a significant part in rendering and might not be mapped to the page in a future version.

Creation, caching, and retrieval of browser capabilities objects

Figure 5 shows how the configuration system works behind the scenes to create, cache, and retrieve browser capability objects.

Figure 5. Creation, caching, and retrieval of browser capabilities objects

Browser data is stored in XML configuration files named with a .browser file name extension. If you look in the ConfigBrowsers directory of your .NET Framework installation, you will see files such Ie.browser, Nokia.browser, and Openwave.browser.

ASP.NET compiles all the .browser files into a browser capabilities factory object, as shown in the figure. The factory object can create HttpBrowserCapabilities objects that correspond to browser definitions in the .browser files. An HttpBrowserCapabilities object is an in-memory representation of browser data for a specific browser, such as Openwave 5.1 or Internet Explorer 6.0. The first time the configuration system receives request headers from a browser, the configuration system invokes its factory object to create an HttpBrowserCapabilities object for that browser. The configuration system then caches the HttpBrowserCapabilities object using some of the request headers as the cache key. For example, the Openwave 5.1 HttpBrowserCapabilities object is cached with a subset of the Openwave 5.1 headers. For a subsequent request from the same browser (same request headers), the configuration system merely returns the corresponding browser capabilities object from the cache.

Browser Definition Files and Control/Adapter Mappings

Browser definition (.browser) files are analogous to the <browserCaps> section in the Machine.config file in versions 1.0 and 1.1 of the .NET Framework. However, .browser files contain more data than the <browserCaps> section, and they allow a separation of browser data from other configuration data. A .browser file is an XML file that contains one or more <browser> elements. Each <browser> element stores data about a specific browser and device. By 'device,' I mean hardware such as a desktop computer, cell phone, or PDA. Mobile device characteristics can vary significantly among different devices. Because device characteristics such as screen size and color support are important for rendering, they are stored with browser information in .browser files. A <browser> element specifies how to identify a browser/device combination, contains browser and device capability information, and specifies mappings between controls and adapters. A .browser file can be system-wide or application-specific. The system-wide files are in the ConfigBrowsers subdirectory of the .NET Framework installation. Application-specific .browser files are in the Browsers directory of the Web application. You will see browser files in more detail in the section Browser Definition File Syntax.

If you look in the <controlAdapters> section of the root <browser> element in the Openwave.browser file, you will see that the WmlLoginAdapter is mapped to the Login control, as follows:

<controlAdapters markupTextWriterType='System.Web.UI.WmlTextWriter'>

<adapter controlType='System.Web.UI.WebControls.Login'

adapterType='System.Web.UI.WebControls.Adapters.WmlLoginAdapter'/>

</controlAdapters>

In the pre-release of the .NET Framework 'Whidbey', LoginAdapter is mapped to the Login control in the root browser definition in the Openwave.browser file. However, in future versions, the mapping could change so that WmlLoginAdapter would be mapped to the Login control as shown above.

The browser definition for Internet Explorer inherits its control/adapter mappings from the default browser definition in the Default.browser file. This definition maps every control to the null type (represented by an empty string):

<adapter controlType='System.Web.UI.WebControls.Login'

adapterType= ''/>

This causes the Adapter property of the Login control to be null when the requesting browser is Internet Explorer, as shown in Figure 4.

Adapter Hierarchy

Figure 6 shows a section of the hierarchy of adapter classes in ASP.NET. This figure is illustrative; the actual adapter classes might change in a future release of the .NET Framework. The adapter hierarchy generally follows the same pattern as the control hierarchy. For example, the ControlAdapter class corresponds to the Control class, WebControlAdapter to WebControl, and CalendarAdapter to Calendar. In general, each control has a corresponding generic adapter class that implements core adaptive rendering logic. Adapters that render specific markup, such as XHTML or WML, are derived from the generic adapter. For example, the Button control has a corresponding ButtonAdapter class from which derive the HtmlButtonAdapter and XmlButtonAdapter classes. (The control adapter classes whose names begin with 'Html' render XHTML markup. However, among the page adapters, the XhtmlPageAdapter renders XHTML)

Figure 6. ASP.NET adapter hierarchy

Notice that CalendarAdapter does not have markup-specific adapters that derive from it. This is because the Calendar control is a composite control, which delegates the task of rendering markup to its child controls, and the child controls have markup-specific adapters mapped to them. The Calendar control does need a generic adapter mainly to modify its layout for mobile-device browsers. When you implement a composite control, you might not need to implement an adapter for the (parent) control.

For the most part, you might not need to implement adapters for specific devices; instead, you can just implement an adapter for a specific type of markup. For example, the WmlCheckBoxAdapter adapts the CheckBox control's rendering for a WML browser, be it in a Nokia cell phone or an Ericsson cell phone. If you do need to implement a device-specific adapter, do so by extending the markup-specific adapter. For example, you could implement a NokiaWmlCheckBoxAdapter that derives from WmlCheckBoxAdapter.

The convention for adapter namespaces is to add 'Adapters' to the control's namespace. For example, System.Web.UI.WebControls.Adapters contains adapters for the controls in System.Web.UI.WebControls. If the namespace for your controls is MyControls, name your adapter namespace MyControls.Adapters.

Adapter Resolution at Run Time

The adapter naming convention described above provides a cue to control developers about which adapter(s) could be associated with a control. However, naming an adapter using the convention does not automatically create an adapter/control mapping. You must create the mapping in the <controlAdapters> section of a <browser> element. A browser defintion inherits (and can override) the control/adapter mappings of its parents. For example the Openwave.browser file does not have a <controlAdapters> entry explicitly for an Openwave 5.1 browser. This browser's definition inherits mappings from the root <browser> element specified in the Openwave.browser file. You will see how control/adapter mappings are inherited in the Browser Definition Tree and Browser Capabilities Object section of this article.

The control/adapter mappings for a requesting browser are available at run time through the browser capabilities object, as you saw in the Adaptive Rendering Architecture section. When a control accesses its Adapter property, it invokes the browser capabilities object under the hood. If the browser capabilities object finds an adapter type mapped to the exact control type, it instantiates that adapter type. (ASP.NET does not instantiate a control adapter when an empty string is mapped to the control type, as described in the next subsection.) If ASP.NET does not find an adapter mapping, it walks up the inheritance chain of the control until it finds a control with an adapter mapping, and then instantiates that adapter. For example, suppose you create a MyCheckBox control that derives from the CheckBox control, but do not provide an adapter for it. If a cell phone with an Openwave 4.x or 5.x browser accesses a page that uses your MyCheckBox control, ASP.NET will not find an adapter matched exactly to MyCheckBox. Therefore, it moves up one level in the inheritance chain, where it finds WmlCheckBoxAdapter as the adapter for CheckBox, so it instantiates WmlCheckBoxAdapter as the adapter for your control. If ASP.NET walks your control's inheritance chain and does not find any adapter mapped to your control, it instantiates ControlAdapter as the adapter for your control.

Null adapter property

When your control's rendering is adequate for a certain browser, you can indicate that the control does not need an adapter for that browser by specifying the adapter type name as an empty string ('') in the <controlAdapters> section of the browser definition:

<browser id='someID' parentID='someParentID '>

<controlAdapters>

<adapter controlType='MyControl' adapterType='' />

</controlAdapters>

</browser>

When the specified browser makes a request, ASP.NET does not create an adapter for the control; it returns null for the adapter type. Specifying the adapter type name as an empty string is preferable to omitting the adapter mapping in the browser definition. A missing mapping causes ASP.NET to walk your control's inheritance chain and, if it finds no other control with an adapter, to eventually instantiate ControlAdapter as the adapter for your control. The functionality of a control with ControlAdapter as its adapter is equivalent to that of a control with an Adapter property that is null because ControlAdapter delegates each call to the control, as you will see in the Adapter Participation in the Control Life Cycle section of this article. However, when an adapter is not needed, it is more efficient to avoid the creation and subsequent invocation of the adapter altogether. In the <controlAdapters> section of the root browser definition in Default.browser, all the ASP.NET controls are mapped to an empty string. Subsequent browser definitions override the default mapping to add adapter mappings as needed.

If you develop a control that is intended only for a specific category of browsers (such as HTML browsers), and you do not implement and map adapters for other browsers, you can exclude the control from the adaptive rendering architecture by making it non-adaptable. To make a control non-adaptable, override the read-only Adapter property and return null, as shown in the following code:

protected override ControlAdapter Adapter

}

Walkthrough: Implementing an Adapter and Mapping It to a Server Control

This walkthrough shows the key steps in authoring an adapter. It implements a simple control, implements an adapter that adapts the control's rendering for WML browsers, and then creates a configuration file that maps the adapter to the control for a class of requesting browsers.

Implementing a Simple Server Control

The following code defines a simple MyLabel control that exposes a Text property and renders it by overriding the RenderContents methods of the base WebControl class. The control uses the methods of the HtmlTextWriter class to render HTML.

// MyLabel.cs

using System;

using System.ComponentModel;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace MyControls

set

}

protected override void RenderContents(HtmlTextWriter writer)

}

}

Implementing a Simple Adapter

The following code defines a WmlMyLabelAdapter class that adapts the rendering of the MyLabel control for WML browsers.

// WmlMyLabelAdapter.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.Adapters;

using MyControls;

namespace MyControls.Adapters

}

public override void Render(MarkupTextWriter markupWriter)

}

}

In keeping with the naming convention for adapter namespaces, the namespace name for WmlMyLabelAdapter, MyControls.Adapters appends .Adapters to the control's namespace, MyControls.

WmlMyLabelAdapter derives from WebControlAdapter, the adapter associated with WebControl, which is the base class for MyLabel. Because the adaptive rendering for MyLabel does not require core logic that is independent of markup language or device, I did not create a base MyLabelAdapter class from which to derive specialized adapters such as WmlMyLabelAdapter. If your control does require core adaptive logic, you should create a generic adapter class that implements the core logic and then derive specialized adapters from it. Or, you might create a generic adapter class without any implementation merely to provide a base adapter from which to derive adapters for derived controls.

To generate markup, WmlMyLabelAdapter uses the methods of the WmlMarkupWriter object passed into its Render method. WmlMyLabelAdapter renders style tags by invoking the EnterStyle and ExitStyle methods of WmlTextWriter, as shown in the Render method.

Mapping an Adapter to a Control

To associate the WmlMyLabelAdapter with the MyLabel control when the request is made by an Openwave browser, I'll create a MyMappings.browser file that contains the following syntax:

<browsers>

<browser refid='UP'>

<controlAdapters>

<adapter controlType='MyControls.MyLabel'

adapterType=

'MyControls.Adapters.WmlMyLabelAdapter' />

</controlAdapters>

</browser>

</browsers>

The value of the refid attribute, 'UP', is the identifier that ASP.NET uses for Openwave browsers. It is defined in the system-wide Openwave.browser file. The MyMappings.browser file must be placed in the Browsers directory of a Web application that uses the MyLabel control if the adapter assembly is in the Bin directory. Alternatively, the .browser file could be placed in the system-wide ConfigBrowsers directory if the adapter assemblies are installed in the global assembly cache.

In a future release of the .NET Framework 'Whidbey', the refid attribute is likely to change to refID. (Since a .browser file is an XML file, the case is important). For details about the syntax of .browser files, see the Browser Definition File Syntax section of this article.

Using the Control in a Page

The following example shows a page that uses the MyLabel control. The UP:Text attribute denotes the Text property of the MyLabel control filtered for devices that use Openwave browsers. This kind of device filtering is built into the ASP.NET page framework and does not require any additional code in either the MyLabel control or its adapter, WmlMyLabelAdapter.

<%@ page language='C#'%>

<%@ register tagprefix='aspSample' namespace='MyControls'

assembly='MyControls' %>

<html>

<head runat='server'>

<title> Adapter Authoring Demo </title>

</head>

<body>

<form runat='server'>

<aspSample:MyLabel UP:Text='Adapters 101'

Text='Introduction to Adapter Authoring'

id='mylabel1' font-bold='true' forecolor='#ff0066'

runat='server' />

<p style='FONT-SIZE: x-small'>

This demo shows how to author a control adapter and map it to a

control.

</p>

</form>

</body>

</html>

Rendering in different browsers

Figure 7 shows the page that uses the MyLabel control, as viewed in Internet Explorer 6.0.

Figure 7. A page that uses the MyLabel control, as viewed in Internet Explorer 6.0

Figure 8 shows the page as viewed in an Openwave 5.1 device emulator. Note that device filtering causes the string 'Adapters 101' to be displayed in the Openwave browser for the value of the Text property of MyLabel, while the string 'Introduction to Adapter Authoring' is displayed in Internet Explorer.

Figure 8. A page that uses the MyLabel control, as viewed in an Openwave 5.1 device emulator. Image courtesy Openwave Systems Inc.

For a list of device emulators for testing adaptive rendering, see

Device Simulators.

Building the Sample

The following sample files (available in the code download for this article) are needed for this walkthrough:

                     MyLabel.cs The source code for the MyLabel control

                     WmlMyLabelAdapter.cs The source code for the WmlMyLabelAdapter adapter

                     MyMappings.browser The mapping between the control type and the adapter type

                     AdapterDemo.aspx A page that uses the MyLabel control

Building the sample using Visual Studio 'Whidbey'

To compile and run the sample using Visual Studio 'Whidbey':

1.                   Create a blank solution.

2.                   Add a new C# project to the solution, add MyLabel.cs to the project, and compile the project.

3.                   Add another new C# project to the solution, add WmlMyLabelAdapter.cs to the project, add a reference to the assembly created in step 2, and compile the project.

4.                   Add a new Web site to the solution.

5.                   Add the AdapterDemo.aspx page to the Web site.

6.                   Add a reference to the assemblies created in steps 2 and 3.

7.                   Add a Browsers directory to the Web site and add the MyMappings.browser file to it.

8.                   Access AdapterDemo.aspx using Internet Explorer and an Openwave 4.x or 5.x device emulator.

When creating controls for distribution, it is preferable to create separate assemblies for controls and adapters as described in steps 2 and 3, so that you can easily provide adapter updates for new browsers or devices.

When testing your controls and adapters, you can skip precompilation (steps 2 and 3) when you have Visual Studio 'Whidbey.' Simply place the source files for the controls and adapters in the Code directory under your Web application. ASP.NET will compile the controls and adapters into a single assembly and access it from any page in your Web application. When you place source files in the Code directory, the page directive for the registering the tag prefix does not need an assembly attribute. (For example, assembly='MyControls' is not needed.)

Building the sample using the .NET Framework 'Whidbey' SDK

Use the following steps to compile and run the sample using the standalone .NET Framework 'Whidbey' SDK and command-line tools:

1.                   Compile the MyLabel control into an assembly using the following command:

2.                        csc /out:MyControls.dll /t:library /r:System.dll /r:System.Web.dll

3.                        MyLabel.cs

4.                   Copy MyControls.dll into the directory that contains WmlMyLabelAdapter.cs. Compile MyLabelAdapter into an assembly using the following command:

5.                        csc /out:MyControls.Adapters.dll /t:library /r:System.dll

6.                        /r:System.Web.dll /r:MyControls.dll WmlMyLabelAdapter.cs

Note that the compiler needs to reference the control assembly when compiling the adapter. Therefore controls must be compiled before the adapters, and the control assembly must be in the same directory as the adapter source code.

7.                   Create an IIS virtual root and create a Bin directory and a Browsers directory under the root.

8.                   Copy the control and adapter assemblies created in steps 1 and 2 into the Bin directory.

9.                   Copy the MyMappings.browser file to the Browsers directory.

10.                Copy AdapterDemo.aspx to the Web application root (IIS virtual root.)

11.                Access AdapterDemo.aspx using Internet Explorer and an Openwave 4.x or 5.x device emulator.

Packaging and Device Updates

If you want to use the MyLabel control in a page accessed by Openwave 4.x and 5.x browsers, you must have the MyControls assembly, the MyAdapters assembly, and the MyMappings.browser configuration file.

When you create custom controls for distribution and want to enable adaptive rendering for them, you must provide the following:

                     The assembly that contains the controls.

                     The assembly that contains adapters for the controls.

                     The .browser files that contain mappings between the control types and adapter types.

                     An installer or directions to the application developer to install the assemblies and the .browser files in the application's Bin and Browsers directories, respectively. Alternatively, the control and adapter assemblies could be installed in the global assembly cache (GAC) and the .browser files in the system-wide ConfigBrowsers directory.

When new browsers or devices become available, you can add support for them by providing updates to your adapter assemblies and .browser files, similar to the device updates that ASP.NET provides.

Adapter Participation in the Control Life Cycle

As you saw in the Adaptive Rendering Architecture section, ASP.NET creates a control's adapter when the control first accesses its Adapter property. This generally happens in the Init phase of the control. If ASP.NET creates an adapter for a control (that is, if the Adapter property is not null), the adapter participates in the life cycle of the control as illustrated in Figure 9.

Figure 9. Main life-cycle phases of a control and its adapter

An adapter has methods such OnInit, OnLoad, OnPreRender, and Render that are analogous to the control's life-cycle methods. In each life-cycle phase, a control invokes the corresponding method of its adapter. After the adapter completes its work, it generally calls its base class, which eventually calls the life-cycle method of the control. Both the adapter and the control can implement logic for each phase, and execution flows from the control to the adapter and back to the control. Commonly, in the Render phase, either the adapter or the control (but not both) implements rendering logic. This is because, if the adapter exists, it generally takes over rendering and generates a markup or layout different from that of the control. Rendering is described in more detail in the next section, Adaptive Rendering and Markup Writer Classes.

Because adapter invocation is built into the Control class, when you implement a custom control you do not have to explicitly invoke your control's adapter.

The following code lists the properties and life-cycle methods of the ControlAdapter class:

public class ControlAdapter

public virtual Page Page

public virtual PageAdapter PageAdapter

protected HttpBrowserCapabilities Browser

// Life-cycle methods.

public virtual void OnInit(EventArgs e)

public virtual void OnLoad(EventArgs e)

public virtual void OnPreRender(EventArgs e)

public virtual void Render(MarkupTextWriter writer)

public virtual void OnUnload(EventArgs e)

public virtual void BeginRender(MarkupTextWriter writer)

public virtual void EndRender(MarkupTextWriter writer)

public virtual void LoadAdapterState(object state)

public virtual object SaveAdapterState()

}

To implement an adapter, you directly or indirectly extend the ControlAdapter class and override one or more of its life-cycle methods. By default, each life-cycle method of ControlAdapter merely invokes the corresponding method of its control. You can override any life-cycle method and implement the logic that your adapter needs to execute at that phase. Afterward, invoke the corresponding method of the base adapter class. For example:

public override void OnInit(EventArgs e)

In each phase except Render, you must call the base class's life-cycle method from your override to enable the base class to execute its logic and the control to execute its life-cycle phase. For example, if you did not invoke base.OnInit in the previous example, the control would not be able to raise its Init event. Note that you should invoke base.OnInit, not Control.OnInit, even if the base adapter does not provide any implementation. The next paragraph explains why.

A control can invoke its adapter's methods because the life-cycle methods of ControlAdapter are public. Interestingly, an adapter is also able to invoke its control's life-cycle methods, although those methods are protected. OnInit, OnLoad, and the other life-cycle methods of the Control class are protected internal and can be accessed by derived classes and classes in the same assembly. Since ControlAdapter and Control are in the same assembly, ControlAdapter can access the life-cycle methods of Control. A custom adapter that you implement might not be in the same assembly as its control, but calls to the adapter's base class eventually resolve to ControlAdapter, which invokes the life-cycle methods of Control. Each life-cycle method of Control is virtual, and resolves to the method's override in the most-derived class. Therefore a call to a life-cycle method of Control resolves to the life-cycle method override (if any) in your custom control. An adapter is thus able to invoke its control's protected life-cycle methods by making calls such as base.OnInit to its base adapter.

An adapter can participate in any life-cycle phase of a control, including state management and postback. To perform custom state management in your adapter, you must override the SaveAdapterState and LoadAdapterState methods, which allow an adapter to save state to and load state from the control's view state. The implementation of these methods is similar to the implementation of the SaveViewState and LoadViewState methods of a control. To process postback data, you must implement the IPostBackDataHandler interface. To handle the postback event, you must implement the IPostBackEventHandler interface. I hope to describe adapter participation in state management and postback in further detail in a future article.

An adapter can handle any event that the control exposes by attaching an event handler to the event. For example, the adapter of the Button control can handle the Click event of the Button control. Note, however, that you should not attach event handlers to life-cycle events. Instead, override the adapter's OnInit, OnLoad, and the other life-cycle methods that I just described. The main reason for this is that when you attach an event handler, the handler's order of invocation is not predictable. On the other hand, when you override a life-cycle method, such as OnInit, your adapter's method is always invoked before the control executes the code in its own OnInit method and raises the Init event.

Adaptive Rendering and Markup Writer Classes

If you have implemented custom controls, you are familiar with the HtmlTextWriter class, which is used by controls to render HTML markup. In 'Whidbey,' in addition to HtmlTextWriter, ASP.NET provides additional classes for rendering other kinds of markup, such as XHTML and WML. Figure 10 shows the hierarchy of the markup writer classes.

Figure 10. Hierarchy of markup writer classes

A control uses HtmlTextWriter for rendering markup, while an adapter can use any markup writer. The MarkupTextWriter base class defines generic methods for writing markup. Specific markup writers derive from MarkupTextWriter and override its methods to render specific types of markup. The markup writer classes greatly simplify the task of the control and adapter developer by providing methods for generating markup tags and attributes. The markup writers are extensible and customizable. You can also implement a new markup writer (for a new kind of markup) by deriving from the MarkupTextWriter base class.

To associate a markup writer type with a browser, specify a value for the markupTextWriterType attribute in the <controlAdapters> section of a browser definition:

<browser id='nokia' parentID='default'>

<controlAdapters markupTextWriterType='System.Web.UI.WmlTextWriter'>

</controlAdapters>

</browser>

At run time, a page creates the MarkupTextWriter specified in the <controlAdapters> section of the requesting browser's definition.

In 'Whidbey,' custom controls render markup using HtmlTextWriter, as in earlier versions. Based on the requesting browser, the exact HtmlTextWriter type that the page passes to the control is HtmlTextWriter itself or a derived type such as XhtmlTextWriter or ChtmlTextWriter. The HTML writers can render HTML 4.0, XHTML, HTML 3.2 or cHTML markup. A control automatically benefits from the uplevel/downlevel rendering capabilities of the HtmlTextWriter types, as you'll see in more detail at the end of this section. When the browser accepts a subset of HTML, a control does not need an adapter merely to render the right markup. However, the control might still need an adapter to adapt its layout or behavior.

The Render method of ControlAdapter takes a MarkupTextWriter instance as its argument:

public virtual void Render(MarkupTextWriter writer)

This allows an adapter to use any markup writer. A control uses internal methods to invoke its adapter's Render method. The adapter invocation is built into the base Control class and does not require any work on the part of the control developer.

Composite Controls and Adapters

Composite controls may not need adapters in many scenarios. In the Render phase, the parent (composite) control invokes the RenderControl method of each child control. As you saw in Adapter Participation in the Control Life Cycle, if an adapter exists, a control invokes its adapter in each life-cycle phase. Each child control therefore invokes its adapter's Render method, which renders appropriate markup for the requesting browser. The markup is written sequentially, creating a basic flow layout. If this rendering is adequate, you do not need to implement an adapter for the parent control.

However, if you want to provide a different layout for a specific form factor, or change the order in which controls are rendered, or enforce how controls are grouped for display, you will have to implement an adapter for the composite (parent) control.

Simple HyperLink Control and Adapter Example

This example implements a simple control and its adapter to demonstrate how to use the markup writer classes. The MyHyperLink control in this example is a simplified version of the ASP.NET Hyperlink control. The example also shows how to render a softkey label for devices such as cell phones that have soft (programmable) keys. Figure 11 shows a softkey label ('Click') displayed on the cell phone's screen above the physical key.

Figure 11. The MyHyperlink control renders a hyperlink and a softkey label. Image courtesy Openwave Systems Inc.

The MyHyperlink control

The following code shows the MyHyperlink control, which renders a hyperlink and a softkey label. The NavigateUrl and SoftkeyLabel properties correspond to the URL of the link and the text string displayed over the softkey, respectively. The Text property specifies the text displayed as the hyperlink.

// MyHyperlink.cs

using System;

using System.Web;

using System.ComponentModel;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace MyControls

[

Bindable(true),

Category('Navigation'),

DefaultValue(''),

Description('URL for the hyperlink')

]

public string NavigateUrl

set

}

[

Bindable(true),

Category('Appearance'),

DefaultValue(''),

Description('Label for programmable soft key')

]

public string SoftkeyLabel

set

}

[

Bindable(true),

Category('Appearance'),

DefaultValue(''),

Description('Text to display on the hyperlink')

]

public virtual string Text

set

}

protected override void AddAttributesToRender(HtmlTextWriter

writer)

}

protected override void RenderContents(HtmlTextWriter writer)

writer.Write(text);

}

}

}

MyHyperlink overrides the control's standard rendering methods, which take an HtmlTextWriter instance as the argument to render HTML. MyHyperlink overrides the AddAttributesToRender method of WebControl to render the URL as an href attribute on the HTML tag for the hyperlink, and overrides RenderContents to write the text of the hyperlink.

Notice that the implementation of MyHyperlink in 'Whidbey' is practically the same as in earlier versions of ASP.NET. The only difference is that MyHyperlink defines a SoftkeyLabel property, which, as you will see, is used by the control's adapter to render a softkey label for mobile-device browsers.

Note that MyHyperlink does not contain code to invoke its adapter. Because controls participate in adaptive rendering by default, you do not have to implement any logic in your control to enable this functionality.

WML adapter for the MyHyperlink control

The following code shows WmLMyHyperlinkAdapter, which is a WML adapter for the MyHyperlink control.

// WmLMyHyperlinkAdapter.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.Adapters;

using MyControls;

namespace MyControls.Adapters

}

// Renders the MyHyperLink control for WML browsers.

public override void Render(MarkupTextWriter markupWriter)

if (softkeyLabel.Length == 0)

writer.EnterStyle(Control.ControlStyle);

targetUrl = Control.ResolveClientUrl(targetUrl);

PageAdapter.RenderBeginHyperlink

(writer,targetUrl,false,softkeyLabel,Control.AccessKey);

writer.WriteEncodedText(text);

PageAdapter.RenderEndHyperlink(writer);

writer.ExitStyle(Control.ControlStyle);

}

}

}

WmlMyHyperlinkAdapter overrides the Control property to cast the Control instance to its exact type. This enables the adapter to access the control's complete object model via the Control property without performing a cast each time.

In the Render method override, WmLMyHyperlinkAdapter casts the markup writer passed into it to WmlTextWriter, the exact markup writer type specified in the browser definition. This pattern allows the adapter to access the complete object model of the specific writer without casting the writer each time.

WmLMyHyperlinkAdapter invokes the EnterStyle and ExitStyle methods of WmlMarkupTextWriter to render opening and closing style tags, and renders the hyperlink and link text between those method calls so that style markup is applied to the rendered content. It invokes the WmlPageAdapter class to render the hyperlink tag and the softkey label.

XHTML and cHTML adapters?

The MyHyperLink control shown earlier does not need adapters for XHTML or cHTML browsers. The HtmlTextWriter type that the page passes to the control automatically generates the kind of HTML that is accepted by the browser. This is similar to the automatic downlevel rendering of ASP.NET controls in earlier versions of the .NET Framework. In 'Whidbey,' controls are capable of uplevel XHTML rendering or downlevel HTML 3.2 (or cHTML) rendering without any work on the part of the control developer. To use automatic uplevel-downlevel rendering, your control should not write raw markup but instead invoke HtmlTextWriter for generating markup.

Although the markup writers do most of the work when the browser accepts a subset of HTML, a control might still need adapters for XHTML and cHTML mobile-device browsers.

Control/adapter mapping in the .browser file

The following syntax adds an empty string mapping for the MyHyperlink control in the browser definition for Internet Explorer, since Internet Explorer does not need an adapter for the MyHyperlink control.

<browser refid='IE'>

<controlAdapters>

<adapter controlType='MyControls.MyHyperlink'

adapterType='' />

</controlAdapters>

</browser>

The following control/adapter mapping associates the WmlMyHyperlinkAdapter with the MyHyperlink control for Openwave browsers.

<browser refid='UP'>

<controlAdapters>

<adapter controlType='MyControls.MyHyperlink'

adapterType='MyControls.Adapters.WmlMyHyperlinkAdapter' />

</controlAdapters>

</browser>

The file MyMappings.browser, available with the code download for this article, contains the above mappings.

Markup rendered by the MyHyperlink control in different browsers

The following example uses the MyHyperlink control in a page. The Font and Color style properties are set on the control so that you can compare how styles render in different browsers.

<%@ page language='C#' %>

<%@ register tagprefix='sample' namespace='MyControls' assembly='MyControls' %>

<html>

<head runat='server'>

<title>Test Page for the MyHyperLink control</title>

</head>

<body>

<form runat='server'>

<p>

MyHyperlink renders a hyperlink to HTML and WML browsers.

</p>

<sample:MyHyperlink id='myhyperlink1' Text='ASP.NET Home'

Font-Bold='true' ForeColor='Green'

NavigateUrl='https://www.asp.net' SoftKeyLabel='Click'

runat='server' />

</form>

</body>

</html>

If you view the page in an HTML 4.0 browser such as Internet Explorer 6.0, you will see the following markup rendered by the MyHyperlink control. MyHyperlink, like all Web controls, renders the control's typed style properties via a CSS style attribute on the control's tag.

<a href='https://www.asp.net' style='color:Green;font-weight:bold;'>ASP.NET

Home</a>

If you view the page in a WML browser such as Openwave 5.1, you will see the following markup rendered by WmlMyHyperlinkAdapter:

<b><a href='https://www.asp.net' title='Click'>ASP.NET Home</a></b>

The adapter renders the SoftkeyLabel property of the MyHyperlink control as the title attribute on the hyperlink tag. The markup rendered by the WmlMyHyperlinkAdapter contains <b> tags but does not contain tags for the font color. This is because WML contains a limited number of tags, such as <b>, <i>, and <size>, for text formatting. If the page developer sets style properties on the control that are not supported in WML, WmlMarkupWriter ignores those properties.

If you view the page in an HTML 3.2 browser, you will see the following markup rendered by the MyHyperlink control.

<a href='https://www.asp.net'><b><font color='Green'>ASP.NET

Home</font></b></a>

Note that in this case down-level rendering is performed by Html32TextWriter, and MyHyperlink does not need an adapter. Because HTML 3.2 does not support CSS styles, Html32TextWriter renders the style properties of the control as tags such as <b> and <font> around text.

Browser Definition File Syntax

A browser definition (.browser) file specifies how to identify browsers, contains data about device and browser capabilities, and specifies mappings between controls and adapters. A browser definition file is an XML file named with a .browser file name extension. You can create a .browser file using an XML authoring tool or a simple text editor. System-wide browser definition files are in the $windirMicrosoft.NETFramework<version number>ConfigBrowsers directory. Application-specific browser definition files must be placed in the Browsers directory of an application. You can get a feel for the syntax of .browser files by looking at the .browser files, such as Nokia.browser or Openwave.browser, in the ConfigBrowsers directory.

The .browser file that I created earlier in the walkthrough was very simple and had only a <controlAdapters> section. However, when you want to enable adaptive rendering for new browsers, you might need to create more complex .browser files. This section describes the detailed syntax of a .browser file.

A .browser file has a <browsers> root element and one or more child <browser> elements. A <browser> element, in turn, has child elements that provide data about browser and device capabilities and specify control/adapter mappings.

The following prototype shows the main sections of a .browser file:

<browsers>

<browser id='<unique identifier>' parentID='<identifier of parent>'

<!-- Use refID instead of parentID to add settings

to an existing node. -->

<identification>

<!-- Regular expression match based on user agent or any header.-->

</identification>

<capture>

<!-- Capture capabilities from user agent

or headers using regular expression matches. -->

</capture>

<capabilities>

<!-- Browser and device capabilities. -->

<!-- The following two capabilities are hard-coded. -->

<capability name='browser' value='<SomeName>' />

<capability name='canInitiateVoiceCall' value='<SomeValue>' />

<!-- The following two capabilities are obtained from regular

expression matches.-->

<capability name='deviceID' value='$' />

<capability name='deviceVersion' value='$' />

</capabilities>

<controlAdapters markupTextWriterType='System.Web.UI.WmlTextWriter'>

<!-- Control/adapter mappings. -->

<adapter controlType='System.Web.UI.WebControls.Label'

adapterType='System.Web.UI.WebControls.Adapters.WmlLabelAdapter'

/>

</controlAdapters>

</browser>

<!-- Other <browser> elements. -->

</browsers>

The id, parentID, and refID Attributes of a <browser> Element

A <browser> element either defines a new browser or adds settings (capabilities and adapter mappings) to an existing browser definition. The syntax for the two cases is different.

To define a new browser, create a <browser> element that has id and parentID attributes and an <identification> section. The following example shows a browser definition from the Nokia.browser file:

<browser id='Nokia' parentID='default'>

<identification>

<userAgent match='Nokia.*' />

</identification>

<browser>

For the id value you must choose a unique string, which ASP.NET assigns to the browser/device combination. The device filter that you see in the ASP.NET property filtering syntax (Nokia:Text='Some Text') is the value of the id attribute in the browser definition. To the parentID attribute you must assign the id of the browser's parent. The parentID is described in more detail in the Browser Definition Tree and Browser Capabilities Object section of this article. The parentID value 'default' refers to the default browser definition (defined in the ConfigBrowsersDefault.browser file), which is at the root of all browser definitions.

In the system-wide .browser files in the ConfigBrowsers directory, you will see examples of browser definitions with id values such as 'Ericsson', 'Nokia', and 'UP'. When you create a new browser definition, you must choose an id value that has not been previously used in system-wide or application-specific .browser files.

To add settings to an existing browser definition, create a <browser> element that has a refID attribute (instead of the id and parentID attributes) and do not provide an <identification> section. The refID must match the id of the browser to which you are adding settings. For example, to add capabilities to a Nokia browser (whose id='Nokia') create the following element:

<browser refID='Nokia'>

<!-- Must not have an <identification> section. -->

<capabilities>

</capabilities>

</browser>

The MyMappings.browser file you saw in the walkthrough has <browser> elements that use the refID attribute to add control/adapter mappings to existing browser definitions. (In the pre-release version of the .NET Framework code-named 'Whidbey,' use refid instead of refID.)

Child Elements of a <browser> Element

A <browser> element has some or all of the following child elements:

                     An <identification> element that identifies the browser using a regular expression match (and optional non-match) based on the headers sent by the browser. In addition to the user agent, you can use other headers and capabilities to further narrow the browser identification. When you are not defining a new browser, but are adding capabilities to an existing browser definition, do not use the <identification> section.

                     A <capture> element that specifies a regular expression match for capturing capabilities from the user agent or headers.

                     A <capabilities> element that specifies the capabilities for a device and its browser through name/value attributes of child <capability> elements. The capability values can be hard-coded or captured from header information in the <identification> or <capture> sections. For efficiency, you should hard-code all the known capabilities for a device and browser combination.

                     A <controlAdapters> element that maps control types to adapter types using <adapter> elements. It also specifies the markup writer type for rendering to the browser.

Some mobile browsers need a gateway (such as a WAP gateway) to connect to a Web server. A gateway has its own capabilities that need to be added to the browser definitions. You can add gateway settings to browser definitions by creating a <gateway> element, which has the same structure as a <browser> element, as shown in the next example. A <gateway> element can add its settings to <browser> definitions that have the same parentID as the specified gateway.

Example .browser File

The following example shows a portion of the system-wide ConfigBrowsersJphone.browser file. The example illustrates how to:

                     Identify a browser using a regular expression match based on the user agent string (<identification> section).

                     Capture the majorVersion, minorVersion, and deviceModel capability values using a regular expression match (<capture> section).

                     Specify capabilities as hard-coded strings or obtain them through regular expression captures (<capabilities> section).

                     Specify the markup writer and control/adapter mappings (<controlAdapters> section).

                     Use a capability value to narrow browser identification. The second browser definition in the example narrows the JPhone browser identification based on the deviceModel capability, which is captured in the first browser definition from the user agent string.

                     Define a gateway (<gateway> element).

<browsers>

<!-- sample UA 'J-PHONE/3.0/J-SH04_a' -->

<browser id='jphone' parentID='default'>

<identification>

<userAgent match='J-PHONE/.*' />

</identification>

<capture>

<userAgent match='J-

PHONE/(?'majorVersion'd)(?'minorVersion'.d)/(?'deviceModel'.*)'

/>

</capture>

<capabilities>

<capability name='browser' value='J-Phone' />

<capability name='cookies' value='false' />

<capability name='canInitiateVoiceCall' value='true' />

<capability name='majorVersion' value='$' />

<capability name='maximumRenderedPageSize' value='6000' />

<capability name='minorVersion' value='$' />

<capability name='version' value='$$' />

</capabilities>

<controlAdapters markupTextWriterType='System.Web.UI.ChtmlTextWriter'>

<adapter controlType='System.Web.UI.Page'

adapterType='System.Web.UI.Adapters.ChtmlPageAdapter' />

<adapter controlType='System.Web.UI.DataBoundLiteralControl'

adapterType='System.Web.UI.Adapters.DataBound

LiteralControlAdapter' />

</controlAdapters>

</browser>

<browser id='jphone_mitsubishi' parentID='jphone'>

<identification>

<capability name='mobileDeviceModel' match='^J-Dd+' />

</identification>

<capture></capture>

<capabilities>

<capability name='mobileDeviceManufacturer' value='Mitsubishi' />

</capabilities>

</browser>

<gateway id='jphone_display' parentID='jphone'>

<identification>

<header name='X-JPHONE-DISPLAY'

match='(?'screenWidth'd+)*(?'screenHeight'd+)' />

</identification>

<capture></capture>

<capabilities>

<capability name='screenPixelsHeight' value='$' />

<capability name='screenPixelsWidth' value='$' />

</capabilities>

</gateway>

</browsers>

Under the Hood: Parsing and Compilation of .browser Files

The system-wide .browser files that you see in the ConfigBrowsers directory of the .NET Framework installation are already parsed into the System.Web.Config.Handlers.BrowserCapsFactory class and precompiled into an assembly. This assembly is installed in the global assembly cache and can be used by any application. If you add or delete .browser files from the ConfigBrowsers directory after the installation of the .NET Framework, you must run the tool aspnet_regbrowsers.exe, which ships with the .NET Framework, to parse the .browser files (new and old).

The .browser files placed in an application's Browsers directory do not require any work on the part of the application developer or site administrator. They are automatically detected by ASP.NET, parsed into a class derived from BrowserCapsFactory, and compiled dynamically into an assembly that is accessible by ASP.NET for the application.

BrowserCapsFactory and its derived classes implement the logic described in the next section, Browser Definition Tree and Browser Capabilities Object. BrowserCapsFactory walks the browser definition tree to match the requesting browser and create an HttpBrowserCapabilities object. This is shown schematically in Figure 5.

Checklist for Creating .browser Files

The following list summarizes the main tasks involved in creating a .browser file:

                     Add one or more <browser> elements.

                     Create a new browser definition using the id and parentID attributes, and provide an <identification> section.

                     Provide a unique value for the id attribute when you create a new browser definition. This value must not be previously used in a system-wide or application-level .browser file.

                     Add capabilities or adapter mappings to an existing browser definition by using the refID attribute. Do not create an <identification> section.

                     Do not modify existing .browser files. Create one or more new .browser files if you want to define new browsers or add settings to existing browsers. Based on whether you want the changes to be system-wide or at the application level, add these files to the system-wide or application-level Browsers directory. You should not modify existing files because your changes will be lost if the original files are replaced or reinstalled, and because you might accidentally introduce changes to existing data. When you create a new .browser file you do not have to copy existing settings into it. ASP.NET automatically merges data from new files with that in existing .browser files.

Browser Definition Tree and Browser Capabilities Object

ASP.NET combines the browser definitions from the system-wide and application-specific .browser files into a browser definition tree, such as the one shown in Figure 12.

Figure 12. Schematic representation of a browser definition tree

The figure is representative only, and the nodes do not correspond to exact definitions in the .browser files. The browser definition tree in this release is likely to change in a future version. Each node in the tree represents a browser definition labeled by the id of the <browser> element. At the root of the tree is the default node which corresponds to the id='default' browser definition in the ConfigBrowsersDefault.browser file. The default node does not represent a specific browser but denotes an abstract representation of a browser. The children of the default node are the browser definitions that have parentID='default'. Similarly, the children of any node are browser definitions whose parentID values equal that of the node's id.

When you create a new browser definition, the value of the parentID tells the ASP.NET configuration system under which node to add the new definition. When you add settings to an existing node, the refID tells ASP.NET which node to add the settings to.

To identify a requesting browser, the ASP.NET configuration system walks down the browser definition tree and looks for a match based on the user agent and other headers. (The match condition is specified in the <identification> section of a <browser> element). The configuration system creates a default System.Web.HttpBrowserCapabilities object at the root (default) node. As it walks down the tree to narrow the browser match, it merges settings from each matched node into the HttpBrowserCapabilities object of the parent node. This means that each node adds to or overrides the previous settings. If a match is eventually found, the resulting HttpBrowserCapabilities object represents a recursive merge of the inheritance hierarchy of the matched node. For example, when the configuration system identifies a requesting browser as nokia_6220, the resulting HttpBrowserCapabilities object is formed by recursively merging (adding to or overriding the parent) the browser definitions in the nodes default, nokia, and nokia_6220. If a match is not found, the resulting HttpBrowserCapabilities object contains the settings in the default node.

The ASP.NET configuration system caches the HttpBrowserCapabilities objects that it creates, using a subset of the request headers as the cache lookup key. These objects are available for reuse within the application. For any browser defined in the browser definition tree, the configuration system walks the tree and creates an HttpBrowserCapabilities object only once, on first request. For a subsequent request by a browser with the same headers, the configuration system merely performs a cache lookup and returns a cached object.

The HttpBrowserCapabilities Class

An HttpBrowserCapabilities object is an in-memory representation of the data in the .browser configuration files for a specific browser. The main properties of an HttpBrowserCapabilities class correspond to the attributes and elements of a browser definition (<browser> element).

The following list describes the main properties of the HttpBrowserCapabilities class.

                     The Browsers property is an ArrayList that contains the id values from each matched node (<browser id='someID'>) in the browser definition tree. This property is used by ASP.NET for device filtering, as you'll see at the end of this section.

                     The Capabilities property is a dictionary of browser and device capabilities, stored as strings. During the browser-identification tree walk, the ASP.NET configuration infrastructure updates this dictionary at each node by merging into it the name/value pairs of the <capability> elements from the <capabilities> section of the node.

                     Adapters is a collection of control/adapter mappings. During the browser-identification tree walk, the configuration system updates this collection at each node by merging into it the mappings from the <controlAdapters> section of the node.

                     MarkupTextWriter is the markup writer type that the page must create for rendering markup to the requesting browser. The configuration system obtains the value of this property from the markupTextwriter attribute of the <controlAdapters> element (<controlAdapters markupTextWriterType='some MarkupTextWriter type name'>).

The Capabilities dictionary stores capability values as strings. The HttpBrowserCapabilities class exposes a set of strongly typed properties that wrap the string values in the Capabilities dictionary. These properties offer the benefits of type checking and type conversion. If you add capabilities to a browser definition that do not correspond to the typed properties of HttpBrowserCapabilities, you can extend the HttpBrowserCapabilities class to define strongly typed properties that wrap the capabilities you added. If you define a derived browser capabilities class, named, for example, MyHttpBrowserCapabilities, you can use the <browserCaps> setting in the Web.config file to tell the configuration infrastructure to use your derived class instead of HttpBrowserCapabilities, as follows:

<browserCaps>

<result type=' MyHttpBrowserCapabilities' />

</browserCaps>

Note that code that accesses the browser capabilities object must downcast it to the derived type, in this case MyHttpBrowserCapabilities, to access the new properties you defined.

How Filters Work

A page developer can customize the value of a control's property or attribute for a browser by using a filter, which is a string qualifier such as 'UP' that precedes the property name. The value of the filter must match the id of some node in the browser definition tree.

<sample:MyLabel id='MyLabel1' runat='server'

UP:Text='Adapters 101'

Text='Introduction to Adapter Authoring' />

This is how property filtering works. The parser parses the filtering syntax and generates code such as the following:

if (Request.Browser.IsBrowser('UP'))

A page developer can also selectively set property values in code by invoking the Request.Browser.IsBrowser method to check for the browser's identity.

At run time, ASP.NET checks each filter (string) passed into IsBrowser against the id values in the Browsers collection of the HttpBrowserCapabilities object. The Browsers collection contains the id values of matched nodes as ASP.NET walks the browser definition tree looking for an exact match for the requesting browser. If a filter matches an id in the Browsers collection, the if clause evaluates to true and the page framework performs the property assignment specified in the if block.

The goal of filtering is not to enable the same page to work with all browsers. Rather, filtering is meant to be used by page developers to customize a page for browsers within a given category. For example, in a page created for mobile-device browsers, a page developer could perform filtering for different cell phone browsers.

Concluding Remarks

This article provided an under-the-hood look at adaptive rendering in ASP.NET in the pre-release version of the .NET Framework, code-named 'Whidbey.' It examined how adapters work and demonstrated the main steps in implementing an adapter and creating a browser definition file.

Acknowledgments

I am grateful to Matt Gibbs, Simon Calvert, and Mike Pope of the ASP.NET team for reviewing this article and providing valuable feedback. Matt and Simon also clarified many technical details in person and in e-mail. I appreciate Carrie Klumpar's editorial help.

Openwave, the Openwave logo, Openwave SDK, Openwave SDK Universal Edition, Openwave SDK WAP Edition are trademarks of Openwave Systems Inc. All rights reserved.

About the Author

Vandana Datye is a freelance author who has written samples and documentation for the .NET Framework SDK. She is co-author of Developing ASP.NET Server Controls and Components, published by Microsoft Press. You can reach Vandana at v-vdatye@microsoft.com.

DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1187
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou



Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024. All rights reserved