[Reprinted] Answer the question in mantalk ID: the source of UniqueID and ClientID

Source: Internet
Author: User
Tags repetition

 

In the article "mantalk ID", the author raised a question: why does access ClientID in the ItemCreated event cause MyButton to fail to respond to the event, in fact, MyButton cannot respond to the event because its client ID has been changed. This article starts with UniqueID and ClientID and explores in depth how UniqueID and ClientID are generated, when to generate and answer the questions of the author in "mantalk ID.

Why does UniqueID and ClientID exist?

I think most people who use WebForm already know this, because WebForm has data binding controls, custom controls, View controls, and other controls that can have subcontrols, as a result, two objects with identical IDs may exist in the control tree. However, because HTML does not allow the repetition of IDS, WebForm creates a thing called NamingContainer, the controls are provided with the UniqueID and ClientID attributes to distinguish them from each other.

Although these two attributes often cause us such troubles, in general, this is a good design and is understandable.

How to calculate UniqueID and ClientID

First, you need to understand that all the controls in WebForm Form A tree. Each control is a node in the tree.

In a tree structure, each node has zero or one parent node, while UniqueID and ClientID calculate UniqueID Based on the path from the root of the tree to the current node, let's take a look at the following example:

<asp:Repeater ID="MyRepeater" runat="server">
<ItemTemplate>
<asp:Button ID="MyButton" runat="server" Text="My Button" /></ItemTemplate>
</asp:Repeater>

We put MyButton in MyRepeater, and the final HTML of this MyButton generated by such simple content is as follows:

<input id="MyRepeater_ctl00_MyButton" type="submit" value="My Button" name="MyRepeater$ctl00$MyButton"/>

Please note that the button ID is changed to MyRepeater_ctl00_MyButton, And the Name is changed to MyRepeater $ ctl00 $ MyButton. According to the description in "mantalk ID", this ID is exactly the ClientID of the server control, the Name is exactly the UniqueID of the server-side control.
What we want to explore now is why the ID is like this. So we can understand it literally and cannot see it:

  1. In this ID, there is "MyRepeater", which is the server ID of the parent node element Repeater of MyButton.
  2. There is "ctl00" in the middle. According to our programming habits, ctl should indicate the meaning of control, and 00 is naturally an index, it indicates that "This MyButton is the 1st elements in the list generated by Repeater (subscript is 0 )"
  3. The last line is "MyButton", which is exactly the server ID of MyButton.

Now, after the ID is decomposed, we can clearly understand the generation policy of ClientID:

The method for generating ClientID is to add the index of this control in data binding (if any) before the control's ID and the ClientID of the parent Control

So how can this be done? Here we propose the concept of NamingContainer. For NamingContainer, we only need to implement one marking interface, INamingContainer.
When the control is calculating ClientID, it will find whether the NamingContainer attribute of the current control is null. If not, it will call the GetUniqueIDPrefix method of the NamingContainer. Of course, this method is recursive, in the method body, another GetUniqueIDPrefix method is called.
As for how the NamingContainer attribute comes from, it is actually calling Controls. when the Add method is added, you can use the decompilation to check the implementation of a Control class named AddedControl, which will not be described here.

Back to question

Now let's go back to the question in the article "mantalk ID". Why does calling ClientID in the ItemCreated event of the DataGrid Control affect future execution.

What puzzles the author is that he only obtains the ClientID without making any changes to it. The author believes that it does not affect the status of the Button, so the Button should be rendered in its normal way.

Here I have to talk about a problem. Many people think that reading the attribute is just a simple return. They didn't think that they could perform a lot of logic in the get body of the attribute, these logics may affect the object state, and I think it is this "take it for granted" that the author did not carefully look at what happened to the Button in the ClientID get process. Let me give a rough look at this idea.

First, I decomcompiled the ClientID attribute to see the content of his get body. The Code is as follows:

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;
}
}

We can see that the get of ClientID is far more than return this. _ clientID; the fact is that the control does not save the current ClientID at all, but calculates the value from the UniqueID every time it is obtained, and returns the delimiter (that is, $) in the UniqueID) replace it with an underscore (_) and return it.

After carefully reading the code, we will find that the EnsureID method is called first, which is like the EnsureChildControls method we call when customizing the control, ensureID checks whether the ID of the current control has been generated. If the ID is not generated, a policy is used to generate it. The following is the code of the EnsureID method:

protected void EnsureID()
{
if (this._namingContainer != null)
{
if (this._id == null)
{
this.GenerateAutomaticID();
}
this.flags.Set(0x800);
}
}

We can see that NamingContainer is used in the EnsureID method. This method works only when NamingContainer is not null.
Why? The result does not involve the calculation of UnqiueID in the EnsureID method. Does it feel cheated?
But in this case, what affects ClientID? Let's take a closer look at the ClientID's get method body. In the end, we can only say that the UniqueID attribute is playing a role.
Yes. Like ClientID, UniqueID is not a simple attribute. It also contains a large number of logics in get. The following are the Logics:

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;
}
}

I think the code is very clear. If NamingContainer is null, UniqueID will only return the ID of the current control, otherwise, the NamingContainer's GetUniqueIDPrefix method will be used to calculate the ID of the format mentioned at the beginning of this article and return it.
Now, we have analyzed the generation process of ClientID. Here we will summarize:

  1. ClientID is formed by replacing the UniqueID with a simple string.
  2. UniqueID is formed through NamingContainer

Now, I think the author of "mantalk ID" understands why accessing ClientID IN THE ItemCreated event causes the final two buttons on the HTML page to be called "MyButton ", if you do not understand, go to the breakpoint to debug the ItemCreated event and see what the NamingContainer of MyButton is.
What do you think is the NamingContainer of MyButton null? So you have never debugged it ~~

In the ItemCreated event, the NamingContainer of MyButton is DataGridItem, but the problem is that
DataGridItem is not added to the DataGrid at this time (it is added to the DataGrid only in the ItemDataBound event ),
Therefore, the NamingContainer of the DataGridItem is null.
The GetUniqueIDPrefix method of the DataGridItem needs to know its index in the DataGrid,
To return a format similar to ctl00. Since it is not added to the DataGrid, The DataGridItem can only return one empty string.
MyButton Concatenates the null string returned by the DataGridItem with its own ID to form a UniqueID. At this time, the UniqueID is exactly its own ID.
In addition, MyButton saves the UniqueID and will not re-calculate it again in future access. Therefore, this nonstandard UniqueID
It will always be used as HTML.
Summary

I found that my expression skills are really poor. I don't know if I have explained this question clearly. In a word:

  1. Access to the ClientID attribute directly causes the control to calculate the UniqueID.
  2. Calculating the UniqueID when it is not appropriate may lead to incorrect results
  3. You must ensure that the control is connected to the root of the Control tree on the current page before accessing ClientID.
  4. . NET has many attributes that are not simple return and change the object state. This is often the starting point to solve the problem.

Reproduced from: http://www.cnblogs.com/GrayZhang/archive/2009/03/05/how-uniqueid-is-generated.html

 

 

In the article "mantalk ID", the author raised a question: why does access ClientID in the ItemCreated event cause MyButton to fail to respond to the event, in fact, MyButton cannot respond to the event because its client ID has been changed. This article starts with UniqueID and ClientID and explores in depth how UniqueID and ClientID are generated, when to generate and answer the questions of the author in "mantalk ID.

Why does UniqueID and ClientID exist?

I think most people who use WebForm already know this, because WebForm has data binding controls, custom controls, View controls, and other controls that can have subcontrols, as a result, two objects with identical IDs may exist in the control tree. However, because HTML does not allow the repetition of IDS, WebForm creates a thing called NamingContainer, the controls are provided with the UniqueID and ClientID attributes to distinguish them from each other.

Although these two attributes often cause us such troubles, in general, this is a good design and is understandable.

How to calculate UniqueID and ClientID

First, you need to understand that all the controls in WebForm Form A tree. Each control is a node in the tree.

In a tree structure, each node has zero or one parent node, while UniqueID and ClientID calculate UniqueID Based on the path from the root of the tree to the current node, let's take a look at the following example:

<asp:Repeater ID="MyRepeater" runat="server">
<ItemTemplate>
<asp:Button ID="MyButton" runat="server" Text="My Button" /></ItemTemplate>
</asp:Repeater>

We put MyButton in MyRepeater, and the final HTML of this MyButton generated by such simple content is as follows:

<input id="MyRepeater_ctl00_MyButton" type="submit" value="My Button" name="MyRepeater$ctl00$MyButton"/>

Please note that the button ID is changed to MyRepeater_ctl00_MyButton, And the Name is changed to MyRepeater $ ctl00 $ MyButton. According to the description in "mantalk ID", this ID is exactly the ClientID of the server control, the Name is exactly the UniqueID of the server-side control.
What we want to explore now is why the ID is like this. So we can understand it literally and cannot see it:

  1. In this ID, there is "MyRepeater", which is the server ID of the parent node element Repeater of MyButton.
  2. There is "ctl00" in the middle. According to our programming habits, ctl should indicate the meaning of control, and 00 is naturally an index, it indicates that "This MyButton is the 1st elements in the list generated by Repeater (subscript is 0 )"
  3. The last line is "MyButton", which is exactly the server ID of MyButton.

