Tips and Tricks: ASP.NET AJAX 1.0 and User Controls
 
Published: 05 Jun 2007
Abstract
In this article Bilal Haidar will explore with you the different ways in which you can integrate ASP.NET AJAX 1.0 UpdatePanels into your Web application that makes extensive use of UserControls.
by Bilal Haidar
Feedback
Average Rating: 
Views (Total / Last 10 Days): 51963/ 43

Introduction

Recently, I have started working on the ASP.NET AJAX 1.0 add-on and so many questions were coming to my mind on the best practices to apply when adding the AJAX taste into currently existing Web applications or even with the new ones.

Using the server-side part of the AJAX 1.0 library, which includes the ScriptManager, UpdatePanel, Timer, and UpdateProgess controls, is straight forward and a matter of just dragging these controls onto the page and that is it! However, it is not always that easy and clear when it comes with working with MasterPages and AJAX or User Controls and AJAX.

In this article we are going to illustrate to you several ways on how you can use the UpdatePanel together with user controls, whether to include UpdatePanel within the UserControl itself or simply embed a UserControl inside an UpdatePanel on the host page.

We will start by developing a simple ASP.NET Web application in which we have two UserControls, mainly the EmployeeList and OrderList UserControls, with the old post-back model. After that we explore the different ways of adding AJAX into the application.

In a future article, hopefully, we will illustrate the best practices on how to use the AJAX 1.0 library with the MasterPages in ASP.NET 2.0.

Requirements

Before you start reading on with this article, make sure you have the following applications and add-ons installed on your machine.

·         Visual Studio Professional 2005

·         Microsoft SQL Server Standard Edition

·         Microsoft ASP.NET 2.0 AJAX Extensions 1.0

·         Internet Information System 5.0, 6.0, 7.0

The above were used to develop the sample downloadable application. However, you can also use the Visual Web Developer 2005 Express and Microsoft SQL Server 2005 Express Editions to run the example that we will be developing in this article. To download both you can visit the following URLs.

Visual Web Developer 2005 Express Edition

Microsoft SQL Server 2005 Express Edition

Microsoft ASP.NET 2.0 AJAX Extensions 1.0

Life without AJAX

To start with we will create a new ASP.NET AJAX-Enabled Web Site by selecting a template that has been added to your Visual Studio Templates upon the installation of the Microsoft ASP.NET 2.0 AJAX 1.0 Extensions. You can refer back to Figure 1 below to see how to select the right template.

Figure 1: Selecting ASP.NET AJAX-Enabled Web Site template

The reason behind creating a new website based on the above template is that the ASP.NET Web Application’s configuration file will have all the sections required to run AJAX successfully.  This way you will save some time trying to add all the seconds required.

In this first version of the application, we will develop two User Controls:

EmployeeList.ascx

OrderList.ascx

Before discussing the functionality of the above two UserControls, you will need to have on whatever version of Microsoft SQL Server that you are using and the Northwind database that was part of the Microsoft SQL Server 2000. If you already do not have it, you can visit the URL below and download it from there.

Northwind and pubs Sample Databases for SQL Server 2000

The EmployeeList.ascx UserControl will show a GridView listing all of the employees found in the Employees data table present inside the Northwind database.

The OrderList.ascx UserControl will show the Orders done by a specific Employee upon clicking on an employee record in the first UserControl’s GridView.

The EmployeeList UserControl will include a custom event called RowSelected. The host page will subscribe to this event so that it will be able to force a refresh on the OrderList, UserControl and show the orders done by the Employee selected.

The event is defined inside the EmployeeList UserControl.

Listing 1

// Define my event using the new Generic EventHandler delegate
private static readonly object EventRowSelected = new object();
public event EventHandler < RowSelectedEventArgs > RowSelected
{
  add
  {
    base.Events.AddHandler(EventRowSelected, value);
  }
  remove
  {
    base.Events.RemoveHandler(EventRowSelected, value);
  }
}
 
protected virtual void OnRowSelected(RowSelectedEventArgs e)
{
  EventHandler < RowSelectedEventArgs > handler = (EventHandler <
    RowSelectedEventArgs > )base.Events[EventRowSelected];
  if (null != handler)
    handler(this, e);
}

As you can see, the event above is making use of the new Generic Delegate Declaration and a RowSelectedEventArgs that will hold the EmployeeID of the row that is selected. The EmployeeID will be retrieved by the event handler located on the host page then pass it to the OrderList UserControl. The RowSelectedEventArgs class is defined below.

Listing 2

public class RowSelectedEventArgs: EventArgs
{
  #region Members
  private int employeeID =  - 1;
  #endregion
 
  #region Constructor
  public RowSelectedEventArgs(int employeeID)
  {
    this.employeeID = employeeID;
  }
  #endregion
 
  #region Properties
  public int EmployeeID
  {
    get
    {
      return this.employeeID;
    }
  }
  #endregion
}

The OrderList and UserControl include a BindData method that takes as input the EmployeeID and calls the Select method of the SqlDataSource placed on the UserControl itself. The code is as follows:

Listing 3

public void BindData(int employeeID)
{
// Set the employeID members
  this.employeeID = employeeID;
  ViewState["EmployeeID"= this.employeeID;
 
// Show the title Panel
  this.pnlTitle.Visible = true;
 
// Force a select for the SqlDataSource
  this.SqlDataSource1.Select(new DataSourceSelectArguments());
 
// Refresh the GridView
  this.GridView1.DataSourceID = "SqlDataSource1";
}

The main role this entire process is for the event handler that is placed inside the host page. This event handler will be fired upon selecting a row in the EmployeeList UserControl’s GridView. Internally, the GridView will fire the SelectedIndexChanged event and within the SelectedIndexChanged event handler a new instance of the RowSelectedEventArgs is populated with the EmployeeID of the row selected and a call to the RowSelected event delegate is issued to fire the event handler placed on the host page.

Listing 4

protected void EmployeeList1_RowSelected(object sender, RowSelectedEventArgs e)
{
  // bind the OrderList with the EmployeeID selected
  this.OrderList1.BindData(e.EmployeeID);
}

As you can see, the sole idea of this handler is simply to fire a call to the OrderList UserControl’s BindData method passing in the EmployeeID.

Figure 2 below shows the two UserControls state after an Employee record has been selected.

Figure 2: Employee selected and Orders shown

 

That was straight forward and nothing new in here!  Let us now start adding some AJAX!!

UpdatePanels inside each UserControl

In this first scenario we will place the GridViews of both UserControls inside an UpdatePanel in such a way the UpdatePanels are placed inside the UserControls themselves.

The EmployeeList UserControl is now as follows:

Listing 5

<%@ Control Language="C#"
 AutoEventWireup="true" CodeFile="EmployeeList.ascx.cs"
 Inherits="UserControls_EmployeeList" %>
<p class="title">
    Select Employee to view Orders:
</p>
<p>
<asp:UpdatePanel ID="UpdatePanel1"
 runat="server" UpdateMode="Always">
<ContentTemplate>
    <asp:GridView 
        ID="GridView1" runat="server"  
        CssClass="Professional"
        HeaderStyle-CssClass="Header"
        AlternatingRowStyle-CssClass="Alternating" 
        SelectedRowStyle-CssClass="Selected"
        GridLines="None" 
        AutoGenerateColumns="False" 
        DataKeyNames="EmployeeID" 
        DataSourceID="SqlDataSource1" 
        AllowSorting="True" 
        OnSelectedIndexChanged="GridView1_SelectedIndexChanged"
     >
        <Columns>
            <asp:CommandField ShowSelectButton="True" />
            <asp:BoundField DataField="EmployeeID" HeaderText="EmployeeID" InsertVisible="False"
                ReadOnly="True" SortExpression="EmployeeID" />
            <asp:BoundField DataField="FirstName"
 HeaderText="FirstName" SortExpression="FirstName" />
            <asp:BoundField DataField="LastName" HeaderText="LastName" SortExpression="LastName" />
            <asp:BoundField
 DataField="Country" HeaderText="Country" SortExpression="Country" />
        </Columns>
        <SelectedRowStyle CssClass="Selected" />
        <HeaderStyle CssClass="Header" />
        <AlternatingRowStyle CssClass="Alternating" />
    </asp:GridView>
<asp:SqlDataSource 
        ID="SqlDataSource1" runat="server" 
        ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
        SelectCommand="SELECT TOP 5 [EmployeeID], [FirstName], [LastName], [Country] 
FROM [Employees]"
/>
</ContentTemplate>
</asp:UpdatePanel>
</p>
The OrderList UserControl is as follows:
<%@ Control Language="C#"
 AutoEventWireup="true" CodeFile="OrderList.ascx.cs"
 Inherits="UserControls_OrderList" %>
<asp:Panel ID="pnlTitle" runat="server" Visible="false">
<p class="title">
    Orders for Employee ID <b><%= this.employeeID %></b>:
</p>
</asp:Panel>
<p>
<asp:UpdatePanel ID="UpdatePanel1"
 runat="server" UpdateMode="Conditional">
<ContentTemplate>
    <asp:GridView 
            ID="GridView1" runat="server" 
            AllowSorting="True" 
            AutoGenerateColumns="False"
            DataKeyNames="OrderID" 
            CssClass="Professional"
            HeaderStyle-CssClass="Header"
            AlternatingRowStyle-CssClass="Alternating" 
            SelectedRowStyle-CssClass="Selected"
            GridLines="None" 
            DataSourceID="SqlDataSource1"
     >
        <Columns>
            <asp:BoundField
 DataField="OrderID" HeaderText="OrderID"
 InsertVisible="False" ReadOnly="True"
                SortExpression="OrderID" />
            <asp:BoundField DataField="CustomerID"
 HeaderText="CustomerID" SortExpression="CustomerID" />
            <asp:BoundField
 DataField="OrderDate" HeaderText="OrderDate" SortExpression="OrderDate" />
            <asp:BoundField
 DataField="ShipCountry" HeaderText="ShipCountry" SortExpression="ShipCountry" />
        </Columns>
    </asp:GridView>
    <p>
    </p>
<asp:SqlDataSource 
    ID="SqlDataSource1" runat="server" 
    ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
    SelectCommand="SELECT TOP 5 [OrderID], [CustomerID], [OrderDate], [ShipCountry]
 FROM [Orders] WHERE ([EmployeeID] = @EmployeeID)" 
    OnSelecting="SqlDataSource1_Selecting">
    <SelectParameters>
        <asp:Parameter DefaultValue="-1" Name="EmployeeID" Type="Int32" />
    </SelectParameters>
</asp:SqlDataSource>
</ContentTemplate>
</asp:UpdatePanel>

To start with, let us discuss how this scenario will work. The Employee’s GridView in the EmployeeList UserControl is placed within an UpdatePanel which means any postback that will be fired within the UpdatePanel will be an asynchronous postback and a part-update will be done.

In the event handler of the host page, we will add a call to the Update method of the UpdatePanel located inside the OrderList UserControl. Since the host page caused a partial-update to the EmployeeList we need a way to tell the host page to cause a partial-update to the OrderList UserControl to reflect the changes in the OrderList GridView.

First of all we expose the UpdatePanel located inside the OrderList UserControl so that the host page can force a partial-update on the OrderList UserControl.

The event handler in the host page now looks like Listing 6.

Listing 6

protected void EmployeeListajax1_RowSelected(object sender,
  RowSelectedEventArgs e)
{
  // bind the OrderList with the EmployeeID selected
  this.OrderListajax1.BindData(e.EmployeeID);
  // Force a partial-update
  this.OrderListajax1.AjaxPanel.Update();
}

Notice the call to the Update method of the UpdatePanel located inside the OrderList UserControl. One important note pops up here is that to be able to call the Update method of the UpdatePanel, the UpdatePanel should have the UpdateMode set to Conditional!

In the above scenario we have seen how to enable the application with AJAX by placing the UpdatePanels inside the UserControls directly. Notice that the UpdatePanels in both UserControls are using the same ScrpitManager that is placed on the host page.

UserControl inside an UpdatePanel

In this scenario we will use the original UserControls and place them inside UpdatePanels on the host page. This means that no additional work of adding any UpdatePanel is needed on the UserControls’ side.

We will get the same behavior as in the above scenario where clicking an Employee record in the EmployeeList UserControl will cause an asynchronous postback and a partial update to the OrderList UserControl.

The code inside the host page is as follows:

Listing 7

<%@ Page Language="C#"
 AutoEventWireup="true" StylesheetTheme="Default"
 CodeFile="UserControlsInsideUpdatePanels.aspx.cs"
 Inherits="UserControlsInsideUpdatePanels" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <div>
            <div>
                <asp:UpdatePanel ID="UpdatePanel2" runat="server">
                    <ContentTemplate>
                        <bhaidar:employeelist ID="EmployeeList1" runat="server" />
                    </ContentTemplate>
                </asp:UpdatePanel>
            </div>
            <br />
            <div>
                <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
                    <ContentTemplate>
                        <bhaidar:orderlist id="OrderList1" runat="server" />
                    </ContentTemplate>
                    <Triggers>
                        <asp:AsyncPostBackTrigger
 ControlID="EmployeeList1" EventName="RowSelected" />
                    </Triggers>
                </asp:UpdatePanel>
            </div>
        </div>
    </form>
</body>
</html>

What you will notice in the code snippet above is the presence of the Triggers property inside the UpdatePanel. This property tells the UpdatePanel to update its content when the control listed in the Triggers section fires its event. In this context, when the EmployeeList event RowSelected is fired, the second UpdatePanel that contains the OrderList UserControl shall be updated in a partial-update fashion.

We could have also not used the Triggers property and instead caused a refresh to the UpdatePanel programmatically as explained in the above scenario.

The only difference between the two scenarios is that the UserControls are left untouched and the UpdatePanels were added on the host page. This has an advantage over the first scenario especially when it comes to adding AJAX to already existing applications. With this scenario in hand, you do not have to do any extra work on your existing UserControls. You only have to add UpdatePanels into the host page. This leaves the UserControl free and usable in any situation, even where no AJAX is required!

Two UserControls and One UpdatePanel

In this last scenario we will show you how the EmployeeList UserControl, which is now not placed inside an UpdatePanel, will cause a partial-update for the UpdatePanel containing the OrderList UserControl.

In the above two scenarios you noticed that we were using the EmployeeList GridView just to show some data in the OrderList GridView. In this case, there is no need for you to have any kind of AJAX for the EmployeeList. However, if you plan to add some functionalities to the EmployeeList GridView, like editing for example, then it would be a good choice to consider one of the above two scenarios.

In this scenario, the EmployeeList will be placed as it is on the host page with no UpdatePanels and the OrderList will still be placed inside an UpatePanel. Now the question is how will the EmployeeList UsreControl’s event cause a partial-update for the OrderList UserControl?

Well, you have two options to work around it in this case. The first option is to add a Triggers property to the UpdatePanel and specify that the EmployeeList UserControl that has the RowSelected event will cause a partial-update. The other option is simply add the following line of code into the Page_Load event handler of the host page:

Listing 8

protected void Page_Load(object sender, EventArgs e)
{
  this.ScriptManager1.RegisterAsyncPostBackControl(this.EmployeeList1);
}

The RegsiterAsyncPostBackControl method that belongs to the ScriptManager changes the behavior of a server control from causing a postback to causing an asynchronous postback. In this case, the EmployeeList UserControl’s event RowSelected will cause an asynchronous postback. Since this UserControl is not registered as a Trigger to the UpdatePanel, you will have to make the UpdateMode of the UpdatePanel to Always. This way, any server control on the host page causing an asynchronous postback, the UpdatePanel will refresh its content.

If you do not want the UpdatePanel to refresh every time the EmployeeList UserControl’s event RowSelected fires, you can change its UpdateMode to Conditional and add the EmployeeList UserControl with its event RowSelected as a Trigger to the UpdatePanel. This way, the UpdatePanel will refresh its content any time the EmployeeList UserControl’s event RowSelected fires.

What to choose?

We have seen several scenarios throughout this article that discuss how to handle UserControls when AJAX is to be added to a website. One would ask which scenario is best to follow. Well the normal answer would be it depends on the situation you are experiencing. However, we do not like normal answers and we would be strict in saying that, it is recommended to leave the UserControls intact and add your AJAX controls to the host pages themselves. Then you can either use Triggers or a programmatic call to update method of the UpdatePanel for refreshing the content of any UpdatePanels.

Downloads

References

To start learning with Microsoft ASP.NET 2.0 AJAX Extensions 1.0 we would strongly recommend starting by reading the AJAX documentation. This should be your first reference to start with. In addition, you can visit the official website of ASP.NET and download all of the videos created by Joe Stagner on the AJAX and AJAX Toolkit. Finally, there are several MSDN web casts that are worth watching. Rob Bagby, Microsoft Developer Evangelist, has started a series of web casts on the Client Script side of AJAX. There are currently two downloadable web casts to watch and next week he will be presenting the rest.

Conclusion

In this article we have shown you how to deal with UserControls when AJAX is to be added to an existing ASP.NET Web application or even when creating new applications. Different scenarios were discussed including placing UpdatePanels inside the UserControls, placing UserControls inside UpdatePanels in the host page, placing one UserControl in an UpdatePanel and causing an asynchronous postback from the first UserControl that has no UpdatePanel.

We hope you enjoyed this article and benefited from the information presented.

Happy Ajaxified Dot Netting!!



User Comments

Title: very nice   
Name: Serif Emek
Date: 2009-07-28 5:32:09 PM
Comment:
That is just what I was looking for.
Thanks
Title: Re: Vbman   
Name: Bilal Haidar [MVP]
Date: 2008-03-27 10:38:17 AM
Comment:
Hello,
Try to show me the code behind of the usercontrol and how you are setting it on the page!

Thanks
Title: Object reference not set to an instance of an object.   
Name: Vbman
Date: 2008-03-27 10:05:40 AM
Comment:
Great Article!
I have the update panel on the page and the user controls inside the update panel.
I create a property inside the usercontrol that accesses a control, Say the gridviews Tooltip or Pagesize or ...
When I try to set the property on page declaratively I get an error:
Object reference not set to an instance of an object.

I've seen this problem posted in other places but no solution.
Have you run into this and do you have a work around.

Thanks!
Title: Re:   
Name: Bilal Haidar
Date: 2007-10-30 10:43:12 AM
Comment:
Hello,
You can use this converter:
http://bhaidar.net/cs/archive/2007/04/18/telerik-c-vb-net-converter.aspx

Regards
Title: VB.Net   
Name: Jo
Date: 2007-10-30 5:51:05 AM
Comment:
Article is so good. If it is available in vb.net also it will be helpful for the beginners.
Title: Well explained   
Name: Jose
Date: 2007-08-09 4:44:16 AM
Comment:
As usual, you did a great job!

I loved the fact that you showed all the possible combinations you can have with a gridview user control and AJAX. And the sample works really fine!

Jose
Title: Great   
Name: Mark W
Date: 2007-06-25 9:24:07 PM
Comment:
Very well summarized, thanks. I agree, good use of events and generics.

Also, there's a small typo in Listing 5: OrderList UC's UpdatePanel ID should be 'AjaxPanel'.
Title: Nice one   
Name: Wesley Bakker
Date: 2007-06-24 4:32:51 AM
Comment:
Nice article. Decent use of events, AJAX, generics, datasourcecontrols etc. etc.

Cheers,
Wes






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-18 11:09:53 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search