- Legacy - This is exactly equivalent to the ASP.NET 2.0 Client Id behavior. This is also the default if no ClientIdMode property is set in the current control's hierarchy.
- Static - You set it, you get it...most controllable but potentially the least 'safe'. If a control is set to 'static' ClientIdMode then exactly what you set for Id is used as the client id, no matter what naming container the control sits in.
- Predictable - Mostly for use in DataBound controls, only uses 'set' Id attributes of parent Naming Containers (so, no automatic id generation using 'ctlXXX' names). This also works in conjunction with the DataBound control property RowClientIdSuffix to allow you to define the 'uniquefying' item for the specific row. Previously, the auto-generated name ctrl0...ctrl1...ctrl2...etc...was used to provide this uniqueifying function for controls in the rows of DataBound controls.
- Inherit -Â Essentially the 'default' behavior for controls, explicitly setting ClientIdMode = 'Inherit' essentially clears the ClientIdMode for the current control and allows this and any child controls (which have either 'Inherit' as ClientIdMode or ClientIdMode not set) will take the ClientIdMode of any parent control (including Page and Config...see below)
Page
You can also set the ClientIdMode at Page level, this defines the default ClientIdMode for all controls within the current page...
<%@ Page Language="C#" AutoEventWireup="true"Â CodeFile="Default.aspx.cs" Inherits="_Default" ClientIdMode="Static"%>
Config
It's also possible to set the ClientIdMode in the config section at either machine or application level...this defines the default ClientIdMode for all controls within all pages in the application.
<system.web>
 <pages clientIdMode="Predictable"></pages>
</system.web>
So what can I do with that?
Restarting Control Naming
As mentioned earlier the client id for a control is derived from the NamingContainers in which the control sits in the Control Hierarchy, normally this is only the actual controls within the page (e.g., in DataBound controls), however when using MasterPages you can end up with ids as found in the following HTML:
   <div id="ctl00_ContentPlaceHolder1_ParentPanel">
       <div id="ctl00_ContentPlaceHolder1_ParentPanel_NamingPanel1">
           <input name="ctl00$ContentPlaceHolder1$ParentPanel$NamingPanel1$TextBox1" type="text" value="Hello!" id="ctl00_ContentPlaceHolder1_ParentPanel_NamingPanel1_TextBox1" />
   </div>
Even though the TextBox shown in the HTML is only within two NamingContainers within the page, due to the way MasterPages hook together you wind up with a control id like the following: ctl00_ContentPlaceHolder1_ParentPanel_NamingPanel1_TextBox1
Obviously this is a pretty long id...guaranteed unique within the page but unnecessarily long for most purposed. In this example we now want to reduce the length of the rendered id and make it more user defined (so, shortened, no ctlXXX etc...). The easiest way to achieve this is the following
<tc:NamingPanel runat="server" ID="ParentPanel" ClientIdMode="Static">
   <tc:NamingPanel runat="server" ID="NamingPanel1" ClientIdMode="Predictable">
       <asp:TextBox ID="TextBox1" runat="server" Text="Hello!"></asp:TextBox>
   </tc:NamingPanel>
</tc:NamingPanel>
In this sample (identical to earlier markup) we've set the ClientIdMode to 'Static' on the outermost NamingPanel as well as setting the next 'Child' control to 'Predictable'. This results in this markup (note, the rest of the page, MasterPages etc,...is identical to the previous example)
           <div id="ParentPanel">
       <div id="ParentPanel_NamingPanel1">
           <input name="ctl00$ContentPlaceHolder1$ParentPanel$NamingPanel1$TextBox1" type="text" value="Hello!" id="ParentPanel_NamingPanel1_TextBox1" />
   </div>
Here we've essentially restarted the naming hierarchy for Controls to the outermost NamingPanel , eliminating the ContentPlaceHolder and MasterPage names from the id (note: the 'name' attribute is unaffected...meaning we retain the normal ASP.NET functionality for events, ViewState etc...). A nice side-effect of restarting the naming hierarchy is that even if the markup defining the NamingPanels is moved to a different ContentPlaceholder, the rendered Client Ids remain the same.
NOTE: The developer does now take more responsibility for ensuring that rendered Control Ids are unique...not doing so can break functionality which expect to find unique HTML elements for each Id (e.g., Javascript's GetElementById()).
Predictable DataBound Client Ids
As we showed previously, the Client Ids generated for Controls within DataBound list controls are pretty messy and not really predictable...How does the new Client Id functionality help?
We want to achieve the following:
- Shorten the Client Ids for Controls
Make the Client Id predictable
Make the Client Id unique across pages (rather than 'within' pages)
So, how do we do this?
<tc:NamingPanel runat="server" id="rootPanel">
     <tc:NamingPanel runat="server">       Â
Â
         <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
             SelectCommand="SELECT [ProductName], [ProductID] FROM [Alphabetical list of products]"></asp:SqlDataSource>
         <asp:ListView ID="ListView1" runat="server" DataSourceID="SqlDataSource1"
             onselectedindexchanged="ListView1_SelectedIndexChanged" ClientIdMode="Predictable" ClientIdRowSuffix="ProductID">
             <ItemTemplate>
                 <tr style="">
                     <td>
                         <asp:Label ID="ProductNameLabel" runat="server"
                             Text='<%# Eval("ProductName") %>' />
                     </td>
                 </tr>
             </ItemTemplate>
                 <LayoutTemplate>
                 <table id="Table1" runat="server">
                     <tr id="Tr1" runat="server">
                         <td id="Td1" runat="server">
                             <table ID="itemPlaceholderContainer" runat="server" border="0" style="">
                                 <tr id="Tr2" runat="server" style="">
                                     <th id="Th1" runat="server">
                                         ProductName</th>
                                 </tr>
                                 <tr ID="itemPlaceholder" runat="server">
                                 </tr>
                             </table>
                         </td>
                     </tr>
                     <tr id="Tr3" runat="server">
                         <td id="Td2" runat="server" style="">
                         </td>
                     </tr>
                 </table>
             </LayoutTemplate>
         </asp:ListView>
     </tc:NamingPanel>
 </tc:NamingPanel>
In the markup above, we have used the ClientIdMode and ClientIdRowSuffix properties. ClientIdRowSuffix is a property which can only be used in DataBound controls and actually differs based on the DataBound control it's used with:
GridView: You can specify the name of a column in the DataSource or multiple columns which are then combined at runtime. As an example if you specified ClientIdRowSuffix as "ProductName, ProductId" in a GridView then the rendered control Id would be "rootPanel_GridView1_ProductNameLabel_Chai_1".
ListView: You can specify a single column in the DataSource which will be appended to the Client Id. As an example if you specified ClientIdRowSuffix as "ProductName" in a ListViewthen the rendered control Id would be "rootPanel_ListView1_ProductNameLabel_1". In this case the last '1' comes from the ProductId of the DataItem.
Repeater: No ClientIdRowSuffix property is allowed. In a Repeater the index of the Row is used. In the case above, you would wind up with "rootPanel_Repeater1_ProductNameLabel_0". The '0' is simply the index of the current row.
Note: FormView, DetailsView do not have multiple rows so do not have a ClientIdRowSuffix property.
Conclusion
So, there you have it...I dare say I'll post again in future about this topic, we have a sample app which will find it's way onto Codeplex in the near future!
Matthew Osborn from our QA team has also posted on this stuff...
NOTE: In current CTP builds you cannot use UpdatePanels with controls when you use the new Client Id functionality. This is fixed internally and will work correctly in future public releases.