Control Adapters


This release of ASP.NET introduces an alternate way to specify renderings of controls through a mechanism called control adapters. Originally designed to provide alternate renderings of standard controls for mobile devices, they actually serve a more general purpose of providing a way of completely changing the rendering of a control based on the client's browser type. In 2006, Microsoft released a supplemental set of examples, the CSS Friendly Control Adapters, that shows how to change the default tabular rendering of many controls like the Menu and FormView to render without tables and to take all of their style settings from cascading stylesheets. We will explore the control adapter architecture in this section and how you might adapt it to your own uses.

Building Control Adapters

At their core, control adapters are just a way of providing alternative renderings for controls without actually modifying the controls themselves. Because control adapters are designed to provide alternate renderings for different clients, you specify control adapter mappings in a .browser file, which is where associations between User Agent strings and browser capabilities are defined. The control adapter class itself must inherit from the System.Web.UI.Adapters.ControlAdapter, which is an abstract base class that looks much like the Control base class, and includes event methods for Init, Load, PreRender, and Unload, as well as a virtual Render method. Internally, when a control is rendering, the Control base class will first check to see if there is a control adapter currently associated with the control. If there is, it will invoke the Render method of the adapter; if not, it calls the standard Render method of the control. Figure 2-11 shows the control adapter architecture.

Figure 2-11. Control adapter architecture


To create your own control adapter, you start by creating a new class that inherits from the ControlAdapter base class, or more commonly one of its derivatives, WebControlAdapter. Which control adapter class you derive from depends on what control you are building the adapter for. If it is one of the built-in WebControls, you should select the WebControlAdapter, because it adds virtual RenderBeginTag, RenderEndTag, and RenderContents methods to more closely mirror how WebControls render. There are also more specific control adapters defined for data-bound controls and the Menu control, each of which provides more virtual methods to access and modify features of the controls it is associated with.

As an example of building a control adapter to change the rendering of a particular control, let's use the BulletedList and create a control adapter named BulletedListControlAdapter. BulletedList is a simple control that renders a collection of items as an ordered or unordered list (<ol> or <ul>). To show how flexible the adapter model is, we'll build the control adapter to completely change the rendering to be table-based instead. Since the BulletedList is derived from WebControl, we will start by creating the BulletedListControlAdapter class as a derivative of WebControlAdapter. Next, we'll override the RenderBeginTag and RenderEndTag methods to render the opening and closing tags of the table. Finally, we'll override RenderContents to iterate across the items of the BulletedList control and render each item as a single-cell row in the table prefixed with an asterisk. The full control adapter implementation is shown in Listing 2-20.

Listing 2-20. Control adapter for the BulletedList control

namespace EssentialAspDotNet2.UIElements {   public class BulletedListControlAdapter :                  WebControlAdapter   {     protected override void RenderBeginTag(HtmlTextWriter writer)     {       writer.WriteLine();       writer.WriteBeginTag("table");       writer.Write(HtmlTextWriter.TagRightChar);       writer.Indent++;     }     protected override void RenderEndTag(HtmlTextWriter writer)     {       writer.WriteEndTag("table");       writer.Indent--;       writer.WriteLine();     }     protected override void RenderContents(HtmlTextWriter writer)     {       writer.Indent++;       BulletedList bl = Control as BulletedList;       if (bl != null)       {         foreach (ListItem i in bl.Items)         {           writer.WriteLine();           writer.WriteBeginTag("tr");           writer.Write(HtmlTextWriter.TagRightChar);           writer.WriteLine();           writer.Indent++;           writer.WriteBeginTag("td");           writer.Write(HtmlTextWriter.TagRightChar);           writer.WriteLine();           writer.Indent++;           writer.Write("*");           writer.Write(HtmlTextWriter.SpaceChar);           writer.Write(i.Text);           writer.WriteLine();           writer.Indent--;           writer.WriteEndTag("td");           writer.WriteLine();           writer.Indent--;           writer.WriteEndTag("tr");           writer.WriteLine();         }       }       writer.Indent--;     }   } } 

The next step is to associate this new BulletedListControlAdapter class with the BulletedList control. As mentioned earlier, this is done by specifying which subset of browsers you want this to apply to. For now, we will simply associate all browsers with this adapter by specifying a refID attribute value of Default in the browser element of a .browser file. The next section covers how to create more granular associations. In this sample, a new file named MyAdapters.browser was created and placed in the application's App_Browsers directory (see Listing 2-21). Any additional browser definitions or control adapters defined in this local file will be added to the collection of browsers and adapters already defined in the system .browser configuration files.

Listing 2-21. Contents of MyAdapters.browser file

<!-- File: MyAdapters.browser --> <browsers>   <browser ref>     <controlAdapters>       <adapter         controlType="System.Web.UI.WebControls.BulletedList"         adapterType=           "EssentialAspDotNet2.UIElements.BulletedListControlAdapter"       />     </controlAdapters>   </browser> </browsers> 

The last step is to actually place an instance of the BulletedList control with some items on a page, as shown in Listing 2-22.

Listing 2-22. Sample page with BulletedList control

<%-- File: Default.aspx --%> <%@ Page Language="C#" %> <html xmlns="http://www.w3.org/1999/xhtml" > <body>     <form  runat="server">     <div>         <asp:BulletedList  runat="server">           <asp:ListItem Value="1">Item 1</asp:ListItem>           <asp:ListItem Value="2">Item 2</asp:ListItem>           <asp:ListItem Value="3">Item 3</asp:ListItem>           <asp:ListItem Value="4">Item 4</asp:ListItem>         </asp:BulletedList>     </div>     </form> </body> </html> 

If we first run the page without the control adapter in place (by removing the .browser file or commenting out the control adapter mapping in the .browser file), the BulletedList will render as expected, using a <ul> element with <li> subelements.

<ul >   <li>Item 1</li>   <li>Item 2</li>   <li>Item 3</li>   <li>Item 4</li> </ul> 


If we then enable the control adapter, the rendering changes to our new table-based rendering defined in our custom adapter, with no changes at all to the actual .aspx page.

<table> <tr>   <td>     * Item 1   </td> </tr>   <tr>   <td>    * Item 2   </td>   </tr> <tr>   <td>   * Item 3   </td> </tr> <tr>   <td>     * Item 4   </td> </tr> </table> 


As you can see, it is reasonably simple to create an alternate rendering for a control using the control adapter architecture. There are, however, some issues that should be raised at this point. First of all, note that our simplified rendering of the BulletedList did not look at any of the attributes of the BulletedList control except its items collection. This includes the rather important BulletStyle attribute that determines what type of list to render (unordered, numeric, alphabetic, and so on). It is generally important to honor all of the behavioral attributes of a control for which you are writing a control adapter. Style attributes, on the other hand, may or may not make sense to incorporate into your rendering, especially if the target device for which the control adapter is being built does not have a means of displaying a particular style (for example, BackColor on a black-and-white device). As you will see later in this chapter, the CSS Control Adapters elect to ignore most style attributes of controls they render, providing instead a set of CSS classes that can be modified to control appearance.

The other reason the simplified example of the BulletedListControlAdapter is not a very realistic one is that we did not actually take advantage of the browser mapping to associate specific user agents with this adapter. Instead, we mapped all browser types (by specifying Default in the refID attribute) to use this control adapter, which means that a site with this control adapter installed would never call the standard rendering of the BulletedListControl, but would always display the rendering supplied by the control adapter. If the only purpose is to create a control that has a different rendering from an existing control, a better approach would probably be to derive a new control class from the existing control and override the necessary methods to change the rendering. The one other potential advantage to using control adapters over custom control derivatives is that they can be applied to a Web application with no modifications to any of the pages (or even the web.config file), so they may be compelling even without browser-contingent rendering for situations where an application is already written and deployed, but a rendering change is needed for a particular control across the entire site.

