Better algorithm for finding UpdatePanel that will be updated during page request.

Other day I was experimenting with finding which UpdatePanel will be updated during page post back.

I found out that my solution was not working correctly for complex triggers, like user controls with custom events, which will trigger async postbacks and update of panel.

Or for grids with buttons. It was big issue. Also it was not pointing to correct UpdatePanel if name of control was not entirely unique (for example when you have multiple same custom controls on page). It would return first panel, even if it wasn’t updating.

So here is updated code:

        public static UpdatePanel FindAsyncPostBackUpdatePanel(this Page page)
        {
            var scriptManager = ScriptManager.GetCurrent(page);
            var pageRequestMngr = scriptManager.GetNonPublicProperty<object>("PageRequestManager");
            var updatePanels = pageRequestMngr.GetNonPublicField<List<UpdatePanel>>("_allUpdatePanels");
            var source = page.FindControl(scriptManager.AsyncPostBackSourceElementID);
            UpdatePanel parentUp = null;
            //check if is child of update parent with children as triggers
            Control parent = source.Parent;
            while (parent != null && parentUp == null)
            {
                parent = parent.Parent;
                parentUp = parent as UpdatePanel;
                if (parentUp != null && (CheckIfPostbackSourceInTriggers(source, parentUp) || parentUp.ChildrenAsTriggers
                    || (parentUp.IsInPartialRendering || parentUp.GetNonPublicProperty<bool>("RequiresUpdate"))))
                {
                    break;
                }
                parentUp = null;
            }
            if (parentUp != null) return parentUp;
            foreach (var up in updatePanels)
            {
                if (CheckIfPostbackSourceInTriggers(source, up))
                    return up;
            }
            return null;
        }

        private static bool CheckIfPostbackSourceInTriggers(Control source, UpdatePanel up)
        {
            foreach (var trigger in up.Triggers)
            {
                var t = trigger as AsyncPostBackTrigger;
                if (t == null)
                {
                    continue;
                }
                if (t.GetNonPublicField<Control>("_associatedControl").UniqueID == source.UniqueID)
                {
                    return true;
                }
            }
            return false;
        }

What have changed?

1. I added code for traversing parent controls of our source control. Most of the time control that is causing post back is children of UpdatePanel. Such a loop is much quicker then finding out all of UpdatePanels, and checking its triggers.

Also if grid with some grid-specific event caused UpdatePanel to update, this grid probably will be child of UpdatePanel and so will be button from that grid. Still it’s not ideal. If you have grid with some entities (customers for example), and you want to clicking at some customer will open it’s edit view in UpdatePanel in some other part of page. Source and UpdatePanel are not in child-parent relation.

If source is child of UpdatePanel method will check if ChildrenAsTriggers is true – panel will be updated cause it’s triggered by child.

If IsInPartialRendering flag and RequiresUpdate, after some test I can say that that this flags indicates if UpdatePanel will be updated. Interesting thing that first flag was not true even if given UpdatePanel was indeed updating. I don’t know why. But second flag non-public property was true in those situations.

2. Checking only for control Id was not reliable since it doesn’t must be unique for page. That’s why I am checking for unique id, of non-public field with trigger control instance and source unique id. That have to give correct answer only for one control, but is much slower since is using reflection.

That’s it for now but I feel that I will have to revise that code once again soon 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

Solve : *
8 + 22 =