将 UpdatePanel 控件用于 Web 服务

更新:2007 年 11 月

UpdatePanel 控件可以简化 ASP.NET 网页的部分页呈现,因为 ASP.NET 的 AJAX 功能可自动管理异步回发请求和更新。您还可利用 AJAX 功能,在浏览器中通过使用 ECMAScript (JavaScript) 来调用 ASP.NET Web 服务。通过使用客户端脚本来调用 Web 服务的优点之一是等待 Web 服务请求的响应时不会阻塞浏览器。用户可以继续工作,而不用等待 Web 服务处理完毕请求。

本教程演示如何通过使用 UpdatePanel 控件来使用 Web 服务。JavaScript 函数调用 Web 服务来检索数据,然后使用从 Web 服务返回的数据填充 UpdatePanel 控件内的 DOM 元素。服务器代码可保留在异步回发之间检索到的 Web 服务数据。

本主题假定您熟悉 UpdatePanel 控件和 Web 服务。如果您不熟悉,请查看以下主题:

先决条件

若要在您自己的开发环境中实现这些过程,您需要:

  • Microsoft Visual Studio 2005 或 Visual Web Developer 速成版。

  • 一个支持 AJAX 的 ASP.NET 网站。

  • 访问 Northwind 数据库和 Web.config 文件中定义的名称为 NorthwindConnectionString 的连接字符串。有关如何创建连接字符串的信息,请参见如何:从 Web.config 文件读取连接字符串

创建 Web 服务

首先,创建一个可调用的 Web 服务。

创建 Web 服务以返回产品数量

  1. 在支持 AJAX 的 ASP.NET 网站中,创建一个名称为 ProductQueryService.asmx 的新 Web 服务文件。

    有关如何创建 Web 服务的信息,请参见从客户端脚本调用 Web 服务

  2. 在 Web 服务代码中,导入 N:System.DataN:System.Data.SqlClientSystem.ConfigurationN:System.Web.Script.Services 命名空间。

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

    这些命名空间的类型将用于您将要创建的 Web 服务方法中。

  3. 将 ProductQueryService 类放入名为 Samples 的命名空间中。

  4. 给类添加 ScriptServiceAttribute 属性。

    此属性使得可从客户端脚本调用 Web 服务。

  5. 使用下面的 GetProductQuantity 方法替换默认 HelloWorld 方法。

    <WebMethod()> _
    Public Function GetProductQuantity(ByVal productID As String) As String
        Dim cn As SqlConnection = _
            New SqlConnection(ConfigurationManager.ConnectionStrings("NorthwindConnectionString").ConnectionString)
        Dim cmd As SqlCommand = _
            New SqlCommand("SELECT [UnitsInStock] FROM [Alphabetical list of products] WHERE ([ProductID] = @ProductID)", cn)
        cmd.Parameters.AddWithValue("productID", productID)
        Dim unitsInStock As String = ""
        cn.Open()
        Using dr As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
            Do While dr.Read()
                unitsInStock = dr(0).ToString()
            Loop
    
        End Using
        System.Threading.Thread.Sleep(3000)
        Return unitsInStock
    End Function
    
    [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;
    }
    

    代码执行下列任务:

    • 创建一个使用名称为 NorthwindConnectionString 的连接字符串的新 SqlConnection 对象

    • 创建一个包含 SQL 命令的新 SqlCommand 对象,以检索指定产品 ID 的库存单位数量。

    • 将返回值设置为一个字符串,该字符串可用作发送到浏览器的响应。

      Bb398898.alert_note(zh-cn,VS.90).gif说明:

      出于本教程的需要,Web 方法引入了人为延迟。在实际应用中,您不会引入延迟。相反,任何延迟都是由服务器通信量或需要花很长时间处理的 Web 服务代码造成的,例如长时间运行的数据库查询。

  6. 保存更改,然后按 Ctrl+F5 在浏览器中查看页面。

  7. 单击**“GetProductQuantity”**链接以调用 Web 方法。

  8. 在**“productID”框中输入 6,然后单击“调用”**。

    产品数量将以 XML 格式返回浏览器中。这说明 Web 服务按预期方式工作。

    <%@ WebService Language="VB" Class="Samples.ProductQueryService" %>
    
    Imports System
    Imports System.Web
    Imports System.Web.Services
    Imports System.Web.Services.Protocols
    Imports System.Data
    Imports System.Data.SqlClient
    Imports System.Configuration
    Imports System.Web.Script.Services
    Namespace Samples
    
        <ScriptService()> _
        <WebService(Namespace:="http://tempuri.org/")> _
        <WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
        Public Class ProductQueryService
            Inherits System.Web.Services.WebService
    
            <WebMethod()> _
            Public Function GetProductQuantity(ByVal productID As String) As String
                Dim cn As SqlConnection = _
                    New SqlConnection(ConfigurationManager.ConnectionStrings("NorthwindConnectionString").ConnectionString)
                Dim cmd As SqlCommand = _
                    New SqlCommand("SELECT [UnitsInStock] FROM [Alphabetical list of products] WHERE ([ProductID] = @ProductID)", cn)
                cmd.Parameters.AddWithValue("productID", productID)
                Dim unitsInStock As String = ""
                cn.Open()
                Using dr As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
                    Do While dr.Read()
                        unitsInStock = dr(0).ToString()
                    Loop
    
                End Using
                System.Threading.Thread.Sleep(3000)
                Return unitsInStock
            End Function
        End Class
    End Namespace
    
    <%@ 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;
            }
        }
    }
    