Browser Recognition

Control adapters are mapped to controls in a particular site by specifying a configuration element in a .browser file. In the 2.0 release of ASP.NET, the browser recognition capability was moved from the <browserCaps> element of the machine.config and web.config files to a collection of .browser files. The <browserCaps> element is still supported, but the preferred way to specify browser capabilities is with a .browser file located either in the local App_Browsers directory of your application or in the machine-wide %SYSTEM%\Microsoft.NET\Framework\v2.0.50727\Config\Browsers directory.

The purpose of browser recognition files, in addition to associating control adapters, is to populate the HttpBrowserCapabilities class for a given request, accessible through the Request.Browser property. Through this class you can find out the name of the requesting browser and whether it supports JavaScript, cookies, and so on. To populate this class, each .browser file contains one or more <browser> elements with a subelement called identification, which contains a regular expression that is applied to the User Agent string for each request (with an option to specify an exclusion expression as well). If it matches, the capabilities listed for that browser are used to populate the HttpBrowserCapabilities class. For example, the browser definition for Internet Explorer (found in the machine-wide Browsers directory in the file ie.browser) looks like this:

[View full width]

<browsers> <browser parent> <identification> <userAgent match="^Mozilla[^(]*\([C|c]ompatible;\s*MSIE (?'version'(?'major'\d+)(?'minor'\. \d+)(?'letters'\w*))(?'extra'[^)]*)" /> <userAgent nonMatch="Opera|Go\.Web|Windows CE|EudoraWeb" /> </identification> <capabilities> <capability name="browser" value="IE" /> <capability name="extra" value="${extra}" /> <capability name="isColor" value="true" /> <capability name="letters" value="${letters}" /> ...


Browsers are also grouped hierarchically using the parentID attribute on the <browser> element. In order for a <browser> element to be applied that has a parentID referencing another <browser> element, the regular expression conditions for the parent element must have succeeded. The root of the hierarchy is "Default," which matches all User Agent strings and assumes that the browser has minimal capabilities. All other browser types have either Default or one of its children as a parent. The predefined set of browser definitions located in the machine-wide Browsers directory contains a useful categorization of browser types, with associated names that can be used to identify subsets of browsers. For example, if you wanted to apply a control adapter to all requests made from Internet Explorer versions 6 and higher, you could use the "IE6to9" browser name in the refId attribute of your local browser element. Figure 2-12 shows the hierarchy of browser types defined in the system .browser files (excluding mobile devices, which constitute another hierarchy at least twice this size).

Figure 2-12. Browser type hierarchy


You can use any of these browser names in the refID attribute when assigning control adapters. As an example, the CSS Control Adapter toolkit enables their adapters for IE6to9, MozillaFirefox, Opera8to9, and Safari, which covers the subset of browsers that support the CSS features needed by the adapters defined in the toolkit. Other browsers will revert to the standard control renderings.

CSS Friendly Adapters

In April of 2006, Microsoft released a set of samples, CSS-friendly ASP.NET 2.0 control adapters Beta 1.1, which contains a suite of control adapters that change the rendering of several common ASP.NET controls to be more "CSS friendly." This was primarily in response to developers using ASP.NET wanting to specify all of the style attributes, including layout, for their controls using CSS stylesheets instead of primarily server-side attributes of controls. The initial release includes adapters for the Menu, TreeView, DetailsView, FormView, and DataList controls. Future releases of this toolkit (which will undoubtedly be out by the time this book is in print) will include enhancements to the adapters for these controls, plus adapters for additional controls. The toolkit is available for download from www.asp.net/cssadapters/.




Essential ASP. NET 2.0
Essential ASP.NET 2.0
ISBN: 0321237706
EAN: 2147483647
Year: 2006
Pages: 104

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net