Предоставление приоритета определенным асинхронным обратным запросам
Обновлен: Ноябрь 2007
По умолчанию, когда страница делает несколько асинхронных обратных вызовов в одно время, чаще всего обратный вызов имеет приоритет. В некоторых случаях может потребоваться предоставить приоритет для конкретного асинхронного обратного вызова и отменить для других.
В этом учебнике будет показано управление предоставлением приоритета обратным запросам. Можно сделать это, создав обработчик событий для события initializeRequest класса PageRequestManager. Сведения о последовательности вызова событий в классе PageRequestManager содержатся в разделе Работа с событиями класса PageRequestManager.
Обязательные компоненты
Для реализации процедур в собственной среде разработки вам потребуются:
Microsoft Visual Studio 2005 или Microsoft Visual Web Developer, экспресс-выпуск.
Веб-узел ASP.NET с поддержкой технологии AJAX.
Создание сценария, который предоставляет приоритет для определенных элементов обратного запроса
Можно начать с создания кода ECMAScript (JavaScript), который управляет асинхронным обратным вызовом в обозревателе.
Создание сценария, который предоставляет приоритет для определенных элементов обратного запроса
В веб-узле ASP.NET добавьте файл JScript и назовите его PostbackPrecedence.js.
Добавьте следующий сценарий в файл:
Sys.Application.add_load(ApplicationLoadHandler) function ApplicationLoadHandler(sender, args) { if (!Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()) { Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(InitializeRequest); } } var divElem = 'AlertDiv'; var messageElem = 'AlertMessage'; var exclusivePostBackElement = 'Button1'; var lastPostBackElement; function InitializeRequest(sender, args) { var prm = Sys.WebForms.PageRequestManager.getInstance(); if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id === exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } else if (lastPostBackElement !== exclusivePostBackElement) { prm.abortPostBack(); } } else if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id !== exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } } lastPostBackElement = args.get_postBackElement().id; } function ActivateAlertDiv(visString, msg) { var adiv = $get(divElem); var aspan = $get(messageElem); adiv.style.visibility = visString; aspan.innerHTML = msg; } if(typeof(Sys) !== "undefined") Sys.Application.notifyScriptLoaded();
Sys.Application.add_load(ApplicationLoadHandler) function ApplicationLoadHandler(sender, args) { if (!Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()) { Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(InitializeRequest); } } var divElem = 'AlertDiv'; var messageElem = 'AlertMessage'; var exclusivePostBackElement = 'Button1'; var lastPostBackElement; function InitializeRequest(sender, args) { var prm = Sys.WebForms.PageRequestManager.getInstance(); if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id === exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } else if (lastPostBackElement !== exclusivePostBackElement) { prm.abortPostBack(); } } else if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id !== exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } } lastPostBackElement = args.get_postBackElement().id; } function ActivateAlertDiv(visString, msg) { var adiv = $get(divElem); var aspan = $get(messageElem); adiv.style.visibility = visString; aspan.innerHTML = msg; } if(typeof(Sys) !== "undefined") Sys.Application.notifyScriptLoaded();
Сценарий выполняет следующие задачи:
Определяет обработчик для события load из класса Sys.Application. Обработчик в свою очередь регистрирует обработчик с именем InitializeRequest для события initializeRequest класса PageRequestManager.
Определяет обработчик InitializeRequest, чтобы проверить, выполняется ли в данный момент асинхронный обратный вызов, и чтобы определить имя элемента, сделавшего обратный запрос. Если элемент, вызвавший обратный запрос, является одним из заданных монопольных элементов обратной передачи (одним из имеющих приоритет), то новый обратный запрос отменяется заданием свойства cancel класса InitializeRequestEventArgs.
Определяет функцию ActivateAlertDiv, переключающую видимость элемента <div> на странице, чтобы отобразить сообщения.
Использование сценария с элементом управления UpdatePanel
В этой процедуре будут использоваться сценарии, созданные на странице. Страница содержит одну кнопку, обратный запрос которой имеет приоритет над обратными запросами от другой кнопки на странице.
Создание страницы для уверенности в том, что один обратный запрос имеет приоритет над другими обратными запросами
Создайте новую однофайловую веб-страницу и переключитесь в режим конструктора.
На вкладке AJAX-расширения панели элементов дважды щелкните элемент управления ScriptManager, чтобы добавить его на страницу.
Дважды сделайте двойной щелчок на элементе управления UpdatePanel для добавления двух экземпляров элемента управления на страницу.
В каждом элементе управления UpdatePanel с вкладки Стандартные панели элементов добавьте элементы управления Label и Button.
Установите Text элементов управления Label с обоих панелей в значение Panel Initially Rendered.
Дважды щелкните каждый элемент управления Button для добавления обработчика для события Click каждой кнопки.
Добавьте следующий код для обработчиков Click, который искусственно создает задержку и затем отображает текущее время на панели, от которой произошел обратный запрос:
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(4000) Label1.Text = "Last update from server " & DateTime.Now.ToString() End Sub Protected Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(1000) Label2.Text = "Last update from server " & DateTime.Now.ToString() End Sub
protected void Button1_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(4000); Label1.Text = "Last update from server " + DateTime.Now.ToString(); } protected void Button2_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(1000); Label2.Text = "Last update from server " + DateTime.Now.ToString(); }
Примечание. Обработчики события Click намеренно представляют задержку для этого примера. На практике в такой задержке нет необходимости. Вместо этого задержка будет возникать в результате трафика сервера или серверного кода, для обработки которого требуется длительное время (например при долго выполняющихся запросах к базе данных).
Перейдите к представлению источника, а затем добавьте следующий блок <style> в элемент <head> страницы:
<style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style>
<style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style>
Правила стиля определяют размер элемента <div>, отображаемого элементом управления UpdatePanel и элемента <div>, который предупреждает пользователя при отмене обратного запроса.
Добавьте следующую разметку внутрь элемента <form> на странице:
<div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div>
<div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div>
Разметка определяет элемент <div>, который выведет сообщение, когда асинхронный обратный вызов будет отменен, так как один уже выполняется.
Перейдите в представление конструирования.
Щелкните внутри первого элемента управления UpdatePanel и затем с вкладки расширения AJAX в панели элементов добавьте элемент управления UpdateProgress.
Щелкните внутри элемента управления UpdateProgress и введите Panel1 updating ….
Это задает свойство ProgressTemplate.
Выберите элемент управления UpdateProgress и задайте в окне свойств свойство AssociatedUpdatePanelID в значение UpdatePanel1.
Страница в конструкторе будет напоминать представленную на следующем рисунке:
Щелкните внутри второго элемента управления UpdatePanel и добавьте второй элемент управления UpdateProgress.
Щелкните внутри элемента управления UpdateProgress и введите Panel2 updating ….
Это задает свойство ProgressTemplate.
Выберите элемент управления UpdateProgress и задайте в окне свойств свойство AssociatedUpdatePanelID в значение UpdatePanel2.
Страница в конструкторе будет напоминать представленную на следующем рисунке:
Выберите элемент управления ScriptManager.
В окне свойств выберите свойство сценарии и нажмите кнопку с многоточием (…), чтобы отобразить диалоговое окно редактор коллекции ScriptReference.
Нажмите кнопку добавить для добавления ссылки сценария.
Задайте свойство путь ссылки сценария в значение PostbackPrecedence.js, который является созданным ранее файлом JavaScript.
Добавление ссылки сценария с помощью коллекции Scripts объекта ScriptManager гарантирует, что сценарий загружается после загрузки Microsoft AJAX (библиотека).
Нажмите кнопку ОК чтобы закрыть диалоговое окно редактора коллекции ScriptReference.
Сохраните изменения и нажмите CTRL + F5 для просмотра страницы в обозревателе.
Нажмите кнопку на первой панели и затем нажмите кнопку на второй панели.
Появится сообщение, указывающее, что новый обратный запрос было отменен. Кнопка с первой панели должна завершить до инициации нового обратного запроса. Файл сценария содержит логику для обеспечения такого поведения.
Нажмите кнопку на второй панели и затем нажмите кнопку на первой панели.
Кнопка со второй панели не имеет приоритета, поскольку это не было закодировано в файле сценария. Таким образом, не выдается предупреждение, и новый обратный запрос инициируется кнопкой с первой панели. По умолчанию это поведение асинхронного обратного запроса — последний обратный запрос имеет приоритет.
<%@ 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 Button1_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(4000) Label1.Text = "Last update from server " & DateTime.Now.ToString() End Sub Protected Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(1000) Label2.Text = "Last update from server " & DateTime.Now.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Postback Precedence Example</title> <style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Scripts> <asp:ScriptReference Path="PostBackPrecedence.js" /> </Scripts> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 1</strong><br /> This postback takes precedence.<br /> <asp:Label ID="Label1" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" /> <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> Panel1 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <asp:UpdatePanel ID="UpdatePanel2" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 2</strong><br /> <asp:Label ID="Label2" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" /> <asp:UpdateProgress ID="UpdateProgress2" runat="server" AssociatedUpdatePanelID="UpdatePanel2"> <ProgressTemplate> Panel2 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div> </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 Button1_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(4000); Label1.Text = "Last update from server " + DateTime.Now.ToString(); } protected void Button2_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(1000); Label2.Text = "Last update from server " + DateTime.Now.ToString(); } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Postback Precedence Example</title> <style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Scripts> <asp:ScriptReference Path="PostBackPrecedence.js" /> </Scripts> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 1</strong><br /> This postback takes precedence.<br /> <asp:Label ID="Label1" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" /> <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> Panel1 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <asp:UpdatePanel ID="UpdatePanel2" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 2</strong><br /> <asp:Label ID="Label2" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" /> <asp:UpdateProgress ID="UpdateProgress2" runat="server" AssociatedUpdatePanelID="UpdatePanel2"> <ProgressTemplate> Panel2 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div> </div> </form> </body> </html>
Обзор
В этом учебнике показано назначение приоритета конкретному асинхронному обратному запросу (то есть для завершения обработки) до запуска другого асинхронного обратного запроса. Логика для реализации такого поведения находится в JavaScript файле, который включается в качестве ссылки сценария для страницы. Сценарий может быть настроен таким образом, чтобы все текущие асинхронные обратные вызовы должны завершаться до запуска любого нового. Тем не менее следует тщательно продумать макет при указании приоритета обратного запроса.
См. также
Основные понятия
Работа с событиями класса PageRequestManager