ASP.NET AJAX: Клиентский объект PageRequestManager

ОГЛАВЛЕНИЕ

ASP.NET AJAX Framework также включает в себя богатую клиентскую библиотеку и модель событий, что позволяет разработчикам создавать и выполнять клиентские скрипты и даже обработчики которые включены в ASP.NET AJAX Framework. Данная статья начинает исследование разработки клиентской части при помощи ASP.NET AJAX Framework. В частности, она исследует объект клиентской стороны PageRequestManager, который включает в себя методы и события привязанные к механизму частичного постбэка. Короче говоря, мы можем использовать объект PageRequestManager для того, чтобы выполнить скрипт клиентской стороны сразу же после частичного постбэка.

Данная статья рассматривает основы клиентского объекта PageRequestManager и содержит примеры, которые показывают, как прервать и отменить частичные постбэки, а также как перейти в определенное место на странице после того, как завершится частичный постбэк. Примеры можно скачать по ссылке указанной в конце данной статьи. Читайте далее, чтобы узнать больше об этом!


 

События частичного постбэка с клиентской стороны

Как уже обсуждалось в статье "Используем UpdatePanel", всегда, когда веб-элемент управления вызывает постбэк в пределах UpdatePanel (например, когда нажата кнопка Button), вместо обычного постбэка UpdatePanel вызывает частичный постбэк. Частичный постбэк асинхронно связывается с веб-сервером и посылает значения полей со страницы. На сервере, страница обрабатывается, но браузеру возвращается только то содержимое в формате HTML, которое необходимо конкретной области соответствующего UpdatePanel. Данные области обновляются динамически. Вся эта тяжелая работа выполняется за нас клиентской и серверной частями ASP.NET AJAX Framework.

Наши задачи относительно страницы просты: просто добавить элемент UpdatePanel на страницу - и вся магическая функциональность AJAX произойдет автоматически. Но что случится, если нам понадобится больше власти над циклом частичного постбэка? Например, представьте что нам необходимо выполнить некоторый скрипт клиентской стороны после того, как будет завершен частичный постбэк? Или, что если мы хотим отменить частичный постбэк при выполнении какого-либо условия? Хорошей новостью является то, что такая настраиваемая функциональность доступна. Мы можем создать обработчики событий клиентской стороны, которые будут выполнятся в ответ на определенные события во время жизненного цикла частичного постбэка.

PageRequestManager обрабатывает частичный постбэк на клиентской стороне. Когда частичный постбэк инициируется, объект PageRequestManager совершает следующие действия на клиентской стороне:

  1. Вызов события initializeRequest event - это самое первое событие, которое вызывается во время жизненного цикла частичного постбэка и предоставляет нам возможность определить тот HTML элемент, который вызывал этот частичный постбэк и отменить его, если это необходимо.
  2. Вызов события beginRequest event - данное события вызывается как раз перед тем, как запрос будет послан на сервер. Элемент управления UpdateProgress использует данное событие чтобы отобразить его результат.
  3. Запрос отсылается на сервер и там страница обрабатывается.
  4. Вызов события pageLoading event происходит, когда сервер посылает ответ.
  5. Вызов события pageLoaded event. Данное событие вызывается когда обновляется содержимое страницы - посредством полного либо частичного постбэка..
  6. Вызов события endRequest event оповещает о завершении цикла частичного постбэка.

Учтите, что что  вышеперечисленные события  - это события клиентской стороны. Данные события вызываются в веб-браузере пользователя. Мы можем использовать данные события путем написания кода в JavaScript который свяжет обработчик события с одним из этих событий.


 

"Просмотр" событий, вызванный во время жизненного цикла частичного постбэка

Чтобы лучше понять стадии жизненного цикла частичного постбэка, я создал демо-код, включающий в себя элементы Button и UpdatePanel. Когда нажата кнопка Button, запускается частичный постбэк. Я также добавил скрипт клиентской стороны, который выводит сообщение при вызове каждого из этих событий. Вот сокращенная версия декларативной разметки страницы:

