Friday, January 26, 2007

ASP.NET control naming (.ClientID and .GetPostBackReference)

If you've done much development with ASP.NET, you know that all of the controls you include in your ASPX code get renamed for the resulting HTML script. For instance, "MyControl" might be converted to "ctl00$MyControl" on the client side. You probably have also read that you shouldn't assume the name of the control on the clients side if you need to access it via JavaScript. This "best practice" is intended to avoid errors resulting from Microsoft switching naming conventions in subsequent versions of the .NET framework. Thus, they provide a number of server-side calls to determine what the ID of the control will be once it gets to the client. This post is intended to point out an issue that I noticed when using MyControl.ClientID and Page.ClientScript.GetPostBackEventReference(MyControl).

MyControl.ClientID returns what you might expect, the ID that the control will have in the client HTML code.

The latter method, GetPostBackEventReference returns a string that is the client-side, JavaScript code to execute if you want to cause the page to post back targeted at the specified control. For instance, calling GetPostBackEventReference(MyControl) would return "__doPostBack('ctl00$MyControl','')". Executing this code in JavaScript causes the page to post back and attempt to execute a server-side postback event handler on MyControl. It achieves this by submitting a form value, "__EVENTTARGET", that is automatically read on the server side to determine which control caused the postback. Anyways, that's the gist of what happens. What was the point of this post again? :) Oh yeah, I remember...

I created a page with a custom control on which I wanted to be able to force postback via JavaScript. In order to implement this, I used GetPostBackEventReference(MyControl) to create the line of JavaScript code to execute and then simply called eval() on it at the appropriate time on the client-side. When postback occurs, the load event handler for the page always runs prior to any other event handlers being called. In my situation, I wanted to perform special functionality if MyControl caused the postback (versus the other controls on the page). To determine this, I decided to simply compare the value available in Page.Request.Params("__EVENTTARGET") to MyControl.ClientID. It seemed simple enough. If the two were equal, then I could assume that MyControl caused the postback. I was surprised to find out that this approach does not work. The client ID returned by GetPostBackEventReference was equal to "ctl00$MyControl" while the value returned by MyControl.ClientID was "ctl00_MyControl".

All in all, it's wasn't a major issue. A little string manipulation and the use of MyControl.ClientIDSeparator did the trick. However, I think it is resonable to expect the two values to be equal since they refer to the same control. The fact that they are not seems like a bug.

On a somewhat related note, shouldn't there be a constant for "__EVENTTARGET" somewhere in the .NET API? After all, you're not supposed to assume the client ID of the control or the postback method call. What happens if Microsoft decides to change the ID of the form element some day. It's odd that they didn't seem to take the same approach as they did with the other client values.