搭配 Web 服務使用 UpdatePanel 控制項
更新:2007 年 11 月
ASP.NET Web 網頁中的 UpdatePanel 控制項可簡化網頁局部呈現,因為 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 Express 版。
具備 AJAX 能力的 ASP.NET 網站。
對 Northwind 資料庫的存取和 Web.config 檔案中定義的連接字串,名為 NorthwindConnectionString。如需如何建立連接字串的詳細資訊,請參閱 HOW TO:從 Web.config 檔案讀取連接字串。
建立 Web 服務
首先,您將建立可呼叫的 Web 服務。
建立 Web 服務傳回產品數量
在具備 AJAX 能力的 ASP.NET 網站中,建立新的 Web 服務檔案,名為 ProductQueryService.asmx。
如需如何建立 Web 服務的詳細資訊,請參閱從用戶端指令碼呼叫 Web 服務。
在 Web 服務程式碼中,匯入 N:System.Data、N:System.Data.SqlClient、System.Configuration 和 N: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 服務方法將會使用這些命名空間中的型別。
將 ProductQueryService 類別放在稱為 Samples 的命名空間內。
請將 ScriptServiceAttribute 屬性加入至類別。
這個屬性可讓用戶端指令碼叫用 Web 服務。
使用下列 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; }
程式碼會執行下列工作:
建立新的 SqlConnection 物件,這個物件使用名為 NorthwindConnectionString 的連接字串。
建立新的 SqlCommand 物件,這個物件包含 SQL 命令,可擷取指定之產品 ID 的庫存單位數目。
將傳回值設定為一個字串,做為傳送至瀏覽器的回應。
注意事項: 在此教學課程中,您可用 Web 方法以手動方式造成延遲。實際上,延遲並非由您造成。任何延遲都是因伺服器流量或需要長時間執行的 Web 服務程式碼 (例如,需長時間執行的資料庫查詢) 所造成。
儲存變更,然後按 CTRL+F5 在瀏覽器中檢視頁面。
按一下 [GetProductQuantity] 連結來呼叫 Web 方法。
在 [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 服務
在這個程序中,您將建立 JavaScript 檔案,以呼叫您在上一個程序中建立的 Web 服務。
建立 JavaScript 檔案以使用 Web 服務
建立新的 JScript 檔案,名為 ProductQueryScript.js。
將下列指令碼加入至檔案:
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 服務呼叫傳回結果時叫用這個函式。
建立顯示資料的 Web 網頁
接著,您將建立包含 UpdatePanel 控制項的 Web 網頁。UpdatePanel 控制項內的控制項會顯示 Northwind 資料庫中的產品資訊。
建立顯示產品的 Web 網頁
建立新的 Web 網頁並切換至 [設計] 檢視。
在工具箱的 [AJAX 擴充功能] 索引標籤中,按兩下 ScriptManager 控制項,將它加入至網頁。
按兩下工具箱中的 UpdatePanel 控制項,將 UpdatePanel 控制項加入至網頁。
按一下 UpdatePanel 控制項內部,然後在工具箱的 [資料] 索引標籤中,按兩下 DataList 控制項。
在 [DataList 工作] 面板中,從 [選擇資料來源] 清單選取 [<新增資料來源>]。
注意事項: 如果您未看到 [DataList 工作] 面板,請以滑鼠右鍵按一下 DataList 控制項,然後選取 [顯示智慧標籤]。
[資料來源組態] 精靈隨即出現。
選取 [資料庫],接受預設名稱 SqlDataSource1,然後按一下 [確定]。
在 [您的應用程式應該使用哪個資料連接來連接資料庫] 清單中,選取 [NorthwindConnectionString],然後按 [下一步]。
選取 [您希望如何從您的資料庫擷取資料] 底下的 [指定資料表或檢視的資料行],然後從清單中選取 [產品],再選取 [資料行] 清單中的 [ProductID] 和 [ProductName]。
按一下 [WHERE] 按鈕。
接著會顯示 [加入 WHERE 子句] 對話方塊。
選取 [資料行] 清單中的 [CategoryID],然後選取 [來源] 清單中的 [無]。
在對話方塊的 [參數屬性] 區段中,在 [值] 文字方塊中輸入 1。
按一下 [加入],將 WHERE 子句加入至 SQL 陳述式。
按一下 [確定],關閉 [加入 WHERE 子句] 對話方塊。
按 [下一步],然後按一下 [完成] 關閉精靈。
切換至 [原始碼] 檢視,確認 SqlDataSource 控制項類似下列範例:
<asp:SqlDataSource ID="SqlDataSource1" 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" 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>
切換至 [設計] 檢視。
選取 DataList 控制項,然後按一下 [DataList 工作] 面板中的 [編輯樣板]。
將兩個 Button 控制項加入至 UpdatePanel 控制項中的 [DataList] 控制項外面。
將第一個按鈕的 [ID] 屬性設定為 [Category1Button],並將它的 [Text] 屬性設定為 [Category 1]。將第二個按鈕的 [ID] 屬性設定為 [Category2Button],並將它的 [Text] 屬性設定為 [Category 2]。
選取 UpdatePanel 控制項,在 [屬性] 視窗中將 UpdateMode 屬性設定為 Conditional,並將 ChildrenAsTriggers 屬性設定為 false。
按一下 [觸發程序] 方塊上的省略符號 (…) 按鈕,然後在 [UpdatePanel 觸發程序集合編輯器] 對話方塊中,加入每一個分類按鈕做為非同步回傳觸發程序。
將第一個按鈕的 Click 事件處理常式設定為 Category1Button_Click,將第二個按鈕的 Click 事件處理常式設定為 Category2Button_Click。
切換至 [原始碼] 檢視,為這兩個按鈕建立下列事件處理常式:
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 參數。這可讓使用者在兩個分類之間切換。
儲存變更,然後按 CTRL+F5 在瀏覽器中檢視頁面。
按一下 [Category 2] 驗證網頁顯示新的資訊,但未重新整理整個網頁。
注意事項: 不要關閉網頁。
<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script > 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 > <title>Products Display</title> </head> <body> <form id="form1" > <div> <asp:ScriptManager ID="ScriptManager1" > </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" ChildrenAsTriggers="False" UpdateMode="Conditional"> <ContentTemplate> <asp:Button ID="Category1Button" Text="Category 1" OnClick="Category1Button_Click" /> <asp:Button ID="Category2Button" OnClick="Category2Button_Click" Text="Category 2" /> <asp:DataList ID="DataList1" DataKeyField="ProductID" DataSourceID="SqlDataSource1" Width="231px"> <ItemTemplate> ProductName: <asp:Label ID="ProductNameLabel" Text='<%# Eval("ProductName") %>'> </asp:Label><br /> ProductID: <asp:Label ID="ProductIDLabel" Text='<%# Eval("ProductID") %>'></asp:Label><br /> <br /> </ItemTemplate> </asp:DataList> <asp:SqlDataSource ID="SqlDataSource1" 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 > 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 > <title>Products Display</title> </head> <body> <form id="form1" > <div> <asp:ScriptManager ID="ScriptManager1" > </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" ChildrenAsTriggers="False" UpdateMode="Conditional"> <ContentTemplate> <asp:Button ID="Category1Button" Text="Category 1" OnClick="Category1Button_Click" /> <asp:Button ID="Category2Button" OnClick="Category2Button_Click" Text="Category 2" /> <asp:DataList ID="DataList1" DataKeyField="ProductID" DataSourceID="SqlDataSource1" Width="231px"> <ItemTemplate> ProductName: <asp:Label ID="ProductNameLabel" Text='<%# Eval("ProductName") %>'> </asp:Label><br /> ProductID: <asp:Label ID="ProductIDLabel" Text='<%# Eval("ProductID") %>'></asp:Label><br /> <br /> </ItemTemplate> </asp:DataList> <asp:SqlDataSource ID="SqlDataSource1" 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 服務傳回產品數量
在頁面中,切換至 [設計] 檢視。
選取 DataList 控制項,然後選取 [DataList 工作] 面板中的 [編輯樣板]。
將 TextBox 和 Button 控制項加入至項目範本。
在範本中現有的文字和標籤下方加入新的控制項。
選取按鈕,在 [屬性] 視窗中將 Text 屬性設定為從 Web 服務取得數量。
DataList 控制項的項目範本應該類似下圖。
選取 DataList 控制項,然後在 [屬性] 視窗的 [事件] 索引標籤中,按兩下 ItemDataBound 事件。
加入以下程式碼:
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 屬性。
將名為 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 服務已加入至網頁的資料。網頁第一次呈現時,清單是空的。在後續的非同步回傳期間,清單中可能會加入項目。
加入下列 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 中。如果沒有在檢視狀態中追蹤資料項目,就不會在後續非同步回傳中保留資料項目。
切換至 [設計] 檢視。
選取 ScriptManager 控制項。
在 [屬性] 視窗中,選取 [Services] 屬性,然後按一下省略符號 (…) 按鈕,顯示 [ServiceReference 集合編輯器] 對話方塊。
按一下 [加入] 以加入服務參考。
將服務參考的 [Path] 屬性設定為 ProductQueryService.asmx,這是您先前建立的 Web 服務。
加入服務參考可讓 ScriptManager 控制項產生用戶端 Proxy 類別,便能使用 JavaScript 呼叫 Web 服務。
按一下 [確定] 關閉 [ServiceReference 集合編輯器] 對話方塊。
選取 ScriptManager 控制項,然後在 [屬性] 視窗中選取 [Scripts] 屬性,再按一下省略符號 (…) 按鈕,顯示 [ScriptReference 集合編輯器] 對話方塊。
按一下 [加入] 以加入指令碼參考。
將指令碼參考的 [Path] 屬性設定為 ProductQueryScript.js,這是您先前建立的 JavaScript 檔案。
加入指令碼參考會造成 ScriptManager 控制項在 Microsoft AJAX Library 載入之後插入指令碼。
按一下 [確定] 關閉 [指令碼參考集合編輯器] 對話方塊。
儲存變更,然後按 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 > 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 > <title>Products Display</title> </head> <body> <form id="form1" > <div> <asp:ScriptManager ID="ScriptManager1" > <Services> <asp:ServiceReference Path="ProductQueryService.asmx" /> </Services> <Scripts> <asp:ScriptReference Path="ProductQueryScript.js" /> </Scripts> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" ChildrenAsTriggers="False" UpdateMode="Conditional"> <ContentTemplate> <asp:Button ID="Category1Button" Text="Category 1" OnClick="Category1Button_Click" /> <asp:Button ID="Category2Button" OnClick="Category2Button_Click" Text="Category 2" /> <asp:DataList ID="DataList1" DataKeyField="ProductID" DataSourceID="SqlDataSource1" Width="400px" OnItemDataBound="DataList1_ItemDataBound"> <ItemTemplate> ProductName: <asp:Label ID="ProductNameLabel" Text='<%# Eval("ProductName") %>'> </asp:Label><br /> ProductID: <asp:Label ID="ProductIDLabel" Text='<%# Eval("ProductID") %>'></asp:Label><br /> <asp:TextBox ID="TextBox1" ></asp:TextBox> <asp:Button ID="Button1" Text="Get Quantity from Web Service" /><br /> </ItemTemplate> </asp:DataList> <asp:SqlDataSource ID="SqlDataSource1" 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 > 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 > <title>Products Display</title> </head> <body> <form id="form1" > <div> <asp:ScriptManager ID="ScriptManager1" > <Services> <asp:ServiceReference Path="ProductQueryService.asmx" /> </Services> <Scripts> <asp:ScriptReference Path="ProductQueryScript.js" /> </Scripts> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" ChildrenAsTriggers="False" UpdateMode="Conditional"> <ContentTemplate> <asp:Button ID="Category1Button" Text="Category 1" OnClick="Category1Button_Click" /> <asp:Button ID="Category2Button" OnClick="Category2Button_Click" Text="Category 2" /> <asp:DataList ID="DataList1" DataKeyField="ProductID" DataSourceID="SqlDataSource1" Width="400px" OnItemDataBound="DataList1_ItemDataBound"> <ItemTemplate> ProductName: <asp:Label ID="ProductNameLabel" Text='<%# Eval("ProductName") %>'> </asp:Label><br /> ProductID: <asp:Label ID="ProductIDLabel" Text='<%# Eval("ProductID") %>'></asp:Label><br /> <asp:TextBox ID="TextBox1" ></asp:TextBox> <asp:Button ID="Button1" Text="Get Quantity from Web Service" /><br /> </ItemTemplate> </asp:DataList> <asp:SqlDataSource ID="SqlDataSource1" 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 服務資料的留存
如果網頁未執行,請按 CTRL+F5 執行網頁。
對清單中任何產品按一下 [從 Web 服務取得數量] 按鈕,然後等待文字方塊中顯示值。
按一下 [Category 2] 按鈕。
按一下 [Category 1] 按鈕。
請注意,即使在非同步回傳之後,Web 服務傳回的值仍會出現。
檢閱
這個教學課程示範使用 UpdatePanel 程式碼和從瀏覽器中的 JavaScript 程式碼呼叫的 Web 服務。Web 服務傳回的資料顯示在 UpdatePanel 控制項內。
無論使用或不使用 UpdatePanel 控制項,從用戶端指令碼呼叫 Web 服務並將傳回的資料填入 DOM 項目的結果都一樣。當網頁執行回傳或當發生非同步回傳時,先前以用戶端指令碼顯示的資料會不見。若要避免這個問題,您可以使用伺服端程式碼,將資料標記為檢視狀態的一部分,以保存資料。這可讓您在後續的非同步回傳期間保存資料。如果 Web 服務傳回的資料顯示在 UpdatePanel 控制項外面的 DOM 項目中,且您不想要在非同步回傳期間保存資料,則不需要提供伺服端程式碼來維護資料。
因為觸發 Web 服務呼叫的按鈕位於 UpdatePanel 控制項內,所以 ChildrenAsTriggers 屬性設定為 false。面板內的其他回傳控制項定義為面板的觸發程序。