Saturday, November 21, 2009

How events are detected and triggered by ASP.net server during Form Submit or Postback

Remember those days when we had only a single communication to the server where we had to handle all the related events? We were not having the flexibility to attach events to each and every control and use specific event handlers in the server that time. Well, we all know those days are long gone after ASP.net came in. Given the fact that the underlying communication architecture between the client browser and server remains almost the same, have you ever wondered how ASP.net managed to attach these events and call the appropriate event handler method in the server. If you're curious, go ahead with this article. We will try to understand the basics of what is going on and how we can override and use them, in case the need arises.

Here is sample asp.net page with one text box control and two server (runat="server") buttons that has server events. They are having their default properties.

Here is the designer code:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="postback.aspx.cs" Inherits="postback" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Events and Postback</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button 1" />
<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Button 2" />
</div>
</form>
</body>
</html>

Here is the server code:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class postback : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}
protected void Button1_Click(object sender, EventArgs e)
{
TextBox1.Text = "1";
}
protected void Button2_Click(object sender, EventArgs e)
{
TextBox1.Text = "2";
}
}


Here is client source code that was pushed by ASP.net:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
Events and Postback
</title></head>
<body>
<form name="form1" method="post" action="postback.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTk0MTQ1MTcxMWRkFKmiofOm3t+a1FeTyiSsorNM4sk=" />
</div>

<div>

<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWBAKWt63zDALs0bLrBgKM54rGBgK7q7GGCNoGUvTkiMJt3F6eDcqfS57GMOdl" />
</div>
<div>
<input name="TextBox1" type="text" id="TextBox1" />
<input type="submit" name="Button1" value="Button 1" id="Button1" />
<input type="submit" name="Button2" value="Button 2" id="Button2" />
</div>
</form>
</body>
</html>


Wait, here there are some additional hidden variables auto rendered by ASP.net. Let's skip that part for now and see that our buttons are rendered as submit buttons. This is because of the reason that the Button's UseSubmitBehavior property was true, which is the default setting. Here for every events like an ENTER press in the textbox or CLICK in any of the buttons, there is a submit happening to the server. The form post data already has the information regarding the events, based on which ASP.net can process and call the appropriate event associated.


Now, let's change the UseSubmitBehavior property for the first button and see what happens.

Designer code:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="postback.aspx.cs" Inherits="postback" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Events and Postback</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button 1" UseSubmitBehavior="False" />
<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Button 2" />
</div>
</form>
</body>
</html>


Server code:
[This is same as the previous server code]

Client source code that was pushed by ASP.net:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
Events and Postback
</title></head>
<body>
<form name="form1" method="post" action="postback.aspx" id="form1">
<div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTk0MTQ1MTcxMWRkFKmiofOm3t+a1FeTyiSsorNM4sk=" />
</div>

<script type="text/javascript"> 
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
//]]>
</script>


<div>

<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWBAKWt63zDALs0bLrBgKM54rGBgK7q7GGCNoGUvTkiMJt3F6eDcqfS57GMOdl" />
</div>
<div>
<input name="TextBox1" type="text" id="TextBox1" />
<input type="button" name="Button1" value="Button 1" onclick="javascript:__doPostBack('Button1','')" id="Button1" />
<input type="submit" name="Button2" value="Button 2" id="Button2" />
</div>
</form>
</body>
</html>


We can see that there is more code from server. There are few more hidden variables and some Javascript coding. The ones that we are interested in are "__EVENTTARGET", "__EVENTARGUMENT" and the "__doPostBack" function that seems to be mimicking a submit operation. This particular function is being associated to the button to which we set a false UseSubmitBehavior. The same button is now rendered as a BUTTON type. From this, we can cut down all the drama that was happening above and infer the logic as below:

ASP. net’s event handling logic:
1. Controls that are tied with a server event are rendered with a client JavaScript event associated.
2. This client code mimics a server submit operation after filling the hidden variables [Note that the name is being filled in "__EVENTTARGET" field.
3. The ASP.net server, when receives the post data, process the hidden variables to understand the event was triggered from a non-submit button and calls the method associated to the event.

Now that we've got a glimpse of what is happening, let's try to go a little deeper. By having a small code in the Page_Load event, as shown below, we can actually get these parameters. There are multiple ways to get these. Here is one:

protected void Page_Load(object sender, EventArgs e)
{
string eventControlName = Request.Params.Get("__EVENTTARGET");
string eventArguments = Request.Params.Get("__EVENTARGUMENT");

}


It may seem that there is pretty much a straight forward process and we can go ahead with our implementations thinking that this will work always. But, it doesn't. Even though pretty basic, there are some quirks and points to be noted. Below are few among them.
a. By any means if you disable this control in the client before a submit operation, submit to the server will not happen. That is, for example, if a button gets disabled on its click event, in the client, the submit will not happen at all. The good news is that, now that we know that this happens through our favorite function "__doPostBack", we can call the same from your client code, if needed.
b. If none of these controls have their visible property set to false, the server will not render with those functions and associate them. It is understandable that we don't need to do anything with an invisible control. But, in case, if you're looking for a "__doPostBack" function and you cannot find it, this may be a reason.
c. For dynamic controls with server events, these ideas will come handy, because now we now, how to identify, trigger and disable the events and its arguments.

Note: For the sake of simplicity, I haven't covered the events related to other controls and their arguments. Also, the hidden controls "__VIEWSTATE" and “__EVENTVALIDATION" does play a role in the server communication. So, if you're looking for extended information, try searching for those two. ASP.net may change this logic in future versions. Watch out for that too.

No comments:

Post a Comment