创建 JavaScript 代码以调用 Web 服务

在此过程中,您将创建一个调用您在上一过程中创建的 Web 服务的 JavaScript 文件。

创建 JavaScript 文件以使用 Web 服务

  1. 创建一个名称为 ProductQueryScript.js 的新 JScript 文件。

  2. 向文件中添加以下脚本:

    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;
       }
    }
    
    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;
       }
    }
    

    该脚本执行下列任务:

    • 创建一个名称为 GetQuantity 的函数,该函数调用 GetProductQuantity Web 服务方法。

    • 创建一个名称为 OnSucceeded 的函数,当 Web 服务调用返回结果时调用该函数。

创建网页以显示数据

接下来,您将要创建一个包含 UpdatePanel 控件的网页。UpdatePanel 控件内的控件可显示来自 Northwind 数据库的产品信息。

创建网页以显示产品

  1. 创建一个新网页并切换到“设计”视图。

  2. 在工具箱的**“AJAX Extensions”**选项卡中,双击 ScriptManager 控件以将其添加到页上。

  3. 在工具箱中双击 UpdatePanel 控件以将 UpdatePanel 控件添加到页中。

    UpdatePanel 教程

  4. UpdatePanel 控件内单击,然后在工具箱的**“数据”**选项卡中双击 DataList 控件。

  5. 在**“DataList 任务”面板中,从“选择数据源”列表中选择“<新建数据源…>”**。

    Bb398898.alert_note(zh-cn,VS.90).gif说明:

    如果没有看到“DataList 任务”模板,请右击 DataList 控件,然后选择“显示智能标记”

    此时将显示**“数据源配置”**向导。

  6. 选择**“数据库”,接受默认名称“SqlDataSource1”,然后单击“确定”**。

  7. 在**“应用程序连接数据库应使用哪个数据连接”列表中,选择“NorthwindConnectionString”,然后单击“下一步”**。

  8. 在**“希望如何从数据库中检索数据”下,选择“指定来自表或视图的列”,再从该列表中选择“产品”,然后在“列”列表中,选择“ProductID”“ProductName”**。

  9. 单击**“WHERE”**按钮。

    显示**“添加 WHERE 子句”**对话框。

  10. 在**“列”列表中,选择“CategoryID”,然后在“源”列表中选择“无”**。

  11. 在该对话框的**“参数属性”部分的“值”**文本框中,输入 1。

  12. 单击**“添加”**以将 WHERE 子句添加到 SQL 语句中。

  13. 单击**“确定”关闭“添加 WHERE 子句”**对话框。

    UpdatePanel 教程

  14. 单击**“下一步”,然后单击“完成”**以关闭该向导。

  15. 切换到“源”视图,并确认 SqlDataSource 控件与以下示例相似:

    <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>
    
    <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. 切换到“设计”视图。

  17. 选择 DataList 控件,然后在**“DataList 任务”面板中,单击“编辑模板”**。

  18. 将两个 Button 控件添加到**“DataList”**控件外的 UpdatePanel 控件。

  19. 将第一个按钮的 ID 属性设置为 Category1Button,将其 Text 属性设置为 Category 1。将第二个按钮的 ID 属性设置为 Category2Button,将其 Text 属性设置为 Category 2。

    UpdatePanel 教程

  20. 选择 UpdatePanel 控件,然后在“属性”窗口中,将 UpdateMode 属性设置为 Conditional,将 ChildrenAsTriggers 属性设置为 false。

  21. 在**“触发器”框中,单击省略号按钮 (…),然后在“UpdatePanel 触发器集合编辑器”**对话框中,添加作为异步回发触发器的每个类别按钮。

    UpdatePanel 教程

  22. 将第一个按钮的 Click 事件处理程序设置为 Category1Button_Click,将第二个按钮的 Click 事件处理程序设置为 Category2Button_Click。

  23. 切换到“源”视图,然后为这两个按钮创建以下事件处理程序:

    Protected Sub Category1Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        SqlDataSource1.SelectParameters(0).DefaultValue = "1"
    End Sub
    
    Protected Sub Category2Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        SqlDataSource1.SelectParameters(0).DefaultValue = "2"
    End Sub
    
    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";
    }
    

    事件处理程序代码将基于所按的按钮设置 SqlDataSource 控件中 SelectParameters 集合的 CategoryID 参数。这使得用户可在两个类别之间切换。

  24. 保存更改,然后按 Ctrl+F5 在浏览器中查看页面。

  25. 单击“类别 2”,并验证该页是否显示新信息,但不刷新整页。

    Bb398898.alert_note(zh-cn,VS.90).gif说明:

    请不要关闭该页。

    <%@ Page Language="VB" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <script runat="server">
    
        Protected Sub Category1Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
            SqlDataSource1.SelectParameters(0).DefaultValue = "1"
        End Sub
    
        Protected Sub Category2Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
            SqlDataSource1.SelectParameters(0).DefaultValue = "2"
        End Sub
    </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>
    
    <%@ 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>
    

调用 Web 服务以检索数据

您现在可以使用先前创建的 JavaScript 文件来调用 Web 服务了。JavaScript 代码会使用返回的数据填充 UpdatePanel 控件内的 DOM 元素。页面中的服务器代码将保留通过 Web 服务填充的数据并将该数据添加到页面的视图状态中。这将保留所有后续异步回发中的数据。

使用 Web 服务返回产品数量

  1. 在页中,切换到“设计”视图。

  2. 选择 DataList 控件,然后在**“DataList 任务”面板中,选择“编辑模板”**。

  3. TextBoxButton 控件添加到项模板。

    将新控件添加到该模板的现有文本和标签下。

  4. 选择按钮,并在“属性”窗口中将其 Text 属性设置为“从 Web 服务获取数量”。

    DataList 控件的项模板应如下图所示。

    UpdatePanel 教程

  5. 选择 DataList 控件,然后在“属性”窗口的**“事件”**选项卡中,双击 ItemDataBound 事件。

  6. 添加下列代码:

    Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As DataListItemEventArgs)
        Dim label As Label = CType(e.Item.FindControl("ProductIDLabel"), Label)
        Dim button As Button = CType(e.Item.FindControl("Button1"), Button)
        Dim textbox As TextBox = CType(e.Item.FindControl("TextBox1"), TextBox)
        button.OnClientClick = "GetQuantity(" & label.Text & ",'" & textbox.ClientID & "','" & _
            label.ClientID + "','" & button.ClientID & "')"
        Dim ProductInfo As SortedList = Me.ProductInfo
        If (ProductInfo.ContainsKey(label.Text)) Then
            textbox.Text = ProductInfo(label.Text).ToString()
        End If
    End Sub
    
    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();
        }        
    }
    

    该代码可设置每个 DataListItem 对象中控件的属性,如下所示:

    • 该按钮的 OnClientClick 属性调用 JavaScript 函数,而该函数又调用 Web 服务。

    • 如果名称为 ProductInfo 的跟踪属性包含产品 ID 密钥,则将设置文本框的值。ProductInfo 属性在此过程的下一步中定义。

  7. 将名称为 ProductInfo 的属性添加到页中。

    Protected Property ProductInfo() As SortedList
        Get
            If ViewState("ProductInfo") IsNot Nothing Then
                Return CType(ViewState("ProductInfo"), SortedList)
            Else
                Return New SortedList()
            End If
        End Get
        Set(ByVal value As SortedList)
            ViewState("ProductInfo") = value
        End Set
    End Property
    
    protected SortedList ProductInfo
    {
        get { return (SortedList)(ViewState["ProductInfo"] ?? new SortedList()); }
        set { ViewState["ProductInfo"] = value; }
    }
    

    此属性是一个 SortedList 对象,它可跟踪从 Web 服务添加到页中的数据。第一次呈现页面时,该列表为空。在后续异步回发过程中,将向该列表添加项。

  8. 添加以下 Page_Load 事件处理程序:

    Protected Sub Page_Load()
        If (ScriptManager1.IsInAsyncPostBack) Then
            Dim ProductInfo As SortedList = Me.ProductInfo
            For Each d As DataListItem In DataList1.Items
                Dim label As Label = CType(d.FindControl("ProductIDLabel"), Label)
                Dim textbox As TextBox = CType(d.FindControl("TextBox1"), TextBox)
                If (textbox.Text.Length > 0) Then
                    ProductInfo(label.Text) = textbox.Text
                End If
            Next
            Me.ProductInfo = ProductInfo
        End If
    End Sub
    
    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;
        }
    }
    

    该代码检查请求是否为异步回发请求。如果为异步回发请求,则由 Web 服务添加的所有数据项都将添加到 ProductInfo 属性中。这使得可在视图状态下跟踪这些数据,并在后续部分页更新过程中将它们显示在 UpdatePanel 中。如果不在视图状态下跟踪这些数据项,则也就不在后续异步回发中保留它们。

  9. 切换到“设计”视图。

  10. 选择 ScriptManager 控件。

  11. 在“属性”窗口中,选择**“服务”属性,然后单击省略号 (…) 按钮以显示“ScriptReference 集合编辑器”**对话框。

  12. 单击**“添加”**以添加服务引用。

  13. 将服务引用的**“路径”**属性设置为 ProductQueryService.asmx,该文件就是您先前创建的 Web 服务。

    添加服务引用会导致 ScriptManager 控件生成客户端代理类,这样就可通过使用 JavaScript 调用 Web 服务。

    UpdatePanel 教程

  14. 单击**“确定”以关闭“ServiceReference 集合编辑器”**对话框。

  15. 选择 ScriptManager 控件,然后在“属性”窗口中选择**“脚本”属性,然后单击省略号 (…) 按钮以显示“ScriptReference 集合编辑器”**对话框。

  16. 单击**“添加”**以添加脚本引用。

  17. 将脚本引用的**“路径”**属性设置为 ProductQueryScript.js,该文件就是您先前创建的 JavaScript 文件。

    添加脚本引用会导致 ScriptManager 控件在加载完 Microsoft AJAX Library 后插入脚本。

    UpdatePanel 教程

  18. 单击**“确定”关闭“ScriptReference 集合编辑器”**对话框。

  19. 保存更改,然后按 Ctrl+F5 在浏览器中查看页面。

    页面将显示 Northwind 数据库中类别 ID 为 1 的产品。每个产品旁的文本框都为空,因为没有调用任何 Web 服务。

    <%@ Page Language="VB" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <script runat="server">
    
        Protected Property ProductInfo() As SortedList
            Get
                If ViewState("ProductInfo") IsNot Nothing Then
                    Return CType(ViewState("ProductInfo"), SortedList)
                Else
                    Return New SortedList()
                End If
            End Get
            Set(ByVal value As SortedList)
                ViewState("ProductInfo") = value
            End Set
        End Property
    
        Protected Sub Category1Button_Click(ByVal sender As Object, ByVal e As EventArgs)
            SqlDataSource1.SelectParameters(0).DefaultValue = "1"        
        End Sub
    
        Protected Sub Category2Button_Click(ByVal sender As Object, ByVal e As EventArgs)
            SqlDataSource1.SelectParameters(0).DefaultValue = "2"
        End Sub
    
        Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As DataListItemEventArgs)
            Dim label As Label = CType(e.Item.FindControl("ProductIDLabel"), Label)
            Dim button As Button = CType(e.Item.FindControl("Button1"), Button)
            Dim textbox As TextBox = CType(e.Item.FindControl("TextBox1"), TextBox)
            button.OnClientClick = "GetQuantity(" & label.Text & ",'" & textbox.ClientID & "','" & _
                label.ClientID + "','" & button.ClientID & "')"
            Dim ProductInfo As SortedList = Me.ProductInfo
            If (ProductInfo.ContainsKey(label.Text)) Then
                textbox.Text = ProductInfo(label.Text).ToString()
            End If
        End Sub
    
        Protected Sub Page_Load()
            If (ScriptManager1.IsInAsyncPostBack) Then
                Dim ProductInfo As SortedList = Me.ProductInfo
                For Each d As DataListItem In DataList1.Items
                    Dim label As Label = CType(d.FindControl("ProductIDLabel"), Label)
                    Dim textbox As TextBox = CType(d.FindControl("TextBox1"), TextBox)
                    If (textbox.Text.Length > 0) Then
                        ProductInfo(label.Text) = textbox.Text
                    End If
                Next
                Me.ProductInfo = ProductInfo
            End If
        End Sub
    </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>
    
    <%@ 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>
    

验证在异步回发过程中是否保留了数据

您现在可以查看在异步回发过程中,是如何保留来自 Web 服务的数据的了。

验证是否在异步回发过程中保留了 Web 服务数据

  1. 如果页没有处于运行状态,请按 Ctrl+F5 运行页。

  2. 单击列表中任一产品的“从 Web 服务获取数量”按钮,然后等待值在文本框中显示。

  3. 单击“类别 2”按钮。

  4. 单击“类别 1”按钮。

    注意,Web 服务先前返回的值仍然显示,即使异步回发执行完毕后也是如此。

回顾

本教程演示了将 UpdatePanel 控件用于 Web 服务的示例,该 Web 服务是在浏览器中通过 JavaScript 代码调用的。Web 服务会返回显示在 UpdatePanel 控件内部的数据。

无论是否使用 UpdatePanel 控件,从客户端脚本调用 Web 服务并使用返回的数据填充 DOM 元素的结果都是一样的。当页执行回发或发生异步回发时,以前通过使用客户端脚本显示的数据将丢失。为了避免此问题的发生,您可以使用服务器代码来保留数据,方法是使数据成为视图状态的一部分。这使得您可以在后续回发过程中继续保留数据。如果来自 Web 服务的数据显示在位于 UpdatePanel 控件外的 DOM 元素中,并且您不想在异步回发过程中继续保留这些数据,则您就不必为了保留这些数据而提供服务器代码。

由于触发 Web 服务调用的按钮在 UpdatePanel 控件内,因此 ChildrenAsTriggers 属性设置为 false。面板内的其他回发控件均定义为面板的触发器。

请参见

概念

向客户端脚本公开 Web 服务

从客户端脚本调用 Web 服务