Now, after the ID is decomposed, we can clearly understand the generation policy of ClientID:

The method for generating ClientID is to add the index of this control in data binding (if any) before the control's ID and the ClientID of the parent Control

So how can this be done? Here we propose the concept of NamingContainer. For NamingContainer, we only need to implement one marking interface, INamingContainer.
When the control is calculating ClientID, it will find whether the NamingContainer attribute of the current control is null. If not, it will call the GetUniqueIDPrefix method of the NamingContainer. Of course, this method is recursive, in the method body, another GetUniqueIDPrefix method is called.
As for how the NamingContainer attribute comes from, it is actually calling Controls. when the Add method is added, you can use the decompilation to check the implementation of a Control class named AddedControl, which will not be described here.

Back to question

Now let's go back to the question in the article "mantalk ID". Why does calling ClientID in the ItemCreated event of the DataGrid Control affect future execution.

What puzzles the author is that he only obtains the ClientID without making any changes to it. The author believes that it does not affect the status of the Button, so the Button should be rendered in its normal way.

Here I have to talk about a problem. Many people think that reading the attribute is just a simple return. They didn't think that they could perform a lot of logic in the get body of the attribute, these logics may affect the object state, and I think it is this "take it for granted" that the author did not carefully look at what happened to the Button in the ClientID get process. Let me give a rough look at this idea.

First, I decomcompiled the ClientID attribute to see the content of his get body. The Code is as follows:

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;
}
}

We can see that the get of ClientID is far more than return this. _ clientID; the fact is that the control does not save the current ClientID at all, but calculates the value from the UniqueID every time it is obtained, and returns the delimiter (that is, $) in the UniqueID) replace it with an underscore (_) and return it.

After carefully reading the code, we will find that the EnsureID method is called first, which is like the EnsureChildControls method we call when customizing the control, ensureID checks whether the ID of the current control has been generated. If the ID is not generated, a policy is used to generate it. The following is the code of the EnsureID method:

protected void EnsureID()
{
if (this._namingContainer != null)
{
if (this._id == null)
{
this.GenerateAutomaticID();
}
this.flags.Set(0x800);
}
}

We can see that NamingContainer is used in the EnsureID method. This method works only when NamingContainer is not null.
Why? The result does not involve the calculation of UnqiueID in the EnsureID method. Does it feel cheated?
But in this case, what affects ClientID? Let's take a closer look at the ClientID's get method body. In the end, we can only say that the UniqueID attribute is playing a role.
Yes. Like ClientID, UniqueID is not a simple attribute. It also contains a large number of logics in get. The following are the Logics:

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;
}
}

I think the code is very clear. If NamingContainer is null, UniqueID will only return the ID of the current control, otherwise, the NamingContainer's GetUniqueIDPrefix method will be used to calculate the ID of the format mentioned at the beginning of this article and return it.
Now, we have analyzed the generation process of ClientID. Here we will summarize:

  1. ClientID is formed by replacing the UniqueID with a simple string.
  2. UniqueID is formed through NamingContainer

Now, I think the author of "mantalk ID" understands why accessing ClientID IN THE ItemCreated event causes the final two buttons on the HTML page to be called "MyButton ", if you do not understand, go to the breakpoint to debug the ItemCreated event and see what the NamingContainer of MyButton is.
What do you think is the NamingContainer of MyButton null? So you have never debugged it ~~

In the ItemCreated event, the NamingContainer of MyButton is DataGridItem, but the problem is that
DataGridItem is not added to the DataGrid at this time (it is added to the DataGrid only in the ItemDataBound event ),
Therefore, the NamingContainer of the DataGridItem is null.
The GetUniqueIDPrefix method of the DataGridItem needs to know its index in the DataGrid,
To return a format similar to ctl00. Since it is not added to the DataGrid, The DataGridItem can only return one empty string.
MyButton Concatenates the null string returned by the DataGridItem with its own ID to form a UniqueID. At this time, the UniqueID is exactly its own ID.
In addition, MyButton saves the UniqueID and will not re-calculate it again in future access. Therefore, this nonstandard UniqueID
It will always be used as HTML.
Summary

I found that my expression skills are really poor. I don't know if I have explained this question clearly. In a word:

  1. Access to the ClientID attribute directly causes the control to calculate the UniqueID.
  2. Calculating the UniqueID when it is not appropriate may lead to incorrect results
  3. You must ensure that the control is connected to the root of the Control tree on the current page before accessing ClientID.
  4. . NET has many attributes that are not simple return and change the object state. This is often the starting point to solve the problem.

Reproduced from: http://www.cnblogs.com/GrayZhang/archive/2009/03/05/how-uniqueid-is-generated.html

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.