This is a nightmarish combination to deal with in ASP.NET 3.5. I will attempt to document what I learned today about how to get this combination working.
Here is the scenario: Start with a GridView and a bunch of templated columns. The first four columns of the GridView contain labels with magnifying glass icons next to them, and the rest of the columns are just TextBoxes. The idea is that when you click the magnifying glass, a “popup” window appears that lets you pick from a list of choices. It’s just like a DropDownList, except we can’t use a DropDownList in this case because the descriptive text of each choice could be huge. Each selection affects the list that appears in the next column over, so the selections “cascade.” We don’t want any postback inside the grid to redraw the full page.
The grid looks something like this, with anything that my boss might consider sensitive fuzzed out (not that it’s the slightest bit sensitive):
I won’t get into the details of binding data to the grid and all the controls inside the grid; that’s a whole different subject (in brief – don’t put the GridView.DataBind call in Page_Load if you want to see any events from controls inside the grid). I’ll just focus on the AJAX functionality.
Here is the template for the first column (oops, I guess you know it’s a Task column now):
<asp:UpdatePanel ID="updateTaskPanel" runat="server" UpdateMode="Always">
<asp:Label ID="lblTask" runat="server" />
<asp:Image ID="imgTask" runat="server" ImageUrl="~/Images/Hourglass.png" />
<cc1:PopupControlExtender ID="pceTask" runat="server"
<asp:Panel ID="popupPanelTask" runat="server" CssClass="modalPopup" style="display:none;">
<asp:UpdatePanel ID="updatePopupTaskPanel" runat="server">
<asp:Label ID="lblTaskPopup" runat="server">Title</asp:Label>
<asp:RadioButtonList ID="radioTasks" runat="server"
DataTextField="DisplayLabel" DataValueField="Id" />
<asp:Button ID="btnPopupTaskOkay" runat="server" Text="OK"
OnClick="btnPopupTaskOkay_Click" UseSubmitBehavior="false" />
<asp:Button ID="btnPopupTaskCancel" runat="server" Text="Cancel"
OnClientClick='AjaxControlToolkit.PopupControlBehavior.__VisiblePopup.hidePopup(); return false;'
<ItemStyle Wrap="false" />
There are several important things to note:
- There are two ContentTemplates; one for the contents of the column and one for the contents of the popup panel. I believe both are necessary to avoid a full-page refresh.
- The outer UpdatePanel is set to UpdateMode=”Always”. I found this necessary because changing the value of one column affected what was in the other columns.
- The Image (aka. TargetControlID) and the PopupControlExtender are both in the same UpdatePanel. That’s a requirement.
- The popup Panel is also inside the ItemTemplate. I needed to do this because each row could have different popup contents. If they were all the same, I believe it could have been moved entirely outside the GridView.
- There is a style=”display:none;” attribute on the popup Panel. That is necessary to avoid seeing the panel flash for a second when the page first loads. (Don’t set Visible=”false”.)
- Both Buttons in the popup panel have the attribute UseSubmitBehavior=”false”. I had all kinds of problems without those.
- The Cancel button has the attribute OnClientClick=’AjaxControlToolkit.PopupControlBehavior.__VisiblePopup.hidePopup(); return false;’. This causes the popup to close with a client-side call, rather than posting back to the server. This results in much faster performance.
Here is the code-behind for one of the Okay buttons in a popup Panel:
protected void btnPopupTaskOkay_Click(object sender, EventArgs e)
Button btn = (Button)sender;
GridViewRow row = (GridViewRow)btn.NamingContainer;
RadioButtonList rbl = (RadioButtonList)row.FindControl("radioTasks");
TaskRowView dataItem = this.editingRows[row.RowIndex];
dataItem.TaskId = int.Parse(rbl.SelectedValue);
dataItem.IsModified = true;
PopupControlExtender pce = AjaxControlToolkit.PopupControlExtender.GetProxyForCurrentPopup(Page);
The important part is the Cancel() at the end. That is what actually dismisses the popup. If you don’t do that, the popup will just sit there on the screen.
One other important thing to note about UpdatePanel if you don’t already know: Even with a partial-page refresh, during the round-trip to the server, the entire page is generated, even though only a subset of the page is sent back to the client. But keep in mind that all the Page events are fired and all the control events are fired, even if they’re outside the UpdatePanel being refreshed. That means you need to be careful in tuning the performance of partial page renders. For example, if your Page_Load event does a time-intensive database load or something, it will occur on every asynchronous refresh and performance will suffer perhaps more than necessary.