ASP.NET AJAX: используем закладки и браузерную кнопку 'Назад'

ОГЛАВЛЕНИЕ

AJAX-приложения предоставляют более интерактивные пользовательские интерфейсы , путем замены традиционных полноценных постбэков более плавными и эффективными частичными постбэками. Данные частичные постбэки выполняются в браузере асинхронно при помощи кода JavaScript. Когда пользователь нажимает на ссылку либо отсылает форму (посредством полноценного постбэка), браузер автоматически добавляет страницу в браузерную историю. Это позволяет пользователю использовать кнопки Back (Назад) и Forward (Вперед) для того, чтобы передвигаться по данной истории. Тем не менее, частичные постбэки, исполняемые AJAX-приложениями, не заставляют браузер регистрировать что-либо в истории. Как следствие, если пользователь посещает страницу, использующую AJAX, выполняет несколько частичных постбэков, и затем нажимает кнопку Back (Назад), то он не будет возвращен на страницу, отображенную до последнего постбэка. На самом деле, он будет возвращен на страницу, которая была отображена до того, как была открыта данная страница, использующая AJAX.

Хорошей новостью является то, что, начиная с ASP.NET 3.5 SP 1, элемент управления ScriptManager в ASP.NET AJAX Framework включает в себя такую функцию, как создание точек истории на страницах, использующих AJAX. Добавление точки истории создает запись в браузерной истории для определенного состояния страницы. Более того, данное состояние страницы зашифровано в строке запроса браузера - это означает, что пользователи смогут установить закладку на определенной странице AJAX приложения.

Данная статья демонстрирует способ добавления точек истории при помощи элемента ScriptManager. В частности, она рассматривает способ записи точек истории во время того, как пользователь листает или сортирует GridView. Читайте далее, чтобы больше узнать об этом!


 

Краткий обзор возможности хранения истории в ASP.NET AJAX

Как уже было отмеченововведении, возможность хранения истории в ASP.NET AJAX Framework становится реальной в ASP.NET версии 3.5, SP 1. Изначально, это было частью Microsoft ASP.NET Futures, хотя на тот момент данная функциональность была реализована при помощи элемента управления History. В ASP.NET 3.5 SP1, данная функциональность была передана элементу ScriptManager.

Свойство хранения истории позволяет разработчику страницы создавать "состояния" в жизненном цикле страницы, использующей AJAX. Данное "состояние" создается путем добавления точки истории. Как только они определены, данные "состояния" можно достичь, используя кнопку Back в браузере; более того, они могут быть добавлены в закладки. Если вы используете GMail в качестве вашего почтового сервера, то вы уже наверняка сталкивались с AJAX-приложением, которое использует данные "состояния" истории. Когда вы читаете письмо в GMail, данное сообщение загружается асинхронно при помощи JavaScript-вызовов обратно на Google сервера. Строка запроса браузера также будет обновлена, и будет включать в себя ID сообщения. Если вы нажмете кнопку Back, вы вернетесь на страницу входящих писем GMail (а не на страницу, которую вы посетили до того, как зайти на GMail). В то же время вы можете добавить страницу в закладки в то время, как вы читаете почтовое сообщение. Если вы решите посетить ее позже, то интерфейс GMail автоматически загрузит то самое сообщение.

Возможность использования истории в ASP.NET AJAX работает следующим образом:

  • Вы, разработчик страницы, решаете, какие действия будут вызывать добавление точек истории. Данные действия могут быть действиями клиентской стороны (нажатие на HTML элемент), либо действиями серверной стороны, вызванными частичным постбэком. Набор действий, которые должны создавать точку истории, зависят от веб-страницы и ее пользовательского интерфейса. В конце данной статьи приводится Приложение, доступное для загрузки и включающее в себя две страницы, которые отображают сортируемый и листаемый GridView. На одной странице каждое сортировочное действие вызывает вставку точки истории; на другой - и сортировка, и листинг вызывают вставку точки истории.
  • Когда запускается действие вам необходимо добавить точку истории, если это именно то действие, которое должно вызывать вставку точки истории. Это выполнимо путем создания какой-то строки, которая моделирует данное состояние, а также регистрации данного состояния истории при помощи элемента управления ScriptManager. Вы также можете обновить заголовок (Title) страницы, основываясь на состоянии, тем самым область заголовка браузера и информация об истории включают в себя подходящее описание каждого состояния.
  • Наконец, вам нужно создать обработчик для события Navigate в ScriptManager. Данное событие вызывается в том случае, если  пользователь достигнет страницы, путем нажатия кнопок Back (Назад) или Forward (Вперед), либо посещает страницу, используя закладку. Обработчик события Navigate отвечает за восстановление состояния истории.

