About Links Archives Search Feed Albums of Note


ASP.NET, Part 8: Introducing ClientIDMode

Welcome to part 8 of my tour through ASP.NET 4.0 [http://blog.hmobius.com/post/2010/02/09/ASPNET-40-Cometh.aspx]. In this episode, we’ll look at a new feature that directly affects two things: page size (and thus scalability and perceived performance) and the use of JavaScript APIs such jQuery. It’s called ClientIDMode.

The Problem Consider a typical development situation. You’re building a page which inherits its structure from a master page or two. The page itself contains a custom user control which its own set of server-side controls in it.  In this particular example, let’s have a simple table.

<%@ Control Language=“C#” AutoEventWireup=“true” CodeFile=“UserControl.ascx.cs” Inherits=“UserControl” %>

On running this page, we’ll see that the generated HTML for the table is perhaps not as succinct as we might otherwise wish. (Note that main and submain are the names of ContentPlaceholder controls in the master pages that the page containing the table is based on.)

 <td id="ctl00_ctl00_main_submain_uc1Control_tdTopRight">       <select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList"                id="ctl00_ctl00_main_submain_uc1Control_ddlChoiceList">          <option value="Choice One">Choice One</option>          <option value="Choice Two">Choice Two</option>          <option value="Choice Three">Choice Three</option>        </select>     </td>    </tr>  </table> 

When ASP.NET was first created, it was decided that it should autogenerate IDs for each element on the page based on the chain of their parent naming containers. This would mean that you would not be able to generate an HTML page where two elements had the same ID. As you can see above, the IDs generated in this way are neither particularly short nor semantically meaningful, but they are unique.

The length and meaning of client-side IDs didn’t matter back in 2000, but the invention and subsequent mass adoption of AJAX a few years ago has repopularised client-side scripting on the page. Scripting libraries like jQuery have only sped this tide of JavaScript up further thanks to the ease with which quite complex, cross-browser functionality can be achieved. And if there’s one thing that JavaScript uses a lot, it’s the ID of elements on a page to identify them.

The problem with ASP.NET up to v3.5 then is that there is no way to dictate the client-side ID of an HTML element and it’s hard to know exactly what the ‘AutoID’ will be for use in your scripts. A fairly complex page with several dozen controls on it will also be significantly larger than it needs to be because of AutoIDs and on high-volume sites, that means extra bandwidth used unnecessarily (and so more cost) and a perceived performance hit on the site from users with slower connections waiting for the page to download.

The Solution It is possible to work around this issue in several ways, but with the new ClientIDMode property in ASP.NET 4.0, we’re presented with a way to instruct ASP.NET to generate element IDs in one of three ways. This new property can be set for any webform control, page, or master page. It can also be set as a property of the <system.web>/ element in web.config. 

ClientIDMode has four possible values

Let’s taking the example code above, and see how the
Choices

element is rendered in each ClientIDMode. First, let’s set the control’s ClientIDMode to Predictable. As noted, this is the default in ASP.NET 4.0, so we need add no extra property. The resultant HTML looks like this:

 <td id="main_submain_uc1Control_tdTopRight">       <select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList"                id="main_submain_uc1Control_ddlChoiceList">         <option value="Choice One">Choice One</option>         <option value="Choice Two">Choice Two</option>         <option value="Choice Three">Choice Three</option>       </select>     </td>   </tr> </table>

As you can see, the resultant IDs are mostly the same as the v3.5 ones but are no longer prepended with a (random) number of ctl00_ strings. Now let’s set ClientIDMode on the table to AutoID.

This should generate old-style IDs for everything again as would happen in v3.5, but actually something different happens.

Choices
 <td id="main_submain_uc1Control_tdTopRight">       <select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList"                id="main_submain_uc1Control_ddlChoiceList">         <option value="Choice One">Choice One</option>         <option value="Choice Two">Choice Two</option>         <option value="Choice Three">Choice Three</option>       </select>     </td>   </tr> </table>

As you can see, only the ID for the table itself has been generated in the v3.5 way. Every other ID is still generated using the ‘Predictable Mode’ algorithm. Now try setting ClientIDMode to AutoID as an attribute of the @Master, @Page, or @Control directive. For example

<%@ Control Language=“C#” AutoEventWireup=“true” CodeFile=“UserControl.ascx.cs” Inherits=“UserControl” ClientIDMode=“AutoID” %>

Sure enough, all the IDs for the table and its children have changed back to the v3.5 naming style as seen earlier. Try setting ClientIDMode to Static.

<%@ Control Language=“C#” AutoEventWireup=“true” CodeFile=“UserControl.ascx.cs” Inherits=“UserControl” ClientIDMode=“Static” %>

Straight to the mark, the HTML element IDs now match their respective server-side control IDs exactly (but their name attributes are still generated as in v3.5-)

Choices
 <td id="tdTopRight">       <select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList"                id="ddlChoiceList">         <option value="Choice One">Choice One</option>         <option value="Choice Two">Choice Two</option>         <option value="Choice Three">Choice Three</option>       </select>     </td>   </tr> </table>
Move the ClientIDMode attribute back from the @Control directive to the element and you’ll see only the
Choices

element return its shorter ID with the rest returning to their ‘predictable’ form.

The rule then is that ClientIDMode only changes the ID generation mode for the control on which it is set. It does not affect the ID generation mode for its child controls. The only way to change the default ClientIDMode for more than one control at a time is to set it in the @Control, @Page, or @Master directives or in web.config. The only exception to this is when setting ClientIDMode on templated list controls as we’ll see later.

If you already use jQuery in your v3.5 sites and want to upgrade them to use the v4 Framework, you would be well may need to default the ClientIDMode for the site to AutoID in web.config until you’ve checked your code works against predictable mode.

<system.web> … … </system.web>

In general however, a good approach to using ClientIDMode is to leave ‘Predictable’ as the default and for those few controls which are targeted by JQuery, set ClientIDMode to Static. ASP.NET will not interfere with an ID set as static so if you inadvertently set two or more identical IDs in a page, you’ll have to identify and disambiguate them yourself.

Using ClientIDRowSuffix With Templated List Controls Consider the case where you’re binding some data into a Templated List control. For example a ListView.

<%@ Page Language=“C#” AutoEventWireup=“true” CodeFile=“DataBoundControls.aspx.cs” Inherits=“DataBoundControls” %> …
 <asp:ListView runat="server" ID="lvwDemo">       <LayoutTemplate>         <table>           <asp:PlaceHolder runat="server" ID="itemPlaceholder" />         </table>       </LayoutTemplate>       <ItemTemplate>         <tr>           <td>             <asp:Label runat="server" ID="lblItemName" Text='<%#Eval("Name") %>' />           </td>         </tr>       </ItemTemplate>     </asp:ListView>   </div>   </form> </body> </html>

And in the code-behind, a simple List is bound to the ListView.

protected void Page_Load(object sender, EventArgs e) { List items = new List { new Item { Id = 11, Name = “Cat” }, new Item { Id = 12, Name = “Dog” }, new Item { Id = 13, Name = “Dragon” } };

lvwDemo.DataSource = items; lvwDemo.DataBind(); }

With all defaults left on, a ClientIDMode of ‘Predictable’ produces the following HTML for the table templated in the ListView.

Cat
Dog
Dragon

Note that an auto-incremented number is used to make the id for the span unique. You can replace that auto-incremented number with something more meaningful using the new ClientIDRowSuffix property on the ListView. The defines the data field (typically the primary key) whose value is suffixed to the row ID to make it unique. If we set it to Id on the ListView…

the Id for each Item object in the list is used in the span ids.

Cat
Dog
Dragon

We could set it to Name instead of Id, as long as we can guarantee that Name is a unique field and get the following HTML.

Cat
Dog
Dragon

ClientIDRowSuffix actually takes a string[], so you can set multiple fields to this property should you wish. FOr example,

will generate the following.

Cat
Dog
Dragon

A second new property – ClientIDRowSuffixDataKeys – will return the values used to generate the client ID for a given row.

ClientIDRowSuffix and StaticModes Don’t Mix Be aware that ClientIDRowSuffix has no effect on either AutoID or Static ID modes and also that server-side controls placed within templates DO inherit the ClientIDMode of their parent control – in this case, the ListView or GridView in whose templates they are placed. For example, should you set ClientIDMode to Static on the ListView control in the current example…

every span id will remain identical despite the presence of ClientIDRowSuffix.

Cat
Dog
Dragon

If you set it AutoID rather than Static, this is not a problem as AutoIDs are unique by design but there’s no influence on them via ClientIDRowSuffix either.

Cat
Dog
Dragon

You could set the ClientIDMode to Predictable on the Label control in your template

Posted on February 25, 2010   #ASP.NET     #Geek Stuff  






← Next post    ·    Previous post →