Introduction
This article will focus on a new feature added in ASP.NET Framework 4.0. More efforts have been put to make client side development model much easier and increase productivity. When it comes to the client side, we all know about ClientID
which gives us a unique ClientID
of control to refer on the client side. But so far it was read-only (you cannot set it in code). The clientID
you get on the client side is something like this ctl00_MasterPageBody_ctl01_Textbox1
, let's say where ctl00
is content id of content placeholder on master page, etc. Basically it inherits outer control’s id from parent control or place holders and generates a unique id.
Background About the Problem and Solution
Problem
The ClientID
property works great to ensure that each element is uniquely identified. But when you do the client side scripting, it becomes much more difficult and you run into the issue in ASP.NET because until runtime you do not know what the client side ID would be, making it difficult to do any kind of client side scripting. In addition any modification of the page, adding removing controls, can result in a different client side ID being generated.
Solution
In the ASP.NET versions we have so far, there is a workaround for this issue. Each control has a property called ClientID
that is a read only and supplies the unique client side ID. You can use this in a code behind when dynamically adding scripts or more commonly use inline code to supply the value to client side scripts.
<script type="text/javascript">
function DoSomething()
{
alert('<%= Control.ClientID %>');
}
</script>
If you move the control or make the control as a user control, ClientID
property you get at client side keeps changing ctl00_MasterPageBody_ctl01_Textbox1
and sometimes we do need to refer to the control using this uniquely generated id (mostly we hard-code the values :) take it easy dude.).
Now Look at ASP.NET 4.0 Solution
ASP.NET 4.0 has the new feature for client side Id which you can set in code and other properties at design time and make use of them at run-time in client side development.
ASP.NET 4.0 has defined a new property on ASP.NET control named ClientIDMode
. Once you set the ClientIDMode
property for a control, the ClientID
is modified accordingly and then used as the client side id. You can even set ClientID
property on the control itself.
The cool thing about this property is that, you define it at control level or page level or application level (in web.config file).
So now look at what the Client ID Modes are and what they do.
There is now a new property on every control (this includes pages and master pages as they inherit from control) called ClientIDMode
that is used to select the behavior of the client side ID. ClientIDMode
supports the Mode Types given below.
Static
This is most basic, simple mode and it makes the client side ID static
. Whatever value you put for the ClientID
or ID
is that which will be used for the client side ID. One odd condition here, if a static ClientIDMode
is used in a repeating control, the developer is responsible for ensuring client side ID uniqueness. Below is the code example:
Markup: <asp:TextBox ID="txtEcho2" runat="server" ClientIDMode="Static" />
Output: <input id="Text1" name="ctl00$MasterPageBody$ctl00$txtEcho2" />
Setting a ClientID
property separates the server side and client side ids. You will have client id for client side development which makes code much easier and clean.
Markup: <asp:TextBox ID="TextBox1" ClientID="”Echo”" runat="server"
ClientIDMode="Static" />
Output: <input id="Text2" name="Echo" />
Legacy
The default value if ClientIDMode
is not set anywhere in the control hierarchy. This causes client side IDs to behave the way they did in version 2.0 (3.0 and 3.5 did not change this code path) of the framework. This mode will generate an ID similar to ctl00_MasterPageBody_ctl01_Textbox1
. Below is a code example:
Markup: <asp :TextBox ID ="txtEcho" runat ="server" ClientIDMode ="Legacy" />
Output: <input id="ctl00_MasterPageBody_ctl00_txtEcho"
name="ctl00$MasterPageBody$ctl00$txtEcho" />
Inherit
This is the default behavior for every control. This looks to the controls parent to get its value for ClientIDMode
. You do not need to set this on every control as it is the default, this is used only when the ClientIDMode
has been changed and the new desired behavior is to inherit from the control's parent.
Predictable
The above mode works well if you have only a single control but what happens when it comes to databound controls where you will have different controls inside the control. For example, let's say, repeater control.
What this mode does is, it says render out the client id followed by some type of predictable suffix but still unique enough to make each individual item different from each other.
This mode is commonly used on databound controls and this mode is used when the framework needs to ensure uniqueness. The framework will traverse the control hierarchy prefixing the supplied ID with its parent control ID until it reaches a control in the hierarchy whose ClientIDMode
is defined as static
. In the event that the control is placed inside a databound control, a suffix with a value that identifies that instance will also be added to the supplied ID. Below is a code example:
Markup
<asp:GridView ID="EmployeesNoSuffix" runat="server"
AutoGenerateColumns="false" ClientIDMode="Predictable">
<Columns>
<asp:TemplateField HeaderText="ID">
<ItemTemplate>
<asp:Label ID="EmployeeID" runat="server" Text='<%# Eval("ID") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label ID="EmployeeName"
runat="server" Text='<%# Eval("Name") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Output
<table id="EmployeesNoSuffix" style="border-collapse: collapse"
cellspacing="0" rules="all" border="1">
<tbody>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
</tr>
<tr>
<td><span id="EmployeesNoSuffix_EmployeeID_0">1</span></td>
<td><span id="EmployeesNoSuffix_EmployeeName_0">EmployeeName1</span></td>
</tr>
...
<tr>
<td>
<span id="EmployeesNoSuffix_EmployeeID_8">9</span>
</td>
<td>
<span id="EmployeesNoSuffix_EmployeeName_8">EmployeeName9</span>
</td>
</tr>
</tbody>
</table>
Another useful property RowClientIDSuffix
is used to generate client side ids for bound control in user friendly manner and with a RowClientIDSuffix
defined. This looks in the control’s datakeys collection for the value and then suffixes the ID with that value.
Markup
<asp:GridView ID="EmployeesSuffix" runat="server"
AutoGenerateColumns="false"
ClientIDMode="Predictable" RowClientIDSuffix="ID">
<Columns>
<asp:TemplateField HeaderText="ID">
<ItemTemplate>
<asp:Label ID="EmployeeID" runat="server" Text='<%# Eval("ID") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label ID="EmployeeName" runat="server" Text='<%# Eval("Name") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Output
<table id="EmployeesSuffix" style="border-collapse: collapse"
cellspacing="0" rules="all" border="1">
<tbody>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
</tr>
<tr>
<td>
<span id="EmployeesSuffix_EmployeeID_1">1</span>
</td>
<td>
<span id="EmployeesSuffix_EmployeeName_1">EmployeeName1</span>
</td>
</tr>
...
<tr>
<td>
<span id="EmployeesSuffix_EmployeeID_9">9</span>
</td>
<td>
<span id="EmployeesSuffix_EmployeeName_9">EmployeeName9</span>
</td>
</tr>
</tbody>
</table>
With a RowClientIDSuffix
defined, but instead of just one value, a compound value will be used. Exhibits the same behavior as one value but it will suffix both values onto the ID.
Markup
<asp:GridView ID="EmployeesCompSuffix" runat="server" AutoGenerateColumns="false"
ClientIDMode="Predictable" RowClientIDSuffix="ID, Name">
<Columns>
<asp:TemplateField HeaderText="ID">
<ItemTemplate>
<asp:Label ID="EmployeeID" runat="server"
Text='<%# Eval("ID") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label ID="EmployeeName"
runat="server" Text='<%# Eval("Name") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Output
<table id="EmployeesCompSuffix" style="border-collapse: collapse"
cellspacing="0" rules="all" border="1">
<tbody>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
</tr>
<tr>
<td>
<span id="EmployeesCompSuffix_EmployeeID_1_EmployeeName1">1</span>
</td>
<td>
<span id="EmployeesCompSuffix_EmployeeName_1_EmployeeName1">
EmployeeName1</span>
</td>
</tr>
..
<tr>
<td>
<span id="EmployeesCompSuffix_EmployeeID_9_EmployeeName9">9</span>
</td>
<td>
<span id="EmployeesCompSuffix_EmployeeName_9_EmployeeName9">
EmployeeName9</span>
</td>
</tr>
</tbody>
</table>
Please share your thoughts and valuable suggestions. Thanks!
Reference
History
- 15th March, 2009: Initial post