Monday, May 29, 2006

UseSubmitBehavior & GetPostBackEventReference

funny thing…

I needed a button (in an ASP.NET 2.0 page) to post-back and raise an event for another control. That means that I click a button but the post-back event is not handled by the button clicked but instead by another control on the same page.

The easy way was to add a System.Web.UI.WebControls.Hyperlink and set its NavigateUrl property to Page.ClientScript.GetPostBackClientHyperlink(myOtherControl, "Arguments")
But I wanted it to look like a Button so instead I added a System.Web.UI.HTMLControls.HTMLButton Control and did the same thing to its OnClientClick property.
All worked fine.

The funny thing happened when I decided that I like most to use a System.Web.UI.WebControls.Button instead.

ASP.NET Buttons are rendered as <input type="submit" /> controls. and by default their client-side onClick event is wired to the __doPostBack javascript method by ASP.NET.
To change that behavior in ASP.NET 2.0 we are supposed to use the UseSubmitBehavior Property which when set to false causes the Button to be rendered as <input type="button" /> and allows you to set the client-side onClick event programmatically (or declaratively) by setting the OnClientClick server-side Property.

That's what I did, only to discover that to whatever I was assigning to the OnClientClick Property, the original __doPostBack call was automatically appended by the framework.
I did not spend all the time in the world to figure this out and as I failed to find a reasonable way out of the problem, what I did, was to trick the client-side out of it by appending a JavaScript “return;” statement to the script I was assigning to OnClicntClick.

To make all these more obvious here is the hands on:
in the aspx/ascx file:

Notice that lines 5 and 6 above define the Hyperlink and HTMLButton that worked fine and line 7 defines the WebControls.Button that did not behave. Lines 1 to 3 define a DetailsView just because that is the control I wanted to handle the post-back event.

Now in the code file:

These get rendered as follows at run-time (if you look at the source in the browser):

This works just fine if you click on the Hyperlink1 or Button1 controls, but not at all if you clicked at Button2. That is because Button2 makes the call to __doPostBack twice. Once because I asked it to do so (by assigning its OnClientClick Property as you see in line 4 of the code-file above) and once because the framework automatically inserted the default post-back event reference for the button (Button2) itself.

To overcome this behavior (as I could not find another way around it) I just changed the last line in the code-file (line 4 in the snippet) by appending a javascript return statement as follows:

This caused Button2 to render as follows:

Notice the return statement (in bold) rendered between the two calls to __doPostBack. Although this does not prevent the second __doPostBack call from beeing inserted by the framework, it does prevent it from executing….

If you know a proper way out of this little puzzle  please let me know….

  1. As you obviously noticed I needed the button to create an Insert button that would enable a DetailsView to go to Insert Mode even if the view has no items to display. The button (when clicked) causes the DetailsView to receive an Insert Command.
  2. Of course you fortunately do not need to do all this to make a details view to enter Insert Mode. It would be enough to just and any button with a server-side Click event handler changing the mode to Insert by just calling DetailsView1.ChangeMode(DetailsViewModes.Insert);