In an application I'm developing, most pages have grids and forms that I want to wrap in UpdatePanels. To simplify this I decided to place the ContentTemplate of the master page in an UpdatePanel so that all pages would get this functionality by default.
This worked fine, until I placed a FileUpload control on one of the pages. It turns out that FileUpload controls don't work with an asynchronous postback. These code snippets show how to overcome this. Read on for a more detailed explanation.
Figure 1: Add a function to the master page to register arbitrary controls as postback triggers.
public void RegisterPostbackTrigger(Control trigger)
{
mainScriptManager.RegisterPostBackControl(trigger);
}
Figure 2: Add a reference to the master page in the user control.
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Products.ascx.cs" Inherits="Controls_Admin_Products" %>
<%@ Reference VirtualPath="~/MasterPage.master" %>
Figure 3: Register the postback control as a PostBackTrigger.
protected void productsFormView_ItemCreated(object sender, EventArgs e);
{
Button InsertButton = ((FormView)sender).FindControl("InsertButton") as Button;
if (InsertButton != null)
((ASP.masterpage_master)Page.Master).RegisterPostbackTrigger(InsertButton);
}
My immediate thought was to somehow disable the UpdatePanel, but only for the posts back that use the FileUpload control. For several reasons, this turned out to be a bit tricky. Initial research showed that I could declare the control initiating the postback as a PostBackTrigger in the UpdatePanel's Triggers collection during design time. There are two problems with this:
- The UpdatePanel is defined on the master page, but the Button initiating the postback is defined inside a user control that's included in a page that uses the master page.
- In my case, the Button is in either a FormView, or a GridView and its ID isn't available during design time.
Master pages are actually implemented as child controls of the pages that use them. I knew that I could register a master page with its parent page with the @ MasterType directive, and then access members of the master page with the Master.<member> syntax. I didn't know how to do this with a user control. I found that this is done with the @ Reference directive. (see Figure 2 above)
As you can see in Figure 3, above, you access the reference from the user control with ASP.masterpage_master and use that to cast the Master property of the user control's parent page as the correct master page type. Then, the members of the master page are directly accessible to the user control.
Now that I could access the master page's members from the user control, I tried, unsuccessfully, to add the postback button to the UpdatePanel's collection of triggers using this syntax:
PostBackTrigger trigger = new PostBackTrigger();
trigger.ControlID = button.ID;
mainContentUpdatePanel.Triggers.Add(trigger);
My guess is this doesn't work because the trigger collection needs to be defined during Page_Init or earlier. After a bit more research, I found that registering the postback control with the ScriptManager is the way to go during runtime. Figure 1 shows the function I put in the master page to handle this.
The last thing was pretty straight-forward. I just needed to register the postback control as it was created in the FormView or DataGrid. To find the postback control in a FormView, I used the FormView_ItemCreated event and then ((FormView)sender).FindControl() as you can see in Figure 3. For a GridView, I would use the GridView_RowCreated event and then e.Row.FindControl() to get the postback control.