DCSIMG
November 2008 - Posts - Ran Wahle's blog

Ran Wahle's blog

November 2008 - Posts

Behind The Scene - Control ID's in ASP.Net

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)

public virtual string UniqueID
{
get
{
if (this._cachedUniqueID == null)
{
Control namingContainer = this.NamingContainer;
if (namingContainer == null)
{
return this._id;
}
if (this._id == null)
{
this.GenerateAutomaticID();
}
if (this.Page == namingContainer)
{
this._cachedUniqueID = this._id;
}
else
{
string uniqueIDPrefix = namingContainer.GetUniqueIDPrefix();
if (uniqueIDPrefix.Length == 0)
{
return this._id;
}
this._cachedUniqueID = uniqueIDPrefix + this._id;
}
}
return this._cachedUniqueID;
}
}

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:

public virtual string ClientID
{
get
{
this.EnsureID();
string uniqueID = this.UniqueID;
if ((uniqueID != null) && (uniqueID.IndexOf(this.IdSeparator) >= 0))
{
return uniqueID.Replace(this.IdSeparator, '_');
}
return uniqueID;
}
}
 

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.

kick it on DotNetKicks.com
Posted: Nov 27 2008, 01:57 AM by Ran Wahle | with 1 comment(s) |
תגים:,