Вкратце: возможность хранения истории позволяет вам сохранять текущее состояние приложения в ответ на определенные действия пользователя и тем самым внедряет информацию о текущей странице в историю браузера, кодируя состояние, которое вы указываете в строке запроса. Позже, если пользователь возвратится к данному "состоянию" нажатием кнопки Back или Forward, либо используя закладку, вам необходимо восстановить данное состояние.

Остальная часть данной статьи рассматривает способ сохранения точки истории в момент, когда сортируется GridView, а также способ восстановления того состояния при необходимости. Начнем!


 

Листание и сортировка по товарам на веб странице, использующей AJAX

Листание и сортировка по товарам на веб-странице, использующей AJAX До того, как мы начнем рассматривать возможность сохранения истории в ASP.NET AJAX, мы создадим и продемонстрирует стандартное поведение AJAX относительно кнопки Back (Назад) и закладок. Данный пример для закрузки  приводится в конце статьи, и включает в себя ASP.NET-страницу, названную StandardBehavior.aspx и содержащую сортируемый и листаемый GridView, который перечисляет содержимое таблицы товаров (Products) в базе данных Northwind. Элемент GridView расположен в пределах UpdatePanel , тем самым любые постбэки, вызванные GridView, обрабатываются как частичные постбэки.

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

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
   <ContentTemplate>
      <asp:GridView ID="gvProducts" runat="server" AllowPaging="True"
      AllowSorting="True" AutoGenerateColumns="False" ...>
         ...
      </asp:GridView>
   </ContentTemplate>
</asp:UpdatePanel>

В дополнение к UpdatePanel и GridView страница включает в себя элемент ScriptManager. Как уже обсуждалось в первой статье данной серии ("Основы AJAX и начало работы с Microsoft's ASP.NET AJAX Framework"), элемент ScriptManager должен присутствовать на любой странице, использующей ASP.NET AJAX Framework, так как он является связью между серверной стороной и клиентской на страницах, использующих AJAX. Как мы увидим далее, элемент ScriptManager также является ключевым при добавлении точек истории и восстановлении предыдущего состояния.

Посетите страницу StandardBehavior.aspx и отсортируйте табличную сетку. При каждом нажатии на ссылку сортировки или листинга частичный постбэк обеспечивает обновление табличной сетки. Поскольку используются частичные постбэки, то браузер не хранит никакой истории о сортировке и листинге. Вследствие этого, если вы нажмете на кнопку Back, то вы не будете возвращены на предыдущее состояние сортировки или страницы. На самом деле, вы будете возвращены на страницу, которую вы посетили до того, как зайти на данную страницу StandardBehavior.aspx. Следующее изображение демонстрирует данное поведение.

Начните с посещения страницы Default.aspx...

Нажмите на ссылку "Behavior With History on Sorting". Теперь отсортируйте и пролистайте Grid несколько раз. Вот как выглядит элемент Grid после того, как я отсортировал его по цене и переместился на 4-ую страницу...

Нажмите на кнопку Back в вашем браузере. Вы были перенесены обратно на страницу Default.aspx...



 

Добавляем точку истории когда пользователь сортирует табличную сетку (Grid)

Давайте расширим возможности нашего примера и сохраним точку истории при любой сортировке табличной сетки. Тем самым, каждый раз, когда пользователь решит отсортировать табличную сетку, браузер запишет новое состояние истории, а также обновится строка запроса в URL браузера, включая в себя информацию о состоянии (это позволяет создать закладку на страницу). С записью точек истории при любой сортировке сетки пользователь может отсортировать таблицу по цене (Price), далее - по названию (Name), а затем - по номеру товара (ProductID). Если при просмотре сетки, отсортированной по номеру товара (ProductID), пользователь нажмет на кнопку Back, от будет возвращен на страницу с сеткой, отсортированной по названию (в отличие от случая, когда пользователь возвращен на страницу, посещенную до данной). Код, который можно закачать в конце данной статьи, включает в себя страницу ASP.NET, названную BehaviorWithHistory.aspx, которая реализует данную функциональность.

Как я уже говорил ранее, нам необходимо сделать 3 вещи при использовании возможности хранения истории в ASP.NET AJAX:

  1. Решить, какие действия вызывают создание вставки точки истории.
  2. Добавитьточку истории при любом выполнении действия из пункта #1.
  3. Создать обработчик для события Navigate из ScriptManager, которое восстанавливает состояние в тот момент, когда пользователь нажимает кнопку Back (Назад) или Forward (Вперед), либо когда он открывает страницу, используя закладку.
Мы уже выполнили пункт #1 - мы решили записывать историю при любой сортировке элемента GridView. Когда сортируется GridView, вызывается событие Sorting. Нам нужно создать обработчик для события Sorting для того, чтобы создать точку истории.

Точка истории добавляется путем вызова AddHistoryPoint класса ScriptManager. Данный метод требует наличия строки, которая описывает текущее состояние. Данная строка будет использована позже в обработчике события Navigate из ScriptManager для того, чтобы восстановить состояние приложения при достижении страницы путем нажатия кнопок Back или Forward, либо из закладки. Так как мы строим нашу точку истории на критерии примененной сортировки, свойства GridView SortExpression и SortDirection определяют данное состояние. Вследствие этого, нам нужно передать значения данных свойств в метод AddHistoryPoint.

Следующий код демонстрирует как мы кодируем состояние GridView и добавляем его как точку истории.

protected void gvProducts_Sorting(object sender, GridViewSortEventArgs e)
{
   // Создаем состояние истории
   MyScriptManager.AddHistoryPoint("SortExpression", e.SortExpression);
   MyScriptManager.AddHistoryPoint("SortDirection", e.SortDirection.ToString());

   Page.Title = GetPageTitle(e.SortExpression, e.SortDirection);
}

Заметьте, что для каждой вещи, которую я хочу хранить в состоянии, я вызываю метод AddHistoryPoint, путем передачи "названия" для состояния и его значение. В вышеизложенном коде я регистрирую две детали в состоянии, названные "SortExpression" и "SortDirection" и соответственно сортирую значения e.SortExpression и e.SortDirection.

После сохранения состояния мы обновляем заголовок страницы. Метод GetPageTitle создает и возвращает подходящий заголовок, переданный в значениях SortExpression и SortDirection. Например, при сортировке колонки с ценами (UnitPrice) по возрастающей, метод GetPageTitle возвращает строку "Viewing Products Sorted by Price (Ascending)" (Просмотр товаров, отсортированных по цене). Данное значение  назначено свойству Title объекта Page. (Для получения более детальной информации по настройке заголовка страницы ASP.NET  - читайте статью "Динамическая настройка заголовка страницы").

Если мы посещаем страницу, используя данный код, то каждый раз при сортировке  табличной сетки URL адресная строка браузера обновляется и теперь включает в себя строку запроса, которая содержит информацию о состоянии. Посмотрите на адресную строку в следующем изображении. Когда страница BehaviorWithHistory.aspx посещается впервые, ее URL , как и ожидалось, содержит только BehaviorWithHistory.aspx.


Но как только табличная сетка сортируется, строка запроса обновляется и теперь включает в себя закодированную информацию о состоянии. Также доступна кнопка Back (Назад). В нижеприведенном изображении демонстрируется табличная сетка отсортированная по цене (Price) в порядке возрастания, и URL в адресной строке теперь содержит: BehaviorWithHistory.aspx#&&+2toRdN1TcuXKFN4BhNSqrMXMtnAEvRpZEk2MUzyrUhoPiJPO4mIypwAP9J+s7VtZ+uMZ0ub2iUXY5KDMgwufg==.

{mosimage}

Хотя кнопкаBack (Назад) теперь доступна, при нажатии мы не возвращаемся в предыдущее состояние сетки, где она не была отсортирована по цене (Price). Мы так и остаемся на странице BehaviorWithHistory.aspx, но таблицаужеотсортирована по цене. Это происходит потому, что нам еще предстоит осуществить последний шаг #3 - который заключается в создании обработчика для события Navigate элемента управления ScriptManager. При посещении страницы пользователем, обработчик события Navigate  вызывается либо при нажатии кнопок Back (Назад) или Forward (Вперед), либо при использовании закладки. Также обработчик ответственен за обработку сайта согласно строке запроса.

protected void MyScriptManager_Navigate(object sender, HistoryEventArgs e)
{
   if (!string.IsNullOrEmpty(e.State["SortExpression"]))
   {
      string sortExpressionFromState = e.State["SortExpression"];
      SortDirection sortDirectionFromState = (SortDirection)Enum.Parse(typeof(SortDirection), e.State["SortDirection"]);

      // Сортируем сетку согласно информации о сортировке, хранящийся в состоянии истории
      gvProducts.Sort(sortExpressionFromState, sortDirectionFromState);

      Page.Title = GetPageTitle(sortExpressionFromState, sortDirectionFromState);
   }
   else
   {
      // Сортируем сетку по названию товара (ProductName) в порядке возрастания
      gvProducts.Sort("ProductName", SortDirection.Ascending);

      Page.Title = GetPageTitle("ProductName", SortDirection.Ascending);
   }
}

Обработчик события Navigate начинает с проверки того, передано ли ожидаемое состояние. Если да, то оно извлекает значения из состояния истории (т.е. e.State), сортирует GridView и обновляет заголовок страницы. Если информация о состоянии не была передана, то оно возвращается в значение по умолчанию - таблица отсортирована по колонке с названием товара (ProductName) в порядке возрастания.

И это все! Используя данный код, кнопки Back (Назад) и Forward (Вперед)  работают, как положено. Более того, пользователь может установить закладку на веб-странице после сортировки по какой-то определенной колонке. Когда пользователь возвращается на данную страницу при помощи закладки, GridView загрузит и автоматически отсортирует по соответствующей колонке.


 

А что насчет перелистывания?

Страница BehaviorWithHistory.aspx добавляет точку истории тогда, когда пользователь сортирует сетку. Тем не менее она не добавляет точку истории при листании. Если пользователь сортирует сетку, например, по цене (Price), строка запроса обновляется и отображает информацию о состоянии.  Если перейти на вторую страницу в сетке, строка запроса не будет обновлена, и никакой записи не будет добавлено в историю браузера. Если в данный момент пользователь нажмет кнопку Back (Назад), он не будет возвращен на первую страницу, отсортированную по цене (Price). Вместо этого он будет возвращен к состоянию табличной сетки до сортировки по цене (Price). То же самое произойдет, если пользователь отсортирует сетку по цене (Price), перейдет на пятую страницу и добавит закладку в свой браузер, а после решит посетить страницу по данной закладке, то браузер загрузит сетку, отсортированную по цене, но будет показана первая страница.

Если вы хотите добавить точку истории в то время, когда сетка сортируется или ее листают, тогда вам надо будет записывать свойство PageIndex, принадлежащее GridView, в качестве дополнительной информации состояния истории в обработчике события Sorting. Вам также нужно будет создать обработчик для события PageIndexChanging GridView , который вызывается, когда пользователь переходит на новую страницу. В данном обработчике события вам понадобится добавлять точки истории, записывая свойства, относящиеся  к сортировке и листингу. Наконец, вам понадобится обновить обработчик события Navigate, чтобы установить свойство PageIndex в значении, сохраненном в состоянии истории.

Приложение к данной статье включает в себя полный рабочий пример элемента GridView, который записывает точки истории, когда его сортируют или листают. Изучите ASP.NETстраницу, названную BehaviorWithHistory2.aspx.


Вывод

Поскольку страницы, которые используют AJAX, частичные постбэки не регистрируются как новые события в истории браузера, их поведение нейтрализует пользу от кнопок Назад (Back) и Вперед (Forward), а также закладок браузера. Хорошей новостью является то, что ASP.NET AJAX Framework, начиная с ASP.NET 3.5 SP1, включает в себя данную функциональность записи точек истории. Запись точек истории сохраняет состояние, которое описывает историю в строке запроса, и добавляет новую запись в историю браузера. Это позволяет посетителям использовать в браузерах кнопки Назад (Back) и Вперед (Forward) для передвижения по различным состояниям страниц, использующих AJAX. Оно также позволяет использовать закладки для различных состояний AJAX-приложений.

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

Scott Mitchel

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