<asp:ScriptManager ID="ScriptManager1"  runat="server">
</asp:ScriptManager>

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
   <ContentTemplate>
      <asp:Button ID="PartialPostbackButton" runat="server" Text="Perform a Partial Postback" />
   </ContentTemplate>
</asp:UpdatePanel>

<asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1">
   <ProgressTemplate>
      <span style="color:Red;font-size:larger;">PERFORMING POSTBACK... THIS WILL TAKE FIVE SECONDS....</span>
   </ProgressTemplate>
</asp:UpdateProgress>

<div id="logOutput"></div> 

В разметке существуют четыре ключевых элемента:

  • Элемент управления ScriptManager. Как уже  говорилось в предыдущих статьях данной серии, ScriptManager является "двигателем", который так необходим любой странице, использующей AJAX.
  • Элемент управления UpdatePanel. Данный элемент включает в себя одиночный элемент управления Button (PartialPostbackButton), который при нажатии выполняет частичный постбэк. Я также создал серверный обработчик события для данного элемента Button, который выполняет код Thread.Sleep(5000), тем самым замедляя процесс на 5 секунд. Это  позволяет  искусственно  задержать обработку страницы  для того, чтобы лучше показать, какие события вызывает объект PageRequestManager до и после запроса на сервер .
  • Элемент управления UpdateProgress. Выводит сообщение во время 5-секундной задержки на сервере.
  • Элемент <div> с id logOutput. Мы добавим JavaScript код, который ведет запись всех сообщений на этот элемент во время выполнения различных обработчиков события PageRequestManager.

В дополнение к данной разметке страница включает в себя следующий код JavaScript. (Примечание: если вы добавляете JavaScript, который использует ASP.NET AJAX Framework, вы можете его добавить либо напрямую в ASP.NET страницу либо добавить в качестве внешнего скриптового файла. Данный код обладает кодом JavaScript, который был внедрен напрямую в страницу. Необходимо отметить, что при добавлении скрипта прямо в страницу, он должен быть размещен после элемента управления ScriptManager.)

<script type="text/javascript">
   // Создайте обработчики события для PageRequestManager
   var prm = Sys.WebForms.PageRequestManager.getInstance();

   prm.add_initializeRequest(PageRequestManager_initializeRequest);
   prm.add_beginRequest(PageRequestManager_beginRequest);
   prm.add_pageLoading(PageRequestManager_pageLoading);
   prm.add_pageLoaded(PageRequestManager_pageLoaded);
   prm.add_endRequest(PageRequestManager_endRequest);

   function PageRequestManager_initializeRequest(sender, args) {
      logEvent("initializeRequest");
   }

   function PageRequestManager_beginRequest(sender, args) {
      logEvent("beginRequest");
   }

   function PageRequestManager_pageLoading(sender, args) {
      logEvent("pageLoading");
   }

   function PageRequestManager_pageLoaded(sender, args) {
      logEvent("pageLoaded");
   }

   function PageRequestManager_endRequest(sender, args) {
      logEvent("endRequest");
   }

   var count = 1;
   function logEvent(msg) {
      var logOutput = document.getElementById('logOutput');
      var rightNow = new Date();
      logOutput.innerHTML += '(' + count + ') ' + msg + ' (' + rightNow.toTimeString() + ')<br />';
      count++;
   }
</script>

Первая строка кода получает копию объекта PageRequestManager. Далее, пять событий объекта PageRequestManager связываются с пятью обработчиками событий. Каждый из этих пяти обработчиков событий вызывает функцию logEvent, посылая сообщение для вывода. Функция logEvent обновляет внутренний код HTML в logOutput <div>, путем добавления сообщения и текущего времени.

Три скриншота, показанных ниже, демонстрируют данный код в действии. Первый скриншот показывает страницу сразу  же после того, как она была загружена. Заметьте, что log output уже срдержит одно значение для события pageLoaded. Как уже было отмечено ранее, событие pageLoaded объекта PageRequestManager вызывается тогда, когда содержимое страницы было обновлено либо при полном постбэке (в  тех случаях, когда страницу посещают в первый раз, либо    когда элемент управления, находящийся за пределами UpdatePanel, вызывает постбэк), либо при частичном постбэке.


Второй скриншот показывает страницу после того, как была нажата кнопка "Perform a Partial Postback" , но до того, как сервер пришлет ответ. Заметьте, что элемент UpdateProgress выводит свое сообщение (красный текст). Также были вызваны два новых клиентских обработчика событий: initializeRequest и beginRequest.


Когда ответ возвращен с веб-сервера, вызываются оставшиеся события объекта PageRequestManager: pageLoading, pageLoaded, и endRequest. В данный момент вы можете увидеть, что ответ был возвращени с сервера: элемент управления UpdateProgress больше не отображается и события pageLoading, pageLoaded, и endRequest указаны в журнале событий. Заметьте, что событие pageLoading было вызвано через 5 секунд после вызова beginRequest. Это происходит потому, что код серверной стороны, который срабатывает во время нажатия кнопки "Perform a Partial Postback", останавливается на 5 секунд.



 

Прерываение и отмена частичных постбэков

Событие initializeRequest объекта PageRequestManager предоставляет разработчикам возможность отмены постбэка, если есть такая необходимость. Более того, объект PageRequestManager включает в себя функцию, которая будучи выполненной прекращает все частичные постбэки - как те, что на данный момент обрабатываются, так и те, что находятся в очереди. Чтобы продемонстрировать прерывание и отмену постбэков, я создал демо-код похожий на тот, что мы только что рассмотрели. В дополнение к элементу управления Button, я добавил элемент Label в UpdatePanel, который выводит на экран время обновления UpdatePanel. Также я добавил два элемента за пределами UpdatePanel, которые используются для демонстрации возможности прекращения и отмены:

  • Элемент управления CheckBox (кнопка с независимой фиксацией ) названный CancelThisPostback , будучи отмеченным, вызывает отмену частичного постбэка
  • Кнопка при нажатии отменяет все ожидающие постбэки.
Страница содержит код в JavaScript, также похожий на предыдущий пример. Я обновил клиентский обработчик события PageRequestManager_initializeRequest, чтобы отменить частичный постбэк ,если отмечена кнопка с независимой фиксацией CancelThisPostback:
function PageRequestManager_initializeRequest(sender,  args) {
         
   logEvent("initializeRequest");
   
   // Если вам необходимо отменить данный постбэк
   var cb = document.getElementById('<%=CancelThisPostback.ClientID%>');
   if (cb.checked) {
      // То отменяем данный запрос постбэка
      args.set_cancel(true);

      logEvent("Postback canceled");
   }
}

Если отмечен флажок CancelThisPostback, частичный постбэк отменяется путем вызова args.set_cancel(true), а также добавляется сообщение в журнал, означающее то, что постбэк был отменен. Чтобы проверить данную функциональность, посетите страницу и нажмите на кнопку "Perform a Partial Postback". Так же, как и до этого, страница проходит через различные шаги, и результат отображен в журнале клиентских обработчиков события. А также время в UpdatePanel заменяется на текущее время. Теперь отметьте флажок "Cancel this postback" и нажмите на кнопку "Perform a Partial Postback". На этот раз частичный постбэк не произойдет.

Следующий скриншот показывает результат при отмеченом флажке "Cancel this postback". Заметьте, что клиентский журнал обработчикав событий имеет всего три записи:

  • pageLoaded - это было отображено в момент, когда страница была впервые загружена
  • initializeRequest - это сообщение было отображено, когда была нажата кнопка "Perform a Partial Postback", тем самым начиная цикл частичного постбэка
  • Postback canceled - постбэк был отменен.

Данный подход позволяет вам отменить текущий частичный постбэк из обработчика события initializeRequest. ASP.NET AJAX Framework также позволяет вам прервать все ожидающие частичные запросы постбэка. Чтобы осуществить это, вызовите функцию abortPostBack() объекта PageRequestManager. Чтобы продемонстрировать данную функциональность, я создал следующую клиентскую функцию, которая выполняется тогда, когда нажата кнопка "Abort Outstanding Partial Page Postbacks".

