Using the UpdatePanel Control with a Web Service

The UpdatePanel control simplifies partial-page rendering in ASP.NET Web pages because the AJAX functionality of ASP.NET manages asynchronous postback requests and updates automatically. The AJAX functionality also enables you to call ASP.NET Web services by using ECMAScript (JavaScript) in the browser. One of the advantages of calling a Web service by using client script is that waiting for the response from a Web service request does not block the browser. Users can continue to work without waiting for the Web service to finish processing the request.

This tutorial shows how to use a Web service by using an UpdatePanel control. A JavaScript function calls the Web service to retrieve data, and then to populate DOM elements inside an UpdatePanel control with data returned from the Web service. Server code preserves the retrieved Web service data between asynchronous postbacks.

This topic assumes that you are familiar with the UpdatePanel control and with Web services. If you are not, review the following topics:

To implement the procedures in your own development environment you need:

  • Microsoft Visual Studio 2005 or Visual Web Developer Express.

  • An AJAX-enabled ASP.NET Web site.

  • Access to the Northwind database and a connection string named NorthwindConnectionString defined in the Web.config file. For information about how to create a connection string, see How to: Read Connection Strings from the Web.config File.

To begin, you will create a Web service that you can call.

To create a Web service to return product quantities

  1. In an AJAX-enabled ASP.NET Web site, create a new Web service file named ProductQueryService.asmx.

    For information about how to create a Web service, see Calling Web Services from Client Script.

  2. In the Web service code, import the N:System.Data, N:System.Data.SqlClient, System.Configuration, and N:System.Web.Script.Services namespaces.

    using System.Web.Script.Services;
    using System.Data;
    using System.Data.SqlClient;
    using System.Configuration;
    
    
    

    Types from these namespaces will be used in the Web service method that you will create.

  3. Put the ProductQueryService class inside a namespace called Samples.

  4. Add the ScriptServiceAttribute attribute to the class.

    This attribute enables the Web service to be invoked from client script.

  5. Replace the default HelloWorld method with the following GetProductQuantity method:

    [WebMethod]
    public string GetProductQuantity(string productID)
    {
        SqlConnection cn =
            new SqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString);
        SqlCommand cmd = new SqlCommand(
            "SELECT [UnitsInStock] FROM [Alphabetical list of products] WHERE ([ProductID] = @ProductID)", cn);
        cmd.Parameters.Add("productID", productID);
        String unitsInStock = "";
        cn.Open();
        using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
        {
            while (dr.Read())
                unitsInStock = dr[0].ToString();
        }
        System.Threading.Thread.Sleep(3000);
        return unitsInStock;
    }
    
    
    

    The code performs the following tasks:

    • Creates a new SqlConnection object that uses a connection string named NorthwindConnectionString.

    • Creates a new SqlCommand object that contains a SQL command to retrieve the number of units in stock for a specified product ID.

    • Sets the return value to a string that is used as the response that is sent to the browser.

      NoteNote:

      For this tutorial, the Web method introduces an artificial delay. In practice, you would not introduce a delay. Instead, any delay would be the result of server traffic or of Web service code that takes a long time to process, such as a long-running database query.

  6. Save your changes, and then press CTRL+F5 to view the page in a browser.

  7. Click the GetProductQuantity link to invoke the Web method.

  8. Enter 6 in the productID box and then click Invoke.

    The quantity of the product is returned as XML in the browser. This demonstrates that the Web service is functioning as intended.

    <%@ WebService Language="C#" Class="Samples.ProductQueryService" %>
    
    using System;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Web.Script.Services;
    using System.Data;
    using System.Data.SqlClient;
    using System.Configuration;
    namespace Samples
    {
        [ScriptService]
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        public class ProductQueryService : System.Web.Services.WebService
        {
    
            [WebMethod]
            public string GetProductQuantity(string productID)
            {
                SqlConnection cn =
                    new SqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString);
                SqlCommand cmd = new SqlCommand(
                    "SELECT [UnitsInStock] FROM [Alphabetical list of products] WHERE ([ProductID] = @ProductID)", cn);
                cmd.Parameters.Add("productID", productID);
                String unitsInStock = "";
                cn.Open();
                using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (dr.Read())
                        unitsInStock = dr[0].ToString();
                }
                System.Threading.Thread.Sleep(3000);
                return unitsInStock;
            }
        }
    }
    
    
    

In this procedure you will create a JavaScript file that calls the Web service that you created in the previous procedure.

To create a JavaScript file to use the Web Service

  1. Create a new JScript file named ProductQueryScript.js.

  2. Add the following script to the file:

    function GetQuantity(productID, elemToUpdate, productLabelElem, buttonElem) {
       var userContext = [productID, elemToUpdate, productLabelElem, buttonElem];
       Samples.ProductQueryService.GetProductQuantity(productID, OnSucceeded, null, userContext, null);
       $get(buttonElem).value = "Retrieving value...";
    }
    function OnSucceeded(result, userContext) {
       var productID = userContext[0];
       var elemToUpdate = userContext[1];
       var productLabelElem = userContext[2];
       var buttonElem = userContext[3];
       $get(buttonElem).value = "Get Quantity from Web Service";
       if ($get(elemToUpdate) !== null && $get(productLabelElem).innerHTML == productID) {
         $get(elemToUpdate).value = result;
       }
    }
    
    
    

    The script performs the following tasks:

    • Creates a function named GetQuantity that invokes the GetProductQuantity Web service method.

    • Creates a function named OnSucceeded that is invoked when the Web service call returns with a result.

Next, you will create a Web page that contains an UpdatePanel control. Controls inside the UpdatePanel control display product information from the Northwind database.

To create a Web page to display products

  1. Create a new Web page and switch to Design view.

  2. In the AJAX Extensions tab of the toolbox, double-click the ScriptManager control to add it to the page.

  3. Double-click the UpdatePanel control in the toolbox to add an UpdatePanel control to the page.

    UpdatePanel Tutorial
  4. Click inside the UpdatePanel control, and then in the Data tab of the toolbox, double-click the DataList control.

  5. In the DataList Tasks panel, from the Choose Data Source list, select <New data source…>.

    NoteNote:

    If you do not see the DataList Tasks panel, right-click the DataList control and select Show Smart Tag.

    The Data Source Configuration wizard is displayed.

  6. Select Database, accept the default name SqlDataSource1, and then click OK.

  7. In the Which data connection should your application use to connect to the database list, select NorthwindConnectionString, and then click Next.

  8. Under How would you like to retrieve data from your database, select Specify columns from a table or view, select Products from the list, and in the Columns list, select ProductID and ProductName.

  9. Click the WHERE button.

    The Add WHERE Clause dialog box is displayed.

  10. In the Columns list, select CategoryID and in the Source list, select None.

  11. In the Parameter properties section of the dialog box, in the Value text box, enter 1.

  12. Click Add to add the WHERE clause to the SQL statement.

  13. Click OK to close the Add WHERE Clause dialog box.

    UpdatePanel Tutorial
  14. Click Next and then click Finish to close the wizard.

  15. Switch to Source view and confirm that the SqlDataSource control resembles the following example:

    <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
        SelectCommand="SELECT [ProductName], [ProductID] FROM [Alphabetical list of products] WHERE ([CategoryID] = @CategoryID)">
        <SelectParameters>
            <asp:Parameter DefaultValue="1" Name="CategoryID" Type="Int32" />
        </SelectParameters>
    </asp:SqlDataSource>
    
    
    
  16. Switch to Design view.

  17. Select the DataList control, and in the DataList Tasks panel, click Edit Templates.

  18. Add two Button controls to the UpdatePanel control outside the DataList control.

  19. .Set the first button's ID property to Category1Button and its Text property to Category 1. Set the second button's ID property to Category2Button and its Text property to Category 2.

    UpdatePanel Tutorial
  20. Select the UpdatePanel control, and in the Properties window, set the UpdateMode property to Conditional and set the ChildrenAsTriggers property to false.

  21. In the Triggers box, click the ellipsis (…) button and in the UpdatePanel Trigger Collection Editor dialog box, add each category button as an asynchronous postback trigger.

    UpdatePanel Tutorial
  22. Set the Click event handler for the first button to Category1Button_Click and set the Click event handler for the second button to Category2Button_Click.

  23. Switch to Source view and create the following event handlers for the two buttons:

    protected void Category1Button_Click(object sender, EventArgs e)
    {
        SqlDataSource1.SelectParameters[0].DefaultValue = "1";
    }
    
    protected void Category2Button_Click(object sender, EventArgs e)
    {
        SqlDataSource1.SelectParameters[0].DefaultValue = "2";
    }
    
    
    

    The event handler code sets the CategoryID parameter of the SelectParameters collection in the SqlDataSource control, based on which button was pressed. This enables users to toggle between two categories.

  24. Save your changes and press CTRL+F5 to view the page in a browser.

  25. Click Category 2 and verify that the page displays new information but does not refresh the whole page.

    NoteNote:

    Do not close the page.

    
    <%@ Page Language="C#" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <script runat="server">
    
        protected void Category1Button_Click(object sender, EventArgs e)
        {
            SqlDataSource1.SelectParameters[0].DefaultValue = "1";
        }
    
        protected void Category2Button_Click(object sender, EventArgs e)
        {
            SqlDataSource1.SelectParameters[0].DefaultValue = "2";
        }
    
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Products Display</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:ScriptManager ID="ScriptManager1" runat="server">
                </asp:ScriptManager>
                <asp:UpdatePanel ID="UpdatePanel1" runat="server" ChildrenAsTriggers="False" UpdateMode="Conditional">
                    <ContentTemplate>
                        <asp:Button ID="Category1Button" runat="server" Text="Category 1" OnClick="Category1Button_Click" />
                        <asp:Button ID="Category2Button" runat="server" OnClick="Category2Button_Click" Text="Category 2" />
                        <asp:DataList ID="DataList1" runat="server" DataKeyField="ProductID" DataSourceID="SqlDataSource1"
                            Width="231px">
                            <ItemTemplate>
                                ProductName:
                                <asp:Label ID="ProductNameLabel" runat="server" Text='<%# Eval("ProductName") %>'>
                                </asp:Label><br />
                                ProductID:
                                <asp:Label ID="ProductIDLabel" runat="server" Text='<%# Eval("ProductID") %>'></asp:Label><br />
                                <br />
                            </ItemTemplate>
                        </asp:DataList>
                        <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
                            SelectCommand="SELECT [ProductName], [ProductID] FROM [Alphabetical list of products] WHERE ([CategoryID] = @CategoryID)">
                            <SelectParameters>
                                <asp:Parameter DefaultValue="1" Name="CategoryID" Type="Int32" />
                            </SelectParameters>
                        </asp:SqlDataSource>
                    </ContentTemplate>
                    <Triggers>
                        <asp:AsyncPostBackTrigger ControlID="Category1Button" />
                        <asp:AsyncPostBackTrigger ControlID="Category2Button" />
                    </Triggers>
                </asp:UpdatePanel>
            </div>
        </form>
    </body>
    </html>
    
    
    

You will now call the Web service by using the JavaScript file that you created earlier. The JavaScript code uses the returned data to populate DOM elements inside the UpdatePanel control. Server code in the page preserves data that is populated from the Web service and adds the data to the page's view state. This retains the data in any subsequent asynchronous postbacks.

