Tuesday, February 16, 2010

How to determine which Control caused PostBack on ASP.NET page?

Very often we have multiple parts of page that can submit some user information to the server.

We can easily divide parts of our WebForm with ValidationGroups intological parts that we can validate before the data is submitted.

But if we have on our page more than one ASP.Net Control that can causepostback to the server (for example if we have multiple Buttons,DropDownLists, ImageButtons etc) how can we determine which one of themuser clicked to submit the page to the server?

To precisely determine this important bit of information we need adetailed overview on what happens when the WebForm is submitted to theserver:

If you lookup the HTML code that ASP.Net generates when you put Form on your page you will find hidden input field named
"__EVENTTARGET"

This is where ASP.Net stores the name of the Control that initiated the postback of the WebForm.

Having the name of the control, we can easily get the reference to theinstance of that control via FindControl method of the Page object.

But not always :)

There are two exceptions to this simple rule: when Button and ImageButton controls are clicked on the form.

We need to handle those two cases differently:

Handling the Button control:

If a Button control was clicked our field "__EVENTTARGET" is not assigned.

The only thing we can do to find out the name of the control thatsubmitted the form is to loop trough all the names of the variables inPage.Request.Form collection that is filled for us, and the only onethat is the instance of ASP.Net Button class is the Button that causedthe postback. This is becasue if Button is causing the postback, theonly Button control value that is sent to the server is the one thatcaused the postback.
Other Button controls are simply ignored and not sent to the server. Is that cool or what?

Handling the ImageButton control:

If image button control was clicked to submit the form, very similar sequence of events happen:

Only the ImageButton control that caused the postback is sent to the server, but with a slight difference:

Instead of one variable in this case two form variables are created forImageControl that caused the postback (and stored in Page.Request.Formcollection).

First variable is called the same as ImageControl only with ".x" suffixand the value of that form variable is the relative horizontal positionof the mouse pointer inside the ImageControl at the time when it wasclicked.

Second variable that is created is named same as the ImageControl but with suffix ".y" and its value is the relative vertical position of the mouse pointer inside the ImageControl at the time when it was clicked.

So if user clicked the top left corner of the ImageControl these variables will both have values 0.

For us it is important to know that first variable submitted that has suffix of ".x" or ".y" isthe name of the ImageButton control that caused postback (with suffix)and then we can easily strip the suffix to get the actual name of theImageButton control.

Note: this will only work if you dont name some other controls on the WebForm with ".x" or ".y" suffix so be careful with that!

Ok here is the method that we can use to retrieve the instance of the control that caused the postback:

    public static Control GetPostBackControl(Page page)
    {
        Control postbackControlInstance = null;

        string postbackControlName = page.Request.Params.Get("__EVENTTARGET");
        if (postbackControlName != null && postbackControlName != string.Empty)
        {
            postbackControlInstance = page.FindControl(postbackControlName);
        }
        else
        {
            // handle the Button control postbacks
            for (int i = 0; i < page.Request.Form.Keys.Count; i++)
            {
                postbackControlInstance = page.FindControl(page.Request.Form.Keys[i]);
                if (postbackControlInstance is System.Web.UI.WebControls.Button)
                {
                    return postbackControlInstance;
                }
            }
        }
        // handle the ImageButton postbacks
        if (postbackControlInstance == null)
        {
            for (int i = 0; i < page.Request.Form.Count; i++)
            {
                if ( (page.Request.Form.Keys[i].EndsWith(".x")) || (page.Request.Form.Keys[i].EndsWith(".y")))
                {
                    postbackControlInstance =page.FindControl(page.Request.Form.Keys[i].Substring(0,page.Request.Form.Keys[i].Length-2) );
                    return postbackControlInstance;
                }
            }
        }
        return postbackControlInstance;
    }   

And here is an example on how to use that method in our Page:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            Control cause = GetPostBackControl(Page);
            // use the control, typecast it etc
        }
    }

No comments: