Использование элемента управления UpdatePanel c веб-службой
Обновлен: Ноябрь 2007
Элемент управления UpdatePanel упрощает частичную отрисовку страниц ASP.NET, поскольку функциональность AJAX в ASP.NET осуществляет обработку асинхронных запросов и обновлений обратной передачи автоматически. Функциональность AJAX также позволяет осуществлять вызовы веб-служб ASP.NET, используя ECMAScript (JavaScript) в обозревателе. Одним из преимуществ вызова веб-служб с помощью клиентского сценария является то, что ожидание ответа от веб-службы не приводит к блокировке обозревателя. Пользователи могут продолжать работу, не дожидаясь, когда веб-служба завершит обработку запроса.
В этом руководстве демонстрируется, как использовать веб-службу с помощью элемента управления UpdatePanel. Функция JavaScript вызывает веб-службу, чтобы извлечь данные, которые потом использует для заполнения элементов DOM в элементе управления UpdatePanel. Серверный код сохраняет полученные от веб-службы данные в промежутках между асинхронной обратной передачей.
В этом разделе предполагается, что вы знакомы с элементом управления UpdatePanel и веб-службами. В противном случае ознакомьтесь со следующими разделами:
Обязательные компоненты
Для реализации процедур в собственной среде разработки потребуется:
Microsoft Visual Studio 2005 или Visual Web Developer, экспресс-выпуск.
Веб-узел ASP.NET с поддержкой AJAX.
Доступ к базе данных "Northwind" и строка соединения с именем NorthwindConnectionString, определенная в файле Web.config. Дополнительные сведения о создании строки соединения см. в разделе Практическое руководство. Чтение строк соединения из файла Web.config.
Создание веб-службы
Для начала будет создана вызываемая веб-служба.
Создание веб-службы, возвращающей количество продукции
На веб-узле ASP.NET, поддерживающем AJAX, создайте новый файл веб-службы с именем ProductQueryService.asmx.
Дополнительные сведения о создании веб-служб см. в разделе Вызов веб-служб из клиентского сценария.
В коде веб-службы импортируйте пространства имен 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;
Типы из этих пространств имен будут использоваться в методе веб-службы, который будет создан в дальнейшем.
Поместите класс ProductQueryService в пространство имен Samples.
Добавьте в класс атрибут ScriptServiceAttribute.
Этот атрибут позволяет вызывать веб-службу из клиентского сценария.
Замените метод HelloWorld, определенный по умолчанию, следующим методом GetProductQuantity:
<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 для извлечения числа единиц хранения по заданному идентификатору товара.
Заносит в возвращаемое значение строку, которая будет использоваться в качестве ответа, отправляемого обозревателю.
Примечание. В этом руководстве в методе веб-службы вводится искусственная задержка. На практике в такой задержке нет необходимости. Вместо этого все задержки будут возникать из-за серверного трафика или долговременного выполнения кода метода веб-службы, например, в результате длительного запроса к базе данных.
Сохраните изменения и нажмите CTRL + F5 для просмотра страницы в обозревателе.
Щелкните ссылку GetProductQuantity, чтобы вызвать метод веб-службы.
В поле productID введите значение 6 и нажмите кнопку Invoke.
Количество товаров будет возвращено в обозреватель в формате XML. Это показывает, что веб-служба работает, как задумано.
<%@ 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, вызывающего веб-службу
В этой процедуре будет создан файл JavaScript, вызывающий веб-службу, созданную в предыдущей процедуре.
Создание файла JavaScript, использующего веб-службу
Создайте новый файл 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 веб-службы.
Создайте функцию OnSucceeded, которая будет вызываться, когда вызов веб-службы возвращает результат.
Создание веб-страницы для отображения данных
Далее будет создана веб-страница, содержащая элемент управления UpdatePanel. Элементы управления внутри элемента управления UpdatePanel будут отображать сведения о товарах из базы данных "Northwind".
Создание веб-страницы для отображения товаров
Создайте новую веб-страницу и переключитесь в режим конструктора.
На вкладке AJAX-расширения панели элементов дважды щелкните элемент управления ScriptManager, чтобы добавить его на страницу.
Дважды щелкните элемент управления UpdatePanel в панели элементов, чтобы добавить его на страницу.
Щелкните внутри элемента управления UpdatePanel и на вкладке Данные в панели элементов дважды щелкните элемент управления DataList.
В панели Задачи DataList выберите в списке Выбор источника данных пункт <Новый источник данных…>.
Примечание. Если панель Задачи DataList не отображается, щелкните правой кнопкой мыши элемент управления DataList и выберите команду Показать смарт-тег.
Появится мастер настройки источника данных.
Выберите параметр База данных, оставьте заданное по умолчанию имя SqlDataSource1 и нажмите кнопку ОК.
В списке Какое подключение ваше приложение должно использовать для работы с базой данных? выберите NorthwindConnectionString и нажмите кнопку Далее.
В списке How would you like to retrieve data from your database выберите Specify columns from a table or view, выберите в списке таблицу Товары, а в списке Столбцы выберите Код товара и Наименование.
Нажмите кнопку WHERE.
Будет отображено диалоговое окно Добавить предложение WHERE.
В списке Столбцы выберите Категория, а в списке Источник выберите Нет.
В разделе Свойства параметра диалогового окна в текстовом поле Значение введите 1.
Нажмите кнопку Добавить, чтобы добавить предложение WHERE в инструкцию SQL.
Нажмите кнопку ОК, чтобы закрыть диалоговое окно Добавить предложение WHERE.
Нажмите кнопку Далее, а затем кнопку Готово, чтобы закрыть мастер.
Переключитесь в режим исходного кода и удостоверьтесь в том, что элемент управления 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>
Перейдите в режим конструктора.
Выберите элемент управления DataList и в панели Задачи DataList выберите Редактирование шаблонов.
Добавьте два элемента управления Button в элемент управления UpdatePanel так, чтобы они находились вне элемента управления DataList.
.Задайте для свойства ID первой кнопки значение Category1Button, а для свойства Text этой же кнопки — значение Категория 1. Задайте для свойства ID второй кнопки значение Category2Button, а для свойства Text этой же кнопки — значение Категория 2.
Выберите элемент управления UpdatePanel и в окне "Свойства" задайте для свойства UpdateMode значение Conditional, а для свойства ChildrenAsTriggers — значение false.
В поле Triggers нажмите кнопку с многоточием ("…") и добавьте в диалоговом окне Редактор коллекции UpdatePanel Trigger обе кнопки категорий в качестве триггеров асинхронной обратной передачи.
В качестве обработчика событий 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"; }
Код обработчиков событий задает параметр CategoryID коллекции SelectParameters в элементе управления SqlDataSource в зависимости от того, какая кнопка была нажата. Это дает пользователям возможность переключаться между двумя этими категориями.
Сохраните изменения и нажмите CTRL+F5 для просмотра страницы в обозревателе.
Нажмите кнопку Категория 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 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>
Вызов веб-службы для извлечения данных
Теперь следует вызвать веб-службу, используя созданный ранее файл JavaScript. Код JavaScript code использует возвращенные данные для заполнения элементов DOM в элементе управления UpdatePanel. Серверный код на странице сохраняет данные, полученные от веб-службы, и добавляет эти данные в состояние просмотра страницы. Это позволяет сохранить данные при последующей асинхронной обратной передаче.
Использование веб-службы для получения количества товара
Переключитесь в режим конструктора на странице.
Выберите элемент управления DataList и в панели Задачи DataList выберите Редактирование шаблонов.
Добавьте к шаблону элемента элемент управления TextBox и элемент управления Button.
Поместите новые элементы управления под существующим текстом и подписями шаблона.
Выберите кнопку и в окне "Свойства" задайте для ее свойства Text значение Получить количество через веб-службу.
Шаблон элемента для элемента управления 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, которая, в свою очередь, обращается к веб-службе.
Значение текстового поля задается в том случае, если следящее свойство ProductInfo содержит ключ идентификатора товара. Свойство 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, отслеживающим данные, добавленные на страницу из веб-службы. При первой отрисовке страницы список будет пуст. При последующей асинхронной обратной передаче в список могут добавляться элементы.
Добавьте следующий обработчик событий 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; } }
Этот код проверяет, является ли запрос асинхронной обработкой. Если это так, то все элементы данных, добавленные веб-службой, добавляются в свойство ProductInfo. Это позволяет отслеживать их в состоянии просмотра и отображать их в панели UpdatePanel при последующих частичных обновлениях страницы. Если элементы данных не отслуживаются в состоянии просмотра, то они не сохраняются при последующей асинхронной обратной передаче.
Перейдите в режим конструктора.
Выберите элемент управления ScriptManager.
В окне "Свойства" выберите свойство Services и нажмите кнопку с многоточием ("…"), чтобы открыть диалоговое окно Редактор коллекции ServiceReference.
Нажмите кнопку ОК, чтобы добавить ссылку на службу.
Для свойства Path ссылки на службу задайте значение "ProductQueryService.asmx" — это веб-служба, созданная ранее.
Добавление ссылки на службу приводит к тому, что элемент управления ScriptManager создает клиентские классы прокси, чтобы веб-службу можно было вызывать с использованием JavaScript.
Нажмите кнопку ОК, чтобы закрыть диалоговое окно Редактор коллекции ServiceReference.
Выберите элемент управления ScriptManager, после чего в окне "Свойства" выберите свойство Scripts и нажмите кнопку с многоточием ("…"), чтобы открыть диалоговое окно Редактор коллекции ScriptReference.
Нажмите кнопку Добавить, чтобы добавить ссылку на сценарий.
Задайте для свойства Path ссылки на сценарий значение "ProductQueryScript.js" — это файл JavaScript, созданный ранее.
Добавление ссылки на сценарий приводит к тому, что элемент управления ScriptManager вставляет сценарий после того, как загрузится Microsoft AJAX (библиотека).
Нажмите кнопку ОК, чтобы закрыть диалоговое окно Редактор коллекции ScriptReference.
Сохраните изменения и нажмите CTRL+F5 для просмотра страницы в обозревателе.
На странице будут отображены товары из базы данных "Northwind", код категории которых равен 1. Текстовые поля рядом с товарами будут пустыми, поскольку вызовы веб-службы не выполнялись.
<%@ 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>
Проверка сохранения данных при асинхронной обратной передаче
Теперь можно видеть, как данные, полученные от веб-службы, сохраняются при асинхронной обратной передаче.
Проверка сохранения данных, полученных от веб-службы, при асинхронной обратной передаче
Если страница не запущена, нажмите клавиши CTRL+F5, чтобы запустить ее.
Нажмите кнопку Получить количество через веб-службу для любого товара в списке, и подождите, когда в текстовом поле появится значение.
Нажмите кнопку Категория 2.
Нажмите кнопку Категория 1.
Обратите внимание, что значение, возвращенное веб-службой ранее, отображается даже после асинхронной обратной передачи.
Итог
В этом руководстве был приведен пример использования элемента управления UpdatePanel вместе с веб--службой, вызываемой из кода JavaScript в обозревателе. Веб-служба возвращает данные, отображаемые внутри элемента управления UpdatePanel.
Результаты вызова веб-службы из клиентского сценария и заполнения элементов DOM полученными данными будут одинаковыми вне зависимости от использования элементов управления UpdatePanel. Когда на странице выполняется обратная отправка или происходит асинхронная обратная передача, данные, отображавшиеся ранее при использовании клиентского сценария, будут утеряны. Чтобы избежать этой проблемы, можно использовать серверный код, сохраняющий данные путем внесения их в состав состояния просмотра. Это позволяет сохранять данные при асинхронной обратной передаче. Если данные, полученные от веб-службы, отображаются в элементах DOM, расположенных за пределами элементов управления UpdatePanel, и сохранять данные при асинхронной обратной передаче не требуется, то серверный код сохранения использовать необязательно.
Поскольку кнопка, инициирующая вызов веб-службы, находится внутри элемента управления UpdatePanel, для свойства ChildrenAsTriggers задается значение false. Другие элементы управления обратной передачи внутри панели определяются в качестве триггеров панели.