Introduction
The other day, I was experimenting with finding which UpdatePanel will be updated during page postback.
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 a big issue. Also it was not pointing to the correct UpdatePanel
if name of the control was not entirely unique (for example, when you have multiple same custom controls on page). It would return the first panel, even if it wasn't updating.
So here is the 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;
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 has Changed?
- 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 than finding out all of UpdatePanel
s, 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 click at some customer
will open its 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 because it's triggered by child.
If IsInPartialRendering
flag and RequiresUpdate
, after some test I can say that that this flag indicates if UpdatePanel
will be updated. The interesting thing is that the first flag was not true
even if given UpdatePanel
was indeed updating. I don't know why. But the second flag non-public property was true
in those situations.
- Checking only for control Id was not reliable since it doesn't have to 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 has to give the correct answer only for one control, but is much slower since it is using reflection.
That's it for now, but I feel that I will have to revise that code once again soon. 
CodeProject