Tuesday, January 20, 2009

Lazy load a .Net TabContainer's TabPanel

In order to increase performance on the ASP.Net TabContainer, it is a good idea to lazy load the tabs. As of February 2009, the ASP.NET Ajax Toolkit TabContainer does not have this support. However thanks to Matt Berseth, we have a very useful article on how to lazy load a tabcontainer's tabpanels. Since the article is already very informative, I will not rewrite it here. However, I wanted to expand on the article: how to use it for user controls within a tabpanel. Here are the steps:

1. Follow the guidelines in the article.
2. Add the user controls to each tabpanel. You will see Matt's original example for the updatepanel inside the tabpanel is for grids:


   12         <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

   13             <Triggers>

   14                 <asp:AsyncPostBackTrigger ControlID="btnCustomersTrigger" />

   15             </Triggers>

   16             <ContentTemplate>

   17                 <asp:GridView ID="gvOrders" runat="server" />

   18             </ContentTemplate>

   19         </asp:UpdatePanel>



In our case, we want to lazy load a user control instead of a grid. The update panel will be as follows:


   49         <asp:UpdatePanel ID="uppCustomers" runat="server" UpdateMode="Conditional" Visible="false">

   50             <Triggers>

   51                 <asp:AsyncPostBackTrigger ControlID="btnCustomersTrigger" />

   52             </Triggers>

   53             <ContentTemplate>

   54                 <uc1:usrcustomers id="usrCustomers1" runat="server"></uc1:usrcustomers>

   55             </ContentTemplate>

   56         </asp:UpdatePanel>



Notice the code changes. We now have a user control called usrcustomers inside the uppcustomers updatepanel instead of gridview control. Marking the panel hidden (visible="false") will remove the update panel div tags completely from the code. With user controls, this will be the way we check if the panel has already been loaded. This takes us to step 3.

3. Modify the clientActiveTabChanged function as follows. This is Matt's original client side javascript function for lazy loading gridviews as part of a partial postback:


    8 

    9     <script type="text/javascript" language="javascript">

   10 

   11         function clientActiveTabChanged(sender, args) {

   12 

   13             //  see if the table elements for the grids exist yet

   14             var isTab1Loaded = $get('<%= this.gvOrders.ClientID %>');

   15             var isTab2Loaded = $get('<%= this.gvEmployees.ClientID %>');

   16 

   17             //  if the tab does not exist and it is the active tab, 

   18             //  trigger the async-postback

   19             if (!isTab1Loaded && sender.get_activeTabIndex() == 1) {

   20                 // load tab1

   21                 __doPostBack('btnCustomersTrigger', '');

   22             }

   23             else if (!isTab2Loaded && sender.get_activeTabIndex() == 2) {

   24                 // load tab2

   25                 __doPostBack('btnEmployeesTrigger', '');

   26             }

   27         }

   28     </script>





Here is the modified version. We will now check if the tab has loaded by update panel visibility instead of gridview table status:


    9     <script type="text/javascript" language="javascript">

   10 

   11         function clientActiveTabChanged(sender, args) {

   12 

   13             // see if the objects exist yet

   14             var isTab1Loaded = $get('<%= this.uppCustomers.ClientID %>');

   15             var isTab2Loaded = $get('<%= this.uppProducts.ClientID %>');

   16 

   17 

   18             // if the tab does not exist and it is the active tab,

   19             // trigger the async-postback

   20             if (!isTab1Loaded && sender.get_activeTabIndex() == 1) {

   21                 // load tab1

   22                 __doPostBack('<%= this.btnCustomersTrigger.UniqueID %>', '');

   23             }

   24             else if (!isTab2Loaded && sender.get_activeTabIndex() == 2) {

   25                 // load tab2

   26                 __doPostBack('<%= this.btnProductsTrigger.UniqueID %>', '');

   27             }

   28 

   29         }

   30     </script>



Again, notice the changes, the code is now checking for the existence of the update panel instead of a gridview. Also the postback code now uses UniqueID to make sure we find the correct buttons.

4. Handle the hidden button events from the partial postback. As Matt points out in his article, create the buttons for the postback as follows:


   36         <input id="btnCustomersTrigger" runat="server" type="button" style="display:none" onserverclick="btnCustomersTrigger_Click" />


Setting up the buttons will be the same for user controls. You will need to setup one button per TabPanel. The only difference is when you handle the button click event:


   15     protected void btnCustomersTrigger_Click(object sender, EventArgs e)

   16     {

   17         // Mark the update panel as visible

   18         uppCustomers.Visible = true; //this is how we will know tab is loaded

   19         // Setup Customers User Control

   20         usrCustomers1.customerId = customerId;

   21         usrCustomers1.initCustomer(); //here you could be doing custom sql calls

   22         //setting up a formview, etc

   23     }




In this function you first mark the update panel as visible. This will tell our javascript function clientActiveTabChanged that the control/tab has already been loaded and not try to reload the tab again the second time it is selected. Finally you would call methods/properties from your user control to setup the details of the user control.

One thing to keep in mind is when you the above technique, the form_load event for all the user controls in all the tabs will still execute when the tabcontainer control is first loaded and on the first time any tab is clicked. Having this in mind, make sure you are not setting up your user control from the form_load event, rather create a method such as initCustomer mentioned in the above example. Hopefully we will not have to do all this extra work once Microsoft changes the tabcontainer control to only load the active tab on a partial postback.

Adiel

2 comments: