Behind The Scene - Control ID's in ASP.Net
We all familiar with the terms of ID, ClientID and UniqueID which are
properties of Control class. In this post I'll dive into the way the last two
read-only properties values are being set and when.
The difference between ClientID and UniqueID
Let's go to the basic HTML form. The form's inputs (I.E text-boxes,
Checkboxes, DropdownLists, RadioButtons) has two basic HTML attributes
called id and name (in small letters which is the standard for HTML attribute) .
The "id" attribute is used by client scripting code to access the element (hopefully
you're using document.getElementById or $get and not accessing it like an inner
property of the form or document.all which are not standard).
The "name" attribute is used as a key in the data being sent to the server when submitting
the form.
On simple form you see that both attributes are the same. However when using a master page,
Field inside User control / CompositeControl / other controls implementing INamingContainer
you see the character of "_" inside the ID attribute and the "$" inside the name attribute.
Note: On ASP.Net 1.1 or sites migrated from it, you may see the ":" seperator on the "name"
attribute instead of the "$" sign. You can set the option of the legacy rendering by changing
the XhtmlConformanceMode on your web.config file.
The HTML attribute of ID corresponds to the server property of ClientID while the Name HTML
attribute corresponds to the UniqueID . One common mistake I've seen is developers confusing
between the two. Internet Explorer will forgive you for binding the UniqueID to client code,
however browser of another kind will issue a script error because it won't find the HTML element.
How both properties are set
Both properties have get accessor only. First, let's have a look of Control.UniqueID property:
(with courtesy of RedGate Reflector)
As you can see, it uses _cachedUniqueId for caching (it also clears when the ID is changing).
However, the main logic of the accessor is assemble the UniqueID with the string returns
from namingContainer.GetUniqueIDPrefix(). This method is recursive while each NamingContainer
calls it's own NamingContainer (The first control that implements INamingContainer contains
this control, not necessarily it's parent) and assemble the UniqueID
of <Container>$<Container>$<Container>$<Container>$...$<ID>.
The CientID is simpler. It simply replaces the seperators. Let's have a look at the code:
In conclusion:
1. UniqueID is the property corresponds to the input's name on client side.
In order to get it's value from the Request object wee need to call
Request[<control>.UniqueID]
2. ClientID corresponds to the input "id" on client side. we need it to bind
to the client code when accessing the HTML element rendered from the
control on the client side.
3. UniqueID is assembled from the control's naming container ID prefix
and the control ID. It is also cached. Therefore it is recommended not
to change the control's ID more than once.
4. The UniqueID won't necessarily contain the parent of the control.
If you have a custom control consists of some controls it is better to
use CompositeControl or any other control implements INamingContainer
than using WebControl or other.
5. After doing so, no need to change the ID property to more than an identifier
to the control itself. Placing an ID of "<parentid>_<controlId>" will cause the
parent id to be chained twice.