function AbortAllPostbacks() {
   var prm = Sys.WebForms.PageRequestManager.getInstance();
   prm.abortPostBack();
   
   logEvent("Partial page postbacks aborted");

Чтобы проверить данную функциональность, просмотрите данный пример страницы и нажмите кнопку "Perform a Partial Postback" (при этом не отмечайте флажок "Cancel this postback"). Это начнет цикл частичного постбэка. После того, как появится результат UpdateProgress, нажмите на "Abort Outstanding Partial Page Postbacks". Это прекратит текущий частичный постбэк и моментально вызовет событие endRequest. Следующий скриншот показывает клиентский журнал обработчиков событий в то время, когда нажата кнопка "Abort Outstanding Partial Page Postbacks" во время частичного постбэка. Вы можете убедиться, что частичный постбэк стартовал нормально, так как его события initializeRequest и beginRequest были вызваны, но во время обработки на сервере постбэк был прекращен. Это немедленно вызвало событие endRequest.



 

Переход на конкретный HTML элемент при завершении частичного постбэка

Недавно мне предоставилась возможность работать с коллегой, который использовал AJAX на странице по поиску информации в базе данных. В общем, пользователи вводят различные данные для фильтрации в текстовый элемент управления TextBox, нажимают на кнопку "Show Results", и результат будет отображен в отсортированной листаемой табличной сетке GridView под элементом управления TextBox. Элементы TextBox и GridView оба находились в UpdatePanel.

Страница была так организована, что кнопка "Show Results" была расположена внизу браузерного окна для пользователей, которые используют низкое разрешение экрана. После того, как значения для поиска (были) введены в элементы TextBox и была нажата кнопка "Show Results", табличная сетка GridView будет отображена под кнопкой "Show Results", но такие пользователи не увидят данные результаты, потому что они не помещаются на их экране.  Таким образом, им необходимо перейти вниз, чтобы увидеть результаты. Но поскольку AJAX предоставляет более сглаженную обработку, то пользователи не  могли заметить, что страница была обновлена. На экране не было никакого оповещения о том, что их действие (нажатие на кнопку "Show Results") привело к какому-либо результату, так как он был расположен ниже.

Решением данной проблемы является автоматическая прокрутка по странице к конкретному HTML элементу, расположеному прямо над результатом. Данная прокрутка необходима тогда, когда был завершен частичный постбэк.

Объект браузера window включает в себя метод scrollTo(x, y) , который переходит на конкретные координаты (x,y). Написав небольшой скрипт, вы сможете определить координаты (x,y) какого-либо HTML элемента. Мы нашли скрипт созданный Eric Pascarello, который содержит в себе всю даннуй функциональность в функции ScrollToElement(element), написанной на JavaScript . Мы вызвали данный метод путем передачи в обработчик события endRequest тот HTML элемент, на который необходимо перейти.

function PageRequestManager_endRequest(sender, args)  {
   // Переход на конкретный HTML элемент
   var scrollTo = document.getElementById('scrollHere');
   ScrollToElement(scrollTo);
}

Значение scrollHere, переданное в функцию getElementById(id), связано с элементом <div>, расположенным на странице прямо над выпадающим списком категорий.

Чтобы увидеть данную функциональность в действии, загрузите демо-код, приведенный в конце данной статьи. Два следующих скриншота демонстрируют данное поведение. Первый скриншот показывает страницу в то время, когда ее просматривают. В самом низу расположен выпадающий список , где пользователь сможет выбрать категорию. Как только он выберет категорию и нажмет кнопку "Display Matching Products", ниже списка будет отображена табличная сетка GridView, которая покажет все товары, принадлежащие выбранной категории.


По умолчанию, после нажатия на кнопку "Display Matching Products" пользовательское окно браузера останется в том же положении. Пользователю пришлось бы вручную перейти вниз, к результатам, и в том случае, если кнопка "Display Matching Products" уже была в самом низу их окна, они могли бы и не понять, что отчет о запрашиваемой информации уже был доступен ниже по странице.

Используя данный скрипт на странице, пользовательский браузер автоматически перейдет к результатам в момент, когда частичный постбэк будет завершен.


Веселого программирования!

Scott Mitchell

Исходный код примеров