The generated HTML code for my custom ASP.net server control generates the name attribute for child controls, instead of the id attribute. Something like this :
<span id="GridView2_ctl02_editdis">
<input type="text" name="GridView2$ctl02$editdis$ctl00"/>
</span>
The ID for the custom control itself is apparently proper.
What is even stranger for me, is that the ID does get generated sometimes (I do not know under what conditions). But a FindControl()
with that ID returns null on the server side. FindControl()
with the value of the name attribute works just fine.
Something like this :
<span class="TextBox" id="GridView2_ctl02_editdis">
<input type="text" id="GridView2_ctl02_editdis_ctl00" name="GridView2$ctl02$editdis$ctl00"/>
</span>
For the above, FindControl("GridView2$ctl02$editdis$ctl00")
works fine, FindControl("GridView2_ctl02_editdis_ctl00")
doesn't.
How do I ensure consistent and predictable IDs?
They are consistent.
Internally, controls that are children of a naming control (i.e. GridView), have their full IDs built by appending IDs of their parents using "$". In case of a grid, it's gridID$rowID$cellID$mycontrolID. This is necessary to differentiate between multiple instances of the same child control (i.e. mycontrolID). Why "$" and not "_"? I guess because many people already tend to name their controls "my_control_something" and the "$" symbol is as good as any.
So, the GridView2$ctl02$editdis$ctl00 is the right ID and that's why it's used as a name for controls such as INPUT. When post back occurs, the framework needs to be able to match form keys with appropriate controls.
The confusion with IDs, I think, comes from the fact that an ID you are using inside .aspx and the ID you see in HTML are two different things. The client-side IDs are just that. For whatever reason, when a control gets rendered (using ClientID property), all "$" get replaced with "_". My guess is that this was done to make it javascript/css friendly.
Now, about that FindControl("GridView2$ctl02$editdis$ctl00")... You really should try avoiding it whenever possible. FindControl is a recursive function, which breaks "GridView2$ctl02$editdis$ctl00" into "GridView2" and "ctl02$editdis$ctl00", finds GridView2 and asks if it has the "ctl02$editdis$ctl00" as a child control. The process repeats for each part separated by $.
On a side note, whenever you find yourself calling Page.FindControl for some deeply buried control, you need to examine the pattern and ask why. For instance, whatever needs to be done with the "GridView2$ctl02$editdis$ctl00", most likely needs to be done with a "GridView2$ctl02$editdis$ctl01" as well. In that case, it probably needs to be handled on OnItemCreated or on OnItemDataBound, where you have access to a row who "knows" about "ctl00".