To use a Web Service to return product quantities

  1. In the page, switch to Design view.

  2. Select the DataList control and in the DataList Tasks panel, select Edit Templates.

  3. Add a TextBox and a Button control to the item template.

    Add the new controls underneath the existing text and labels in the template.

  4. Select the button and in the Properties window, set its Text property to Get Quantity from Web Service.

    The item template of the DataList control should resemble the following figure.

    UpdatePanel Tutorial
  5. Select the DataList control, and then in the Events tab of the Properties window, double-click the ItemDataBound event.

  6. Add the following code:

    protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
    {
        Label label = (Label)e.Item.FindControl("ProductIDLabel");
        Button button = (Button)e.Item.FindControl("Button1");
        TextBox textbox = (TextBox)e.Item.FindControl("TextBox1");
        button.OnClientClick = "GetQuantity(" + label.Text + ",'" + 
            textbox.ClientID + "','" + label.ClientID + "','" + button.ClientID + "')";
        SortedList ProductInfo = this.ProductInfo;
        if (ProductInfo.ContainsKey(label.Text))
        {
            textbox.Text = ProductInfo[label.Text].ToString();
        }        
    }
    
    
    

    The code sets properties for controls in each DataListItem object as follows:

    • The OnClientClick property of the button calls a JavaScript function that in turn calls the Web service.

    • The value of the text box is set if a tracking property named ProductInfo contains a key for the product ID. The ProductInfo property is defined in the next step of this procedure.

  7. Add a property named ProductInfo to the page.

    protected SortedList ProductInfo
    {
        get { return (SortedList)(ViewState["ProductInfo"] ?? new SortedList()); }
        set { ViewState["ProductInfo"] = value; }
    }
    
    
    

    This property is a SortedList object that tracks data that has been added to the page from the Web service. The first time that the page is rendered, the list is empty. During subsequent asynchronous postbacks, items might be added to the list.

  8. Add the following Page_Load event handler:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (ScriptManager1.IsInAsyncPostBack)
        {
            SortedList ProductInfo = this.ProductInfo;
            foreach (DataListItem d in DataList1.Items)
            {
                Label label = (Label)d.FindControl("ProductIDLabel");
                TextBox textbox = (TextBox)d.FindControl("TextBox1");
                if (textbox.Text.Length > 0)
                {
                    ProductInfo[label.Text] = textbox.Text;
                }
            }
            this.ProductInfo = ProductInfo;
        }
    }
    
    
    

    The code checks whether the request is an asynchronous postback. If it is, any data items that are added by the Web service are added to the ProductInfo property. This enables them to be tracked in view state and displayed in the UpdatePanel during subsequent partial-page updates. If the data items are not tracked in view state, they are not retained in subsequent asynchronous postbacks.

  9. Switch to Design view.

  10. Select the ScriptManager control.

  11. In the Properties window, select the Services property and click the ellipsis (…) button to display the ServiceReference Collection Editor dialog box.

  12. Click Add to add a service reference.

  13. Set the Path property of the service reference to ProductQueryService.asmx, which is the Web service that you created previously.

    Adding a service reference causes the ScriptManager control to generate client proxy classes so that the Web service can be called by using JavaScript.

    UpdatePanel Tutorial
  14. Click OK to close the ServiceReference Collection Editor dialog box.

  15. Select the ScriptManager control, and then in the Properties window, select the Scripts property and click the ellipsis (…) button to display the ScriptReference Collection Editor dialog box.

  16. Click Add to add a script reference.

  17. Set the Path property of the script reference to ProductQueryScript.js, which is the JavaScript file that you created previously.

    Adding a script reference causes the ScriptManager control to insert the script after the Microsoft AJAX Library has loaded.

    UpdatePanel Tutorial
  18. Click OK to close the ScriptReference Collection Editor dialog box.

  19. Save your changes and press CTRL+F5 to view the page in a browser.

    The page shows the products from the Northwind database whose category ID is 1. The text boxes next to each product are empty because no Web service calls have been made.

    
    <%@ Page Language="C#" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <script runat="server">
    
        protected SortedList ProductInfo
        {
            get { return (SortedList)(ViewState["ProductInfo"] ?? new SortedList()); }
            set { ViewState["ProductInfo"] = value; }
        }
    
        protected void Category1Button_Click(object sender, EventArgs e)
        {
            SqlDataSource1.SelectParameters[0].DefaultValue = "1";
        }
    
        protected void Category2Button_Click(object sender, EventArgs e)
        {
            SqlDataSource1.SelectParameters[0].DefaultValue = "2";
        }
    
        protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
        {
            Label label = (Label)e.Item.FindControl("ProductIDLabel");
            Button button = (Button)e.Item.FindControl("Button1");
            TextBox textbox = (TextBox)e.Item.FindControl("TextBox1");
            button.OnClientClick = "GetQuantity(" + label.Text + ",'" + 
                textbox.ClientID + "','" + label.ClientID + "','" + button.ClientID + "')";
            SortedList ProductInfo = this.ProductInfo;
            if (ProductInfo.ContainsKey(label.Text))
            {
                textbox.Text = ProductInfo[label.Text].ToString();
            }        
        }
    
        protected void Page_Load(object sender, EventArgs e)
        {
            if (ScriptManager1.IsInAsyncPostBack)
            {
                SortedList ProductInfo = this.ProductInfo;
                foreach (DataListItem d in DataList1.Items)
                {
                    Label label = (Label)d.FindControl("ProductIDLabel");
                    TextBox textbox = (TextBox)d.FindControl("TextBox1");
                    if (textbox.Text.Length > 0)
                    {
                        ProductInfo[label.Text] = textbox.Text;
                    }
                }
                this.ProductInfo = ProductInfo;
            }
        }
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Products Display</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:ScriptManager ID="ScriptManager1" runat="server">
                    <Services>
                        <asp:ServiceReference Path="ProductQueryService.asmx" />
                    </Services>
                    <Scripts>
                        <asp:ScriptReference Path="ProductQueryScript.js" />
                    </Scripts>
                </asp:ScriptManager>
                <asp:UpdatePanel ID="UpdatePanel1" runat="server" ChildrenAsTriggers="False" UpdateMode="Conditional">
                    <ContentTemplate>
                        <asp:Button ID="Category1Button" runat="server" Text="Category 1" OnClick="Category1Button_Click" />
                        <asp:Button ID="Category2Button" runat="server" OnClick="Category2Button_Click" Text="Category 2" />
                        <asp:DataList ID="DataList1" runat="server" DataKeyField="ProductID" DataSourceID="SqlDataSource1"
                            Width="400px" OnItemDataBound="DataList1_ItemDataBound">
                            <ItemTemplate>
                                ProductName:
                                <asp:Label ID="ProductNameLabel" runat="server" Text='<%# Eval("ProductName") %>'>
                                </asp:Label><br />
                                ProductID:
                                <asp:Label ID="ProductIDLabel" runat="server" Text='<%# Eval("ProductID") %>'></asp:Label><br />
                                <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
                                <asp:Button ID="Button1" runat="server" Text="Get Quantity from Web Service" /><br />
                            </ItemTemplate>
                        </asp:DataList>
                        <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
                            SelectCommand="SELECT [ProductName], [ProductID] FROM [Alphabetical list of products] WHERE ([CategoryID] = @CategoryID)">
                            <SelectParameters>
                                <asp:Parameter DefaultValue="1" Name="CategoryID" Type="Int32" />
                            </SelectParameters>
                        </asp:SqlDataSource>
                    </ContentTemplate>
                    <Triggers>
                        <asp:AsyncPostBackTrigger ControlID="Category1Button" />
                        <asp:AsyncPostBackTrigger ControlID="Category2Button" />
                    </Triggers>
                </asp:UpdatePanel>
            </div>
        </form>
    </body>
    </html>
    
    
    

You can now see how data from the Web service is persisted during asynchronous postbacks.

To verify that Web service data is persisted during asynchronous postbacks

  1. If the page is not already running, press CTRL+F5 to run the page.

  2. Click the Get Quantity from Web Service button for any product in the list and then wait for the value to be displayed in the text box.

  3. Click the Category 2 button.

  4. Click the Category 1 button.

    Notice that the value that was returned earlier by the Web service still appears, even after the asynchronous postbacks.

This tutorial showed an example of using an UpdatePanel control with a Web service that is called from JavaScript code in the browser. The Web service returns data that is displayed inside the UpdatePanel control.

The result of calling the Web service from client script and of populating DOM elements by using the returned data is the same with or without UpdatePanel controls. When a page performs a postback or when an asynchronous postback occurs, the data that was previously displayed by using client script is lost. To avoid this problem, you can use server code to persist the data by making it part of view state. This enables you to maintain the data during subsequent postbacks. If the data from the Web service is displayed in DOM elements outside UpdatePanel controls and you do not want to persist the data during asynchronous postbacks, you do not have to provide server code to maintain it.

Because the button that triggered the Web service call is inside the UpdatePanel control, the ChildrenAsTriggers property is set to false. Other postback controls inside the panel are defined as triggers for the panel.

Community Additions

ADD
Show: