Игорь Чакрыгин Игорь Чакрыгин

У любой задачи существует по крайней мере одно очевидное и невероятно простое для понимания неправильное решение

ASP.NET - Пользовательские элементы управления, самостоятельно загружающие себя при помощи AJAX

Довольно часто на страницах сайта можно встретить блоки (например, баннеры, случайные опросы или что-то ещё), содержимое которых не зависит от содержимого остальной части страницы и, теоретически, может быть отображено не сразу, а загружено при помощи AJAX.

В данной статье я хочу рассказать о создании базового класса пользовательских элементов управления AjaxUserControl, экземпляры которого не отображают своё содержимое, а загружают его при помощи AJAX.

Пример 1

Создадим на сайте пользовательский элемент управления Time.ascx, который будет отображать текущее время после двухсекундной задержки.

Разместим его на странице Default.aspx и запустим пример.

После двухсекундной задержки я увидел следующие результаты:

09:04:28
09:04:30

Очевидно, что элемент управления Time.ascx своим присутствием на странице замедляет её работу. Попробуем сделать так, чтобы этот он загружался при помощи AJAX.

Пример 2

Создадим на сайте второй пользовательский элемент управления AjaxTime.ascx, который будет работать аналогично элементу управления Time.ascx с одним единственным отличием - он будет унаследован не от класса UserControl, а от класса AjaxUserControl.

Также разместим его на странице Default.aspx.

Создание класса AjaxUserControl

Сам класс AjaxUserControl не должен содержать никакого функционала. Всё что нам потребуется, это унаследовать его от базового класса UserControl и назначить ему сборщик элемента управления AjaxUserControlBuilder при помощи соответствующего атрибута.

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

Разметка, которую будет генерировать элемент управления AjaxUserControlLoader будет содержать контейнер (div) в который при помощи AJAX будет загружаться нужный элемент управления и скрипт, который будет выполнять эту загрузку.

Следующим шагом будет создание сборщика элементов управления AjaxUserControlBuilder.

Заранее оговорюсь, что в данной статье я позволю себе существенно сократить код и буду считать, что пользовательские элементы управления, унаследованные от AjaxUserControl не поддерживают атрибуты в тегах. При желании, думаю, будет не сложно добавить этот функционал.

Как я уже говорил выше сборщик элементов управления AjaxUserControlBuilder необходим для того, чтобы заменить элемент управления, объявленный в разметке, на элемент управления AjaxUserControlLoader.

Важным моментом является то, что при этом необходимо передать ему в качестве параметра путь элемента управления, который он должен загружать. Обычно этот путь присваивается в конструкторе свойству AppRelativeVirtualPath и я был сильно удивлён, что нет другого способа его получить (а может я просто его не нашёл), кроме как создать один экземпляр этого элемента управления. (Напомню, что код сборщиков элементов управления выполняется один раз на этапе компиляции страницы.)

Финальным шагом необходимо создать обработчик запросов AjaxUserControlHandler который будет возвращать разметку пользовательских элементов управления в ответ на AJAX-запросы.

Этот обработчик необходимо зарегистрировать в файле Web.config по адресу AjaxUserControl.axd в разделах system.web/httpHandlers или system.webServer/handlers.

После запуска примера, первое что я увидел:

10:59:19

А через две секунды, когда загрузился элемент управления AjaxTime.ascx:

10:59:19
10:59:21

Цель достигнута.

Скачать демо (ASP.NET 4.0, Web Site)

7 комментариев

  1. 1. если проект сложный и есть связка контрола со странице, мастерпейджем, другими контролами на странице - метод не стработает, по крайней мере, основываюсь на той информации которую Вы предоставили
    2. чтобы подгрузить что-то несвязанное на страницу - iframe или jQuery.get рулят
    3. Я реализовал отложенную загрузку через отложенный UpdatePanel http://inln.blogspot.com/2010/04/blog-post.html

    ОтветитьУдалить
    Ответы
    1. Спасибо за комментарий.

      1. Я в самом начале написал, что содержимое контролов не зависит от содержимого остальной части страницы. Поэтому я считаю, что никаких связок с другими контролами нет. Обычно это всякие баннеры и т.п.

      2. Дело вкуса. Для меня iframe вообще не рулит, а серьёзной разницы в get или post запросе нет, т.к в моём случае они оба будут работать практически одинаково.

      3. К сожалению, у вас не работает ссылка на демо. Если я правильно понял, то у вас сначала грузится страница без областей, которые помечены DeferredUpdatePanel, потом происходит постбек (асинхронно), и фактически таже самая страница загружается ещё раз (проходит заново весь жизненный цикл) и обновляет нужные области.

      Я бы не стал так делать хотя бы потому, что если нужно будет для какого-то блока отключить отложенную загрузку (ну например оптимизировали его, кэшировали, и он теперь быстро грузится) то вам скорее всего придётся и код править и разметку.

      В моём случае меняется только базовый класс элемента управления.
      К тому же я иногда не использую серверную форму и ScriptManager (полностью их убираю с сайта) и UpdatePanel в этом случае не будет работать.

      Удалить
    2. 1. Сам юзаю strict, так что iframe и у меня не рулит )
      2. Ссылка нерабочая, верно, потому вычеркнута, но можно качнуть сам контрол
      3. по поводу повторной загрузки всей страницы - !IsPostBack рулит :)
      4. ASP.NET WebForms Life Cycle как таковой мне в принципе не нравится. Постепенно перехожу на ASP.NET MVC via Razor

      Удалить
    3. Да, теперь получилось скачать, просто утром писалось, что "сервис не доступен".

      Ваш контрол попробовал, но он работает немного не так, как я ожидал, например если в него поместить мой контрол Time.ascx (из этой статьи), то от долгой загрузки это не спасёт, т.к. Thread.Sleep всё равно сработает (даже дважды сработает).

      Хотя идея тоже интересная, возможно, попробую развить её, может хватит на ещё одну статью =)

      Удалить
    4. Thread.Sleep - это конечно замечательный пример задержки, но лично у меня в коде задержки обычно происходят, а не в разметке =)
      В любом случае - прям идеального решения нет, так что нужно каждое решение брать на заметку лично Ваше я взял, даже сделаю в выходные краткий перепост с линком на полную Вашу статью, спасибо!

      Удалить
    5. Ну в разметке - это для экономии места, то же самое можно было и в OnLoad поместить. Можно сказать, что это аналог сложного запроса к БД, который выполняется значительное время.

      Будем искать идеальное решение =)

      Удалить
    6. Согласен, для этого я специально сделал обработчик события, типа - подгрузка на клиента, который уже вместо OnLoad нужно юзать, но это все костыли...

      Удалить

© Игорь Чакрыгин. Все права защищены при помощи чёрной магии. Технологии Blogger.