Microsoft ASP.NET 2.0 AJAX

Page 1

С Е Р И Я

00Z> t**,v


Microsoft'

Introducing Microsoft ASP.NET 2.0 AJAX Extensions

Dino Esposito (Solid Quality Learning)


Дино Эспозито

шш ш тшгашшшггашй

3 Microsoft

ЙЖШ шм шш "D

.РУССКАЯ РЕДАКЦИЯ

£^ПП7ШР°

Москва ■ Санкт-Петербург ■ Нижний Новгород ■ Воронеж Новосибирск ■ Ростов-на-Дону ■ Екатеринбург ■ Самара Киев ■ Харьков ■ Минск

2007


ББК 32.973-018.1 УДК 004.43

Эспозито Д. Э85

Знакомство с технологией Microsoft ASP.NET 2.0 AJAX. — М.: «Русская Редакция»; СПб.: Питер, 2007. — 320 с : ил. ISBN 978-5-7502-0307-9 («Русская редакция») ISBN 978-5-91180-435-0 («Питер») Книга представляет собой экспертное введение в расширение языка JavaScript (кодовое название Atlas) для Microsoft ASP.NET 2.0, написанное специалистами, работающими в группе разработки. Эта книга является идеальным справочным пособием, поскольку содержит наиболее свежие сведения о рассматриваемых в ней инструментах разработки, а также множество примеров на языках С# и JavaScript. ББК 32.973-018.1 У Д К 004.43

Подготовлено к печати по лицензионному договору с Microsoft Corporation, Редмонд, Вашингтон, США Microsoft, Microsoft Press, ActiveX, Internet Explorer, MSDN, MS-DOS, MSN, Outlook, PowerPoint, Verdana, virtual Earth, Visual Basic, Visual C#, Visual C++, Visual Studio, Windows, Windows Live, Windows Server и Windows Vista являются товарными знаками или охраняемыми товарными знаками корпорации Microsoft в США и/или других странах. Все другие товарные знаки являются собственностью соответствующих фирм. Все названия компаний, организаций и продуктов, а также имена лиц, используемые в примерах, вымышлены и не имеют никакого отношения к реальным компаниям, организациям, продуктам и лицам.

ISBN 0-7356-2345-7 (англ.) ISBN 978-5-7502-0307-9 («Русская редакция») ISBN 978-5-91180-435-0 («Питер»)

© Оригинальное издание на английском языке, Dino Esposito, 2007 © Перевод на русский язык, Microsoft Corporation, 2007 © Оформление и подготовка к изданию ООО «Питер Пресс», ООО «Издательство «Русская Редакция», 2007

Дино Эспозито

Знакомство с технологией Microsoft® ASP.NET 2.0 AJAX Перевел с английского Е. Матвеев Совместный проект издательства «Русская Редакция» и издательства «Питер»

« . Р У С С К А Я РЕДАКЦИЯ

С^ПНТЕР

ООО «Питер Пресс», 198206, Санкт-Петербург, Петергофское шоссе, д. 73, лит. А29. Налоговая льгота — общероссийский классификатор продукции ОК 005-93, том 2; 95 3005 — литература учебная. Подписано в печать 30.04.07. Формат 70x100/16. Усл. п. л. 25,8. Тираж 2000. Заказ 0000. Отпечатано по технологии CtP в ОАО «Печатный двор» им. А. М. Горького. 197110, Санкт-Петербург, Чкаловский пр., д. 15.


Содержание Благодарности От издательства

9 10

Для кого написана книга

12

Настройка конфигурации SQL Server 2005 Express Edition

13

Глава 1. Структурные элементы приложений в стиле AJAX

15

Получение вывода в страницах Достоинства и недостатки

17 18

Обновление страниц Модель DOM Роль широкофункционального браузера Расширенную функциональность — в массы!

20 20 21 21

Объект XmlHttpRequest в Mozilla XmlHttpRequest как интегрированный объект в Internet Explorer 7 Объектная модель HTTP Поведение и возможности

24 25 25 26

Использование объекта XmlHttpRequest Внеполосные вызовы из страниц ASP.NET

28 28

Существующие прикладные среды AJAX в .NET

32

Метод GetCallbackEventReference Интерфейс ICallbackEventHandler ASP.NET Script Callback: все вместе Основные факты

35 36 37 38


6

Содержание

Библиотека AJAX.NET: все вместе Основные факты Прикладные среды на базе AJAX Web.UI (ComponentArt)

41 41 42 43

Глава 2. Структурные элементы Atlas

46

Развертывание приложений Atlas

49

JSON Atlas на других серверных платформах

51 52

Серверо-центрическая программная модель Клиенто-центрическая программная модель

54 57

Удаленный вызов методов

70

Глава 3. Частичное обновление страниц Цели и побудительные мотивы

83 84

Включение частичного обновления страниц

86

Использование элемента UpdatePanel Управление обновлением страниц

91 96

Обновление панели по триггеру

96

Обратная связь во время обновления

103

Общие сведения об элементе UpdateProgress Отмена незавершенного обновления Таймер

104 105 107


Содержание

7

Глава 4. Элементы Microsoft ASP.NET AJAX

110

Регистрация компонентов ACT в странице Содержимое ASP.NET AJAX Control Toolkit

113 114

Общие сведения об элементе Accordion Использование элемента Accordion Элемет-Rating Общие сведения об элементе Rating Использование элемента Rating

117 119 121 121 123

Общие сведения об элементе ReorderList Использование элемента ReorderList Серверные расширители Atlas

124 127 129

Общие сведения об аспектах поведения Atlas

130

Расширители временных окон Расширители пользовательского интерфейса

136 140

Глава 5. Библиотека Microsoft AJAX

162

Расширения JavaScript

163

Объектно-ориентированные расширения в Atlas JavaScript

Компоненты Atlas Декларативные действия Другие компоненты и функциональные возможности Элементы пользовательского интерфейса

167

175 177 183 188

Элементы проверки данных Аспекты поведения клиентской стороны

193 199

Глава 6. Обращение к серверным службам

214

Встроенные прикладные службы

215

Связь с внешними веб-службами Вызов нелокальных веб-служб Atlas

253 253


8

Содержание Настройка веб-служб во внешнем приложении Atlas

254

Глава 7. Привязка данных на стороне клиента

265

Клиентские компоненты источников данных Расширенные функции Построение расширенных списковых представлений Отправка исходных данных Создание детализированных представлений

278 286 286 286 294

Live.com, Windows Vista и гаджеты Гаджеты и Atlas

Алфавитный указатель

303 307

318


Посвящается Сильвии «Изменяться и изменяться к лучшему — отнюдь не одно и то же». Немецкая пословица

Благодарности Написать вводную книгу по новой технологии — задача не из простых. Это особенно справедливо в тех случаях, когда новая технология еще не была выпущена и по ней, естественно, не существует простой, готовой документации. Я основательно проработал материалы проекта, используя все доступные средства — декомпиляторы, электронную почту, слайды с конференций, примеры кода и всю документацию, которая попадалась мне на глаза. Картину дополняли эксперименты с бесчисленными примерами различной сложности. Короче говоря, я учился методом проб и ошибок. Первым, кто извлек пользу из этой книги, был я сам. Я использовал ее для изучения ASP.NET Atlas. Могу сказать, что после знакомства с ней я неплохо разбираюсь в этой теме. Не знаю, какой вывод из этого можно сделать — то ли я быстро учусь, то ли книга действительно получилась неплохой. Я чрезвычайно благодарен Скотту Гатри (Scott Guthrie) за то, что он тратил на меня свое время, объясняя, что нас ждет впереди, и что группа разработчиков узнала в ходе работы над проектом — до и после Конференции профессиональных разработчиков в 2005 году, когда появилась первая информация о платформе Atlas. Саймон Кэлверт (Simon Calvert) стал моим «личным проводником», а Стефан Шекоу (Stefan Schakow) предоставлял содержательные ответы на мои вопросы (которые не всегда были умными). Я жадно читал блоги Бертрана Лероя (Bertrand LeRoy) и Нихила Котари (Nikhil Kothari) — а также множество других блогов — и нашел в них массу полезной информации. Во время работы над книгой я также общался с парой фирм, работающих в области AJAX. Хочу особо поблагодарить Миляна Братишевича (Miljan Braticevic) из ComponentOne и Светозара Георгиева (Svetozar Georgiev) из Telerik. Atlas — замечательная штука, но эти парни со своими командами добились больших успехов в создании приложений, которые работают по Сети почти так же, как программы для настольных систем. Чтобы написать эту книгу, я побывал на трех континентах и в шести странах: Италия, США, Франция, Испания, Австралия и Малайзия. Мои друзья Грег Линвуд (Greg Linwood) и Бретт Кларк (Brett Clarke) усомнились, удастся ли мне написать целую главу за неделю, одновременно с обучением, экскурсиями по Мельбурну и подготовке весьма специфической презентации Microsoft PowerPoint для весьма специфического, весьма специфического дня рождения. Я справился с задачей — и это было очень занятно.


10

Благодарности

Кого еще я должен поблагодарить? Ну конечно, моих любимых Линн Финнел (Lynn Finnel) и Кимберли Ким (Kimberly Kim) со всей группой редактирования, за их гибкость и снисходительность к перенесению сроков сдачи материала вне всяких разумных пределов. Кенн Скрибнер (Kenn Scribner) осваивал Atlas вместе со мной и несколько раз наставил меня на путь истинный. (Конечно, иногда я делал то же самое, но между друзьями это дело обычное…) Бен Райан (Ben Ryan) очень хотел увидеть эту книгу, и вот она вышла в свет. Спасибо, Бен! Как обычно, я благодарен своей жене Сильвии и моим детям, Франческо и Микеле, которые становятся все выше с каждой написанной мной книгой. P.S.: Я пишу это предисловие в особый день — 11 сентября, пять лет спустя. Выражаю сочувствие всем, кого затронула эта трагедия. Дино

От издательства К тому моменту, когда книга уже была сверстана, компания Майкрософт неожиданно сменила кодовое имя технологии Atlas на ASP .NET 2.0 Ajax. Ввиду того, что объем исправлений по всей книге был бы неоправданно большим, редакция приняла решение оставить в тексте кодовое имя Atlas. Мы извиняемся перед читателями за возможные неудобства и просим обратить внимание на то, что везде, где в книге упоминается Atlas, под этим кодовым именем следует понимать ASP .NET 2.0 Ajax. Ваши замечания, предложения и вопросы отправляйте по адресу электронной почты comp@piter.com (издательство «Питер», компьютерная редакция). Мы будем рады узнать ваше мнение! Подробную информацию о наших книгах вы найдете на веб-сайте издательства: http://www.piter.com.


Введение Прошли те времена, когда веб-приложение можно было сконструировать и реализовать в виде простого набора связанных страниц. Появление так называемого «стиля AJAX» в корне изменило представления пользователей о веб-приложениях, и как следствие, заставило разработчиков переходить на более новые и совершенные модели построения современных веб-приложений. Но что обычно понимают под термином «стиль AJAX»? Сокращение AJAX происходит от слов «Asynchronous JavaScript and XML» («асинхронный код JavaScript и XML»). Этим общим термином обозначаются высокоинтерактивные приложения, быстро реагирующие на действия пользователя, выполняющие большую часть работы на стороне клиента и взаимодействующие с сервером посредством внеполосных обращений. Внеполосным (out-ofband) обращением называется запрос к серверу, который приводит к оперативному обновлению страницы (вместо ее замены). В результате веб-приложения на базе AJAX обычно в большей степени напоминают классические приложения Microsoft Windows, поддерживают перетаскивание и асинхронные операции, быстро реагируют на действия пользователя, не мигают при перерисовке и не раздражают пользователя. Microsoft Atlas — важное расширение платформы Microsoft ASP.NET, обеспечивающее эффективную поддержку AJAX-функциональности в приложениях. Технология Atlas проектировалась как составная часть ASP.NET, и как следствие, она полностью интегрируется с существующей платформой разработки и моделью приложений. Полноценная поддержка Atlas будет включена в следующую версию Microsoft Visual Studio (кодовое обозначение Orcas). С архитектурной точки зрения, инфраструктура Atlas состоит из двух элементов: библиотеки клиентских сценариев и набора серверных расширений. Библиотека клиентских сценариев полностью написана на JavaScript и поэтому работает в любом современном браузере. Так как платформа Atlas является расширением ASP.NET, она полностью интегрируется с серверными службами и управляющими элементами ASP.NET. В результате разработчики могут создавать веб-страницы с расширенной функциональностью на базе Atlas, используя практически такую же методику, которая использовалась при разработке классических серверных страниц ASP.NET. Большинство Atlas-разработчиков имеет опыт разработки ASP.NET, и как следствие, хорошо знакомы с серверной моделью разработки, основанной на элементах управления. Серверо-центрическая модель программирования Atlas


12

Введение

является крупным шагом на пути эволюции программной модели ASP.NET. Серверные элементы Atlas чрезвычайно удобны — особенно если вы не чувствуете достаточной уверенности для того, чтобы создавать клиентские сценарии Atlas вручную. В книге приводится общий обзор инфраструктуры Atlas, а многочисленные примеры помогут в освоении различных приемов и инструментов программирования. Технология Atlas обещает стать настоящим прорывом для профессиональных разработчиков ASP.NET. Благодаря ей кросс-браузерное программирование воплощается в реальность, а веб-приложения обретают функциональность, характерную для настольных систем. А сегодня вы получаете Atlas в свое распоряжение, с лицензией Go-Live и полным набором возможностей — изучайте, пробуйте и экспериментируйте, невзирая на официальный бета-статус этой технологии. ПРИМЕЧАНИЕ

К моменту издания книги официальные имена продуктов семейства Atlas едва успели определиться. Новые имена делятся на три категории и могут заменять кодовые обозначения Atlas, использованные в тексте. Далее приводится список официальных имен. 1. Клиентская библиотека JavaScript Atlas будет называться «библиотекой Microsoft AJAX». Она будет работать в любом браузере и поддерживаться всеми внутренними веб-серверами. 2. Серверная функциональность Atlas, интегрированная с ASP.NET, будет обозначаться термином «ASP.NET 2.0 AJAX Extensions». Как следствие, префикс тегов элементов Atlas <atlas:> заменяется на <asp:>. 3. Инструментарий Atlas Control Toolkit на текущий момент представляет собой набор свободно распространяемых элементов и компонентов с открытыми исходными кодами, которые помогают извлечь максимум пользы из расширений ASP.NET AJAX Extensions. В будущем проект будет называться ASP.NET AJAX Control Toolkit.

Для кого написана книга Книгу можно порекомендовать практически всем разработчикам и профессионалам в области ASP.NET. Как упоминалось ранее, платформа Atlas, которая должна стать следующим значительным этапом в эволюции ASP.NET, следует главной тенденции отрасли — модели AJAX. Кроме того, Atlas выходит за рамки модели AJAX и расширяет инфраструктуру взаимодействия клиента с сервером, обеспечивая комплексное решение для веб-приложений. В том, что касается веб-платформы Microsoft, Atlas объединяет расширенную функциональность с универсальностью — старая мечта веб-профессионалов, которая воплотилась в жизнь. Наконец-то. Если вы принадлежите к числу профессионалов, занимающихся разработками для веб-технологий Microsoft, Atlas откроет для вас благоприятную возможность; не упускайте ее. Пусть эта книга станет отправной точкой в изучении новой технологии.


Настройка конфигурации SQL Server 2005 Express Edition

13

Как устроена книга Материал делится на три части: общие сведения о платформе, разработка на стороне сервера и разработка на стороне клиента. В первой части рассматриваются основные принципы модели AJAX и их реализация в Atlas. Вторая часть посвящена элементам инфраструктуры Atlas, используемым для расширения возможностей серверных элементов и преобразования существующих классических страниц ASP.NET в полноценные страницы Atlas. Наконец, в третьей части книги подробно разбирается внутреннее устройство Atlas: клиентская библиотека, клиентские элементы, интеграция веб-служб, привязка данных и гибридные (mash-up) приложения. Вторая и третья части построены на независимом материале. Если вас интересует лишь включение поддержки AJAX в классических страницах ASP.NET, заполненных элементами runat=server, вероятно, материал третьей части вам практически не понадобится. Для начала хватит и содержания первых глав. Тем не менее в третьей части рассматриваются методы и инструменты, лежащие в основе мощи Atlas — клиентские элементы и фрагменты XML Script, которые могут использоваться для преодоления различий между браузерами и синтаксисом JavaScript.

Системные требования Для построения и запуска примеров кода, приводимых в книге, потребуется следующее аппаратное и программное обеспечение: I Microsoft Windows XP с обновлением Service Pack 2, Microsoft Windows Server 2003 c обновлением Service Pack 1 или Microsoft Windows 2000 с обновлением Service Pack 4. I Microsoft Visual Studio 2005 Standard Edition или Microsoft Visual Studio 2005 Professional Edition. I Microsoft SQL Server 2005 Express (поставляется с Visual Studio 2005) или Microsoft SQL Server 2005. I 600-МГц Pentium или совместимый процессор (рекомендуется 1 ГГц Pentium). I 192 Мбайт оперативной памяти (рекомендуется 256 Мбайт и более). I Монитор (разрешение 800x600 и выше) с поддержкой не менее 256 цветов (рекомендуется 1024x768 с 16-разрядным цветом High Color). I Мышь Microsoft или совместимое указывающее устройство.

Настройка конфигурации SQL Server 2005 Express Edition В некоторых главах книги вам потребуется доступ к SQL Server 2005 Express Edition (или SQL Server 2005) для создания и работы с базой данных Northwind Traders. Если вы используете SQL Server 2005 Express Edition, выполните


14

Введение

следующие действия для получения доступа к учетной записи пользователя, которая будет использоваться для выполнения упражнений данной книги: 1. Войдите в Windows со своего компьютера, используя учетную запись с административными привилегиями. 2. В меню Пуск (Start) выберите команду Все программы (All Programs) ► Стандартные (Accessories) ► Командная строка (Command Prompt), чтобы открыть окно с приглашением командной строки. 3. Введите в приглашении командной строки следующую команду (с соблюдением регистра символов): sqlcmd -S Сервер\SQLExpress -Е

4. Замените Сервер именем своего компьютера. 5. Чтобы определить имя компьютера, введите команду hostname в окне командной строки перед выполнением команды sqlcmd. 6. Введите в приглашении 1> следующую команду (с квадратными скобками) и нажмите Enter: sp_grantlogin \_Сервер\Пользователь

7. Замените Сервер именем своего компьютера, а Пользователь — именем используемой учетной записи. 8. Введите в приглашении 2> следующую команду, затем нажмите Enter: go

9. Если на экране появится сообщение об ошибке, проверьте правильность ввода команды sp_grantlogin (в том числе наличие квадратных скобок). 10. Введите в приглашении 1> следующую команду (с квадратными скобками) и нажмите Enter: sp_addsrvrolemember [Сервер\Пользователь^, dbcreator

11. Введите в приглашении 2> следующую команду, затем нажмите Enter: go

12. Если на экране появится сообщение об ошибке, проверьте правильность ввода команды sp_ addsrvrolemember (в том числе наличие квадратных скобок). 13. Введите в приглашении 1 > следующую команду и нажмите Enter: exit 14. Закройте окно командной строки. 15. Выйдите из системы.


ГЛАВА 1

Структурные элементы приложений в стиле AJAX В I I I

этой главе: Сдвиг парадигмы Объект XmlHttpRequest Существующие прикладные среды AJAX в .NET

Сокращение AJAX происходит от слов «Asynchronous JavaScript and XML» («асинхронный код JavaScript и XML»). Этим общим термином обозначаются высокоинтерактивные приложения, быстро реагирующие на действия пользователя. Но о чем речь? Разве веб-приложения не были задуманы лет десять назад именно как интерактивные, быстро реагирующие и запускаемые при помощи специального инструмента, называемого браузером? Что изменилось в наши дни? Невероятный успех Интернета разжег аппетиты пользователей выше всяких ожиданий. Многолетняя потребность пользователей в еще более мощных приложениях с веб-поддержкой заставляла архитекторов и разработчиков внедрять все больше функций на серверной платформе и в клиентских браузерах. В результате традиционная схема веб-приложения с каждым днем становится все менее актуальной. Стало очевидно, что откладывать на будущее радикальные изменения в архитектуре и модели программирования далее невозможно. На данный момент отрасли требуется нечто большее, чем усовершенствованная и более мощная платформа, построенная по традиционным принципам и правилам веб-приложений, — необходим настоящий сдвиг парадигмы. AJAX воплощает новую парадигму для следующего поколения веб-приложений, и, скорее всего, этой парадигме суждено оставаться с нами как минимум в течение ближайшего десятилетия. Если взглянуть на ситуацию с точки зрения разработчика, термином AJAX обозначается совокупность компонентов разработки, инструментов и методов создания высокоинтерактивных веб-приложений. В соответствии с парадигмой AJAX, веб-приложения в процессе работы обмениваются с веб-сервером данными (а не страницами). Для конечного пользователя это означает более быстрое получение обновленных данных и, что более важно, — существенное ускорение загрузки и обновления страниц. Веб-приложения приближаются к классическим приложениям Microsoft Windows, поддерживают перетаскивание и асинхронные операции, быстро реагируют на действия пользователя, не мигают при перерисовке и не раздражают пользователя.


16

Глава 1. Структурные элементы приложений в стиле AJAX

Философия AJAX способствовала появлению нового поколения компонентов и прикладных сред, каждая из которых была ориентирована на конкретную платформу, предоставляла заданный набор возможностей и интегрировалась с существующими прикладными средами. Microsoft Atlas представляет собой AJAX-дополнение для платформы Microsoft ASP.NET 2.0. ВНИМАНИЕ

В действительности Atlas — нечто большее, чем реализация AJAX для платформы ASP.NET. Бесспорно, концепции AJAX были заложены в основу Atlas. Однако сегодня Atlas воспринимается как значительное расширение платформы AST.NET, обеспечивающее типичную функциональность AJAX в сочетании с новыми расширенными клиентскими возможностями. В будущем Atlas и ASP.NET 2.0 объединятся в новой версии ASP.NET. Таким образом, AJAX и Atlas не следует считать синонимами. Скорее, Atlas реализует «стиль AJAX» для платформы ASP.NET, однако при этом Atlas выходит далеко за рамки типичной функциональности AJAX и обеспечивает клиентскую инфраструктуру для построения веб-приложений нового уровня. В этой главе мы сначала рассмотрим основные системные требования, общие для всех решений и прикладных сред AJAX, а затем познакомимся с планами Microsoft относительно следующей версии Microsoft Visual Studio (кодовое обозначение «Orcas»). В Orcas технология Atlas будет официально интегрирована с ASP.NET, остальными составляющими веб-платформы Microsoft и моделью приложений.

Сдвиг парадигмы Все мы наблюдаем интересное, беспрецедентное явление (и вносим в него свой вклад) — Web претерпевает эпохальные изменения, обусловленные нашими действиями. Как бы радикально это ни прозвучало, но среда Web произвела переворот в концепции приложения. Всего восемь лет назад большинство разработчиков считало приложение чем-то слишком серьезным для превращения в беспорядочную смесь сценарного кода и разметки. В конце 1990-х годов считалось, что приложение должно быть оплачено кровью, потом, слезами и бесконечными сеансами отладки. Для «настоящего» программиста в написании веб-приложений не было ни чести, ни славы. С тех пор среда Web прошла долгий путь развития. И хотя за 10 лет эволюции Web на стороне сервера сформировалась толстая прослойка абстракций, базовая структура — HTTP и страницы — осталась неизменной. ПРИМЕЧАНИЕ

Впрочем, в конце 1990-х годов было спроектировано и создано немало веб-сайтов. За последующие годы некоторые из них невероятно выросли, превратились в столпы современной мировой экономики и даже изменили наш подход к выполнению многих повседневных операций. Нужны примеры? Google, Amazon, eBay. И все же десять лет назад люди, создавшие эти и другие приложения, принадлежали к наиболее передовой части разработчиков или были умными, дальновидными любителями.


Веб-приложения в наши дни

17

Исходная инфраструктура — простая, универсальная и эффективная — стала определяющим фактором стремительного успеха модели веб-приложений. Следующее поколение веб-приложений по-прежнему будет базироваться на HTTP и страницах, однако содержимое страниц и возможности оборудования, работающего на стороне сервера, существенно изменятся. В результате впечатление от работы с веб-приложениями заметно изменится — последние приблизятся к классическим Windows-приложениям для настольных систем.

Веб-приложения в наши дни Работа сегодняшних веб-приложений основана на отправке форм, заполненных пользователем, на веб-сервер и последующем отображении разметки, возвращенной сервером. При обмене данными между браузером и сервером используется классический протокол HTTP. Как известно, HTTP относится к числу протоколов без состояния; иначе говоря, каждый запрос никак не связан с предыдущим, а автоматическое сохранение информации состояния отсутствует (объекты состояния, известные всем нам, например, по ASP.NET, представляют собой абстракцию, реализуемую средой серверного программирования). Обмен данными между браузером и веб-сервером происходит при помощи форм. С точки зрения пользователя пересылка осуществляется постранично. Каждое действие пользователя, в результате которого серверу отправляется новый запрос, приводит к пересылке и отображению совершенно новой страницы (или измененной версии текущей страницы). Небольшой анализ этой модели поможет выявить ее недостатки и те причины, по которым сегодня стала необходима новая модель.

Отправка ввода в формах На основании URL, введенного в строке адреса, браузер отображает страницу. Страница, в конечном счете, состоит из разметки HTML и содержит одну или несколько форм HTML. Пользователь вводит данные, а затем приказывает браузеру отправить форму по URL, заданному для этой цели (URL действия). Браузер преобразует заданный URL в IP-адрес и открывает сокет. Пакет HTTP, содержащий форму вместе со всеми полями, пересылается по каналу связи заданному получателю. Веб-сервер принимает запрос и обычно передает его внутреннему модулю для дальнейшей обработки. В конце процесса создается пакет ответа HTTP, а возвращаемое браузером значение вставляется в тело пакета.

Получение вывода в страницах Получив запрос, например, на ресурс .aspx, веб-сервер передает его подсистеме ASP.NET для обработки и получает разметку HTML. Сгенерированная разметка включает все теги классической страницы HTML (<html>, <body>, <form> и т. д.). Исходный код страницы встраивается в ответ HTTP и помечается


18

Глава 1. Структурные элементы приложений в стиле AJAX

соответствующим типом MIME, чтобы браузер знал, как его следует обработать. В зависимости от типа MIME браузер выбирает дальнейшие действия. Если ответ содержит страницу HTML, браузер полностью заменяет текущее содержимое новой разметкой. Во время обработки запроса сервером на экране отображается «старая» страница. Как только «новая» страница будет полностью загружена, браузер стирает изображение и выводит ее.

Достоинства и недостатки Описанная модель хорошо работала в начале эпохи Web, когда содержимое веб-страниц ограничивалось форматированным текстом, гиперссылками и небольшим количеством графики. Из-за успеха Web пользователи становились все более требовательными, а это заставляло разработчиков и дизайнеров создавать все более сложный сервис и графику. В качестве примера возьмем рекламу. Сегодня на большинстве страниц (причем даже очень простых, как страницы блогов) присутствуют рекламные ротаторы, пересылающие клиенту немалый объем данных. В результате страницы становятся тяжелыми и громоздкими — даже если упорно говорить о «расширенной функциональности», от этого ничего не изменится. Какой бы термин вы ни выбрали, веб-страницы современных приложений именно таковы. Никто не поверит, что возможен возврат к минималистским, спартанским страницам HTML, существовавшим лет десять назад. При действующей архитектуре веб-приложений каждое действие пользователя требует полной перерисовки страницы. Как следствие, более тяжелые («широкофункциональные») страницы медленнее воспроизводятся на экране, а их прорисовка сопровождается мерцанием. При большом наборе страниц крупного приложения (например, портала) такой механизм лишь вызовет раздражение у конечного пользователя. Хотя для построения страницы на сервере разработчик может воспользоваться целым рядом гибких архитектур (ASP.NET — всего лишь один из возможных примеров), с точки зрения клиента веб-страницы изначально проектировались в расчете на статичность и отсутствие изменений. В конце 1990-х годов этот основополагающий факт изменился: сначала появился стандарт Dynamic HTML, затем модель объекта документа W3C (World Wide Web Consortium). В наши дни страница, отображаемая браузером, может модифицироваться с учетом изменений, вносимых исключительно на стороне клиента, в зависимости от действий пользователя (как будет показано далее, в AJAX и Atlas этот принцип получил дальнейшее развитие). Стандарт Dynamic HTML стал серьезным шагом вперед, но одного его было недостаточно для качественного изменения Web.

Веб-приложения завтра Перерисовку страниц было необходимо свести к минимуму. Для этой цели около 1997 года появились первые примитивные формы сценарного удаленного вызова


Веб-приложения в наши дни

19

процедур (RPC, Remote Procedure Call). В частности, компания Microsoft со своей технологией RS (Remote Scripting) стала одним из новаторов в этой области. В RS для получения данных с удаленного URL были задействованы апплеты Java. URL предоставлял формализованный программный интерфейс и сериализацию данных, пересылаемых в обе стороны в строковом формате. На стороне клиента компактная инфраструктура JavaScript принимала данные и активизировала функцию обратного вызова, определяемую пользователем, для обновления пользовательского интерфейса средствами Dynamic HTML или аналогичными методами. Технология RS работала в Microsoft Internet Explorer 4.0, Netscape Navigator 4.0 и последующих версиях. Позднее компания Microsoft заменила апплет Java объектом COM с именем XmlHttpRequest и сняла большинство ограничений программного интерфейса, предоставляемого удаленным URL. В то же время стараниями сообщества появился ряд аналогичных прикладных сред, которые должны были поднять технологию RS на новый уровень и расширить область ее практического применения. Апплет Java исчез и был заменен объектом XmlHttpRequest.

Что такое AJAX? Термин AJAX появился в 2004 году в сообществе Java. Первоначально он использовался для обозначения семейства взаимосвязанных технологий, реализующих различные формы удаленного исполнения сценариев. В наши дни все разновидности удаленных сценарных технологий обычно помечаются префиксом AJAX. Современные решения на базе AJAX для платформы Windows основаны на объекте XmlHttpRequest. В AJAX-приложениях сочетаются два противоречивых фактора. С одной стороны, приложение должно передавать пользователям свежие данные, полученные с сервера. С другой стороны, новые данные должны интегрироваться в существующую страницу без ее полного обновления. Браузер обычно выдает новый запрос при отправке формы HTML, инициированной либо сценарием, работающим на стороне клиента, либо действием пользователя (скажем, щелчком на кнопке). Получив ответ, браузер заменяет старую страницу новой. На рис. 1.1 наглядно показана традиционная схема работы веб-приложений. Основным фактором, заложенным в основу удаленного исполнения сценариев, является возможность выдачи внеполосных запросов HTTP. В данном контексте под внеполосным вызовом понимается запрос HTTP, который выдается за пределами встроенного модуля, обеспечивающего отправку форм HTTP (то есть вне традиционного механизма, показанного на рис. 1.1). Внеполосный вызов инициируется событием страницы HTML и обслуживается компонентом-посредником (proxy component). В новейших AJAX-решениях таким посредником является объект XmlHttpRequest; в самых первых реализациях RS им был апплет Java.


20

Глава 1. Структурные элементы приложений в стиле AJAX

Браузер Стандартный запрос HTTP

Рис. 1 . 1 . Браузер отправляет форму HTML и получает новую страницу, отображаемую на экране

Обновление страниц Компонент-посредник (например, объект XmlHttpRequest) отправляет обычный запрос HTTP и дожидается — синхронно или асинхронно — завершения его обработки. Получив готовые данные ответа, посредник вызывает функцию обратного вызова JavaScript; эта функция должна обновить все части страницы, нуждающиеся в обновлении. На рис. 1.2 показано графическое представление этой модели. Внеполосный запрос HTTP (AJAX) Браузер

л

URL, параметры

Л DHTML л

Посредник JS

Запрос HTTP

л Данные

Ответ HTTP Рис. 1.2. Внеполосные вызовы обслуживаются компонентом-посредником, а функция обратного вызова JavaScript обновляет все части страницы, зависящие от полученных данных

Любой браузер умеет заменять старые страницы новыми, однако не все браузеры поддерживают объектную модель, представляющую текущее содержимое страницы. В браузерах с поддержкой обновляемой объектной модели функция обратного вызова JavaScript может обновить отдельные части старой страницы, не требуя ее полной перезагрузки.

Модель DOM Спецификация DOM (Document Object Model) определяет общий интерфейс обновления содержимого, структуры и стиля документов HTML и XML, не зависящий от языка и платформы. Стандарт DOM получил признание и был ратифицирован комитетом W3C, поэтому сейчас он поддерживается все большим


Веб-приложения в наши дни

21

количеством браузеров. DOM определяет стандартный набор объектов для представления элементов, образующих документы HTML и XML. Совокупность этих объектов образует стандартный интерфейс для работы с элементами страниц HTML, или на более общем уровне — документов XML. Следует заметить, что хотя первые работоспособные прикладные среды удаленного исполнения сценариев появились около десяти лет назад, ограниченная поддержка динамического изменения отображаемого документа в браузере тормозила массовое принятие таких технологий в масштабах отрасли. До сегодняшнего дня.

Роль широкофункционального браузера Как видно из рис. 1.2, модель AJAX предъявляет два ключевых требования к браузеру: наличие компонента-посредника и поддержка обновляемой модели DOM. В течение долгого времени оба требования выполнялись только передовыми, элитными браузерами (также часто встречается термин «широкофункциональный браузер»). За прошедшие годы только компании, способные жестко контролировать возможности клиентских браузеров, могли выбрать модель AJAX для своих сайтов. Слишком долго термин «широкофункциональный» подразумевал «редко встречающийся», и для большинства коммерческих отраслей такие браузеры определенно не подходили.

Расширенную функциональность - в массы! Вероятно, благодаря какому-то редкому и совершенно непредсказуемому сочетанию звезд около 90 % современных браузеров обладает встроенной поддержкой расширенных возможностей, необходимых для модели AJAX. Internet Explorer, начиная с версии 5.0, Firefox, Netscape версии 6 и выше, Safari 1.1 и более новых версий, Opera 8.0 и различные мобильные устройства — все эти браузеры в полной мере поддерживают модель программирования AJAX. Впервые сложилась ситуация, когда широкофункциональный браузер перестал быть синонимом браузера с ограниченным кругом пользователей. Проектирование высокоинтерактивных веб-приложений, реализующих методы удаленного исполнения сценария, перестало быть недостижимой мечтой и стало совершенно реальным делом. Каждая платформа и каждая фирма-разработчик предлагает собственную прикладную среду и инструментарий, но это не изменяет основополагающего факта — реализация стиля AJAX стала возможной в 90 % существующих браузеров. Это настоящий прорыв, благодаря которому сегодня можно создавать и распространять приложения, невозможные вчера.

Необходимые требования Что же именно требуется от браузера для поддержки функциональности AJAX? Как упоминалось ранее, браузер должен удовлетворять двум ключевым требованиям: поддержке посредников, чтобы клиентский код мог выдавать внеполосные вызовы HTTP, и поддержке обновляемой модели DOM.


22

Глава 1. Структурные элементы приложений в стиле AJAX

В настоящее время стандарт обновляемой модели DOM был ратифицирован комитетом W3C. Стандарт W3C для компонента-посредника пока находится в стадии разработки. Он воплощен в форме объекта XmlHttpRequest и представляет собой интерфейс, предоставляемый браузером и позволяющий сценарному коду выполнять клиентские функции HTTP — такие, как отправка данных формы или загрузка данных с удаленного веб-сайта. Последняя версия рабочего проекта находится по адресу http://www.w3.o^R/XMLHttpRequest. Также браузер должен поддерживать JavaScript и желательно CSS (каскадные таблицы стилей, Cascading Style Sheets). Выходит, что стиль AJAX доступен практически для любого разработчика и для 90 % аудитории Web, независимо от платформы. Инструменты, необходимые для работы AJAX, получили такое же повсеместное распространение, как парсеры HTML/XML и обработчики JavaScript. Перефразируя лозунг популярной рекламной кампании, можно сказать: «AJAX здесь и сейчас!» А в том, что касается Windows и платформы ASP.NET, AJAX воплощается в форме Microsoft Atlas (напомню: Atlas — нечто большее, чем простая реализация типичных функций AJAX вроде внеполосных вызовов и обновляемых страниц; эта тема более подробно рассматривается в последующих главах, начиная с главы 4).

Объект XmlHttpRequest Объект XmlHttpRequest впервые появился в Internet Explorer 5.0. Этот внутренний объект публикуется браузером для работы с его подсистемой исполнения сценариев. Сценарный код, входящий в клиентскую страницу (как правило, код JavaScript), обращается к объекту и использует его функциональность. Что касается функциональности (несмотря на префикс XML), объект XmlHttpRequest представляет собой компактную объектную модель для отправки сценарием обращений HTTP в обход браузера. Когда пользователь щелкает на кнопке отправки формы или выполняет любое действие, приводящее к вызову метода submit объекта form модели DOM, браузер вмешивается в происходящее и берет последующую отправку запроса HTTP под свой полный контроль. С точки зрения пользователя, отправка запроса работает по принципу «черного ящика» с единственным видимым результатом: на экране появляется новая страница. Клиентский код сценария не может управлять процессом размещения и результатом отправки запроса. Объект XmlHttpRequest дает возможность сценарному коду отправлять запросы HTTP и обрабатывать полученные ответы.

История и причины Объект XmlHttpRequest, созданный компанией Microsoft и вскоре принятый в Mozilla, в наши дни поддерживается большинством веб-браузеров. Как вы вскоре убедитесь, его реализация в значительной мере зависит от браузера, хотя интерфейс верхнего уровня остается практически неизменным. По этой причине комитет W3C в настоящее время работает над точным описанием минимального набора общих возможностей, основанного на существующих реализациях.


Объект XmlHttpRequest

23

ПРИМЕЧАНИЕ

Объект XmlHttpRequest изначально поставлялся с Internet Explorer 5.0 в виде отдельного компонента (это было весной 1999 года). Во всех операционных системах Microsoft, выпущенных с того времени, он присутствует в виде интегрированного компонента. В частности, вы всегда найдете его на любом компьютере с Microsoft Windows 2000, Windows XP или более новой операционной системой.

Объект Internet Explorer В те времена, когда объект XmlHttpRequest только появился, в мире господствовала модель COM (Component Object Model), разработанная компанией Microsoft. Модель расширяемости программных продуктов и приложений базировалась на технологии COM и реализовывалась с использованием компонентов COM. В конце 1990-х годов было совершенно правильно и естественно реализовать новый компонент в виде объекта автоматизации COM, которому было присвоено имя Microsoft.XmlHttp. За последующие годы были выпущены разные версии того же компонента (даже со слегка различающимися именами), но во всех случаях исходная модель компонента оставалась неизменной — COM. Например, в Internet Explorer 6.0 объект XmlHttpRequest поставляется в форме объекта COM. В чем проблема, спросите вы? Объекты COM представляют собой внешние компоненты, для выполнения которых в веб-браузере необходимо специальное разрешение. В частности, для инициализации объекта XmlHttpRequest и последующего использования любой функциональности AJAX, построенной на его базе, клиентский компьютер должен принимать компоненты ActiveX, помеченные как безопасные для использования в сценариях (рис. 1.3).

Рис. 1.3. Окно свойств для изменения настроек безопасности в Internet Explorer


24

Глава 1. Структурные элементы приложений в стиле AJAX

Несомненно, компонент XmlHttpRequest безопасен. Но чтобы использовать его, пользователь должен понизить общий уровень безопасности и принять все остальные компоненты, объявленные «безопасными» для использования в сценариях, во всех посещаемых веб-сайтах. ВНИМАНИЕ

Внутренняя реализация XmlHttpRequest отделена от реализации всех AJAX-сред (таких, как Microsoft Atlas). Во внутреннем представлении любая прикладная среда в конечном итоге лишь обращается с вызовами к объекту, предоставляемому браузером.

Объект XmlHttpRequest в Mozilla Поддержка XmlHttpRequest была принята в Mozilla сразу же после его первого появления в Internet Explorer 5.0. Однако в браузерах Mozilla XmlHttpRequest является частью объектной модели браузера и не зависит от внешних компонентов. Иначе говоря, браузеры семейства Mozilla — например, Firefox — публикуют собственный объект XmlHttpRequest в сценарной подсистеме и не используют внешний компонент COM, даже если последний установлен на клиентском компьютере и является частью операционной системы. На рис. 1.4 представлены разные модели поддержки XmlHttpRequest в браузерах Internet Explorer (до версии 6.0) и Mozilla. Браузер Mozilla

Опубликованные объекты Объекты JavaScript Объекты DOM

Механизм исполнения сценариев

JavaScript VBScript

Запрос XmlHttp Microsoft XmlHttp Браузер IE

Рис. 1.4. Сценарный компонент XmlHttpRequest предоставляется браузером в продуктах Mozilla и реализуется в виде внешнего компонента СОМ в Internet Explorer (до версии 6.0)


Объектная модель HTTP

25

Как следствие, в браузерах Mozila объект XmlHttpRequest выглядит как интегрированный («родной») объект JavaScript, а для создания его экземпляра применяется классический оператор new: // Префикс XML в имени объекта должен быть записан прописными буквами var proxy = new XMLHttpRequestO;

В браузере Internet Explorer экземпляр объекта XmlHttpRequest создается с использованием «обертки» ActiveXObject: var proxy = new ActiveXObjectC'Microsoft.XmlHttp");

Обычно прикладные среды в стиле AJAX определяют текущий браузер, а затем выбирают способ создания объекта в зависимости от результата проверки. Излишне говорить, что функциональность XmlHttpRequest в браузерах Mozilla отличается большей безопасностью — хотя бы потому, что пользователю не придется изменять настройки безопасности в своем браузере.

XmlHttpRequest как интегрированный объект в Internet Explorer 7 Объект XmlHttpRequest, который в браузерах Internet Explorer по историческим причинам всегда реализовывался в виде компонента COM, в Internet Explorer 7.0 наконец-то превратится в объект браузера. Все потенциальные проблемы безопасности сразу отпадут, а прикладные среды AJAX могут быть переведены на единый синтаксис создания объекта XmlHttpRequest независимо от браузера: var proxy = new XMLHttpRequestO;

Кроме того, это изменение в Internet Explorer 7.0 полностью отделит AJAXфункциональность (например, функциональность Microsoft Atlas) от среды с поддержкой ActiveX.

Объектная м о д е л ь HTTP Я довольно пространно описал объект XmlHttpRequest и его предполагаемое поведение, но все еще не показал, как им пользоваться. В этом разделе рассматриваются свойства компонента, выполняемые им действия и подробности синтаксиса. Как упоминалось ранее, префикс XML в имени компонента ни о чем не говорит и ни в коей мере не ограничивает возможности компонента. Несмотря на префикс XML, объект может использоваться как полноценный механизм автоматизации исполнения запросов HTTP и управления ими, из клиентского кода, сгенерированного страницами ASP.NET или неконтролируемыми (unmanaged) приложениями Visual Basic 6.0 и C++. Использовать объект COM XmlHttpRequest в приложениях .NET бессмысленно, так как аналогичная функциональность присутствует в пространстве имен System.Net .NET Framework.


26

Глава 1. Структурные элементы приложений в стиле AJAX

ВНИМАНИЕ

Если вы собираетесь применять Microsoft Atlas или другую AJAX-среду при построении своих приложений, вам вряд ли придется иметь дело с объектом XmlHttpRequest, не говоря уже об его использовании в программном коде. Microsoft Atlas полностью инкапсулирует объект, скрывая его от разработчиков страниц и приложений. Чтобы написать приложение Atlas, даже самое сложное и хитроумное, вам не нужно ничего знать о XmlHttpRequest. Тем не менее знание основ XmlHttpRequest поможет лучше понять платформу и повысит эффективность диагностики возникающих проблем.

Поведение и возможности Объект XmlHttpRequest предназначен для выполнения одной ключевой операции: отправки запросов HTTP. Запросы могут отправляться как синхронно, так и асинхронно. В следующем фрагменте показан программный интерфейс объекта в том виде, в каком он представлен в рабочем проекте W3C на момент написания книги: interface XMLHttpRequest { function onreadystatechange; readonly unsigned short readyState; void open(string method, string url); void open(string method, string url, bool async); void open(string method, string url, bool async, string user); void open(string method, string url, bool async, string user, string pswd); void setRequestHeader(string header, string value); void send(string data); void send(Document data); void abortO; string getAllResponseHeadersO; string getResponseHeader(string header); string responseText; Document responseXML; unsigned short status; string statusText; };

Операции с компонентом выполняются в два этапа. Сначала вы открываете канал к URL, выбираете метод (GET, POST и т. д.) и указываете, должен ли запрос быть отправлен асинхронно. На втором этапе задаются все необходимые заголовки, а запрос отправляется серверу. Если для запроса используется метод POST, методу send передается тело запроса. При выполнении асинхронных операций метод send немедленно возвращает управление. Написанная вами функция onreadystatechange должна проверить состояние текущей операции и определить момент ее завершения.

Отправка запроса В большинстве прикладных сред AJAX экземпляр объекта XmlHttpRequest для текущего браузера создается кодом следующего вида: var xmlRequest, е; try {


Объектная модель HTTP

27

xmlRequest = new XMLHttpRequestO ; } catch(e) { try { xmlRequest = new ActiveXObjectC'Microsoft.XMLHTTP"); } catch(e) { } }

Сначала мы пытаемся создать экземпляр внутреннего объекта XMLHttpRequest, а в случае неудачи выбираем способ с объектом ActiveX. Как видите, при создании объекта для браузера Internet Explorer перехватывается исключение. В Internet Explorer 7.0 такой код будет работать без изменений (в этом случае исключение не возникает). Метод open готовит канал к передаче запроса; впрочем, физический сокет на этой стадии еще не создается. Для выполнения команды POST необходимо добавить соответствующий заголовок типа содержимого. Логический аргумент указывает, является ли операция асинхронной: xmlRequest.open("POST", url, false); xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlRequest.send(postData);

Метод send открывает сокет и отправляет пакет. В предыдущем фрагменте кода он возвращает управление только после полного получения ответа. Для асинхронного запроса аналогичный код выглядит несколько иначе: xmlRequest.open("POST", url, true); xmlRequest.onreadystatechange = CallbackComplete; xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlRequest.send(postData);

На место элемента CallbackComplete подставляется функция JavaScript, которая получает и обрабатывает ответ, сгенерированный по отправленному запросу. Обратите внимание: функция, указанная в поле onreadystatechange, вызывается при всех изменениях состояния готовности. Допустимыми значениями являются целые числа от 0 до 4, которые означают соответственно «Запрос не инициализирован», «Метод open вызван успешно», «Метод send вызван успешно», «Прием данных» и «Ответ получен». Функция CallbackComplete обычно начинает свою работу с проверки состояния готовности.

Получение ответа Ответ доступен в двух форматах — физического текста и документа XML. В состояниях готовности 0-2 свойство responseText остается пустым — данные еще не приняты. При переходе в состояние 3 («Прием данных») свойство содержит данные, полученные до настоящего момента, интерпретированные в заданной кодировке символов. Если кодировка не указана, то по умолчанию используется UTF-8.


28

Глава 1. Структурные элементы приложений в стиле AJAX

Свойство responseXml остается недоступным до того момента, когда запрос будет полностью принят и успешно разобран как документ XML. Если тело ответа не содержит XML или разбор по какой-то причине завершается неудачей, свойство возвращает null. Обратите внимание на важное обстоятельство: документ XML конструируется на стороне клиента после полного приема физического ответа HTTP.

Использование объекта XmlHttpRequest Как упоминалось ранее, вам не придется использовать объект XmlHttpRequest в своих AJAX-приложениях, независимо от используемой прикладной среды (например, Atlas). И все же для полноты картины давайте кратко рассмотрим действия, необходимые для использования объекта в странице ASP.NET 2.0. Пример кода также может использоваться с ASP.NET 1.x.

Внеполосные вызовы из страниц ASP.NET Для веб-страницы, выдающей внеполосные вызовы, определяется одно или несколько триггерных событий, которые в результате обработки кодом JavaScript выдают запросы через объект XmlHttpRequest. Триггерными событиями могут быть только события HTML, отслеживаемые реализацией DOM текущего браузера. Код JavaScript должен инициировать обращение к удаленному URL и управлять им, как показано в следующем фрагменте: <script type="text/javascript"> function SendRequest(url, params) { // Включение параметров в строку запроса var pageUrl = url + "?outofband=true&param=" + params; // Инициализация объекта XmlHttpRequest var xmlRequest, e; try { xmlRequest = new XMLHttpRequestO ; } catch(e) { try { xmlRequest = new ActiveXObjectC'Microsoft.XMLHTTP"); } catch(e) { } } // Подготовка к отправке синхронного запроса POST xmlRequest.open("POST", pageUrl, false); xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlRequest.send(null); return xmlRequest;

} </script>

Функция в приведенном примере получает две строки — вызываемый URL и список параметров. Формат строки запроса никак не стандартизирован и может произвольно изменяться в каждой конкретной реализации.


Использование объекта XmlHttpRequest

29

В нашем примере URL, заданный программистом, дополняется парой параметров. Первый параметр (outofband в нашем примере) представляет собой логический флаг, который указывает, должен ли запрос обрабатываться пользовательской функцией обратного вызова. Располагая этой информацией, целевая страница сможет обеспечить правильную обработку запроса. Во втором параметре (param в нашем примере) передаются входные параметры для кода, исполняемого на стороне сервера. Код страницы выглядит примерно так: <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Testing Out-of-band</title> </head> <body> <form id="Forml" runat="server"> <hl>Demonstrate Out-of-band Calls</hl> <h2><^=Request.Url °1><I\\2> <hr /> <asp:DropDownList runat="server" ID="EmployeeList" /> <input id="Buttonl" type="button" value="Go Get Data" onclick="MoreInfo()" /> <hr /> <span id="Msg" /> </form> </body> </html> Класс программной логики (code-behind class) приведен в следующем листинге: public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (IsOutOfBandO) return; if (!IsPostBack) PopulateListO; } private bool IsOutOfBandO { if (Request.QueryString["outofband"] != null) { string param = Request.QueryString["param"].ToStringO ; Response.Write(ExecutePageMethod(param)); Response.FlushO; Response.End(); return true; } return false; }


30

Глава 1. Структурные элементы приложений в стиле AJAX private void PopulateListO { SqlDataAdapter adapter = new SqlDataAdapter( "SELECT employeeid, lastname FROM employees", "SERVER=(local);DATABASE=northwind;UID=...;"); DataTable table = new DataTableO; adapter.Fill(table); EmployeeList.DataTextField = "lastname"; EmployeeList.DataValueField = "employeeid"; EmployeeList.DataSource = table; EmployeeList.DataBindO; }

}

string ExecutePageMethod(string eventArgument) { return "You clicked: " + eventArgument; }

Некоторые моменты в этом листинге стоит упомянуть особо. Прежде всего обратите внимание на необходимость определения типа запроса (внеполосный запрос или обычная отправка). Затем посмотрите, как генерируется ответ. Метод IsOutOfBand проверяет поле outofband отправленной формы. Если поле outofband присутствует, то запрос обслуживается и завершается без прохождения последней части классического жизненного цикла запроса ASP.NET— событий изменения значений, возврата формы (postback) и т. д. Таким образом, внеполосные запросы быстрее обрабатываются и требуют передачи минимального объема данных. Что должна делать страница при отправке внеполосного вызова? Как она определяет ответ? Большинство существующих AJAX-сред по-разному отвечают на эти вопросы, поэтому можно сказать, что единого ответа на эти вопросы не существует. В общем случае вы определяете открытый программный интерфейс, активизируемый при внеполосном вызове. В рассмотренном примере был создан метод ExecutePageMethod с фиксированным именем и сигнатурой, вывод которого интерпретируется как ответ на запрос. В нашем примере метод получает и возвращает строки; это означает, что для всех входных и выходных параметров должно быть возможно преобразование к строковому формату (сериализация). string param = Request.QueryString["param"].ToStringO ; Response.Write(ExecutePageMethod(param)); Response.FlushO; Response.End();

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


Использование объекта XmlHttpRequest

31

ВНИМАНИЕ

Скорее всего, вам никогда не придется писать подобный код. И все же учтите: мы только что рассмотрели минимальное, но эффективное описание базового механизма, общего для всех прикладных сред, предоставляющих AJAX-функциональность. В каждой среде (например, Atlas, AJAX.NET) инкапсулируются многие технические детали и добавляются новые функции и возможности. Тем не менее на самом общем уровне библиотеки AJAX работают именно так.

Отображение результатов Остается сделать еще один шаг — что происходит на стороне клиента при получении ответа на внеполосный запрос? Следующий фрагмент клиентского кода связывается с кнопкой, инициирует внеполосный вызов и обновляет пользовательский интерфейс: function MorelnfoO { var empID = document.getElementByldC'EmployeeList").value: var xml = SendRequestC'default.aspx", empID); // Обновление пользовательского интерфейса var label = document.getElementByldC'Msg"); label.innerHTML = xml.responseText: }

Каждый раз, когда пользователь щелкает на кнопке, на URL следующего вида отправляется внеполосный запрос (число 3 в примере URL обозначает код работника — рис. 1.5): default.aspx?outofband=true&param=3

Рис. 1.5. «Ручное» программирование внеполосных запросов в ASP.NET 1 .х и ASP.NET 2.0


32

Глава 1. Структурные элементы приложений в стиле AJAX

Правильное отображение результатов в большинстве браузеров — задача не из простых. Так, модель DOM в Internet Explorer поддерживает ряд нестандартных сокращений, не работающих в других браузерах. Самая распространенная проблема — получение ссылок на элементы HTML с использованием метода document.getElementByld вместо прямого обращения по имени элемента. Например, следующий код работает в Internet Explorer, но не в Firefox и других браузерах семейства Mozilla: // Msg - идентификатор тега <span>. // Команда работает только в IE Msg.innerHTML = xml.requestText;

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

Существующие прикладные среды AJAX в .NET В наши дни появилось немало прикладных интерфейсов (API), реализующих функциональность AJAX в ASP.NET, причем один из них уже интегрирован в ASP.NET 2.0. Другие API были созданы сторонними разработчиками или появились в проектах с открытым исходным кодом. В последней части настоящей главы приводится краткий обзор таких API. Однако при этом я должен заметить, что если вашей основной средой разработки является ASP.NET, самым логичным выбором будет Microsoft Atlas. Хотя Microsoft Atlas также распространяется в виде отдельной надстройки для ASP.NET 2.0, предполагается, что Atlas станет частью одной из следующих версий ASP.NET, которой присвоено кодовое обозначение «Orcas». Бесспорно, Atlas является большим шагом в эволюции ASP.NET. Atlas происходит от двух API для ASP.NET — ASP.NET Script Callback и AJAX.NET (сейчас BorgWorX AJAX.NET), значительно расширяя их. На момент написания книги другие компании (такие, как Telerik и ComponentArt) также интегрировали функциональность AJAX в существующие компоненты для быстрой, полнофункциональной веб-разработки. В частности, компания Telerik также выпустила обновление для своего набора компонентов, в результате чего большая часть компонентов стала совместима с Atlas. В оставшейся части главы описаны способы использования Atlas на платформе ASP.NET. Сначала мы познакомимся с подсистемой ASP.NET Script Callback, входящей в ASP.NET 2.0, и сравним ее с BorgWorX AJAX.NET (библиотека с открытым исходным кодом). После этого станет очевидно, что для создания современных приложений с расширенными возможностями потребуется прикладная


ASP.NET Script Callback

33

среда, спроектированная специально под «стиль AJAX» — а еще лучше, если она будет хорошо интегрирована с «базовой» (underlying) платформой. Если вы программируете с использованием компонентов ASP.NET, вероятно, вам стоит остановить свой выбор на Microsoft Atlas. Если вы пользуетесь услугами пакета элементов с поддержкой ASP.NET, скорее всего, в его следующей версии появится интегрированная поддержка функциональности Atlas. Так или иначе, будущее веб-программирования на платформе ASP.NET тесно связано с AJAX.

A S P . N E T Script Callback ASP.NET 2.0 содержит интегрированный API, называемый ASP.NET Script Callback, для реализации внеполосных вызовов к URL текущей страницы. В этом API внеполосный запрос выглядит как запрос страницы особого рода. В нем состояние отображения передается вместе с исходными полями ввода. Для передачи дополнительной информации в тело запроса включаются дополнительные поля ввода. Оказавшись на сервере, запрос проходит через обычный конвейер модулей HTTP и доводит ожидаемую последовательность событий на стороне сервера до фазы предварительного построения (pre-rendering). Непосредственно перед фазой предварительного построения выполняется метод страницы, возвращаемое значение сериализуется в строку, а строка возвращается клиенту. Фаза построения никогда не наступает, а состояние отображения передается обратно без обновления.

Код на стороне клиента Внеполосный вызов ASP.NET начинается с выполнения функции JavaScript, предоставленной системой. Как правило, эта функция связывается с обработчиком события уровня страницы — такого, как щелчок на кнопке или изменение выбранной строки в раскрывающемся списке. Для наглядности рассмотрим конкретный пример. Представьте веб-страницу, на которой отображается информация о работнике. Пользователь выбирает имя из списка и щелкает на кнопке, чтобы получить дополнительную информацию. Чтобы механизм внеполосного вызова успешно работал, кнопка не должна быть кнопкой отправки (submit button); в противном случае произойдет обычная отправка данных с полным обновлением страницы. Возьмем следующую разметку: <asp:dropdownlist id="cboEmployees" runat="server" DataTextField="lastname" DataValueField="employeeid" /> <input type="button" runat="server" id="buttonTrigger" value="More Info">

Пользователь выбирает элемент из списка и щелкает на кнопке, чтобы получить дополнительную информацию. Обработчик события onclick кнопки добавляется динамически при обработке страницы на сервере. Такие вещи уместно делать при обработке события Page_Load: protected void Page_Load(object sender, EventArgs e) {


34

Глава 1. Структурные элементы приложений в стиле AJAX if (!IsPostBack) { // Заполнение списка cboEmployees.DataSource = GetEmployeeListO; cboEmployees.DataBind(); // Построить средствами ASP.NET строку с функцией Javascript, // вызываемой для инициирования внеполосного вызова string rpc = ClientScript.GetCallbackEventReference( this, "document.forms[0].elements['cboEmployees'].value", "UpdatePage", "null", "null", false); // Инициирование внеполосного вызова кнопкой string js = String.Format("javascript:{0}", rpc); buttonTrigger.Attributes["onclick"] = js; }

}

private Collection<Employee> GetEmployeeListO { }

ASP.NET Script Callback предоставляет собственный JavaScript API для инкапсуляции всех необходимых обращений к XmlHttpRequest. Вам как разработчику не обязательно знать этот API во всех деталях. Вместо этого следует направить все внимание на программный интерфейс метода GetCallbackEventReference объекта Page.ClientScript. Метод получает параметры, используемые при вызове, и возвращает строку с исходным кодом JavaScript, предназначенную для внедрения в страницу. Когда страница передается пользователю, кнопка, инициирующая внеполосный вызов, связывается со следующим сценарием: <input name="buttonTrigger" type="button" id="buttonTrigger" value="More Info" onclick="javascript:WebForm_DoCallback(' Page', document.forms[0].elements['cboEmployees'].value, UpdatePage, null, null, false)" />

Где определяется WebForm_DoCallback? Сценарий хранится в ресурсах сборки system.web и связывается со страницей при помощи динамически сгенерированной директивы включения сценария: <script src="/ProAspNetAdv/WebResource.axd?d=...&t=..." type="text/javascript"></script>

В странице, предоставленной пользователю, этот элемент располагается непосредственно за открывающим тегом <form>. Не стоит и говорить, что WebForm_ DoCallback вызывает объект XmlHttpRequest и управляет запросом HTTP.


ASP.NET Script Callback

35

Метод GetCallbackEventReference Аргументы, передаваемые методу GetCallbackEventReference, определяют, как должен активизироваться и работать н и ж е л е ж а щ и й механизм обратного вызова: public string GetCallbackEventReference( Control target, string argument, string clientCal1 back, string context, string clientErrorCallback, bool useAsync) Смысл каждого параметра описан в табл. 1.1. Таблица 1.1. Параметры функции GetCallbackEventReference Параметр

Описание

target

Задает целевой объект, то есть объект на стороне сервера, который будет обрабатывать запрос. Таким объектом может быть страница ASP.NET или любой серверный элемент, определенный в странице. Целевой объект должен реализовать интерфейс ICallbackEventHandler. Если целевой объект не реализует необходимый интерфейс, инициируется исключение

argument

Задает входные параметры метода, вызываемого на сервере в ответ на получение вызова. Если методу должны передаваться несколько параметров, разработчик обязан упаковать их в одну строку. Содержимое параметра без какой-либо дополнительной обработки передается серверному методу, обслуживающему запрос

clientCallback

Задает имя функции JavaScript на стороне клиента, которой будет передан результат вызова. Любые данные, возвращаемые серверным методом (даже строковые), передаются без какой-либо дополнительной обработки указанной функции JavaScript. Если функция не определена или недоступна для клиентской страницы, происходит ошибка сценария

context

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

clientErrorCallback

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

useAsync

Если параметр равен true, вызов выполняется асинхронно, если false — синхронно. По умолчанию используется значение false

Вооружившись этой информацией, вернемся к коду, который мы кратко рассматривали ранее: // Вызывается из Page_Load string rpc = ClientScript.GetCallbackEventReference( this, "document.forms[0].elements['cboEmployees'].value",


36

Глава 1. Структурные элементы приложений в стиле AJAX "UpdatePage", "null", "null", false);

Целевым объектом вызова является страница; это означает, что класс программной логики страницы должен реализовать интерфейс ICallbackEventHandler. Аргумент, передаваемый методам интерфейса, представляет собой результат заданного выражения JavaScript: document.forms[0].elements['cboEmployees'].value

Ничто не гарантирует, что в свойствах серверного элемента будут содержаться самые свежие данные, обновленные с учетом последних изменений на стороне клиента. Чтобы от клиента на сервер заведомо передавались обновленные данные, необходимо указать выражение JavaScript для их выборки в явном виде.

Интерфейс ICallbackEventHandler В клиентском коде страницы, использующей ASP.NET Script Callback, с каждым событием HTML связывается фрагмент кода JavaScript, сгенерированный системой. Этот код JavaScript использует внутренние обращения к XmlHttpRequest для выдачи внеполосных вызовов. Код JavaScript также ссылается на другой фрагмент JavaScript, используемый для обновления страницы результатами, полученными от сервера. Но что происходит на сервере при выдаче вторичного запроса? Какой метод страницы при этом выполняется? ASP.NET Script Callback определяет интерфейс с именем ICallbackEventHandler; этот интерфейс может быть реализован любым серверным объектом, который является целевым для внеполосных вызовов. Таким объектом может быть либо страница, либо любой из ее дочерних элементов. Интерфейс состоит из двух методов: public interface ICallbackEventHandler { string GetCallbackResultO; void RaiseCallbackEvent(string eventArgument); }

Исполнение внеполосного вызова делится на две фазы: подготовку и получение результатов. Сначала вызывается метод RaiseCallbackEvent для подготовки к удаленному выполнению кода. Метод GetCallbackResult вызывается на более поздней стадии жизненного цикла запроса, когда runtime-среда ASP.NET должна сгенерировать ответ для браузера. Традиционно метод RaiseCallbackEvent сохраняет аргумент во внутренней переменной, а весь содержательный код выполняется в GetCallbackResult. Впрочем, это всего лишь общепринятая практика. Следующий листинг показывает, как происходит получение и передача информации клиенту: private int _empID; void ICallbackEventHandler.RaiseCallbackEvent(string argument) { Int32.TryParse(argument, out _empID); }


ASP.NET Script Callback

37

string ICal1backEventHandler.GetCal1backResult() { // Получение дополнительной информации о заданном работнике Employeelnfo emp = GetEmployeeDetai1s(_empID); // Подготовка ответа для клиента: используется строковый формат // с разделением данных запятыми. // Вы сами выбираете формат возвращаемой строки. Этот формат // должен быть известен функции обратного вызова JavaScript. string[] buf = new string[6]; buf[0] = emp.ID.ToStringO; buf[l] = emp.FirstName; buf[2] = emp.LastName; buf[3] = ешр.Title; buf[4] = ешр.Country; buf[5] = ешр.Notes; _results = String.Join(",", buf); return ; }

Возвращаемые данные должны быть представлены в виде строки. Если исходные данные представляют собой управляемый объект, он сериализуется в строку. Затем на стороне клиента строка разбирается, и из нее извлекается вся важная информация.

ASP.NET Script Callback: все вместе Предполагается, что метод GetCallbackResult возвращает строку, которая представляет ответ на запрос. Возвращаемая строка передается функции JavaScript, обновляющей страницу. Клиентская функция обратного вызова выглядит примерно так: <script language="javascript"> function UpdateEmployeelnfoOesponse, context) { // Разбиение ответа на массив строк var о = response.split(','); // Получение ссылок на обновляемые элементы страниць var e_ID = document.getElementByldC"e_ID"); var e_FName = document.getElementById("e_FNaine"); var e_LName = document.getElementById("e_LName"); var e_Title = document.getElementById("e_Title"); var e_Country = document.getElementById("e_Country"); var e_Notes = document.getElementById("e_Notes"); // Обновление элементов страницы данными с сервера e_ID.innerHTML = о[0]; e_FName.innerHTML = о[1]; e_LName.innerHTML = о[2]; e_Titie.innerHTML = o[3]; e_Country.innerHTML = o[4]; e_Notes.innerHTML = o[5]; } </script>

За присутствие такой функции в клиентской странице отвечает автор страницы.


38

Глава 1. Структурные элементы приложений в стиле AJAX

Основные факты В общем и целом программный интерфейс ASP.NET Script Callback неудобен. Он скрывает от разработчика многие внутренние технические детали, но при этом требует хороших навыков использования JavaScript и немалого объема шаблонного серверного кода. Серверный код необходим для связывания элементов HTML с обработчиками событий на стороне клиента, а также для публикации программного интерфейса, вызываемого со стороны клиента. Каждый запрос содержит копию исходного состояния отображения, а на сервере воссоздается последнее заведомо работоспособное состояние. Другими словами, исходные значения всех полей ввода на странице, отображаемой в настоящий момент (без учета изменений, внесенных перед внеполосным вызовом), передаются серверу вместе со всеми параметрами серверного метода. Все внеполосные вызовы обрабатываются как обычные запросы на возврат данных вплоть до фазы предварительного построения; это означает, что в ходе обработки инициируются все стандартные серверные события: I nit, Load, LoadComplete и т. д. Перед фазой предварительного построения происходит подготовка и выполнение обратного вызова. После выполнения серверного метода обработка запроса немедленно прекращается. Состояние отображения не обновляется в соответствии с изменением страницы после внеполосного вызова и не возвращается клиенту. Преимущество ASP.NET Script Callback состоит в том, что этот механизм интегрирован в ASP.NET и легко инкапсулируется в серверных элементах. Например, элемент TreeView в ASP.NET 2.0 использует его для развертывания узлов. Тем не менее механизм ASP.NET Script Callback не свободен от целого ряда серьезных недостатков. В частности, серверный метод ограничивается фиксированной сигнатурой, получает и возвращает только строки. Конечно, в строку можно поместить любые данные, однако сериализацию и десериализацию объектов вам придется выполнять самостоятельно. Кроме того, страница, основанная на ASP.NET Script Callback, может иметь только одну конечную точку для удаленных вызовов. Это означает, что если клиентская страница должна обращаться к одной удаленной странице с двумя разными вызовами, вам придется включить в реализацию интерфейса ICallbackEventHandler логику выбора вызываемого метода.

Библиотека AJAX.NET В сущности, ASP.NET Script Callback представляет собой низкоуровневый API, предоставляющий в распоряжение программиста очень тонкую абстрактную прослойку, функции которой сводятся к автоматическому построению сценарного кода для инициирования вызова. Для эффективной реализации внеполосных вызовов в сценариях уровня приложения необходима библиотека, которая бы скрывала все технические подробности обмена данными HTTP и предоставляла дополнительный сервис более высокого уровня.


Библиотека AJAX.NET

39

AJAX.NET — популярная библиотека с открытым исходным кодом, создающая абстрактное представление для механизма XmlHttpRequest. Чтобы использовать библиотеку, необходимо внести изменения в файл web.config и зарегистрировать в нем специализированный обработчик HTTP. Обработчик предоставляет дополнительные функции (такие, как сериализация объектов). В частности, обработчик гарантирует, что любой управляемый объект .NET, возвращаемый серверным методом, будет сериализован в динамически созданный объект JavaScript, который без проблем используется на стороне клиента. Давайте переработаем предыдущий пример с использованием библиотеки AJAX.NET. ПРИМЕЧАНИЕ

Исходная версия библиотеки AJAX.NET была разработана для ASP.NET 1.x Майклом Шварцем (Michael Schwarz), а затем была обновлена для ASP.NET 2.0. За дополнительной информацией обращайтесь на сайт http://www.ajaxpro.info. Недавно разработка библиотеки была прекращена и возобновилась в виде проекта с открытым исходным кодом под руководством BorgWorX (http://www.borgworx.net).

Подготовка библиотеки Для использования библиотеки AJAX.NET потребуется сборка AJAX.dll, которая находится в глобальном кэше сборок (GAC) или в папке Bin приложения. Затем в файл web.config включается запись обработчика HTTP: <httpHandlers> <add verb="POST,GET" path="AJAX/*.ashx" type="AJAX.PageHandlerFactory, AJAX" /> </httpHandlers>

В результате все запросы к URL, соответствующим шаблону AJAX/*.ashx, передаются обработчику AJAX.NET. Обратите внимание: вам не придется создавать в приложении подкаталог AJAX со всеми страницами, вызываемыми через AJAX.NET. Библиотека использует специализированный обработчик HTTP (компонент AJAX.PageHandlerFactory) для перехвата запросов к ресурсам с заданными именем. Клиентская структура AJAX.NET гарантирует, что все URL, к которым производятся обращения со стороны клиента, обладают соответствующим форматом. Обработчик HTTP автоматически внедряет в поток ответа весь клиентский код, необходимый для инициирования внеполосных вызовов. С точки зрения разработчика для использования библиотеки AJAX.NET в приложении необходимо лишь включить приведенную ранее секцию в файл web.config.

Клиентский код Клиентская страница AJAX.NET содержит код JavaScript, инициирующий внеполосные вызовы. Представьте страницу, которая рассматривалась ранее для ASP.NET Script Callback. На странице находится кнопка для получения дополнительной информации о выбранном работнике. С кнопкой (которая, как


40

Глава 1. Структурные элементы приложений в стиле AJAX

уже говорилось ранее, не является кнопкой отправки данных!) связывается код следующего вида: <script type="text/javascript"> function GetCustomerDetailО { var customerlD = document.getElementByldC'customerlD"); var response = TestPage.GetCustomerByID(customerID.value); var oCustomer = response.value; if (oCustomer.Type == "UnknownCustomer") { alertC'Customer not found"); } else { var fn = document.getElementByldC'firstName"); var In = document.getElementByldC'lastName"); fn.innerHTML = oCustomer.FirstName; ln.innerHTML = oCustomer.LastName; }

} </script>

Как видите, функция JavaScript обращается с вызовом к вроде бы несуществующему клиентскому объекту (объект TestPage), а объект JavaScript используется для обработки возвращаемого значения (переменная oCustomer). Где определяется метод TestPage.GetCustomerBylD? И где определяется прототип переменной oCustomer? В обоих случаях ответ один: в классе программной логики.

Серверный код Класс программной логики страницы AJAX.NET должен удовлетворять паре требований. Во-первых, он предоставляет для класса страницы один или несколько открытых методов, помеченных атрибутом [AJAXMethod]. Во-вторых, событие Page_Load должно регистрировать один или несколько типов в инфраструктуре AJAX. public partial class TestPage : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Uti1ity.RegisterTypeForAJAX(typeof(TestPage)); Uti1ity.RegisterTypeForAJAX(typeof(Samples.Customer)); Uti1ity.RegisterTypeForAJAX(typeof(Samples.UnknownCustomer)); } [AJAXMethodO" public Customer GetCustomerByID(string customerlD) { // Здесь размещается код выборки информации о работнике // из базы данных switch (customerlD)


Библиотека AJAX.NET

{

}

}

}

41

case "1": return new CustomerC'John", "Doe"); case "3": return new CustomerC'Jim", "Idontknowthisguy"); default: return new UnknownCustomerO;

Серверная страница воспроизводится на стороне клиента через объект JavaScript, имя которого совпадает с именем класса страницы — TestPage в нашем примере. Любой серверный метод, помеченный атрибутом [AJAXMethod], определяется как метод этого объекта JavaScript. Автоматически генерируемое тело метода выдает внеполосный вызов к соответствующему серверному методу с использованием XmlHttpRequest. Серверный метод может вернуть любой объект, зарегистрированный в инфраструктуре AJAX; в отличие от ASP.NET Script Callback он ни в коей мере не ограничивается возвратом строк. Каждый объект, зарегистрированный в AJAX, строится как объект JavaScript, определение которого затем связывается с клиентской страницей. В результате вы можете обращаться с удаленными вызовами к любому количеству методов на стороне сервера и использовать собственные объекты для передачи информации, реализуя своего рода «HTTP-вызовы с сильной типизацией».

Библиотека AJAX.NET: все вместе Подготовительная настройка решений AJAX.NET достаточно проста. Сначала посредством редактирования файла web.config к приложению подключается перехватчик AJAX.NET. Затем пишется встроенный код JavaScript для управления внеполосным вызовом. Каждый вызов обращен к объекту JavaScript, представляющему открытый интерфейс серверной страницы ASP.NET. Когда к странице поступает первый запрос, обработчик страниц AJAX.NET автоматически внедряет сценарный код с определением «объекта страницы» JavaScript. Каждый метод этого объекта ссылается на соответствующий серверный метод. Возвращаемое значение серверного метода инкапсулируется в объекте JavaScript, определяемом в AJAX. Чтобы получить возвращаемое значение, обратитесь к свойству value полученного объекта. Возвращаемое значение серверного метода представляет собой объект JavaScript, который имитирует открытый интерфейс реально возвращаемого типа данных. Другими словами, серверный метод возвращает тип .NET, а клиентский код получает соответствующий объект JavaScript. Этот процесс называется сериализацией JSON (JavaScript Object Notation).

Основные факты AJAX.NET обладает рядом преимуществ по сравнению с ASP.NET Script Callback API. Методы, вызываемые со стороны клиента, помечаются специальным атрибутом. Такой способ обеспечивает наибольшую гибкость при определении


42

Глава 1. Структурные элементы приложений в стиле AJAX

открытого интерфейса сервера, вызываемого со стороны клиента. Кроме того, возможность регистрации серверных типов, используемых на стороне клиента, позволяет реализовать пересылку данных с сильной типизацией. Инфраструктура AJAX.NET сериализует типы .NEТ в объекты JavaScript, и наоборот. AJAX.NET перехватывает и заменяет стандартный механизм обработки запросов ASP.NET (обработчик страниц). Как следствие, вы не будете получать классические серверные события ASP.NET типа Init и Load, а состояние отображения не будет автоматически передаваться с каждым внеполосным запросом. Однако при этом запросы AJAX.NET по-прежнему обрабатываются runtime-системой HTTP ASP.NET, то есть с запросом работают различные модули, зарегистрированные в конвейере HTTP, включая управление состоянием сеанса, управление ролями и аутентификацию. Дело в том, что запрос поглощается и обрабатывается обработчиком AJAX.NET HttpHandler, поэтому на него распространяются общие принципы, управляющие обработкой запросов в ASP.NET.

Прикладные среды на базе AJAX По мере того как стиль программирования AJAX получает признание как следующий крупный шаг в области веб-разработки, вступают в действие два очевидных фактора. Во-первых, для разработчиков приложений очень важно иметь модель программирования, которая бы не слишком сильно отличалась от уже знакомых им моделей. Разработчики должны относительно быстро переходить на AJAX. Однако быстрый переход возможен только в том случае, если новая прикладная среда является расширением старой. Но во-вторых, новая прикладная среда должна предоставлять в распоряжение разработчика широкофункциональные и эффективные готовые компоненты и службы, сводящие к минимуму влияние таких сложных и объективно существующих факторов, как написание кросс-браузерного сценарного кода, модели программирования на стороне сервера и сериализация данных. Вместо автономных библиотек — таких, как ASP.NET Script Callback и AJAX.NET (по крайней мере, в исходной версии проекта), — разработчики отдают предпочтение прикладным средам и пакетам компонентов ASP.NET, обеспечивающим расширенную поддержку AJAX. Технология Microsoft Atlas, представленная на Конференции профессиональных разработчиков в 2005 году, представляет собой надстройку для платформы ASP.NET 2.0. Она станет одним из столпов следующей версии ASP.NET (Orcas). Atlas воплощается в форме нового семейства серверных элементов, для которых за счет использования новых runtime-функций становится возможной полная интеграция программирования на стороне клиента и сервера. Вся оставшаяся часть книги полностью посвящена архитектуре и модели программирования Atlas. По этой причине будет полезно кратко познакомиться с парой коммерческих продуктов, которые обеспечивают AJAX-функциональность, а в будущем с большой вероятностью будут обладать интегрированной поддержкой Atlas.


Прикладные среды на базе AJAX

43

Web.UI (ComponentArt) Компонент Callback является частью Web.UI — пакета элементов ASP.NET компании ComponentArt. Он добавляет функциональность AJAX к ряду других компонентов семейства Web.UI. Кроме того, он способен наделить поддержкой AJAX практически любой из существующих элементов ASP.NET. Компонент CallBack, как и его «родной» аналог из Atlas (компонент UpdatePanel подробно рассматривается в главе 3), способен работать в обход стандартного жизненного цикла страницы для ускоренного выполнения логики на стороне сервера, а также поддерживать обновленную информацию о состоянии всех элементов ASP.NET, содержащихся в странице, за счет использования состояния отображения. Компонент CallBack ориентирован на серверо-центрическую модель программирования, но пока не обладает встроенными функциями для разработки клиенто-центрических решений (обращения к веб-службам, методы страниц или привязка данных на стороне клиента). Впрочем, в будущих версиях Web.UI для ASP.NET будет реализована полная поддержка Atlas. За дополнительной информацией обращайтесь на сайт http://www.componentart.com.

r.a.d.ajax (Telerik) Элемент r.a.d.ajax компании Telerik представляет собой новую прикладную среду AJAX; заявка на его патент сейчас находится в стадии рассмотрения. Он позволяет включить поддержку AJAX в любое приложение ASP.NET без программирования. Главной целью этого продукта является устранение всех сложностей, связанных с программированием на JavaScript при построении AJAX-приложений; это позволяет разработчикам использовать новую технологию без значительных затрат времени и сил на ее освоение. Задача решается инкапсуляцией механизма AJAX и всей примыкающей логики в компонентах ASP.NET, настройка которых в Visual Studio 2005 может осуществляться на визуальном уровне, с помощью удобных построителей (builders). В результате разработчик пишет обычное приложение, основанное на возврате данных, и легко превращает его в AJAX-приложение; при этом ему даже не приходится писать какой-либо код JavaScript или серверный код. Главным достоинством этого продукта является технология автоматической замены функций возврата данных элементов страницы обратными вызовами AJAX (в настоящее время решается вопрос с ее патентованием). В результате элемент «AJAX’ифицируется» и начинает выдавать обратные вызовы без вмешательства со стороны пользователя — без специального кода JavaScript и производимых «вручную» обращений к XmlHttpRequest. Эта технология способна наделить поддержкой AJAX любые стандартные элементы ASP.NET, элементы Telerik для ASP.NET (пакет r.a.d.controls) и большинство элементов сторонних разработчиков (за дополнительной информацией обращайтесь по адресу http://www.telerik.com/).


44

Глава 1. Структурные элементы приложений в стиле AJAX

Прикладная среда Telerik r.a.d.ajax включает следующие компоненты: I AJAX Panel — универсальный контейнер, включающий поддержку AJAX. Все элементы, размещенные внутри контейнера, начинают выдавать обратные вызовы вместо возврата данных. I AJAX Manager — компонент, который позволяет на стадии конструирования указать, какие элементы страницы должны инициировать обратные вызовы и какие элементы нуждаются в соответствующем обновлении. I AJAX Timer — компонент, выполняющий обратные вызовы AJAX с заданными интервалами. Кроме того, встроенная поддержка AJAX присутствует во всех компонентах пользовательского интерфейса Telerik. Например, поддержка AJAX задействована во внутренней работе всех компонентов для работы с данными из пакета Telerik r.a.d.controls; тем самым обеспечивается производительность, близкая к производительности приложений реального времени. Режим AJAX включается при помощи одного свойства. Среда Telerik полностью сохраняет жизненный цикл страниц ASP.NET, что крайне важно для правильной работы приложения. Состояние отображения, проверка событий и сценарии клиентской стороны полностью сохраняются, как при обычном возврате данных. Все данные форм автоматически отправляются серверу для обработки. Прикладная среда Telerik r.a.d.ajax представляет собой комплексное решение, позволяющее даже начинающему разработчику ASP.NET создавать сложные вебприложения с поддержкой AJAX, схожие с Microsoft Office Outlook Web Access.

Заключение Самые прозорливые разработчики сообщества занимаются разработками на базе интерактивных веб-технологий с конца 1990-х годов. За прошедший период появились различные технологии (например, Microsoft Remote Scripting и другие разработки — как коммерческие, так и бесплатные), однако это не привело к формированию критической массы — как по распространению, так и по использованию. А может быть, критическая масса накопилась, но ей не хватало детонатора в виде сверхпопулярного приложения. Другим фактором, замедлившим принятие более совершенных клиентских технологий, было отсутствие кроссбраузерной поддержки. Сегодняшняя ситуация в корне отличается от той, которая существовала всего три или четыре года назад. Сейчас около 90 % браузеров поддерживают все минимальные требования для реализации интерактивных веб-приложений, известных как приложения AJAX. Кроме того, комитет W3C стандартизировал объект XmlHttpRequest, на базе которого строится большинство существующих платформ для AJAX. Следующее поколение веб-приложений будет основано на новом механизме: этим механизмом будут уже не формы, отправляемые при смене страниц (или по крайней мере не только они), а индивидуальные запросы данных и динамические обновления отображаемых страниц.


Заключение

45

ASP.NET как серверная технология, ориентированная на создание веб-страниц, не упускает возможность предоставить эту столь востребованную функциональность. Первой попыткой создания API для построения страниц «в стиле AJAX» был механизм ASP.NET Script Callback. Построенный по образцу классических событий возврата данных, он оказался излишне тяжеловесным и недостаточно гибким. Проект с открытым исходным кодом AJAX.NET был более удачным решением: он превосходил Script Callback по широте возможностей, по гибкости и прямолинейности модели программирования. Тем не менее обеим технологиям были присущи два главных недостатка — отсутствие широкофункциональной клиентской объектной модели, компенсирующей различия между реализациями D O M в разных браузерах, и отсутствие четкой границы между клиентскими и серверными аспектами программирования. Microsoft Atlas определяет ближайший путь развития AJAX-приложений с позиций платформы ASP.NET. Atlas интегрирует стиль AJAX с существующей моделью приложений ASP.NET. Результатом такой интеграции является знакомая модель программирования со значительно расширенной и обогащенной функциональностью.


ГЛАВА 2

Структурные элементы Atlas В I I I

этой главе: Архитектура Atlas Программная модель Atlas Пример страницы Atlas

Microsoft Atlas — прикладная среда, внедряющая функциональность в стиле AJAX на платформу Microsoft ASP.NET 2.0. В отличие от других аналогов, среда Atlas проектировалась как составная часть ASP.NET, и как следствие, она идеально интегрируется с существующей платформой и моделью приложения. Технология Atlas будет полностью интегрирована в следующую версию Microsoft Visual Studio и наряду с поддержкой LINQ и Model-View-Controller будет заложена в основу будущей платформы ASP.NET, выход которой ожидается в 2007 году (за дополнительной информацией о LINQ и ее роли в контексте следующей версии .NET Framework обращайтесь по адресу http://msdn.microsoft.com/data/ ref/linq). С самого начала вы должны думать об Atlas как о «родной» части платформы ASP.NET, а не как о «приделанной» (и более или менее ненавязчивой) внешней библиотеке. В контексте ASP.NET подсистеме Atlas отведена вполне понятная и четко определенная роль. Она придает «изюминку» всей платформе, превращая ее в высокопроизводительную платформу для веб-приложений в стиле AJAX. Благодаря Atlas разработчики смогут строить интерактивные, кроссбраузерные веб-приложения, базирующиеся на существующих стандартах — таких, как Dynamic HTML, JavaScript, CSS (Cascading Style Sheets) и, наконец, XmlHttpRequest. Взяв на вооружение Atlas, вы наконец-то сможете писать вебприложения, работающие в браузерах Microsoft Internet Explorer, Firefox, Netscape и Safari, отличающиеся универсальностью и простотой развертывания. Atlas вносит усовершенствования как на стороне клиента, так и на стороне сервера. Богатый набор компонентов, элементов и функций упрощает AJAX-программирование, и что еще важнее, делает его полностью прозрачным для разработчиков ASP.NET. На стороне клиента Atlas использует клиентскую инфраструктуру пользовательского интерфейса (UI) и модель сценарных компонентов (script component model - см. http://www.microsoft.com/technet/scriptcenter/resources/ scriptshop/default.mspx) для быстрого и эффективного проектирования богатой, интерактивной функциональности. По мере того как платформа Atlas развивается по пути полной интеграции с веб-платформой Microsoft, новые инструменты для разработчиков и дизайнеров будут создаваться и интегрироваться в следующую версию Visual Studio.


Архитектура Atlas

47

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

Архитектура Atlas С архитектурной точки зрения прикладная среда Atlas состоит из двух элементов: библиотеки клиентских сценариев и набора серверных расширений, интегрирующих Atlas в ASP.NET. Библиотека клиентских сценариев написана полностью на JavaScript, а следовательно, работает с любым современным браузером. Так как Atlas является расширением ASP.NET, среда полностью интегрирована с серверными службами и элементами ASP.NET. Как следствие, разработчики могут создавать широкофункциональные веб-страницы на базе Atlas практически теми же методами, которые применяются при разработке классических серверных страниц ASP.NET. Atlas в сочетании с ASP.NET предлагает комплексную программную модель, которая распространяется как на клиентскую, так и на серверную среду. Тем не менее область применения Atlas не ограничивается серверной платформой ASP.NET и даже не привязывается к ней. Если говорить точнее, платформа Atlas совершенно независима от сервера и может работать с любым веб-сервером, включая Apache. Библиотека клиентских сценариев Atlas может использоваться на любом клиенте и на любом сервере. Разумеется, если вы пытаетесь использовать Atlas на платформе другого вебсервера, то вам придется предоставить все необходимые службы и компоненты Atlas в формате, действительном для этой серверной платформы. Например, при использовании веб-сервера Apache с поддержкой P H P (PHP: Hypertext Preprocessor) потребуются как минимум менеджер сценариев Atlas и прокси-генератор, написанные в виде модулей PHP. На платформе ASP.NET эти компоненты становятся доступными после установки Atlas. Мы еще вернемся к этому моменту позднее в настоящей главе.

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

Установка Atlas Чтобы начать работать с Atlas, прежде всего следует загрузить новейшую версию прикладной среды. Информацию о том, какой пакет вам потребуется, можно найти на сайте http://atlas.asp.net — официальной домашней странице Atlas. Пакет, загруженный с сайта, содержит отладочную и окончательную версии библиотеки сценариев Atlas и все необходимые двоичные файлы. Если на вашем компьютере установлена любая версия Visual Studio 2005, пакет также настраивает


48

Глава 2. Структурные элементы Atlas

интегрированную среду разработки (IDE) так, чтобы в ней отображался готовый шаблон проекта Atlas (рис. 2.1). Пакет Atlas в формате MSI (Microsoft Windows Installer) копирует на ваш компьютер ряд файлов в следующую папку: %DRIW%:\Program

Fi 1 es\Microsoft ASP.NET\Atlas\v2.0.50727\Atlas

Рис. 2 . 1 . Шаблон проекта Atlas, появляющийся при создании новой веб-страницы

В частности, копируется сборка с именем Microsoft.Web.Atlas.dll и файл web. config с включенной поддержкой Atlas. Эта версия конфигурационного файла содержит все записи, необходимые для нормального выполнения приложений Atlas. Сборка Atlas также включает группу файлов JavaScript (.js), образующих библиотеку клиентских сценариев. Примерное содержимое папки после установки Atlas показано на рис. 2.2. Мастер установки спрашивает вашего разрешения на установку шаблонов проектов Visual Studio для конкретных языков и на регистрацию нового расширения .asbx. Оба действия не являются обязательными. Расширение .asbx идентифицирует мостовые файлы Atlas. Мостовые файлы представляют собой компоненты, взаимодействующие с веб-службами вне вашего приложения. Типичный пример использования файлов .asbx — получение данных с других веб-сайтов для создания гибридных сайтов (mash-ups). Содержимое таких сайтов формируется посредством объединения информации, полученной из сторонних источников. Если ваше приложение не получает данные из внешних веб-источников, регистрировать расширение .asbx не обязательно. Мостовые файлы будут более подробно рассмотрены в главе 6.


Архитектура Atlas

49

Рис. 2.2. Содержимое пакета Atlas

Развертывание приложений Atlas Самый простой способ создать приложение Atlas — выбрать шаблон проекта Visual Studio 2005 (рис. 2.1). Visual Studio 2005 копирует runtime-сборку Atlas из установочной папки в папку Bin нового сайта. В то же время Visual Studio включает в проект файл web.config со всеми параметрами, необходимыми для запуска приложений Atlas. Чтобы включить поддержку Atlas в существующее приложение ASP.NET или создать приложение Atlas без шаблона проекта Visual Studio 2005, выполните следующие действия. Сначала добавьте runtime-сборку Atlas (Microsoft.Web.Atlas.dll) в папку Bin приложения (при включении ссылки на сборку Atlas это делается автоматически). Учтите, что сборка обычно находится в папке %DRIME%:\Program Fi 1 es\Microsoft ASP.NET\Atlas\v2.0.50727\Atlas

Затем отредактируйте файл web.config приложения и введите все необходимые параметры (они будут описаны позднее, в разделе «Пример страницы Atlas»). ПРИМЕЧАНИЕ

Компания Microsoft сообщила первую информацию об Atlas на Конференции профессиональных разработчиков в 2005 году. С того времени каждые несколько недель публикуется бюллетень CTP (Community Technology Preview). В выпуске CTP за март 2006 года компания Microsoft также опубликовала лицензию Go-Live, разрешавшую использовать инструментарий Atlas для реализации AJAX-функциональности в рабочих проектах. Иначе говоря, вам не обязательно ждать выхода окончательной версии Atlas, чтобы заняться разработкой собственных решений; и разумеется, не нужно дожидаться, пока поддержка Atlas будет включена в Visual Studio версии Orcas, которая выйдет в 2007 году. Технология Atlas доступна здесь и сейчас!


50

Глава 2. Структурные элементы Atlas

Базовые компоненты Прикладная среда Atlas состоит из клиентской и серверной части. Клиентская библиотека JavaScript используется приложениями в основном для управления пользовательским интерфейсом страниц и для вызова серверных компонентов. Серверные компоненты генерируют ответ для клиента и выдают заранее определенный клиентский сценарий, который интегрируется с клиентской библиотекой, а иногда и расширяет ее. Серверная часть Atlas включает веб-службы, специальные элементы и инфраструктуру JSON (JavaScript Object Notation). (Технология обмена данными JSON будет описана позднее в этой главе.)

Клиентская библиотека Клиентская библиотека Atlas состоит из набора файлов JavaScript (*.js), подключаемых из клиентских страниц в случае надобности. Эти файлы *.js устанавливаются как файлы с исходным кодом при установке Atlas на компьютер разработчика. На рабочий компьютер они не копируются, потому что они также внедряются в сборку Atlas, а страницы ссылаются на все необходимые сценарные файлы как на ресурсы сборки. Клиентская библиотека содержит объектно-ориентированные и кросс-браузерные расширения языка JavaScript, такие как классы, пространства имен, наследование и типы данных. В ней также определяется (по большей части закрытая) версия библиотеки базовых классов .NET, включающая поддержку построителей строк, регулярных выражений, таймеров и трассировки. Ключевой частью клиентской библиотеки Atlas является сетевой уровень, который берет на себя все сложности асинхронных вызовов с использованием XmlHttpRequest. Сетевой уровень позволяет клиентской странице взаимодействовать с веб-службами и вебстраницами посредством внеполосных вызовов. Клиентская библиотека Atlas также содержит поддержку таких функций, как перетаскивание, всплывающие окна, задержка указателя мыши над объектом, а также группу элементов с поддержкой привязки данных полностью на стороне клиента, навигации и автозаполнения. Такие элементы могут вставляться в исходный код страниц с использованием JavaScript (в дополнение к новому декларативному синтаксису XML Script).

Серверные компоненты Технология Atlas появилась как расширение ASP.NET, а ASP.NET является платформой разработки на стороне сервера. По этой причине в Atlas входит набор серверных компонентов, включая веб-службы и элементы управления. С одной стороны, вы можете программировать эти компоненты со стороны клиента и обновлять текущую страницу без полного обновления; с другой стороны, программная модель большей частью остается неизменной. В этом отношении (по крайней мере, в том, что касается базовой функциональности) срок освоения Atlas оказывается на удивление коротким. Встроенные веб-службы Atlas предоставляют клиентским страницам набор стандартных функций ASP.NET, в числе которых профили пользователей, член-


Базовые компоненты

51

ство в группах, роли и глобализация. Серверные элементы Atlas выглядят как классические серверные элементы ASP.NET, но в отличие от последних они выдают дополнительный сценарный код. Код расширяет возможности элемента за счет (необязательного) использования функций, предоставленных клиентской библиотекой Atlas. Большинство элементов Atlas имеет близкие аналоги среди существующих элементов ASP.NET — таких, как кнопки, надписи, текстовые поля и элементы проверки данных. Другое важное подмножество серверных элементов Atlas выдает код JavaScript для связывания аспектов поведения клиентских сценариев с элементами разметки HTML. Код поведения находится в клиентской библиотеке; некоторые серверные элементы позволяют управлять связыванием аспектов поведения с определенными видами разметки (например, связыванием автозаполнения с конкретным элементом TextBox). Элементы Atlas ни в чем принципиально не отличаются от классических серверных элементов ASP.NET. После того как они будут включены в Visual Studio Orcas, вы сможете работать с элементами Atlas в конструкторе страниц точно так же, как с обычными элементами. К моменту выхода Visual Studio Orcas не останется реальных различий между элементами ASP.NET и элементами Atlas.

JSON В связи с расширением применения внеполосных вызовов в веб-приложениях возникает новая проблема с передачей все более и более сложных данных. Проблема не сводится к простой сериализации, для которой в .NET Framework и других платформо-зависимых прикладных средах уже имеется готовое решение. Задача сериализации при внеполосных вызовах не сводится к простому обеспечению кросс-платформенности; в ней также задействованы разные логические уровни с участием сильно различающихся инструментов и языков. При внеполосных вызовах данные передаются на сервер и обратно. Но сторона клиента представлена браузером (без учета мобильных устройств), для которого «родным» форматом данных является JavaScript. Сторона сервера представлена веб-сервером, способным работать на разных комбинациях аппаратных и программных платформ, с некоторой прикладной средой веб-приложений. Формат JSON набирает популярность как способ передачи структурированных данных в Web. Этот формат обмена данными основан на подмножестве языка JavaScript, а его полное описание можно найти по адресу http://www.json.org. JSON относительно легко читается человеком, разбирается и генерируется компьютером. Для описания данных в нем используются две универсальные структуры данных — коллекции и массивы, в той или иной форме поддерживаемые всеми современными языками программирования и библиотеками классов. Текстовый формат JSON совершенно не зависит от языка программирования, хотя в нем применяются многие условные обозначения, унаследованные от языков семейства C. Клиентская инфраструктура JSON сериализует объект JavaScript в формат обмена данными и пересылает его по каналу связи получателю, находящемуся на стороне сервера. Получатель, привязанный к конкретной платформе, разбирает поток данных и строит платформо-зависимый объект. Аналогично, серверная


52

Глава 2. Структурные элементы Atlas

инфраструктура JSON может взять любой платформо-зависимый объект и сериализовать его в формат обмена данными. На стороне клиента поток данных быстро преобразуется в объект JavaScript. С точки зрения .NET Framework и Atlas потребуется проанализировать внутреннюю структуру классов и создать для них подходящие «обертки» JavaScript. Инфраструктура JSON реализована практически во всех средах на базе AJAX. Atlas не является исключением. Впрочем, как упоминалось в главе 1, в ASP.NET 2.0 Script Callback поддержка JSON отсутствует. ПРИМЕЧАНИЕ

Одно время XML рекламировался как «единый язык» Web; ведь он создавался специально для того, чтобы разработчики и проектировщики могли упаковывать данные и передавать их совершенно независимо от платформы. В наши дни ту же роль часто отводят JSON (технология, не зависящая от XML). В чем различия? JSON и XML решают одну и ту же задачу. Формат XML сложнее и запутаннее, но он лучше подходит для описания данных, к которым применяются трансформации XSLT. Для низкоуровневых данных предпочтение отдается формату JSON — он « легче» по объему передаваемых данных, проще читается людьми и разбирается компьютерами.

Atlas на других серверных платформах Веб-приложения по определению не должны зависеть от серверной платформы. Тем не менее это не значит, что комбинация оборудования и программного обеспечения сервера вообще ни на что не влияет. Это утверждение остается истинным даже в том случае, когда благодаря Atlas и другим прикладным средам на базе AJAX фокус приложения перемещается на сторону клиента. Как упоминалось ранее, в приложениях Atlas клиентский код может смешиваться с серверным, а для построения страниц необходима специализированная поддержка на сервере. Если на веб-сервере используется ASP.NET и IIS, эта поддержка распространяется бесплатно и загружается вместе с Atlas. На других серверных платформах ее придется строить отдельно.

Необходимые функции Минимальный набор функций для поддержки Atlas на серверных платформах, не связанных с ASP.NET, включает доступ к веб-службам и прикладным службам, а также распознавание браузера. Кроме того, страницы Atlas могут использовать широкофункциональные элементы управления для выдачи разметки и сценарного кода. На других серверных платформах такие элементы могут не существовать. Давайте немного подробнее рассмотрим функции, необходимые для поддержки Atlas. ASP.NET автоматически генерирует клиентского посредника для веб-службы, ссылка на которую включена в страницу ASP.NET Atlas. Посредник автоматически сериализует объекты .NET в JavaScript и обратно. Аналогичная инфраструктура должна быть построена и на других платформах, хотя для некоторых конкретных платформ удается воспользоваться сериализатором JSON. Страницы ASP.NET Atlas включают инициализирующий компонент ScriptManager, который автоматически распознает возможности текущего браузера


Atlas на других серверных платформах

53

и вносит соответствующие поправки в пересылаемый код JavaScript. Если вы используете веб-сервер, отличный от IIS (Internet Information Server), в сочетании с ASP.NET, код для выполнения этих операций придется добавить самостоятельно. Наконец, ASP.NET предоставляет набор прикладных служб, которые могут вызываться удаленно клиентскими страницами: аутентификация, кэширование, пользовательские профили и т. д. Если эта функциональность будет сочтена полезной, вероятно, она будет реализована и для других платформ.

Приложения Atlas и РНР Страницы Atlas представляют собой обычные веб-страницы, написанные с использованием некоторого языка и модели программирования, будь то ASP.NET, классический вариант ASP, Perl, Rails или PHP. С позиций веб-программирования у каждого языка имеется своя предпочтительная программная модель, и наоборот; тем не менее базовая схема остается неизменной. Технология Atlas полностью интегрирована с платформой ASP.NET, но результат ее применения все равно представляет собой веб-приложение. Если Atlas используется за пределами ASP.NET, вы не сможете пользоваться серверными элементами. Впрочем, элементы всего лишь помогают быстро сгенерировать широкофункциональный, сложный сценарный код без изучения JavaScript. Серверные элементы являются ключевым компонентом прикладной среды ASP.NET, но аналогичные компоненты можно создать с другими моделями для других серверных сред. Чтобы использовать Atlas, допустим, с PHP, всю интеграцию придется реализовать самостоятельно — это и есть основной объем работы. Если вас заинтересует пример, демонстрирующий написание страниц Atlas с PHP на веб-сервере Apache, загляните в блог Шанку Нийоги (Shanku Niyogi) по адресу http://www.shankun.com. Приложение содержит папку кода с написанным на PHP сериализатором JSON, базовым классом веб-службы и проксигенератором сценарного кода. Пример показывает, как создать веб-службу Atlas с использованием PHP. Файл P H P ссылается на базовый класс веб-службы и объявляет класс, производный от базового класса веб-службы Atlas. Вот небольшой фрагмент этого файла: <?php require_once("AtlasPhp/AtlasService.php"); class AutoComplete extends AtlasService { function GetWords($prefixText, $count) { }

return $results;

} $service = new AutoComplete(); $service->ProcessRequest(null); ?>

Если вас заинтересует полный исходный код, а также различные сообщения и комментарии, обратитесь по адресу http://www.shankun.com/atlasphp.aspx.


54

Глава 2. Структурные элементы Atlas

Программная модель Atlas Atlas предлагает два основных подхода к веб-разработке: серверо-центрический и клиенто-центрический. В обоих случаях создается широкофункциональное веб-приложение с большим количеством кода JavaScript; различается только используемая программная модель. Серверо-центрическая модель позволяет последовательно наращивать готовые приложения AJAX-расширениями пользовательского интерфейса. Также можно создавать новые приложения на базе AJAX, с классическим серверо-центрическим подходом. Основная часть пользовательского интерфейса и логики приложения на сервере пишется на Microsoft Visual Basic или C#. Таким образом, объем необходимого кода JavaScript минимален или близок к нулю. Выбор серверо-центрической модели позволяет разработчикам ASP.NET наделить веб-приложение интерактивными возможностями при чрезвычайно высокой скорости обучения. Однако настоящая сила Atlas (и решений на базе AJAX вообще) проявляется при полноценном использовании JavaScript и модели DOM браузера в клиентоцентрической модели. В этом варианте открывается гораздо больше возможностей по обогащению и расширению интерактивности пользовательского интерфейса. Вы можете строить гибридные приложения, приблизить веб-приложение по скорости реакции на ввод пользователя к настольным приложениям и т. д. Впрочем, не все разработчики достаточно уверенно чувствуют себя с JavaScript и DOM, поэтому в Atlas существует две модели программирования. Итак, Atlas предоставляет отличную прикладную среду на базе AJAX для разработок на основе серверо- и клиенто-центрических подходов. Ни одна модель не является предпочтительной во всех ситуациях; выбор модели зависит от ваших личных навыков и предпочтений, а также специфики решаемой задачи. Например, гибридное приложение значительно труднее реализовать без применения JavaScript и DOM. С другой стороны, если вас интересует лишь частичное обновление страницы, инициируемое по таймеру или условиям времени выполнения, вам не обязательно возиться с кодом JavaScript — задача успешно решается применением серверных элементов Atlas.

Серверо-центрическая программная модель На рис. 2.3 представлена серверо-центрическая программная модель. Как обычно, изначально отображаемая клиентская страница состоит из HTML, CSS и небольшого объема сценарного кода. Она генерируется на сервере и передается клиенту при помощи серверных элементов и логики, содержащейся в классах программной логики страницы. Вся логика выражается с использованием управляемого кода и языков .NET (таких, как C#). На стороне сервера в Atlas работает целый ряд специализированных серверных элементов, классов и прикладных служб, доступных для клиентского кода. В страницу, передаваемую пользователю, встраиваются сценарные обработчики для клиентских событий страницы. Эти обработчики пишутся на JavaScript и генерируются серверными элементами Atlas. Из их кода инициируются внеполосные


Программная модель Atlas

55

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

Браузер Исходное построение

1 Представление (HTMUCSS)

1

(пользовательский интерфейс + сценарный код)

^

Входные данные

—"ЧЧ-

(обновленный интерфейс + сценарный код)

Логика страницы (управляемый код)

ASP.NET

Сценарная среда Atlas Среда компонентов/ пользовательского интерфейса, элементы управления

Страницы

Клиентские прикладные службы

Страничная среда, серверные элементы

Прикладные службы

Рис. 2.3. Серверо-центрическая программная модель Atlas

В этой модели Atlas ближе всего подходит к классической модели программирования ASP.NET. Просто разработчик использует другой набор серверных элементов, а эти элементы, в сочетании со страничной средой Atlas, выполняют всю работу. Страницы получаются гораздо более интерактивными, чем в классической модели ASP.NET, и не требуют «физического контакта» разработчика с JavaScript.

Страничная среда Atlas Для работы Atlas необходима работающая на стороне сервера runtime-среда, состоящая из компонентов — таких, как компонент ScriptManager. Он гарантирует, что страница будет содержать все необходимые сценарные блоки, адаптированные для текущего браузера. Наличие такого компонента помогает авторам страниц, а также разработчикам элементов в написании элементов с поддержкой Atlas. Компонент ScriptManager стоит за частичными и пошаговыми обновлениями страниц; он отвечает за включение ссылок на сценарный код в клиентскую страницу для вызова веб-служб, отладочных функций и сервиса времени выполнения. Любая страница, использующая прикладную среду Atlas, должна содержать ровно один экземпляр элемента ScriptManager. Элемент также можно разместить на главной странице, в результате чего он станет доступным для всех производных страниц.


56

Глава 2. Структурные элементы Atlas

Страничная среда Atlas, представленная в первую очередь элементом ScriptManager, выполняет ряд функций: включение режима частичного построения страниц, загрузка полной или облегченной версии ядра Atlas, загрузка необязательных сценариев Atlas, определение веб-служб, которые могут вызываться страницей.

Прикладные службы Atlas поддерживает пару стандартных прикладных служб, при помощи которых клиентские страницы обращаются к специфической функциональности на стороне сервера. Прикладные службы оформлены в виде веб-служб, но рассматривать их нужно именно как сервис приложений. Речь идет о службе аутентификации и службе профилей. Служба аутентификации представляет собой «обертку» для операций регистрации входа и проверки данных пользователя. Вызывая методы службы и предоставляя регистрационные данные, клиентская страница может через внеполосный вызов определить, прошел ли пользователь аутентификацию, и получить мандат (ticket) в случае успешной аутентификации. Служба аутентификации взаимодействует со стандартными провайдерами ролей и принадлежности. Служба профилей читает словарь пользовательских данных в формате, определяемом моделью профильных данных в файле web.config. Для выполнения своих функций служба профилей взаимодействует со стандартным провайдером профилей ASP.NET.

Серверные элементы Самые первые версии Atlas столкнулись с критикой со стороны сообщества программистов, потому что для построения улучшенных приложений требовалось владение JavaScript и навыками асинхронного программирования. В последующих версиях компания Microsoft значительно изменила программную модель. Сегодня при построении приложений Atlas используются элементы, объектная модель и события, уже знакомые разработчикам по ASP.NET. Кроме того, разработчик может воспользоваться новыми элементами ASP.NET (календари, расширенные текстовые поля и т. д.), действующими как серверные «обертки» для клиентских компонентов Atlas. Эти серверные элементы выдают разметку HTML с серверным кодом, способным использовать клиентскую библиотеку Atlas и инициировать внеполосные вызовы. Красота модели в том, что вам как автору страницы не придется беспокоиться об изучении JavaScript. «Умные» элементы с поддержкой Atlas сами выдадут весь необходимый код. Другой ключевой аспект серверной стороны Atlas — расширители элементов. Расширители (extenders) представляют собой компоненты Atlas, добавляющие специальные клиентские возможности к любому существующему элементу ASP.NET. К числу необязательных возможностей относятся всплывающие подсказки (ToolTips), автозаполнение и перетаскивание. Они реализуются в виде блока сценарного кода, связанного с расширяемым элементом ASP.NET. Расширители и серверные элементы Atlas будут рассматриваться в главе 4.


Программная модель Atlas

57

Клиенто-центрическая программная модель На рис. 2.4 показана клиенто-центрическая программная модель Atlas, объединяющая типичную статическую разметку и таблицы стилей со специальным поведением, приближенным к функциональности настольных приложений. В отличие от страниц, создаваемых в серверо-центрической модели, клиенто-центрические страницы содержат код JavaScript, который управляет удаленными операциями (такими, как сетевые вызовы и привязка данных). В типичной для Atlas ситуации клиент передает серию внеполосных вызовов для обмена данными с сервером. Возвращаемые блоки данных затем используются для обновления частей пользовательского интерфейса. Приложение ASP.NET

Браузер Исходное построение

Представление (HTML/CSS) I Посредники! для обращения ужбам J I кслу Atlas - " ^ Поведение пользовательского интерфейса (сценарный код)

(пользовательский интерфейс + сценарный код)

Сценарная среда Atlas Среда компонентов/ пользовательского интерфейса, элементы управления

Страницы

Данные Данные

ASP.NET

Клиентские прикладные службы

Страничная среда, серверные элементы

Прикладные службы

Рис. 2.4. Клиенто-центрическая программная модель Atlas

Принципиальное отличие между серверо-центрическими и клиенто-центрическими страницами заключается в отношении к коду JavaScript и его использованию. Очевидно, JavaScript — единственное средство управления операциями, доступное в большинстве распространенных браузеров. Клиентская библиотека Atlas дополнительно обогащает арсенал JavaScript расширенной системой типов времени выполнения и иерархией классов.

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


58

Глава 2. Структурные элементы Atlas

связываться с целевым элементом на сервере. Расширяемый элемент ASP.NET связывается с клиентским сценарием, обеспечивающим требуемое расширенное поведение. Расширенное поведение инициируется клиентскими событиями, в том числе перемещениями мыши, нажатиями клавиш и срабатыванием таймеров. Аспекты поведения реализуются блоками сценарного кода, которые могут обновлять свойства, вызывать методы и предоставлять относительно сложную функциональность (такую, как автозаполнение). С одним элементом можно связать несколько аспектов поведения, причем это можно сделать как со стороны сервера при помощи расширителей, так и со стороны клиента при помощи сценариев. В Atlas включено несколько заранее определенных аспектов поведения, в том числе AutoComplete, Click, Hover и Pop-up windows. AutoComplete расширяет клиентский элемент TextBox и вовремя отображает раскрывающийся список с предполагаемыми строками. Click позволяет программировать на JavaScript действия, выполняемые при щелчке на элементе HTML. Hover активизируется тогда, когда указатель мыши задерживается над элементом разметки. Наконец, Pop-up windows преобразует блок разметки в «плавающий» элемент, который может перемещаться по странице.

Подключение к внешним службам Клиентские страницы с поддержкой Atlas могут легко подключаться к веб-службам для получения и отправки данных. Прикладная среда Atlas автоматически загружает метаданные веб-службы и генерирует JavaScript-посредника, вызывающего методы службы с использованием объекта XmlHttpRequest. Такие посредники полностью избавляют разработчика от необходимости напрямую работать с протоколом SOAP (Simple Object Access Protocol) или языком WSDL (Web Services Description Language). Веб-службы, предназначенные для вызова из Atlas, пишутся с использованием либо WCF (Windows Communication Foundation), либо классической платформы ASP.NET. В обоих случаях Atlas работает со службами одинаково. Однако страницы с поддержкой Atlas ограничиваются обращением только к службам, находящимся на их домашнем веб-сервере. А если потребуется написать страницу, обращающуюся к внешней веб-службе? Atlas включает технологию создания мостов, позволяющую создавать локальные шлюзы к удаленным веб-службам. При этом браузер взаимодействует исключительно с кодом моста, находящемся на домашнем сервере. В свою очередь, мост взаимодействует с указанной веб-службой (находящейся где угодно в Интернете), пересылает и принимает данные для клиентского приложения.

Atlas X M L Script Вследствие своей архитектуры Atlas охватывает как клиентскую, так и серверную разработку, а также устанавливает собственные механизмы на каналах связи, существующих между клиентом и сервером. Как следствие, компоненты, находящиеся на стороне клиента, принципиально отличаются от компонентов, находящихся на стороне сервера.


Программная модель Atlas

59

С точки зрения программирования клиент относится к «миру JavaScript», тогда как на сервере правят управляемые языки вроде C# и Visual Basic .NET (если ограничиться серверной средой ASP.NET). Как и обычные элементы ASP.NET, серверные элементы Atlas выдают клиентскую разметку HTML и сценарный код. Разметка в сочетании со сценарным кодом формирует своего рода клиентскую программируемую версию серверного элемента. Различия между обычными элементами ASP.NET и элементами Atlas заключаются в модели программирования, предоставляемой клиентской стороне. Традиционные элементы ASP.NET практически не предоставляют никакой модели программирования; в Atlas такая модель обладает богатыми возможностями. Кроме того, для обеспечения совместимости браузеров клиентская разметка должна представлять собой «чистый» код HTML с элементами, интерпретируемыми парсерами HTML всех основных браузеров. Разметка HTML должна быть достаточно полной для передачи всей информации, необходимой runtime-среде Atlas для создания подходящей модели программирования для клиентских элементов, но не слишком сложной для правильного воспроизведения в любом конкретном браузере. Для связывания элементов HTML со сценарным кодом и формирования виртуальных элементов на стороне клиента, а также для того, чтобы оградить разработчика от различий в программных моделях браузеров, в Atlas появился новый декларативный язык разметки, называемый XML Script.

Цели и преимущества XML Script Для настройки конфигурации элементов на стороне клиента, а также связывания задач с событиями обычно применяется язык JavaScript. В дополнение к императивному подходу, обеспечиваемому языком программирования, Atlas также предлагает другой, декларативный подход, основанный на языке XML Script. Используя XML Script, можно на декларативном уровне определить аспекты поведения, ожидаемые от элементов клиентской страницы, и связать их между собой или поступающими данными. В XML Script вы создаете уровень декларативного кода, который на основании существующих тегов HTML и предоставленной информации привязки расширяет модель DOM страницы для создания сети программируемых компонентов, логически сходных с серверными элементами ASP.NET. Основной идеей при создании Atlas XML Script была возможность определения поведения страниц Atlas на отдельном уровне. В этом случае каждую страницу можно представить как состоящую из трех уровней: содержимого, стиля и поведения. Разумеется, уровень поведения клиентской страницы может быть выражен посредством кода JavaScript, но декларативный подход имеет свои преимущества. I Генерировать разметку с использованием серверных элементов проще, чем с использованием кода JavaScript. Кроме того, декларативная разметка изначально проще конструируется, что может упростить будущую разработку специализированных функций в Visual Studio.


60

Глава 2. Структурные элементы Atlas

I При декларативном подходе вы указываете, что будут делать элементы страницы, но не как они должны это делать. Затем происходит разбор разметки, и в конечном итоге генерируется код JavaScript, однако все это делается незаметно для разработчика. I Читатели, обладающие опытом работы с Dynamic HTML и Dynamic HTML Behaviors в Internet Explorer 5.0 и 5.5, знают, каким сложным и длинным часто становится код даже для относительно простых сценариев. Управление несколькими элементами страницы через сценарный код кажется простой и быстрой задачей; управление всеми элементами современной широкофункциональной веб-страницы является делом сложным и чреватым ошибками. Несомненно, средства автоматизации справятся с этой задачей лучше и быстрее. К XML Script не стоит относиться как к новому языку, который вам как разработчику Atlas обязательно придется изучать. XML Script скорее является частью модели программирования Atlas и во многих ситуациях остается невидимым для разработчиков. Серверные элементы Atlas проектировались так, чтобы полностью оградить разработчика от тонкостей этой модели. Для целей разработки вы продолжаете использовать знакомые серверные элементы. Тем не менее «за кулисами» элементы Atlas генерируют XML Script для описания ожидаемого поведения своих аналогов на стороне клиента. ПРИМЕЧАНИЕ

XML Script не всегда избавляет от необходимости во встроенном коде JavaScript. При желании разработчики могут внедрять его вручную в клиентские страницы. Синтаксис Atlas XML Script базируется на XML. Отправной точкой для его создания стала необходимость вставки в страницу специализированной разметки без нарушения нормальной работы парсера HTML и без ущерба для области применения приложений. Скажем, нестандартные теги для этой цели попросту не подходят. В итоге было принято решение использовать тег <script> со специализированным атрибутом типа, встроенный в страницу. Из-за нестандартного атрибута типа («text/xml-script») браузерный парсер HTML полностью игнорирует весь тег. Внутри тега содержимое выражается в синтаксисе XML и обрабатывается на стороне клиента менеджером сценариев Atlas.

XML Script в действии Чтобы поближе познакомиться с синтаксисом и семантикой XML, рассмотрим конкретный пример. Представьте страницу ASP.NET с поддержкой Atlas, содержащую элементы TextBox и Label. Мы хотим, чтобы текст Label синхронизировался с содержимым TextBox после редактирования текста. И конечно, это должно быть сделано без применения традиционных возвратов данных (postbacks). Страница ASP.NET будет содержать два обычных серверных элемента. Обратите внимание: чтобы код XML Script работал, страница также должна включать элемент ScriptManager, как показано в следующем фрагменте: <atlas:ScriptManager ID="scriptManagerl" runat="server" /> <asp:textbox runat="server" id="TextBoxl" />


Программная модель Atlas

61

<br /> <asp:label runat="server" id="Label1" />

Разметка страницы, передаваемая браузеру, содержат следующий фрагмент: <input name="TextBoxl" type="text" id="TextBoxl" /> <br /> <span id="Labell"></span>

Для реализации желаемого поведения в страницу включается встроенная разметка XML Script: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <textBox id="TextBoxl" /> <label id="Labell"> <bindings> <binding dataContext="TextBoxl" dataPath="Text" property="Text" /> </bindings> </label> </components> </page> </script>

Тег <textBox> приказывает клиентской инфраструктуре Atlas преобразовать элемент HTML TextBoxl в клиентский элемент Atlas TextBox. Каждый тег <textBox> представляется клиентским элементом Web.Ul.TextBox, определяемым в Microsoft AJAX Library. Аналогичный элемент <label> обновляет указанную разметку HTML (LabeM в данном примере) до экземпляра клиентского элемента Web.Ul.Label (клиентские элементы рассматриваются в главе 5). Клиентские элементы Atlas имеют ряд привязок. В частности, свойство Text элемента Label (атрибут property) связывается со свойством Text (атрибут dataPath) элемента TextBoxl. В результате, как только пользователь нажимает клавишу Tab, чтобы покинуть клиентский элемент TextBox, содержимое свойства Text синхронизируется со свойством Text элемента Label. На стороне клиента элемент TextBox представлен объектом JavaScript, свойство Text которого инкапсулирует значение нижележащего текстового поля. Аналогично, клиентский элемент Label отображается на объект JavaScript, а свойство Text связывается с внутренним текстом тега <span>. Класс JavaScript Component является базовым для компонентов — таких, как элементы управления и аспекты поведения. Класс реализует систему оповещения об изменениях свойств, посредством которой элементы получают информацию об изменении свойств других элементов. Клиентский элемент Atlas TextBox делает это для свойства Text. Система оповещений Atlas срабатывает при вызове метода записи свойства Text или при инициировании события onchange. В результате Atlas получает возможность обновить все привязки, в которых задействовано свойство Text элемента TextBox.


62

Глава 2. Структурные элементы Atlas

Компонент ScriptManager Любая страница Atlas может содержать ровно один экземпляр менеджера сценариев — серверного элемента ScriptManager. Элемент ScriptManager, определяемый в пространстве имен Microsoft.Web.UI, организует работу большей части серверной инфраструктуры Atlas, а также управляет многими аспектами клиентской функциональности. В частности, элемент ScriptManager отвечает за выдачу кода XML Script для обращения к внешним веб-службам и расширения элементов. Также на элемент ScriptManager возлагается задача частичного обновления клиентских страниц через элемент UpdatePanel (эта тема более подробно рассматривается в следующей главе).

Программный интерфейс В табл. 2.1 перечислены открытые свойства, определенные для элемента ScriptManager. Все они могут задаваться как на стадии конструирования, так и на программном уровне при инициализации страницы Atlas. Таблица 2.1. Свойства элемента ScriptManager

Свойство

Описание

EnablePartialRendering

Свойство указывает, поддерживает ли элемент частичное обновление страниц с использованием элемента UpdatePanel — см. главу 3 (по умолчанию false)

EnableScriptComponents

Свойство указывает, какой набор runtime-возможностей должен быть включен в клиентскую страницу в виде заранее определенных файлов JavaScript. Если свойство равно true, в страницу включается ссылка на файл atlas.js, а при значении false — ссылка на файл atlasruntime.js, содержащий облегченный, минимальный набор функций (по умолчанию true)

EnableScriptGlobalization

Свойство указывает, должен ли менеджер сценариев включить в страницу обработчик atlasglob.axd. Этот обработчик генерирует в страницу объект Culture JavaScript, который используется некоторыми методами классов Atlas Class Library, зависимыми от локального контекста (по умолчанию true)

ErrorTemplate

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

IsInPartialRenderingMode

Свойство доступно только для чтения; указывает, поддерживает ли элемент в настоящее время частичное обновление с использованием элемента UpdatePanel — см. главу 3 (по умолчанию false)

Scripts

Свойство возвращает коллекцию объектов ScriptReference; каждый объект представляет сценарный файл, ссылка на который присутствует в странице


Компонент ScriptManager

Свойство Services

63

Описание Свойство возвращает коллекцию объектов ServiceReference; каждый объект представляет веб-службу, ссылка на которую присутствует в странице

В табл. 2.2 перечислены методы элемента ScriptManager. Таблица 2.2. Методы элемента ScriptManager

Метод

Описание

RegisterAsyncPostbackControl

Метод регистрирует серверный элемент, который может использоваться со стороны клиента для обновления панели

RegisterControl

Метод регистрирует элемент с включенной поддержкой Atlas; регистрация необходима для управления элементом со стороны механизма XML Script

RegisterScriptNamespace

Метод регистрирует пространство имен XML для разметки XML Script

RegisterScriptReference

Метод регистрирует URL сценарного файла, включаемого в коллекцию Scripts

RegisterScriptService

Метод регистрирует URL веб-службы, включаемой в коллекцию Scripts

RegisterUpdatePanel

Метод регистрирует элемент UpdatePanel для текущей страницы

Элемент ScriptManager также инициирует событие с именем PageError. Событие инициируется при любых ошибках, происходящих в странице в процессе частичного обновления. Обработчик события имеет следующую форму: public delegate void PageErrorEventHandler( object sender, PageErrorEventArgs e);

Структура данных события расширяет базовый класс EventArgs двумя дополнительными полями: public class PageErrorEventArgs : EventArgs { public PageErrorEventArgs(Exception error); public Exception Error { get; } public string ErrorMessage { get; set; } }

Наконец, элемент ScriptManager содержит статический метод с именем GetCurrent, который обычно используется элементами для получения текущего менеджера сценариев страницы. Элемент ScriptManager управляет многими операциями в контексте страницы Atlas. Давайте в общих чертах обсудим самые важные из них.


64

Глава 2. Структурные элементы Atlas

Настройка конфигурации страниц Atlas Как упоминалось ранее, элемент ScriptManager является «нервным узлом» страницы Atlas. Для работы страницы Atlas необходим большой объем сценарного кода клиентской стороны, который обеспечивает работу с клиентской библиотекой Atlas через API, соответствующий возможностям текущего браузера. Менеджер сценариев определяет, какие из стандартных файлов необходимо подключить, и решает эту задачу в зависимости от значения свойства EnableScriptComponents и версии приложения (отладочной или окончательной). Не все страницы Atlas требуют одинакового набора клиентских возможностей, но ни одна страница не обойдется без минимального объема кода JavaScript. Если для конкретной страницы нужна облегченная версия Atlas, свойство EnableScriptComponents задается равным false. В этом случае менеджер сценариев включает ссылку на файл AtlasRuntime.js вместо файла Atlas.js (за дополнительной информацией о клиентских файлах Atlas обращайтесь к табл. 2.3). Облегченная runtime-среда Atlas включает модель ООП JavaScript, а также возможность вызова веб-служб и методов страницы. Элемент SciptManager просматривает данные конфигурации в файле web.config и определяет, нужна ли отладочная информация для текущей версии приложения. В этом случае включаются отладочные версии необходимых сценариев Atlas. ПРИМЕЧАНИЕ

Клиентская библиотека Atlas предоставляет высокоуровневый API, маскирующий различия между реализациями DOM и JavaScript разных браузеров. Этот уровень кода часто называется прослойкой совместимости.

Обработка исключений в страницах Atlas Элемент ScriptManager может перехватывать любые исключения, возникающие при частичном обновлении страницы (то есть при использовании в странице элемента UpdatePanel), и выводить шаблон, назначенный пользователем, — этот шаблон задается содержимым свойства ErrorTemplate: <atlas:ScriptManager ID="scriptManager" runat="server" EnablePartialRendering="true"> <ErrorTemplate> An error occurred </ErrorTemplate> </atlas:ScriptManager>

Если для страницы задан шаблон ошибки, в нее включается скрытая таблица HTML. Когда в процессе частичного обновления происходит исключение, клиентская инфраструктура Atlas делает таблицу видимой и блокирует все поля страницы.

Добавление внешних ссылок Страница Atlas может содержать ссылки на файлы JavaScript и веб-службы — как веб-службы ASP.NET, так и службы WCF. Подключение необходимых служб может осуществляться на программном уровне при помощи соответствующих


Компонент ScriptManager

65

методов регистрации (табл. 2.3). Кроме того, ссылки можно добавить вручную в коллекции Scripts и Services. Наконец, статические ссылки на сценарии и веб-службы можно определить на стадии конструирования при помощи тегов ASP.NET: <atlas:ScriptManager ID="scriptManagerl" runat="server"> <Services> <atlas:ServiceReference Path="YourService.asmx" /> </Services> </atlas:ScriptManager>

Класс ServiceReference содержит три свойства: public class ServiceReference { public bool GenerateProxy { get; set; } public string Path { get; set; } public string Type { get; set; } }

Свойство GenerateProxy (по умолчанию true) указывает, должен ли компонент ScriptManager создать класс-посредника JavaScript для подключаемой веб-службы. Свойство Path определяет URL веб-службы ASP.NET (находящейся на одном сервере с приложением). Свойство Туре задает тип службы для служб WCF. Пример включения сценарных файлов в страницу Atlas: <atlas:ScriptManager runat="server" id="scriptManager"> <Scripts> <atlas:ScriptReference ScriptName="AtlasllIGlitz" /> <atlas:ScriptReference Path=" MyControls.js" Browser="Firefox" /> <atlas:ScriptReference Path="MyControls.js" /> </Scripts> </atlas:ScriptManager>

Класс ScriptReference содержит три свойства, как видно из следующего определения класса: public class ScriptReference { public string Browser { get; set; } public string Path { get; set; } public FrameworkScript ScriptName { get; set; } }

Свойство ScriptName используется для связывания со стандартными клиентскими файлами Atlas. Список таких файлов приводится в табл. 2.3 позднее в этой главе. Учтите, что из всех стандартных файлов обязательным для каждой страницы является только файл Atlas.js или AtlasRuntime.js. Все остальные файлы не обязательны и подключаются в случае необходимости. Для идентификации стандартных клиентских файлов используется не путь и не имя, а заранее определенные символические константы перечисляемого типа FrameworkScript. Для нестандартных сценарных файлов атрибут Path обозначает URL, а атрибут Browser — браузер, для которого предназначен данный файл.


66

Глава 2. Структурные элементы Atlas

ПРИМЕЧАНИЕ

Компонент ScriptManagerProxy тесно связан с элементом ScriptManager. Если основная страница содержит элемент ScriptManager, ни одна из страниц содержимого ни при каких условиях не может содержать второй экземпляр ScriptManager. Тем не менее в некоторых ситуациях может возникнуть необходимость в явной ссылке на ScriptManager для включения сценария Atlas в страницу содержимого. В таких случаях вместо ScriptManager используется компонент ScriptManagerProxy (с таким же синтаксисом, как и для ScriptManager): <asp:Content ...> <atlas:ScriptManagerProxy runat="server" id="proxy"> </atlas:ScriptManagerProxy> </asp:Content>

Пример страницы Atlas Итак, прикладная среда Atlas позволяет создавать широкофункциональные клиентские приложения, которые после исходного построения работают на сервере и передают результаты клиенту. Затем клиентский интерфейс обновляется в соответствии с изменениями в данных. Обновление элементов для отражения измененных данных на построенной странице требует использования языка JavaScript. Несмотря на свое сходство с некоторыми языками семейства C, JavaScript не пользуется популярностью у многих разработчиков. По этой причине Atlas предлагает разработчику две модели программирования: серверо-центрическую и клиенто-центрическую. Результаты, полученные при использовании двух моделей, почти не отличаются; однако серверо-центрическая модель позволяет наделить страницы поддержкой Atlas практически без написания кода JavaScript. К этому моменту вы уже достаточно хорошо понимаете, что собой представляет прикладная среда Atlas и как она работает. Пора переходить от теории к практике — давайте построим и протестируем свои первые страницы Atlas. Для начала я выбрал пару несложных примеров, которые дают хорошее представление о стиле AJAX. ПРИМЕЧАНИЕ

В примерах будет использоваться код JavaScript; таким образом, оба примера относятся к клиенто-центрической модели разработки. И все же прежде всего постарайтесь разобраться в реализуемых функциях, вместо того чтобы обдумывать и оценивать объем используемого кода JavaScript. Созданные нами страницы представляют настоящий прорыв в веб-программировании и позволяют оценить преимущества Atlas. Мы также вернемся к этим примерам позднее, при более подробном обсуждении некоторых сопутствующих аспектов.

Конфигурация приложения Приложение Atlas, прежде всего, является приложением ASP.NET с рядом дополнительных требований к конфигурации. Аналогично, страницы с поддержкой Atlas представляют собой классические страницы ASP.NET с дополнительной


Пример страницы Atlas

67

разметкой и кодом JavaScript. Дополнительная разметка и сценарный код большей частью генерируются серверными компонентами. Затем структурные модули Atlas находятся под управлением runtime-модулей, расширяющих конвейер ASP.NET.

Изменения в проекте К проектам Atlas предъявляется одно ключевое требование: runtime-сборка Atlas должна находиться в папке Bin. Сборке присваивается имя Microsoft.Web.Atlas. Сборка включает в качестве ресурсов ряд файлов JavaScript, формирующих клиентскую библиотеку Atlas. Имена этих файлов с краткими описаниями перечислены в табл. 2.3. Таблица 2.3. Исходные файлы JavaScript в приложениях Atlas

Исходный файл Atlas.js

AtlasRuntime.js

Описание Полный набор функций Atlas, включая runtime-среду Atlas, клиентские компоненты и элементы управления, а также инфраструктура декларативного синтаксиса Atlas, применяемого для определения привязки данных и деталей клиентского пользовательского интерфейса. Файл Atlas.js включается по умолчанию при добавлении компонента ScriptManager Минимальный набор функций Atlas — веб-службы, сетевые службы и основные объектно-ориентированные расширения JavaScript. Файл используется для ускорения загрузки сценария и в тех случаях, когда другие возможности Atlas не нужны

AtlasUIDragDrop.js

Содержит сценарий для реализации перетаскивания для клиентских элементов ASP.NET

AtlasUIGlitz.js

Содержит сценарий для реализации специальных визуальных эффектов — таких, как растворение, прозрачность и анимация

AtlasUIMap.js

Необязательный файл; реализует функцию виртуального отображения, используемую в примере приложения Microsoft Virtual Earth

AtlasCompat.js, AtlasCompat2.js

Два файла содержат прослойки совместимости сценариев, необходимые для кросс-браузерной работы Atlas

Вам как разработчику страниц не нужно много знать об этих файлах, их содержимом и задачах. Элемент ScriptManager сделает все необходимое и подключит нужные файлы в зависимости от функций Atlas, активизированных для каждой конкретной страницы.

Изменения в файле web.config Приложениям с поддержкой Atlas обычно требуется специально настроенный файл web.config. Если проект Atlas создается с использованием шаблона Visual Studio 2005 (см. рис. 2.1), подходящий конфигурационный файл будет сгенерирован


68

Глава 2. Структурные элементы Atlas

автоматически. В противном случае вы должны проследить за тем, чтобы в файле web.config присутствовала по крайней мере следующая информация: I настроенная группа Microsoft.Web с секцией Converters; I объявление префикса atlas для серверных элементов, определенных в пространствах имен Microsoft.Web.UI и Microsoft.Web.Ul.Controls; I модуль HTTP с именем ScriptModule для перехвата каждого запроса, обращенного к открытому методу страницы; I обработчик HTTP, переназначающий расширение ASMX для обработки запросов, выдаваемых классами-посредниками JavaScript. Рассмотрим каждый из пунктов более подробно. Следующий сценарий конфигурации определяет новую группу секций с именем Microsoft.Web. Как видно из приведенного фрагмента, в этой группе разрешается создание дочерней секции с именем Converters. <configSections> <sectionGroup name="microsoft.web" type="Microsoft.Web.Configuration.MicrosoftWebSectionGroup"> <section name="converters" type="Microsoft.Web.Configuration.ConvertersSection" /> </sectionGroup> </configSections>

В секции Converters перечисляются все стандартные конвертеры JSON, поддерживаемые в Atlas. Список можно расширить и добавить в него узкоспециализированные конвертеры для ваших типов данных: <microsoft.web> <converters> <add type="Microsoft.Web.Script.Serialization. Converters.DataSetConverter" /> <add type="Microsoft.Web.Script.Serialization. Converters.DataRowConverter" /> <add type="Microsoft.Web.Script.Serialization. Converters.DataTableConverter" /> </converters> </microsoft.web>

Конвертер Atlas JSON представляет собой класс, производный от абстрактного базового класса с именем JavaScriptConverter. По умолчанию специализированные конвертеры определяются для нескольких объектов ADO.NET — DataSet, DataTable и DataRow. Вообще говоря, вам не обязательно создавать конвертер JSON для каждого типа, связанного с внеполосными вызовами. Atlas автоматически сериализует и десериализует многие управляемые типы в классы JavaScript и обратно. В частности, примитивные типы и нестандартные классы автоматически обрабатываются с применением рефлексии. В большинстве случаев сериализация нестандартных объектов обходится без написания нестандартного конвертера JSON. Впрочем, вы можете написать нестандартный конвертер JSON, если потребуется взять процесс сериализации под


Пример страницы Atlas

69

полный контроль. Обычно нестандартный конвертер создается тогда, когда объект слишком сложен для представления в формате класса JavaScript, а сериализатор JSON может сделать неверные предположения по поводу корректного способа сериализации. Если вы хотите быть полностью уверены в том, что ваш (сложный) объект сериализуется верно, а встроенный сериализатор JSON выдает ошибки, по крайней мере, у вас имеется запасной вариант с построением нестандартного конвертера. Если в приложении используются серверные элементы Atlas (которые будут рассматриваться, начиная с главы 3), также необходимо зарегистрировать префикс atlas, который будет использоваться вместо стандартного префикса asp в качестве отличительного признака элементов: <pages> <controls> <add namespace="Microsoft.Web. Ш " assembly="Microsoft.Web.Atlas" tagPrefix="atlas" /> <add namespace="Microsoft.Web.Ill.Controls" assembly="Microsoft.Web.Atlas" tagPrefix="atlas" /> </controls> </pages>

Префикс тега atlas ассоциируется с элементами, определяемыми в сборке Microsoft.Web.Atlas, и конкретно в пространствах имен Microsoft.Web.UI и Microsoft.Web. Ul.Controls.

Изменения в конвейере ASP.NET Работа страницы Atlas в основном состоит из отправки внеполосных вызовов серверу и последующего обновления объектной модели документа страницы полученными данными. В некоторых случаях для успешной обработки внеполосных вызовов, инициируемых клиентом, требуется внести небольшие изменения в конвейер ASP.NET. В частности, когда клиентская страница обращается с вызовом к веб-службе, потребуется специализированный обработчик HTTP для ресурса .asmx. Новый обработчик должен различать традиционные обращения к веб-службам (через браузер или классы-посредники .NET) и вызовы через классы-посредники JavaScript. В последнем случае обработчик должен гарантировать, что объект JavaScript содержит возвращаемое значение метода. Для обработки всех ресурсов .asmx вместо исходного обработчика ASP.NET регистрируется новый объект ScriptHandlerFactory, как показано в следующем фрагменте: <httpHandlers> <remove verb="*" path="*.asmx"/> <add verb="*" path="*.asmx" type="Microsoft.Web.Services.ScriptHandlerFactory" /> </httpHandlers>

Обращения к веб-службам, проходящие через посредников JavaScript, можно отличить по суффиксу /js в URL.


70

Глава 2. Структурные элементы Atlas

ScriptHandlerFactory проверяет суффикс (если он есть) и использует обработчик Atlas HTTP или традиционный обработчик веб-служб ASP.NET, в зависимости от результата. Другая распространенная разновидность внеполосных вызовов обращена к методам страниц. В этом случае код JavaScript, встроенный в страницу HTML, обращается с вызовом к той же странице .aspx и требует выполнить конкретный метод. Для удовлетворения подобных запросов необходимо внести некоторые изменения в стандартный жизненный цикл страницы. <httpModules> <add name="ScriptModule" type="Microsoft.Web.Services.ScriptModule" /> </httpModules>

Учтите, что сценарный модуль HTTP не понадобится, если ваше приложение не собирается вызывать методы страниц из своих страниц ASP.NET. Аналогично, вам не придется заменять обработчик HTTP для ресурсов .asmx, если ни одна страница не обращается с вызовами к веб-службам.

Удаленный вызов методов Наше знакомство с программной моделью Atlas начнется с рассмотрения двух страниц, реализующих внеполосные вызовы — несомненно, самая типичная операция, характерная для AJAX-ориентированных решений. Сначала мы разберемся, как использовать Atlas для вызова веб-службы со стороны клиента, а затем я покажу, как построить страницу Atlas с вызовом метода страницы.

Обращение к веб-службе Каждая страница с включенной поддержкой Atlas должна содержать элемент ScriptManager. Кроме того, если страница вызывает веб-службы, менеджер сценариев должен быть явно связан с каждой вызываемой службой, как показано в следующем фрагменте: <atlas:ScriptManager ID="scriptManagerl" runat="server"> <Services> <atlas:ServiceReference Path="~/WebServices/MyDataService.asmx" /> </Services> </atlas:ScriptManager>

Для каждой веб-службы, которая может вызываться страницей, создается узел ServiceReference. Ссылка создается статически на стадии конструирования или динамически, посредством добавления объекта ServiceReference в коллекцию Services менеджера сценариев. Вот как это делается: ServiceReference service = new ServiceReference(); serviее.Path = "-/WebServices/MyDataService.asmx"; service.GenerateProxy = true; scriptManagerl.Services.Add(service);

Лучшим местом для размещения этого кода является событие Page_Load страницы.


Пример страницы Atlas

71

Инфраструктура Atlas генерирует клиентский объект-посредника JavaScript для каждой веб-службы, для которой создается ссылка. В результате появляется возможность включить в клиентскую страницу сценарный код, который вызывает методы веб-службы через класс JavaScript. Допустим, на странице находится раскрывающийся список для выбора имени клиента и кнопка. <body> <form id="forml" runat="server"> <atlas:ScriptManager ID="scriptManagerl" runat="server"> <Services> <atlas:ServiceReference Path="~/WebServices/MyDataService.asmx" /> </Services> </atlas:ScriptManager> <asp:DropDownList ID="CustomerList" runat="server" DataSourceID="CustomerDataSource" DataTextField="CompanyName" DataValueField="ID" /> <asp:ObjectDataSource ID="CustomerDataSource" runat="server" TypeName="IntroAtlas.CustomerManager" SelectMethod="LoadAll"> </asp:ObjectDataSource> <input type="button" value="Find customer" onclick="findCustomer()" /> <hr /> <div id="CustomerData" style="visibi1ity:hidden"> <table><tr> <td class="label">ID</td> <td style="width:10px;" /> <td><span id="companyID"></span></td> </tr><tr> <td class="label">Company</td> <td style="width:10px;" /> <td><span id="companyNanie"></span></td> </tr><tr> <td class="label">Contact</td> <td style="width:10px;" /> <td><span id="companyContact"></span></td> </tr><tr> <td class="label">City</td> <td style="width:10px;" /> <td><span id="companyCity"></span></td> </tr><tr> <td class="label">Country</td> <td style="width:10px;" /> <td><span id="companyCountry"></span></td> </tr></table> </div> </form> </body>

Список заполняется с использованием элемента источника данных, связанного с объектом DAL (Data Access Layer). Выбрав клиента в списке, пользователь


72

Глава 2. Структурные элементы Atlas

щелкает на кнопке, после чего выполняется код JavaScript. Чтобы щелчок не инициировал полную передачу данных, следует использовать либо обычную кнопку HTML, как в данном примере, либо кнопку ASP.NET со свойством ОпClientClick, содержащим код findcustomerQ; return false; (возврат false предотвращает операцию отправки): <asp:Button ID="Button1" runat="server" OnClientClick="findcustomer(); return false;" Text="Find customer" />

А вот как выглядит вызываемый код клиентской стороны: function findCustomerO { // Получение элемента раскрывающегося списка var list = document.getElementByldC'CustomerList"); // Получение текущего выделенного значения в списке var custID = list.options[list.selectedIndex].value

}

// Вызов веб-службы через класс-посредника IntroAtlas.WebServices.MyDataService.LookupCustomer( custID, onSearchComplete);

Типичный код JavaScript для события щелчка на кнопке сначала получает текущее выделенное значение в раскрывающемся списке, а затем вызывает метод веб-службы через класс-посредника. Класс-посредник JavaScript обладает тем же именем, что и класс веб-службы (IntroAtlas.WebServices.MyDataService в нашем примере), а количество его методов совпадает с количеством веб-методов исходной веб-службы. В рассмотренном примере веб-служба MyDataService.asmx содержит как минимум веб-метод с именем LookupCustomer. Далее приводится выдержка из кода веб-службы: namespace IntroAtlas.WebServices { [WebService(Namespace = "http://introatlas.book/"); [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfilel_l)I public class MyDataService : System.Web.Services.WebService { public MyDataServiceO { } [WebMethod; public Customer LookupCustomer(string id) { return CustomerManager.Load(id); } }

Посредник JavaScript для веб-службы генерируется на сервере инфраструктурой Atlas и связывается с клиентской страницей в виде сценарного кода.


Пример страницы Atlas

73

Метод LookupCustomer класса-посредника JavaScript содержит один дополнительный аргумент — функцию обратного вызова, которая активизируется при появлении готовых результатов веб-службы. В нашем примере эта функция обратного вызова в следующем примере называется onSearchComplete: function onSearchComplete(data) { // Возвращаемые данные представляют собой объект JavaScript, // "похожий" на возвращаемое значение вызванного метода веб-службы // в данном примере это тип Customer. // of the invoked Web service method-in this case, the Customer type. document.getElementById("CustomerData").style.visibility = "visible"; document.getElementByldC'companylD").innerText = data.ID; document.getElementById("companyName").innerText = data.CompanyName; document.getElementById("companyContact").innerText = data.ContactName; document.getElementByldC'companyCity").innerText = data.City; document.getElementById("companyCountry").innerText = data.Country; }

Инфраструктура Atlas JSON гарантирует, что во входном аргументе функции обратного вызова будет передаваться JavaScript, полученный в результате сериализации возвращаемого значения метода (тип .NET) в объект JavaScript. Количество полей в объекте, представленном формальным параметром data, соответствует количеству открытых свойств в исходном типе Customer, возвращенным методом LookupCustomer. Функция обратного вызова JavaScript использует результаты, полученные от метода веб-службы, для обновления частей текущей страницы через интерфейс DOM. На рис. 2.5 показан пример страницы, которая была по щелчку обновлена данными, полученными от веб-службы.

Рис. 2.5. Пример страницы Atlas, обращающейся с вызовом к веб-службе


74

Глава 2. Структурные элементы Atlas

ПРИМЕЧАНИЕ

Вероятно, вы заметили, что в этом примере используется локальная веб-служба, находящаяся на одном сервере и в одном приложении с вызывающей страницей. Данный факт не случаен — по соображениям быстродействия Atlas поддерживает прямые вызовы веб-служб только в том случае, если они обращены к службам, предоставляемым тем же веб-приложением. Учтите, что речь идет не о техническом ограничении, а о сознательном архитектурном решении. Как будет показано подробнее в главе 6, Atlas также позволяет создавать серверные шлюзы для связывания клиентских страниц с внешними веб-службами. Конечно, обращения к внешним веб-службам из страницы Atlas возможны, но выполняются они не так, как показано в этой главе.

Вызов метода страницы Помимо вызова веб-служб из клиентской страницы также существует возможность вызова методов той же серверной страницы. Второй подход на логическом уровне близок к ASP.NET 2.0 Script Callback API, кратко описанному в главе 1. Метод страницы в Atlas представляет собой открытый метод класса программной логики (code-behind class), помеченный конкретным атрибутом WebMethod. Точно такой же атрибут используется для пометки методов веб-служб, предназначенных для открытого вызова через Web. Иначе говоря, определяя методы страницы, вы превращаете страницу в некую разновидность веб-службы, доступной для клиентских страниц Atlas. Чтобы использование атрибута WebMethod стало возможным, необходимо импортировать в страницу пространство имен System.Web.Services: <%$ Import Namespace="System.Web.Services" %>

Затем вы конструируете страницу, как считаете нужным, но обязательно размещаете на ней элемент ScriptManager и клиентскую кнопку (или другой клиентский элемент разметки HTML, инициирующий событие). Предположим, на форме имеется поле TextBox для ввода кода клиента и кнопка: <asp:TextBox ID="CustomerID" runat="server" /> <input type="button" value="Find customer" onclick="findCustomer()" />

Обработчик JavaScript onclick сходен с обработчиком, созданным нами для предыдущего примера, если не считать того, что в нем используется другой посредник. На этот раз объекту-посреднику присвоено имя PageMethods: function findCustomerO { var id = document.getElementByldC'CustomerlD").value; PageMethods.LookupCustomer(id, onSearchComplete); }

Функция обратного вызова onSearchComplete совпадает с одноименной функцией из предыдущего примера. Посредник PageMethods создается инфраструктурой Atlas на основании содержимого нового сценарного блока: <script type="text/C#" runat="server"> [WebMethod;


Пример страницы Atlas

75

public Customer LookupCustomer(string id) { return LookupCustomerInternal(id); } </script>

Atlas разбирает секцию <script> c атрибутом type, равным text/C#, и генерирует соответствующий объект JavaScript PageMethods. Методы страниц должны определяться как встроенные в соответствующих тегах <script>. Тем не менее, чтобы большую часть метода не приходилось оформлять в виде встроенного кода, можно просто передать управление защищенному методу, определенному в классе программной логики. Учтите, что метод класса программной логики не может быть приватным; в противном случае он будет недоступен для динамического класса страницы, созданного ASP.NET для обслуживания запроса. В главе 6 мы вернемся к этим клиенто-центрическим примерам, заглянем «за кулисы» и разберемся, что именно происходит, когда клиентская страница вызывает веб-службу или метод страницы. Мы также вернемся к атрибуту WebMethod и его параметрам (таким, как CacheDuration).

Методы веб-служб и методы страниц Чем же методы веб-служб отличаются от методов страниц и существуют ли такие различия? И какой из двух механизмов лучше подходит для того, чтобы предоставить клиенту доступ к серверной функциональности? На функциональном уровне эти два механизма эквивалентны, хотя они и используют разные модели реализации. В обоих случаях в конечном итоге вызывается блок кода, определяемый на стороне клиента. Выбирая решение с «простой» веб-службой (то есть файл .asmx или службу WCF), вы получаете службу, которую можно настроить на прием запроса от других клиентов. Код службы отделен от кода страницы, ее использующей, и одна служба может принимать запросы от нескольких страниц. К достоинствам этого способа относятся гибкость и универсальность. С другой стороны, набор методов страниц позволяет опубликовать страницу в виде веб-службы в контексте приложения. Как видите, перспектива получается совершенно иной. Какой программный аспект должен выйти на первый план? Если страница ASP.NET с поддержкой Atlas, выбирайте методы страниц, а если предоставляемая функциональность — предпочтение отдается веб-службе. Методы страниц быстрее реализуются, но взамен вы получаете краткосрочное решение, область применения которого не выходит за рамки страницы. Вебслужбы обладают заметно большим потенциалом для повторного использования; вероятно, это единственный вариант, который стоит рассматривать в приложениях уровня предприятия. Еще раз напоминаю, что веб-службы Atlas находятся на том же серверном компьютере и в том же веб-приложении. Как будет показано в главе 6, это объясняется тем, что веб-службам Atlas необходим специальный обработчик HTTP, генерирующий посредника JavaScript.


76

Глава 2. Структурные элементы Atlas

ВНИМАНИЕ

Для вызова методов страниц необходим модуль HTTP ScriptModule, тогда как при вызове веб-служб из ваших страниц без него можно обойтись. С другой стороны, вызовы веб-служб требуют обработчика HTTP для ресурсов .asmx, специфического для Atlas. При простом вызове методов страниц этот обработчик игнорируется.

Отладка приложений Atlas Приложения Atlas отличаются от других веб-приложений в нескольких отношениях. Во-первых, они содержат значительный объем клиентского сценарного кода, который должен отлаживаться наряду с серверным кодом. Во-вторых, браузер может выдавать дополнительные (и даже асинхронные) запросы данных, в результате которых некоторые традиционные приемы отладки ASP.NET становятся частично неприменимыми. Впрочем, для отладки приложения, прежде всего, необходимо включить в двоичные файлы отладочную информацию. В файле web.config, генерируемом шаблоном проекта Atlas в Visual Studio 2005, средства отладки отключены, поэтому по умолчанию в компилируемые сборки отладочная информация не включается. Следовательно, первое, что необходимо сделать, — это включить отладочный режим в приложении Atlas, по крайней мере, на стадии разработки: <system.wet» compilation debug="true"> </compilation> </system.wet»

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

Настройка Internet Explorer Для пошагового выполнения серверного кода в приложениях Atlas разработчик устанавливает точки прерывания в нужных строках кода и нажимает F5. Visual Studio 2005 не позволяет устанавливать точки прерывания в коде JavaScript, однако это не означает, что пошаговое выполнение сценарного кода невозможно. Прежде всего убедитесь в том, что отладка разрешена в Internet Explorer. В меню Сервис (Tools) выберите команду Свойства обозревателя (Internet Options), перейдите на вкладку Дополнительно (Advanced). Убедитесь в том, что для Internet Explorer разрешена отладка сценариев, а также (при желании) отключите подробные сообщения об ошибках (рис. 2.6). При включении подробных сообщений об ошибках Internet Explorer заменяет сообщения сервера внутренними сообщениями, ориентированными на конечного пользователя, а не на разработчика. Таким образом, для получения более полезной информации подробные сообщения стоит временно отключить.


Пример страницы Atlas

77

Рис. 2.6. Настройка отладочных параметров в Internet Explorer

Чтобы выполнить клиентский код приложения Atlas в пошаговом режиме, необходимо остановить отладчик сразу же, как только он приступит к выполнению кода JavaScript страницы. Для этого в сценарный код включается вызов debug.fail: function findCustomer() { // Этот вызов останавливает отладчик и дает возможность // выполнить код сценария в пошаговом режиме debug.fail(); // Содержимое сценарной функции }

Сценарный объект debug входит в отладочные библиотеки Atlas. Вызов его метода fail останавливает работу отладчика. На этой стадии Visual Studio 2005 позволяет выполнить сценарный код в пошаговом режиме (клавиша F11), как показано на рис. 2.7. СОВЕТ

Окно Script Explorer, показанное на рис. 2.7, доступно при отладке веб-приложения в Visual Studio 2005. Чтобы вызвать его, выполните команду Debug ► Windows и выберите окно Script Explorer.


78

Глава 2. Структурные элементы Atlas

Рис. 2.7. Пошаговое выполнение сценарного кода после вызова debug.fail

Объект debug Откуда взялся сценарный объект debug? Этот объект JavaScript определяется в отладочной версии файла atlas.js. Экземпляр объекта JavaScript (с настоящим именем Web._Debug) создается и присоединяется к свойству window.debug в модели DOM страницы. Методы объекта debug перечислены в табл. 2.4. Таблица 2.4. Методы сценарного объекта debug Метод

Описание

assert

Проверяет выполнение заданного условия

clearTrace

Стирает трассировочный вывод

dump

Выводит заданный объект в понятной форме в конце страницы

fail

Передает управление отладчику. Метод работает только в браузере Internet Explorer

trace

Записывает текстовый аргумент в трассировочный вывод

Метод assert имеет следующий прототип: assert(condition, message, displayCaller);


Пример страницы Atlas

79

Если условие condition выполняется, метод просто возвращает управление, а выполнение продолжается со следующей команды. Если условие ложно, метод выводит окно с заданным сообщением. Если параметр displayCaller равен true, метод также выводит информацию о вызывающей стороне. Метод clearTrace вызывается без аргументов и очищает часть страницы, в которой отображаются трассировочные сообщения Atlas. Говоря точнее, метод clearTrace просто скрывает компонент-панель, на котором выводятся все трассировочные сообщения. Метод dump записывает содержимое заданного объекта в форме, доступной для человека, в трассировочную область Atlas в конце страницы. Простейший вариант синтаксиса выглядит так: function onSearchComplete(results) { debug.dump(results); }

Результаты показаны на рис. 2.8.

Рис. 2.8. Отладочная трассировка Atlas

При вызове метода dump могут передаваться еще три параметра: debug.dump(object, паше, recursive, indentationPadding)


80

Глава 2. Структурные элементы Atlas

Параметр name содержит текст, отображаемый в заголовке дампа объекта. Текст может содержать разметку HTML. Если параметр recursive равен true (значение по умолчанию), в общий дамп также включаются рекурсивные дампы вложенных объектов. Наконец, параметр indentationPadding определяет текст, который должен выводиться в начале каждой строки дампа. Если отладчик Visual Studio подключен к Internet Explorer, трассировочные сообщения также отображаются в окне Output. Метод trace записывает свой текстовый аргумент в трассировочный вывод. Он получает единственный параметр — выводимый текст. Как упоминалось ранее, метод fail останавливает отладчик, но только для браузера Internet Explorer. Что делать, если приложение Atlas тестируется в другом браузере? Вы не сможете использовать отладчик Visual Studio для пошагового выполнения клиентского кода в Firefox или другом браузере на базе Mozilla. Впрочем, отладчик для Firefox, реализованный в виде расширения Firefox, можно загрузить по адресу http://www.mozilla.org/projects/venkman.

Трассировка на стороне сервера В ASP.NET часто применяются серверные трассировочные сообщения, содержащие информацию о состоянии объекта или о реальной последовательности возникновения некоторых событий. Функция трассировки настолько популярна, что группа разработки Atlas сочла необходимым создать объект debug — своего рода трассировщик для клиентской стороны. В том, что касается трассировки на стороне сервера, Atlas создает некоторые проблемы. Страница Atlas изначально выдает весь трассировочный вывод в конце страницы, однако трассировка не обновляется по мере выдачи асинхронных вызовов. Трассировочный вывод, дописанный в конце страницы, остается неизменным независимо от фактической реализации асинхронных вызовов (методы страниц, веб-службы или, возможно, серверный элемент UpdatePanel — см. главу 3). В частности, при использовании элемента UpdatePanel будет обновляться только часть страницы, представленная элементом. В этом случае для вывода трассировочной информации следует использовать подсистему просмотра трассировки (trace.axd). Trace.axd выводит 10 последних запросов приложения и для каждого может предоставить вывод трассировочного механизма. За дополнительной информацией о средствах трассировки в ASP.NET обращайтесь к главе 5 моей книги «Programming Microsoft ASP.NET 2.0: Core Reference» (Microsoft Press, 2005).

Отслеживание трафика HTTP Во время своей работы приложение Atlas выдает серию классических отправок данных и внеполосных запросов. Для целей тестирования может потребоваться просмотреть содержимое пакетов запросов и ответов HTTP. В классической модели ASP.NET такая необходимость возникает редко, и даже в таких случаях она легко реализуется следующей строкой кода: void Page_Load(object sender, EventArgs e) { Request.SaveAs(file, true); }


Пример страницы Atlas

81

С другой стороны, в Atlas асинхронные вызовы производятся с гораздо большей частотой, а в теле запроса используется нестандартный синтаксис. В то же время в случае любых неполадок или сбоев особенно важно проанализировать ответ асинхронного вызова. Нравится вам это или нет, но для выполнения качественных Atlasразработок вам придется вооружиться средствами отслеживания трафика HTTP. Несомненно, одним из лучших кандидатов является утилита Fiddler. За информацией о ее возможностях обращайтесь по адресу http://www.fiddlertool.com/ fiddler. Утилита сохраняет весь трафик HTTP, чтобы позднее вы могли проанализировать содержимое каждого запроса и ответа. Fiddler поддерживает Internet Explorer и другие браузеры. Утилита ASP.NET Development Helper, разработанная участником группы Atlas, распространяется бесплатно, но работает только с Internet Explorer. Ее можно загрузить по адресу http://www.nikhilk.net/Project.WebDevHelper.aspx. Утилита позволяет просмотреть информацию о текущей странице ASP.NET (например, состояние отображения и трассировочную информацию). Кроме того, она позволяет выполнять некоторые операции на сервере (такие, как перезапуск приложения или управление объектом Cache). Наконец, утилита предоставляет возможность «вживую» просматривать модель HTML DOM, а также отслеживать запросы и ответы для диагностических сценариев. Большая часть функций программы работает только в том случае, если приложение работает на локальном хосте. При установке программы ряд операций приходится выполнять вручную, потому что она реализована как вспомогательный объект браузера Internet Explorer. Впрочем, ничего такого, что могло бы напугать отважного веб-разработчика, делать не придется. На рис. 2.9 показано рабочее окно утилиты. После того как утилита будет установлена, выполните команду Вид (View) ► (Explorer Bar) и выберите утилиту в меню.

Рис. 2.9. Утилита ASP.NET Development Helper


82

Глава 2. Структурные элементы Atlas

Заключение Технология Atlas, проектировавшаяся как составная часть платформы ASP.NET, а не как внешний подключаемый API, имеет две стороны: клиенто-центрический и серверо-центрический API и программная модель. Главной целью при проектировании Atlas была возможность создания приложений с существенно расширенными интерфейсными возможностями. В Web эти возможности обеспечиваются немалым объемом кода JavaScript и облегченными вызовами передачи данных. Кто должен писать этот код? Кто должен иметь дело со сценарным языком? Большинство разработчиков Atlas составляют бывшие разработчики ASP.NET, знакомые с моделью разработки на стороне сервера, основанной на элементах. Серверо-центрическая программная модель Atlas стала следующим шагом в эволюции модели программирования ASP.NET в процессе ее постепенного смещения к модели AJAX. Серверные элементы Atlas полезны, если вы не настолько уверены в своих силах, чтобы создавать клиентские сценарии Atlas вручную. Подлинная сила Atlas проявляется в клиентских сценариях. Возможность генерирования клиентского сценарного кода серверными элементами полезна, но ее гибкость и мощь оставляет желать лучшего. Клиенто-центрическая модель Atlas требует навыков владения JavaScript и DOM, а также хорошего знания клиентской библиотеки Atlas. Впрочем, независимо от вашей квалификации и личных предпочтений, один факт остается несомненным — для написания хороших приложений AJAX необходима прикладная среда, будь то Atlas или другая разработка сторонней фирмы.


ГЛАВА 3

Частичное обновление страниц В I I I I

этой главе: Серверные элементы Atlas Включение частичного обновления страниц Управление обновлением страниц Обратная связь во время обновления

Главной движущей силой за проектом Atlas было желание упростить использование клиентского компьютера (в дополнение к серверной системе) для обработки запроса. В настоящее время работа на стороне клиента требует от разработчиков использования языка сценариев, что вовсе не приводит их в восторг. Одни веб-разработчики либо обожают JavaScript (и сценарные языки вообще) и могут добиться практически любого желаемого результата за счет гибкости синтаксиса, но других выводит из себя один вид клиентского тега <script>. Несмотря на то что многие прикладные среды на базе AJAX по возможности стараются скрыть технические подробности от разработчика, страница с включенной поддержкой AJAX как минимум содержит код JavaScript для обновления частей страницы, задействованных в удаленных операциях. А по мере того, как разработчик применяет более мощные и сложные функции, ему приходится все чаще иметь дело с JavaScript. Этот принцип остается истинным и для прикладной среды Atlas. Группа разработки Atlas знала об этой проблеме и предложила для ее решения своего рода метаязык — XML Script. Он нивелирует различия в объектных моделях документов разных браузеров и гораздо проще генерируется серверными элементами. Разработчик ASP.NET должен достаточно хорошо знать XML Script, чтобы извлечь максимум пользы из возможностей Atlas. Впрочем, даже не обладая такими познаниями, разработчики ASP.NET благодаря серверным элементам Atlas смогут легко пользоваться преимуществами AJAX, затратив минимум времени и сил на освоение новой технологии. Как будет показано в этой главе, для построения эффективных приложений Atlas вам не придется изучать новую программную модель, сценарный язык или диалект XML. Тем не менее следует учитывать, что здесь рассматривается лишь небольшая часть возможностей Atlas, а для полноценного использования платформы ASP.NET Atlas вам все равно потребуется хорошее знание XML Script. Язык XML Script подробно рассматривается в главе 5.


84

Глава 3. Частичное обновление страниц

ПРИМЕЧАНИЕ

XML Script — метаязык, используемый для описания привязок и аспектов поведения компонентов Atlas. Теги XML Script ссылаются на объекты библиотеки Microsoft AJAX (см. главу 5). Все, что выражается на XML Script, также может быть выражено командами JavaScript. Выбор между XML Script и JavaScript является делом вкуса; для серверных элементов предпочтение отдается XML Script.

Серверные элементы Atlas Серверные элементы Atlas играют важнейшую роль в сближении модели программирования Atlas с моделью ASP.NET. Если мир AJAX привлекает вас, но вам не хочется использовать JavaScript, — для начала подумайте о возможности использования этих элементов.

Цели и побудительные мотивы Разработчики могут легко и без лишних затрат наделить функциональностью AJAX новую или существующую страницу/приложение; ведь для этого необходимо лишь добавить новые серверные элементы и настроить их конфигурацию. При использовании серверных элементов Atlas вы идете по пути поэтапной разработки и добавляете AJAX-расширения пользовательского интерфейса без повторного проектирования кода и структуры страниц.

Избегайте больших объемов кода JavaScript В наши дни веб-разработчики делятся на две группы по уровню своей профессиональной подготовки: первые хорошо разбираются в сценарном программировании, вторые — нет. Первая группа состоит из профессионалов, обладающих значительным опытом работы со страницами HTML, PHP, Perl и, возможно, специализированными инструментариями веб-программирования. Все эти разработчики знакомы со сценарными конструкциями лучше, чем с классами или интерфейсами. Во вторую группу входят разработчики, которые начинали свою карьеру с Windows SDK и принципов ООП, а затем перешли к написанию веб-приложений в процессе своей профессиональной эволюции. Для профессионалов первой группы инициирование удаленного действия, получение результатов и обновление объектной модели документа — вполне привычное дело. Для второй группы этот процесс может стать сущим кошмаром. Тем не менее обе группы преследуют одну цель — расширение возможностей веб-приложений. Серверные элементы позволяют разработчикам обеих групп добиваться этой цели без написания объемистого кода JavaScript. Впрочем, необходимо учесть одно принципиальное обстоятельство: использование серверных элементов избавляет от написания больших объемов кода JavaScript, но не от использования кода JavaScript вообще. Проще говоря, серверные элементы генерируют весь необходимый код JavaScript за вас. Задавая свойства элемента, вы направляете работу элемента, чтобы он сгенерировал сценарий с нужной вам функциональностью. Тем не менее код JavaScript все равно


Серверные элементы Atlas

85

задействован на стороне клиента. Вы отдаете директивы серверному элементу, но не управляете каждым шагом построения сценария и не изменяете сгенерированный сценарий. При использовании серверного элемента Atlas вы включаете в свою страницу компонент, работающий по принципу «черного ящика», и начинаете зависеть от его работы.

Держите базовую логику на сервере При помощи серверных элементов Atlas вы также можете указать, что базовый интерфейс пользователя и логика приложения остаются на сервере написанными на полноценном языке вроде Microsoft Visual C# или Microsoft Visual Basic. Большая часть взаимодействий «пользователь/сервер» обрабатывается на сервере (как и при классическом возврате данных ASP.NET), а клиенту отправляется только разметка для изменяющихся частей страницы. Используя серверные элементы, вы можете немедленно приступить к построению страниц Atlas — при условии, что вы уже знаете C# или Visual Basic и знакомы с основами ASP.NET.

Серверные элементы Atlas Серверные элементы Atlas можно разделить на три категории: обновляемые панели, расширители и обертки для компонентов клиентской стороны. В этой главе основное внимание уделяется обновляемым панелям, а расширители и другие элементы будут рассматриваться в главе 4.

Обновляемые панели В упрощенном изложении модель AJAX позволяет пользователям инициировать операции на стороне сервера, получать данные и обновлять текущую страницу. Другими словами, модель AJAX логически делит страницу на обновляемые области. Логика построения нового содержимого находится на сервере (так называемые «операции на стороне сервера») и инициируется в цикле обмена данными. Логика обновления страницы должна быть внедрена в сценарный код, присоединенный к странице. Серверные элементы Atlas определяют обновляемые области страницы при помощи шаблонов ASP.NET. Когда любой из элементов шаблона инициирует отправку данных, инфраструктура Atlas перехватывает событие и запускает специальный внеполосный вызов. В результате сервер заново генерирует разметку только для элементов шаблона и возвращает ее клиенту. Логика обновления страницы в этом случае очень проста — она сводится к замене разметки в объектной модели документа страницы. Ключевое место в этом семействе занимает элемент UpdatePanel — Atlas-версия классического элемента ASP.NET Panel. Вскоре мы рассмотрим его более подробно.

Расширители и аспекты поведения Как упоминалось в главе 2, расширители Atlas представляют собой компоненты, связывающие различные аспекты поведения (перетаскивание, всплывающие подсказки и автозаполнение) с элементами ASP.NET. Аспект поведения (behavior) —


86

Глава 3. Частичное обновление страниц

компонент, всегда работающий на стороне клиента. Аспекты поведения привязываются к целевым элементам при помощи кода XML Script. Необходимый код привязки внедряется в страницу вручную или автоматически генерируется сервером для расширителя. В первом случае от разработчика потребуется знание XML Script. С другой стороны, при использовании серверных расширителей достаточно изучить программный интерфейс нового серверного элемента.

Обертки для клиентских компонентов Библиотека Microsoft AJAX содержит множество клиентских элементов — таких, как текстовые поля и списки. Для настройки конфигурации и активизации этих клиентских элементов потребуется код JavaScript или XML Script. Если вы предпочитаете обойтись без лишнего кода, также можно использовать серверные обертки для некоторых клиентских элементов Atlas. Настройка свойств элементов осуществляется так же, как в классической модели ASP.NET. Далее в соответствии с заданными свойствами элемент генерирует код XML Script для достижения желаемых целей.

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

Элемент UpdatePanel Контейнерный элемент UpdatePanel реализует возможность частичного изменения разметки в страницах ASP.NET. Такие страницы почти не отличаются от обычных страниц ASP.NET, если не считать того, что в них включается элемент ScriptManager и один или несколько элементов UpdatePanel. Каждый экземпляр элемента UpdatePanel задает области страницы, которые могут обновляться независимо. Элементы UpdatePanel также могут размещаться внутри пользовательских элементов, на главной странице и на страницах содержимого. Механизм частичного обновления разделяет страницу на независимые области. Каждая область самостоятельно управляет получением своих данных и обновляется нужным образом без обновления всей страницы. Такое поведение желательно в тех ситуациях, когда при получении данных должна изменяться лишь часть


Включение частичного обновления страниц

87

страницы (возможно, весьма небольшая). Частичное обновление снижает объем перерисовки и помогает повысить степень интерактивности создаваемых приложений. С точки зрения программиста эта схема позволяет принимать от сервера больше возвратов данных, чем было бы возможно при полном обновлении страницы.

Общие сведения об элементе UpdatePanel Элемент UpdatePanel определяется в сборке Microsoft.Web.Atlas, а в программной модели он принадлежит пространству имен Microsoft.Web.UI. Класс элемента объявляется следующим образом: public class UpdatePanel : Control { }

Несмотря на логическое сходство с классическим элементом ASP.NET Panel, элемент Atlas UpdatePanel отличается от него по нескольким аспектам. В частности, он не является производным от Panel и, соответственно, не поддерживает некоторых функций панелей ASP.NET (прокрутка, стили, управление содержимым и т. д.). Элемент Up+ лишь является контейнером для дочерних элементов, наделенным поддержкой Atlas. Все необходимые стили и форматирование обеспечиваются дочерними элементами. Вскоре мы разберем это обстоятельство более подробно.

Программный интерфейс элемента В табл. 3.1 перечислены свойства элемента UpdatePanel, которые определяют круг аспектов поведения элемента, находящихся под управлением разработчика. Таблица 3.1. Свойства элемента UpdatePanel

Свойство

Описание

ContentTemplate

Свойство определяет шаблон, то есть исходное содержимое UpdatePanel

ID

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

IsUpdating

Свойство указывает, находится ли панель в процессе обновления по асинхронному возврату данных Свойство определяет, при каких условиях происходит обновление панели (чтение и запись). Допустимые значения объединены в перечисляемый тип UpdatePanelMode. По умолчанию используется режим Always

Mode

RenderMode

Указывает, как генерируется содержимое панели — как встроенный код или в виде блока. Допустимые значения объединены в перечисляемый тип UpdatePanelRenderMode. По умолчанию используется режим Block

Triggers

Коллекция объектов-триггеров. Каждый объект предоставляет событие, приводящее к автоматическому обновлению панели


88

Глава 3. Частичное обновление страниц

В дополнение к свойствам, перечисленным в табл. 3.1, элемент UpdatePanel также поддерживает открытый метод с именем Update: public void Update()

Метод сам по себе никаких специальных действий не выполняет, но запрашивает обновление дочерних элементов, определенных в шаблоне содержимого элемента UpdatePanel. Используя метод Update, можно на программном уровне управлять обновлением области страницы в ответ на стандартное событие возврата данных или при инициализации страницы. Элемент UpdatePanel не выдает нестандартные события. Поддерживаются только события, производные от базового класса и общие для всех элементов ASP.NET - In it, Load, PreRender, DataBinding и Unload.

Определение обновляемого содержимого Содержимое панели UpdatePanel выражается специальным шаблоном — свойством ContentTemplate. Шаблон обычно определяется на декларативном уровне, с использованием элемента <ContentTemplate>. Впрочем, свойству ContentTemplate можно назначить любой объект, реализующий интерфейс ITemplate, в том числе и пользовательский элемент: void Page_Load(object sender, EventArgs e) { UpdatePanel1.ContentTemplate = this.LoadTemplate(ascx); }

В общем случае элементы ASP.NET, включенные в шаблон, остаются невидимыми на уровне страницы. Чтобы получить действительную ссылку на элемент, находящийся в шаблоне, следует вызвать метод FindControl для экземпляра элемента, содержащего шаблон. Рассмотрим следующий фрагмент кода: <atlas:UpdatePanel id="UpdatePanell" runat="server" .„> <ContentTemplate> <asp:textbox runat="server" id="TextBoxl" ... /> <asp:button runat="server" id="Buttonl" onclick="Buttonl_Click" ... /> </ContentTemplate> </atlas:UpdatePanel>

Как вы думаете, что произойдет со следующим кодом на стадии выполнения? Отработает ли он нормально или вызовет исключение null-ссылки? void Buttonl_Click(object sender, EventArgs e) { Response.Write(TextBoxl.Text); }

Элемент TextBoxl определяется внутри шаблона, и как следствие, остается невидимым на уровне страницы. В ASP.NET 1.x попытка выполнения этого кода закончится сбоем; в ASP.NET 2.0 он может работать нормально, если свой-


Включение частичного обновления страниц

89

ство шаблона помечено атрибутом Templatelnstance. Как видно из следующего фрагмента, именно это происходит со свойством ContentTemplate элемента UpdatePanel: [Browsable(false); [PersistenceMode(PersistenceMode.InnerProperty); [TempiateInstance(TemplateInstance.Single); public ITemplate ContentTemplate { get { return _contentTemplate; } set { _contentTemplate = value; } }

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

Режимы обновления Свойство Mode определяет условия, при которых обновляется содержимое панели. По умолчанию элемент UpdatePanel обновляет свое содержимое каждый раз, когда происходит возврат данных. Следовательно, если на странице находятся несколько элементов UpdatePanel, они все будут обновлены — даже в том случае, если событие касается только одного из них. Через свойство Mode элемент UpdatePanel поддерживает два режима обновления — Always (используется по умолчанию) и Conditional. Значения объединены в перечисление UpdatePanelMode (табл. 3.2). Таблица 3.2. Перечисление UpdatePanelMode

Значение

Описание

Always

Панель обновляется для каждого возврата данных, инициированного страницей

Conditional

Панель обновляется только при срабатывании триггеров панели или при программном запросе на обновление

Содержимое панели может объединяться со страницей-носителем одним из двух способов — либо в виде встроенной разметки, либо на уровне блоков. Способ выбирается при помощи свойства RenderMode, допустимые значения которого представлены в табл. 3.3. Таблица 3.3. Перечисление UpdatePanelRenderMode

Значение

Описание

Block

Содержимое панели заключается в тег <div>

Inline

Содержимое панели заключается в тег <span>


90

Глава 3. Частичное обновление страниц

По умолчанию содержимое панели образует новый блок и заключается в тег <div>: <div id="UpdatePanell"> </div>

Если будет выбрано значение Inline, содержимое встраивается в поток кода страницы, как показано в следующем примере: <form id="forml" runat="server"> <atlas:ScriptManager runat="server" ID="scriptManager" /> <big>This panel has been generated at: <atlas:UpdatePanel ID="UpdatePanell" runat="server" rendermode="inline"> <ContentTemplate> <b style="background-color:lime"> <% =DateTime.Now %> </b> </ContentTemplate> </atlas:UpdatePanel>. </big> <hr /> <asp:Button ID="Buttonl" runat="server" Text="Refresh" /> </form>

Результат работы приведенного фрагмента показан на рис. 3.1. Обновляемая часть страницы представляет собой не блок разметки, а простой фрагмент абзаца.

Рис. 3 . 1 . Использование элемента UpdatePanel для частичного обновления текста абзаца

Проверка текущих обновлений Логическое свойство IsUpdating The IsUpdating read-only Boolean property указывает, находится ли содержимое элемента UpdatePanel в процессе обновления. Свойство проверяется при обработке возврата данных; если свойство возвращает true, значит, текущий запрос выполняется в результате возврата данных Atlas.


Включение частичного обновления страниц

91

Элементы UpdatePanel могут быть вложены в другие элементы UpdatePanel. Вложенная панель всегда обновляется в случае обновления внешней родительской панели. В этом случае свойство IsUpdating остается равным false. У вложенных панелей IsUpdating может быть равно true только в том случае, если родитель не нуждается в обновлениях.

Использование элемента UpdatePanel Одного размещения элемента UpdatePanel на странице еще недостаточно для активизации частичного обновления. Вы также должны разместить на странице элемент ScriptManager и убедиться в том, что его свойству EnablePartialRendering задано значение true. Страница ASP.NET может содержать несколько элементов UpdatePanel; это позволяет разбить страницу на несколько областей, обновляемых независимо друг от друга, и даже по особым условиям. ПРИМЕЧАНИЕ

Если свойству EnablePartialRendering элемента ScriptManager задано значение false (используемое по умолчанию), то элемент UpdatePanel работает как обычная панель ASP.NET. Если вам вдруг показалось, что UpdatePanel работает некорректно (скажем, обновленное содержимое мерцает в процессе обновления), наиболее вероятная причина заключается именно в этом.

Первый пример страницы с частичным обновлением Допустим, на вашей странице определена область (скажем, блок <div>), в которой отображается информация реального времени, нуждающаяся в частом обновлении. На странице также размещается кнопка, обновляющая критическую информацию. В классических страницах ASP.NET эта схема реализуется так: <form id="forml" runat="server"> <hl>What time is it worldwide? [Using postback]</hl> <asp:panel runat="server" style="background-color:Lime"> <hl> <asp:Label ID="Labell" runat="server" Text="What time is on the server?" /> </hl> <asp:Button ID="Buttonl" runat="server" Text="Read time on the server" OnClick="Buttonl_Click" /> </asp:panel> <h3>This page has been generated at: <^=DateTime.Now ^></h3> </form>

Элемент Panel содержит два элемента, надпись (Label) и кнопку (Button). Когда пользователь щелкает на кнопке, на сервере выполняется код обновления надписи. Однако в классической схеме ASP.NET каждый щелчок на кнопке приводит к обновлению всей страницы. ASP.NET Atlas позволяет отделить часть страницы, нуждающуюся в обновлении, и заключить ее в элемент UpdatePanel. В результате по щелчку будет


92

Глава 3. Частичное обновление страниц

обновляться только содержимое панели. Вот как выглядит версия предыдущей формы с поддержкой Atlas: <form id="forml" runat="server"> <atlas:ScriptManager runat="server" ID="scriptManager" EnablePartialRendering="true" /> <hl>What time is it worldwide? [Using Atlas]</hl> <atlas:UpdatePanel ID="UpdatePanell" runat="server"> <ContentTemplate> <div style="background-color:Lime"> <hl> <asp:Label ID="Label1" runat="server" Text="What time is on the server?" /> </hl> <asp:Button ID="Buttonl" runat="server" Text="Read time on the server" OnClick="Buttonl_Click" /> </div> </ContentTemplate> </atlas:UpdatePanel> <h3>This page has been generated at: <^=DateTime.Now ^></h3> </form>

В сущности, содержимое тега <asp:Panel> переместилось в секцию <ContentTemplate> элемента UpdatePanel, и на странице добавился элемент ScriptManager. Изменения невелики, но их последствия весьма значительны. На рис. 3.2 показана страница в действии. Обратите внимание: страница и панель были сгенерированы в разное время. Это означает, что после того, как страница была создана и отображена в браузере, пользователь щелкнул на кнопке с целью получения времени на сервере. Содержимое панели регенерируется, но большая часть страницы осталась в прежнем виде.

Рис. 3.2. Пример страницы с элементом UpdatePanel

Глагол «регенерируется» в данном случае более уместен, чем кажется на первый взгляд. Разметка сегмента страницы, внедренная в элемент UpdatePanel,


Включение частичного обновления страниц

93

действительно регенерируется (то есть генерируется заново) на сервере вследствие запроса Atlas. Сгенерированная разметка вставляется в ответ HTTP и возвращается клиенту. Инфраструктура Atlas обрабатывает его и обновляет страницу.

Как работает механизм частичного обновления Волшебство обновления отдельных частей страницы строится на совместных усилиях элемента UpdatePanel и компонента Atlas ScriptManager. В фазе инициализации элемент UpdatePanel получает ссылку на компонент ScriptManager страницы и регистрируется в нем. В результате ScriptManager знает, сколько обновляемых панелей содержит страница, и хранит ссылки на каждую из них. Компонент ScriptManager также генерирует код клиентского сценария для перехвата любых действий отправки данных, инициированных на стороне клиента. Сценарный код подключается к событию формы submit и заменяет отправку данных асинхронным вызовом Atlas, выполняемым через объект XmlHttpRequest. В следующем листинге показан код XML Script, внедряемый в клиентскую страницу при включении частичного обновления: <script type="text/xml-script"> <page xmlns:script="http://schernas.niicrosoft.coni/xnil-script/2005"> <components> <pageRequestManager id="_PageRequestManager" UpdatePanelIDs="UpdatePanell" asyncPostbackControlIDs="" scriptManagerID="scriptManager" form="forml" /> </components> </page> </script>

Элемент <pageRequestManager> содержит ссылку на экземпляр класса PageRequestManager из библиотеки Microsoft AJAX. В частности, этот класс отвечает за асинхронные вызовы для обновления (полного или частичного) текущей страницы через объект XmlHttpRequest. Среди прочего, класс подключает новые обработчики к событиям формы onsubmit и onclick. Вы можете ознакомиться с фактической реализацией класса, просмотрев исходный код в файле atlas.js или класс из пространства имен Sys. WebForms в электронной документации Atlas. Тело запроса, отправленного серверу, выглядит примерно так: scriptManager=Update& VIEWSTATE=^2Fw ... 3D& EVENTVALIDATI0N=^2Fw ... 3D& Buttonl=Read^20time^20oru20the^20server

Помимо состояния отображения (VIEWSTATE) и данных проверки событий (EVENTVALIDATION), специфических для ASP.NET 2.0, запрос содержит имя элемента HTML, инициировавшего отправку данных (Button"!), и идентификатор элемента ScriptManager, обрабатывающего вызов. Чтобы просмотреть все составляющие запроса, либо воспользуйтесь специальной утилитой отслеживания


94

Глава 3. Частичное обновление страниц

трафика (см. главу 2), либо просто включите отладочный код в обработчик события Page_Load: protected void Page_Load(object sender, EventArgs e) { Request.SaveAs(@"c:\req_details.txt", true); }

На сервере запрос обрабатывается как обычно; в самом деле, runtime-среда ASP.NET не может определить, что запрос предназначен для обновления только части исходной страницы. Менеджер сценариев подключается к стандартному процессу построения страницы кода посредством регистрации метода обратного вызова. Эта малоизвестная возможность присутствует в ASP.NET с первых дней появления платформы, а реализуется она методом SetRenderMethodDelegate класса Page. Измененный процесс построения страницы перебирает все зарегистрированные обновляемые панели и приказывает каждой из них вывести обновленную разметку. В конечном итоге клиент возвращает только содержимое обновляемых областей, а функция обратного вызова JavaScript, скрытая в недрах библиотеки Microsoft AJAX, обеспечивает замену текущего содержимого областей принятой разметкой. ПРИМЕЧАНИЕ

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

Более реальный пример страницы с частичным обновлением Многие элементы ASP.NET содержат элементы, активизируемые щелчком, — такие, как кнопки и ссылки. Взаимодействие пользователя с разметкой таких элементов приводит к отправке данных и полному обновлению всей страницы. Особенно дорогостоящими в отношении загрузки канала, задержки и потребления вычислительных мощностей сервера являются сетки (табличные элементы) вроде DataGrid и GridView. Они могут генерировать многостраничный вывод, и пользователю нередко приходится снова щелкать и передавать запрос для просмотра содержимого определенной страницы. Существует ли способ получения постраничного, отсортированного или отфильтрованного представления содержимого сеток без полного обновления всей страницы? В ASP.NET 2.0 элемент GridView содержит логическое свойство с именем EnableSortingAndPagingCallbacks. При установке этого свойства элемент активизирует механизм обратного вызова на стороне клиента для операций сортировки и постраничного вывода. Содержимое отдельного страничного блока данных элемента запрашивается с сервера, но разметка элемента обновляется без полного


Включение частичного обновления страниц

95

обновления страницы. В ASP.NET 2.0 эта функция реализуется на базе ASP.NET Script Callback. Давайте посмотрим, как добиться подобного результата в прикладной среде Atlas: <form id="forml" runat="server"> <atlas:ScriptManager ID="scriptManager" runat="server" EnablePartialRendering="true" /> <atlas:UpdatePanel ID="UpdatePanell" runat="server"> <ContentTemplate> <asp:GridView ID="GridViewl" runat="server" DataSourceID="ObjectDataSourcel" AllowPaging="True" AutoGenerateColumns="False"> <Columns> <asp:BoundField DataField="ID" HeaderText="ID" /> <asp:BoundField DataField="CompanyName" HeaderText="Company" /> <asp:BoundField DataField="Country" HeaderText="Country" /> </Columns> </asp:GridView> <asp:ObjectDataSource ID="ObjectDataSourcel" runat="server" TypeName="IntroAtlas.CustomerManager" SelectMethod="LoadAll" /> </ContentTemplate> </atlas:UpdatePanel> </form>

В секцию ContentTemplate элемента UpdatePanel включены элементы GridView и ObjectDataSource. Первый элемент обеспечивает отображение в табличном формате гиперссылок на данные, полученные вторым элементом. Установка свойства AllowPaging равным true включает элементы перехода между страницами данных в сетке. Щелкнув на любой гиперссылке, пользователь переходит к соответствующему страничному блоку набора данных. Обычно за это действие приходится расплачиваться обновлением всей страницы; но поскольку в данном случае сетка инкапсулирована в элементе UpdatePanel, при щелчке инициируется упрощенный внеполосный вызов, а обновляются только элементы в секции ControlTemplates. ПРИМЕЧАНИЕ

Самой интересной особенностью данного примера является не функциональность как таковая, а тот факт, что в элементе UpdatePanel можно разместить практически любую комбинацию элементов ASP.NET. Если какой-либо из этих элементов инициирует отправку данных с обновлением страницы, то соответствующий сегмент страницы будет обновляться независимо от остальных участков. Кроме того, реализация такого решения не требует специальных знаний среды Atlas. На первый взгляд может показаться, что элемент UpdatePanel в целом повторяет функциональность свойства EnableSortingAndPagingCallbacks. Тем не менее он обладает много большей гибкостью и может применяться в гораздо более широком диапазоне ситуаций.


96

Глава 3. Частичное обновление страниц

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

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

Использование внешних кнопок Чтобы содержимое панели обновлялось при щелчке на внешней кнопке (то есть на кнопке, находящейся за пределами панели), кнопка регистрируется в качестве триггера для панели: <asp:button runat="server" id="ExternalButton" text="Click" onclick="ExternalButton_Click" /> <atlas:UpdatePanel runat="server"> <ContentTemplate> </ContentTemplate> <Triggers> <atlas:ControlEventTrigger ControlID="ExternalButton" EventName="Click" /> </Triggers> </atlas:UpdatePanel>

Для каждого элемента UpdatePanel можно определить сразу несколько триггеров. При срабатывании любого из них панель автоматически обновляется. Триггеры UpdatePanel могут определяться как на декларативном, так и на программном уровне. В первом варианте используется секция <Triggers>, а во втором — свойство-коллекция Triggers (см. табл. 3.1).


Управление обновлением страниц

97

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

Триггеры UpdatePanel Триггер панели UpdatePanel определяет событие, приводящее к обновлению элемента UpdatePanel при работе страницы в режиме частичного обновления. Atlas поддерживает два типа триггеров, ControlEventTrigger и ControlValueTrigger; оба являются потомками абстрактного класса UpdatePanelTrigger. Точнее говоря, два класса триггеров наследуют от промежуточного класса ControlTrigger, который, в свою очередь, наследует от UpdatePanelTrigger, как показано на рис. 3.3. ControlEventTrigger

ControlValueTrigger

ControlTrigger

Прочие

UpdatePanelTrigger Рис. 3.3. Иерархия классов триггеров

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

Триггеры событий Класс ControlValueTrigger представляет триггер, обновляющий содержимое элемента UpdatePanel по событию серверного элемента. Декларативное определение триггера осуществляется в секции <Triggers> элемента UpdatePanel: <atlas:UpdatePanel runat="server"> <ContentTemplate> </ContentTemplate> <Triggers> <atlas:ControlEventTrigger ControlID="DropDownListl" EventName="SelectedIndexChanged" /> </Triggers> </atlas:UpdatePanel>

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


98

Глава 3. Частичное обновление страниц

фрагменте кода, обновляется либо в том случае, если возврат данных был инициирован изнутри панели, либо при изменении текущего выбора в раскрывающемся списке с именем DropDownListl. Важно заметить, что ControlEventTrigger перехватывает только события на стороне сервера, обладающие следующей сигнатурой: void handler(object sender, EventArgs e);

Но при этом следует учесть, что аргумент е может относиться к любому классу, производному от EventArgs. Давайте создадим более содержательный пример на базе примера с сеткой, который рассматривался несколько ранее в этой главе. Допустим, имеется многостраничная сетка со списком всех клиентов. Сетка находится внутри контейнера UpdatePanel, чтобы пользователь мог просматривать информацию по группам клиентов без полного обновления всей страницы. Теперь задача усложняется — для сетки требуется организовать дополнительную фильтрацию по строковому критерию. Допустим, во вспомогательном текстовом поле вводится буква «A», а в сетке появляются имена всех клиентов, начинающихся с этой буквы. Не стоит и говорить, что это должно происходить без полного обновления всей страницы. Следующий фрагмент разметки закладывает основу для решения этой задачи: <form id="forml" runat="server"> <atlas:ScriptManager ID="scriptManager" runat="server" EnablePartialRendering="true" /> Query string:<br /> <asp:TextBox ID="TextBoxl" runat="server" OnTextChanged="TextBoxl_TextChanged" /> <asp:Button ID="Buttonl" runat="server" Text="Refresh" /> <atlas:UpdatePanel ID="UpdatePanell" runat="server"> <ContentTemplate> <asp:GridView ID="GridViewl" runat="server" DataSourceID="ObjectDataSourcel" AllowPaging="True" AutoGenerateColumns="False" > <FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" /> <Columns> <asp:BoundField DataField="ID" HeaderText="ID" /> <asp:BoundField DataField="CompanyName" HeaderText="Company" /> <asp:BoundField DataField="Country" HeaderText="Country" /> </Columns> </asp:GridView> <asp:ObjectDataSource ID="ObjectDataSourcel" runat="server" TypeName="IntroAtlas.CustornerManager" SelectMethod="LoadAll"> </asp:ObjectDataSource> </ContentTemplate>


Управление обновлением страниц

99

<Triggers> <atlas:ControlEventTrigger ControlID="TextBoxl" EventName="TextChanged" /> <atlas:ControlEventTrigger ControlID="Buttonl" EventName="Click" /> </Triggers> </atlas:UpdatePanel> </form>

По сравнению со страницей, рассматривавшейся ранее, в этом коде добавилось текстовое поле, инициирующее триггерное событие наряду с кнопкой. Триггер связывается с событием TextChanged текстового поля. Панель обновляется как при возврате данных страницей, так и при срабатывании события TextChanged. Событие TextChanged само по себе не вызывает возврата данных, поэтому в этом случае все равно потребуется щелчок на кнопке. Чтобы предотвратить полное обновление страницы, событие Click кнопки регистрируется в качестве триггера для панели: protected void Page_Load(object sender, EventArgs e) { // Восстановление исходной ситуации для предотвращения null-ссылок ObjectDataSourcel.SelectMethod = "LoadAll"; ObjectDataSourcel.SelectParameters.Clear(); } protected void TextBoxl_TextChanged(object sender, EventArgs e) { // Изменение метода, используемого для заполнения сетки ObjectDataSourcel.SelectMethod = "LoadBylnitial"; ControlParameter p = new ControlParameter("query", "TextBoxl", "Text"); ObjectDataSourcel.SelectParameters.Add(p); }

При срабатывании TextChanged мы изменяем метод заполнения для элемента ObjectDataSource, используемого для заполнения сетки, и передаем в качестве параметра текущее значение свойства Text элемента TextBox. Результаты показаны на рис. 3.4 и 3.5.

Триггеры значений Класс ControlValueTrigger представляет триггер, изменяющий содержимое элемента UpdatePanel в ответ на изменение значения заданного свойства элемента. Декларативное определение триггера осуществляется в секции <Triggers> элемента UpdatePanel: <atlas:UpdatePanel runat="server"> <ContentTemplate> </ContentTemplate> <Triggers> <atlas:ControlValueTrigger ControlID="TextBoxl" PropertyName="Text" /> </Triggers> </atlas:UpdatePanel>


100

Глава 3. Частичное обновление страниц

Рис. 3.4. Сетку с полным списком клиентов можно прокручивать без полного обновления страницы 3 Event triggers - Microsoft Internet Explorer File

Edit

View

Favorites В

Ш

Tools &

- J|_

5earl:n

P

Address Щ] http://localhost:1386/IntroAtlas/5a v

IS If

Help i^ Favorites

0Go

L nks

»

>J

Query string:

v

К Refresh |]

ID

Company

Country

VAFFE Vafferjernet

Denmark

VICTE Victuailles en stock

France

VINET Vins et alcools Chevalier France

dJ Ш\ Done

V j Local intranet

Рис. 3.5. Если ввести в текстовом поле критерий фильтрации, содержимое сетки обновляется без полного обновления страницы


Управление обновлением страниц

101

В объявлении триггера необходимо указать два значения: идентификатор элемента и имя свойства, изменения которого должны отслеживаться. Значения ControllD и PropertyName представляют собой строковые свойства. Если значение PropertyName не задано, используется свойство элемента по умолчанию (такое свойство, если оно есть, помечается атрибутом ControlValueProperty).

Условное обновление Когда свойству Mode задано значение Always, каждый возврат данных, инициированный со страницы (как с элементов внутри панели, так и вне ее), приводит к обновлению панели. Если свойству Mode задано значение Conditional, панель обновляется только в том случае, если при возврате данных был вызван метод Update, или при срабатывании триггера, связанного с элементом UpdatePanel.

Активизация условных обновлений Если на странице находится только один элемент UpdatePanel, проблема условного обновления не актуальна. Тем не менее, если страница содержит несколько панелей, придется позаботиться о том, чтобы каждая панель обновлялась только в случае необходимости. Для включения условных обновлений свойству Mode элемента UpdatePanel задается значение Conditional (по умолчанию используется значение Always). <atlas:UpdatePanel runat="server" Mode="Conditional"> </atlas:UpdatePanel>

Условное обновление производится на уровне триггеров или на программном уровне. При использовании триггеров вы определяете список событий, приводящих к обновлению панели. С другой стороны, чтобы реализовать условное обновление на программном уровне, следует вызвать метод Update для панели при достижении некоторой критической точки в ходе выполнения.

Независимое обновление нескольких панелей Давайте доработаем страницу с сеткой, описанную ранее, и поместим на нее второй элемент UpdatePanel: <atlas:UpdatePanel ID="UpdatePanell" runat="server" Mode="Conditional"> <ContentTemplate> </ContentTemplate> <Triggers> <atlas:ControlEventTrigger ControlID="TextBoxl" EventName="TextChanged" /> <atlas:ControlEventTrigger ControlID="Buttonl" EventName="Click" /> </Triggers> </atlas:UpdatePanel> <atlas:UpdatePanel ID="UpdatePanel2" runat="server" Mode="Conditional"> <ContentTemplate>


102

Глава 3. Частичное обновление страниц

<div stylе="background-color:Lime"> <big>This panel has been generated at: <^=DateTime.Now ^></big> </div> </ContentTemplate> <Triggers> <atlas:ControlEventTrigger ControlID="Button2" EventName="Click" /> </Triggers> </atlas:UpdatePanel> <asp:Button ID="Button2" runat="server" Text="Refresh time" 0nClick="Button2_Click" />

У второго элемента UpdatePanel событие Click кнопки Button2 определяется как триггер. Для обеих панелей активизирован режим условного обновления. Рассмотрим их поведение более подробно. Первая панель обновляется при инициировании события TextChanged для текстового поля. Обратите внимание на интересную подробность: сетка не обновляется при щелчках на кнопках навигации. После того как панель была помечена для условного обновления, она обновляется только по явно заданным событиям. В конечном итоге, чтобы разрешить постраничную навигацию в сетке, необходимо добавить новый триггер для события PagelndexChanged сетки: <Triggers> <atlas:ControlEventTrigger ControlID="TextBoxl" EventName="TextChanged" /> <atlas:ControlEventTrigger ControlID="Buttonl" EventName="Click" /> <atlas:ControlEventTrigger ControlID="GridViewl" EventName="PagelndexChanged" /> </Triggers>

Вторая панель обновляется только при щелчке на нижней кнопке или при полном возврате данных. Программный запрос на обновление панели реализуется следующим образом: protected void ButtonlJClick(object sender, EventArgs e) { UpdatePanel2.UpdateO; }

К этому решению следует прибегать только в том случае, если между двумя панелями существует неявная зависимость. В этом случае во время обновления одной панели могут возникнуть условия, требующие обновления другой панели. Так как на этой стадии код выполняется на сервере, задачу невозможно решить другим способом, кроме явного вызова метода Update для панели. На рис. 3.6 изображена страница с несколькими панелями. На рисунке пользователь собирается щелкнуть на кнопке для обновления второй панели. Однако содержимое элемента TextBox изменилось — в поле был введен текст «v». При возврате данных страницей проверяется новое содержимое TextBox, и в случае обнаружения события TextChanged сетка также обновляется.


Обратная связь во время обновления

103

Рис. 3.6. Два элемента UpdatePanel на одной странице

Обратная связь во время обновления С точки зрения пользователя с обновлением панели дело обстоит не столь гладко. Выполнение компьютером потенциально продолжительной операции создает проблемы. Устоит ли пользователь перед искушением снова и снова щелкать на кнопке? Сможет ли он терпеливо дожидаться появления результатов? Наконец, захочется ли ему ждать, не имея ни малейшего представления о происходящем, не вызовет ли это раздражения? В процессе полного возврата данных браузер обычно каким-то образом сообщает пользователю о происходящем. При использовании Atlas частичное обновление не приводит к полному возврату данных, поэтому визуальная обратная связь не оповещает пользователя о происходящих событиях. Помните, что Atlas представляет собой воплощение парадигмы AJAX на платформе ASP.NET, а в сокращении AJAX буква «A» означает «асинхронный». Следовательно, разработчик Atlas должен тщательно продумать, как оповестить пользователя о ходе выполняемых операций, и по возможности предоставить возможность отмены незавершенных запросов.


104

Глава 3. Частичное обновление страниц

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

Элемент UpdateProgress Элемент UpdateProgress обеспечивает обратную связь в браузере во время обновления одного или нескольких элементов UpdatePanel. Элемент UpdateProgress можно разместить в любом месте страницы, определяя его стиль и позицию при помощи каскадных таблиц стилей (CSS).

Общие сведения об элементе UpdateProgress Элемент UpdateProgress определяется в пространстве имен Microsoft.Web.UI и предоставляется сборкой Microsoft.Web.Atlas. Он является производным от класса Control и реализует пару интерфейсов: public class UpdateProgress : Control, INamingContainer, IScriptControl

Специфический интерфейс Atlas IScriptControl описывает нестандартный серверный элемент как элемент Atlas. Интерфейс содержит всего один метод RenderScript, который должен генерировать весь необходимый для элемента код XML Script. Страница может содержать не более одного элемента UpdateProgress, который обслуживает все обновляемые панели на странице. Если страница содержит несколько панелей, постарайтесь найти удобное место на странице для UpdateProgress или, если возможно, переместите его на программном уровне в нужное место к обновляемой панели. Элемент UpdateProgress обладает единственным свойством ProgressTemplate. Свойство определяет шаблон с разметкой, которая должна отображаться во время обновления панели.

Вывод информации о ходе операции Atlas отображает содержимое шаблона ProgressTemplate во время ожидания обновления панели. Шаблон определяется либо декларативно, либо на программном уровне. Во втором случае свойству задается любой объект, реализующий интерфейс ITemplate. В первом случае разметка легко задается декларативным способом, как показано в следующем фрагменте: <atlas:UpdateProgress runat="server" ID="UpdateProgressl"> <ProgressTemplate> </ProgressTemplate> </atlas:UpdateProgress>

Шаблон может содержать произвольную комбинацию элементов. Впрочем, обычно в него помещается небольшой текстовый фрагмент и анимированное изображение в формате GIF (рис. 3.7). Связь между элементом UpdatePanel и UpdateProgress устанавливается автоматически. Учтите, что элемент UpdateProgress спроектирован не как класси-


Обратная связь во время обновления

105

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

Рис. 3.7. Шаблон сообщает пользователю, что сервер в настоящий момент занят ПРИМЕЧАНИЕ

Внешний вид и поведение элемента UpdateProgress можно настраивать динамически (до определенной степени). Для этого необходимо написать обработчик события Load элемента UpdateProgress и получить ссылку на элементы шаблона методом FindControl. Аналогичным образом можно сменить анимированное изображение и выбрать то, которое лучше отображает текущий контекст.

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


106

Глава 3. Частичное обновление страниц

Чтобы шаблон содержал кнопку отмены, в него включается следующий тег: <input id="abortButton" runat="server" type="button" value="Cancel" />

Прежде всего, для отмены используется кнопка клиентской стороны. Следовательно, она может быть представлена как элементом <input>, так и элементом <button> для браузеров, поддерживающих этот элемент. Если вы выберете элемент <input>, атрибут type должен быть задан равным button. Чтобы кнопка была идентифицирована как кнопка отмены в Atlas, атрибут id должен быть равен abortButton. Наконец, атрибут runat должен содержать текст server. На рис. 3.8 показана панель с кнопкой отмены текущей операции.

Рис. 3.8. Отмена незавершенного обновления

Менеджер сценариев связывает специально сгенерированный сценарный код с событием onclick кнопки отмены. Далее при щелчке на кнопке асинхронная операция отменяется через стандартный API объекта XmlHttpRequest.


Таймер

107

ПРИМЕЧАНИЕ

Элемент UpdateProgress прост в использовании и эффективен, однако он скрывает некоторые действия, которые могут быть крайне существенными в некоторых случаях. Например, автор страницы не может связать пользовательский код с отображением шаблона. Например, такая возможность могла бы пригодиться для блокировки активных элементов или запуска второго, параллельного асинхронного вызова. Для чего это может понадобиться? Например, для реализации контекстно-чувствительного индикатора процесса во время продолжительной операции.

Таймер На страницах ASP.NET, требующих частого обновления, можно разместить элементы, активизируемые по щелчку; пользователь запрашивает обновление тогда, когда сочтет нужным. Но что, если обновление должно происходить часто и с регулярными интервалам — скажем, каждые n миллисекунд? Нельзя же требовать, чтобы пользователь постоянно щелкал мышью. В подобных ситуациях на помощь приходят таймеры. Кстати, поддержка таймеров присутствовала в модели DOM практически любого браузера с первых дней существования Web. Клиентский таймер создается методом setTimeout объекта DOM window. Запущенный таймер периодически выполняет фрагмент кода JavaScript. В свою очередь, этот сценарный код может сделать все, что сочтет нужным — например, запустить асинхронный вызов к серверу и автоматически обновлять страницу. Сценарные таймеры можно использовать в любой версии ASP.NET, однако для этого необходимо обладать навыками программирования на JavaScript и знать особенности модели DOM браузера. Atlas избавляет вас от всех сложностей и предоставляет в ваше распоряжение специальный компонент TimerControl.

Общие сведения об элементе TimerControl Элемент TimerControl определяется в пространстве имен Microsoft.Web.Ul.Controls и предоставляется сборкой Microsoft.Web.Atlas. Он наследует от класса Control и реализует интерфейсы IPostBackEventHandler и IScriptControl. public class TimerControl : Control, IPostBackEventHandler, IScriptControl

TimerControl реализуется как серверный элемент, но в действительности он создает на стороне клиента таймер, выполняющий возврат данных с регулярными интервалами. При возврате серверный элемент TimerControl инициирует серверное событие с именем Tick. public event EventHandler Tick

Элемент обладает двумя свойствами, описанными в табл. 3.4. В сущности, TimerControl представляет собой серверный интерфейс, построенный на базе клиентского таймера. Он избавляет разработчика от необходимости создавать таймер на уровне сценарного кода и заставляет его возвращать данные при истечении интервала.


108

Глава 3. Частичное обновление страниц

Таблица 3.4. Свойства элемента TimerControl

Свойство Enabled Interval

Описание Свойство указывает, нужно ли генерировать клиентский сценарий таймера при первоначальном построении страницы (по умолчанию true) Целочисленное свойство, определяющее интервалы между событиями Tick клиентского таймера. Интервал выражается в миллисекундах, значение по умолчанию — 60 000 (одна минута)

Использование таймера Элемент TimerControl чаще всего используется в сочетании с триггером UpdatePanel для обновления панели с заданной периодичностью. В следующем фрагменте определяется таймер, который инициирует ответное обращение каждую секунду: <atlas:TimerControl ID="timerl" runat="server" Interval="1000" 0nTick="timerl_Tick" />

Очень важно тщательно проанализировать влияние слишком короткого интервала на общую производительность и масштабируемость приложения. Выбор слишком малого значения (например, одна или две секунды) порождает слишком много ответных обращений и увеличивает трафик на пути к серверу. В результате даже асинхронные обращения, выполняемые в режиме частичного обновления, приводят к лишним непроизводительным затратам. В следующем примере элемент UpdatePanel работает в сочетании с таймером (TimerControl). Таймер срабатывает каждую секунду. Элемент UpdatePanel связывается с событием Tick таймера при помощи событийного триггера: <form id="forml" runat="server"> <atlas:ScriptManager ID="scriptManager" runat="server" EnablePartialRendering="true" /> <atlas:UpdatePanel ID="UpdatePanell" runat="server" Mode="Conditional"> <ContentTemplate> <hl> <asp:Label ID="Label1" runat="server" /> </hl> </ContentTemplate> <Triggers> <atlas:ControlEventTrigger ControlID="Timerl" EventName="Tick" /> </Triggers> </atlas:UpdatePanel> <atlas:TimerControl ID="Timerl" runat="server" Interval="1000" 0nTick="Timerl_Tick" /> </form>

В результате панель обновляется каждую секунду. Иначе говоря, на сервере ежесекундно выполняется следующий код: protected void Timerl_Tick(object sender, EventArgs e) {


Заключение

}

109

_abell.Text = DateTime.Now.ToStringO;

На рис. 3.9 показано, как выглядит эта страница в действии.

Рис. 3.9. Часы на базе Atlas в действии

Заключение По своей сущности Atlas является клиентской средой, в которой JavaScript играет ключевую роль. Чтобы извлечь максимум пользы из приложений Atlas, хорошие навыки программирования клиентских сценариев абсолютно необходимы. Точка. Тем не менее UpdatePanel и другие серверные элементы обеспечивают превосходный компромисс между необходимостью реализации асинхронной и внеполосной функциональности и стремлением к использованию уже знакомой прикладной модели ASP.NET. Как было показано в этой главе, любая существующая страница ASP.NET легко преобразуется в страницу Atlas. Исходная страница делится на области, а с каждой областью разметки связывается свой элемент UpdatePanel. Начиная с этого места каждая область обновляется отдельно; при этом используются независимые и асинхронные вызовы, не влияющие на остальные части страницы. Текущая страница продолжает нормально работать, а области обновляются благодаря помощи среды Atlas. Такой режим называется режимом частичного обновления.


ГЛАВА 4

Элементы Microsoft ASP.NET AJAX В I I I i i

этой главе: ASP.NET AJAX Control Toolkit Элемент Accordion Элемент Rating Элемент ReorderList Серверные расширители Atlas

Хотя Atlas является прикладной средой, расширяющей возможности программирования на стороне веб-клиента, он используется в основном серверными разработчиками — скажем, разработчиками Microsoft ASP.NET или веб-разработчиками с высокой квалификацией в области серверного программирования. К сожалению, в настоящее время существует только один путь добавления новых возможностей и функциональности на стороне веб-клиента: это написание качественного код JavaScript. Для этой цели Atlas предоставляет разработчикам три способа построения широкофункциональных веб-приложений на базе серверо-центрической модели. Во-первых, разработчик может обновлять отдельные участки страниц, используя частичное обновление вместо обычного возврата данных ASP.NET (см. главу 3). Для создания таких областей используется специальный набор серверных элементов, среди которых центральное место занимает элемент UpdatePanel. Помимо частичного обновления, разработчики могут использовать расширители элементов для добавления заранее определенных аспектов клиентской стороны к новым и существующим элементам ASP.NET. Аспекты поведения (behaviors) реализуются в виде блоков JavaScript, наделяющие целевые элементы ASP.NET новыми возможностями. Расширитель фактически представляет собой серверный элемент, который генерирует сценарный код для расширения поведения элемента ASP.NET на стороне клиента. Расширитель представляет общее поведение, а не специфическую функцию конкретного элемента; как следствие, он может применяться к различным типам целевых элементов. В комплект поставки Atlas входит набор готовых расширителей и аспектов поведения. Некоторые расширители входят в базовые сборки Atlas; для других требуется дополнительная сборка ASP.NET AJAX Control Toolkit (также называемая Atlas Control Toolkit или ACT). Тем не менее следует учесть, что в оконча-


ASP.NET AJAX Control Toolkit

111

тельной версии ситуация может измениться, и все расширители войдут в двоичные файлы Atlas. Наконец, если ни один из базовых расширителей не предоставляет нужной функции, вы всегда можете написать собственный расширитель и определить новые аспекты поведения для серверных элементов. Базовые и пользовательские расширители элементов ASP.NET позволяют автору внедрить в клиентскую страницу новые аспекты поведения без написания кода XML Script или JavaScript. В этой главе приводится краткий обзор характеристик, способов использования и реализаций расширителей элементов.

ASP.NET AJAX Control Toolkit Как уже говорилось выше, в настоящее время большинство расширителей элементов откомпилировано в сборку, которая называется ASP.NET AJAX Control Toolkit или Atlas Control Toolkit (ACT). Эта сборка загружается отдельно и регистрируется во всех приложениях, с которыми вы собираетесь ее использовать. Скорее всего, в будущем содержимое сборки будет интегрировано с базовыми двоичными файлами Atlas.

Рис. 4 . 1 . Пример веб-сайта, демонстрирующий возможности ACT


112

Глава 4. Элементы Microsoft ASP.NET AJAX

Настройка среды Чтобы загрузить новейшую версию ACT, посетите сайт http://atlas.asp.net и загляните в секцию Download. Инструментарий распространяется в виде файла Microsoft Windows Installer, поэтому после загрузки остается дважды щелкнуть на нем, чтобы установить компоненты ACT.

Проект ACT После завершения установки на жестком диске появляется проект Microsoft Visual Studio 2005 ASP.NET. Запустив проект, вы увидите страницу, показанную на рис. 4.1. Ознакомившись с файлами проекта, вы увидите, что ACT представляет собой набор элементов и расширителей Atlas, а также найдете многочисленные примеры, демонстрирующие их возможности. Кроме того, ACT содержит средства разработки, упрощающие создание и повторное использование ваших собственных элементов и расширителей. В пакет входят шаблоны Visual Studio 2005 для Microsoft Visual Basic и C#, а также библиотека вспомогательных классов, упрощающих создание элементов и расширителей.

Добавление компонентов ACT на панель элементов Итак, ACT включает ряд элементов и расширителей, которые могут использоваться при создании страниц ASP.NET. Эти компоненты удобнее всего разместить на панели элементов (toolbox) Visual Studio 2005, чтобы разработчик мог легко выбрать нужный компонент и вставить его в текущую страницу. Чтобы добавить компонент ACT на панель элементов, сначала создайте на панели новую вкладку (щелкните правой кнопкой мыши на панели и выберите в контекстном меню команду Add Tab). После создания новой вкладке можно присвоить любое имя — например, Atlas Control Toolkit. Затем вкладка заполняется всеми элементами сборки ACT; для этого следует щелкнуть правой кнопкой мыши на панели элементов ниже созданной вкладки и выбрать команду Choose Items. На экране появляется стандартное диалоговое окно со сборками N E T и компонентами COM. Откройте папку, в которую был установлен пакет ACT, и выберите сборку AtlasControlToolkit из папки Bin папки SampleWebsite. Все! Когда это будет сделано, панель элементов будет выглядеть так, как показано на рис. 4.2. ПРИМЕЧАНИЕ

В этой главе я разделяю серверные элементы и расширители ACT. Строго говоря, четкого различия между ними не существует. В ACT входит ряд новых серверных элементов (таких, как ReorderList) и гораздо большее количество расширителей — DragPanel, DropShadow и TextBoxWatermark. Чем элементы отличаются от расширителей? Элемент предоставляет замкнутый набор функций и хорошо известное, фиксированное поведение, которое может настраиваться только на уровне свойств и событий. Расширитель представляет поведение клиентской стороны, которое может быть связано практически с любым элементом, для которого оно имеет смысл. Например, расширитель DropShadow можно связать как с кнопкой, так и с текстовым полем, и в обоих случаях целевой элемент будет воспроизводиться с отбрасываемой тенью. Несмотря на логические различия, серверные элементы и расширители реализуются по одному принципу — в конечном счете, и те и другие являются серверными элементами ASP.NET.


ASP.NET AJAX Control Toolkit

113

Рис. 4 . 2 . Новая вкладка с компонентами ACT на панели элементов Visual Studio 2005

Регистрация компонентов ACT в странице Несмотря на то что пакет ACT является «родной» частью прикладной среды Atlas и будет интегрирован в следующую версию платформы ASP.NET, на данный момент это всего лишь внешняя библиотека. По этой причине пакет ACT необходимо регистрировать в каждой без исключения странице, использующей какиелибо из его элементов или расширителей. Регистрация осуществляется директивой ©Register, как показано в следующем фрагменте: <%$ Register Assembly="AtlasControlToolkit" Namespace="AtlasControlToolkit" TagPrefix="act" %>

Если вам не нравится дублирование одного фрагмента кода во всех страницах приложения, воспользуйтесь приемом, который использовался группой разработки ASP.NET для решения подобной проблемы с элементами Atlas. Стоит заметить, что базовая библиотека Atlas фактически также является внешней библиотекой для платформы ASP.NET, и поэтому для нее также необходима


114

Глава 4. Элементы Microsoft ASP.NET AJAX

явная регистрация на каждой странице. В ASP.NET 2.0 для включения директивы ©Register во все страницы приложения можно воспользоваться следующим конфигурационным сценарием: <pages> <controls> <add namespace="Microsoft.Web.Ill" assembly="Microsoft.Web.Atlas" tagPrefix="atlas" /> <add namespace="Microsoft.Web.UI.Controls" assembly="Microsoft.Web.Atlas" tagPrefix="atlas"/> </controls> </pages>

Все элементы Microsoft.Web.UI и Microsoft.Web.UI.Controls автоматически ассоциируются с префиксом atlas и не требуют явного включения директивы ©Register во все страницы, в которых они используются. Для элементов ACT в файл Web. config также можно включить аналогичный блок кода: <pages> <controls> <add namespace="AtlasControlToolkit" assembly="AtlasControlToolkit" tagPrefix="act"/> </controls> </pages>

Содержимое ASP.NET AJAX Control Toolkit ACT — библиотека, предназначенная для расширения возможностей прикладной среды Atlas. Входящие в нее элементы могут измениться в будущем, их функциональность может быть расширена или урезана. На момент написания книги ACT содержит достаточно большой набор расширителей и элементов, хотя, конечно, они не решают все возможные проблемы, возникающие при разработке.

Расширители в ACT В табл. 4.1 перечислены расширители элементов из библиотеки ACT. Обратите внимание: полное имя класса расширителя содержит суффикс Extender, который в таблице для краткости не указывается. Так, в сборке ACT нет компонента CollapsiblePanel, но есть элемент CollapsiblePanelExtender и т. д. Таблица 4.1. Расширители в ACT Расширитель

Описание

AlwaysVisibleControl

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


ASP.NET AJAX Control Toolkit

115

Расширитель

Описание

CascadingDropDown

Связывается с элементом DropDownList. Расширитель автоматически заполняет список данными, полученными от метода веб-службы. Он удобен при создании иерархий раскрывающихся списков, в которых расширитель автоматически заполняет дочерние списки в зависимости от текущего состояния выделения в предыдущих списках иерархии

CollapsiblePanel

Создает на веб-странице сворачиваемые секции. Расширитель может использоваться только с панельными элементами (то есть с элементом ASP.NET Panel или любого класса, производного от него). Расширителю передается информация о том, какая панель страницы выполняет функции заголовка, а какая панель предоставляет содержимое для свертки

ConfirmButton

Связывается с кнопкой. Расширитель добавляет к событию click кнопки диалоговое окно подтверждения, реализованное на JavaScript. Расширитель поддерживается всеми классами, реализующими интерфейс IButtonControl, включая Button, LinkButton и ImageButton

DynamicPopulate

Обновляет содержимое элемента результатом вызова метода страницы или веб-службы

DragPanel

Связывается с панелью. Расширитель реализует функцию перетаскивания и позволяет перемещать панель по странице. Разработчик задает перемещаемое содержимое, а также манипулятор, при нажатии которого активизируется операция перетаскивания

DropShadow

Добавляет тень к любому элементу на странице. Расширитель позволяет задать степень прозрачности и ширину тени

FilteredTextBox

Связывается с текстовым полем. Расширитель блокирует ввод недопустимых символов

HoverMenu

Отображает содержимое ассоциированной панели, когда указатель мыши задерживается над заданным элементом. Расширитель можно связать с любым элементом ASP.NET. В сущности, это специализированное и в высший степени гибкое средство реализации всплывающих подсказок

ModalPopup

Связывается с элементом, способным инициировать клиентское событие onclick (как правило, кнопки и гиперссылки). Расширитель реализует классическое модальное диалоговое окно средствами HTML. Он отображает содержимое заданной панели, не позволяя пользователю взаимодействовать со страницей

NumericUpDown

Связывается с текстовым полем. Расширитель позволяет переходить к следующему или предыдущему значению поля, щелкая на автоматически отображаемых кнопках. Поддерживается работа с числами, пользовательскими списками и методами веб-служб продолжение ■&


116

Глава 4. Элементы Microsoft ASP.NET AJAX

Таблица 4.1 (продолжение)

Расширитель PagingBulletedList

Описание Связывается с элементами BulletedList. Расширитель группирует все элементы, связанные со списком, и упорядочивает их в отсортированные страницы на стороне клиента

PasswordStrength

Связывается с текстовым полем, предназначенным для ввода пароля, и обеспечивает визуальную обратную связь Преобразует содержимое заданной панели во всплывающее окно без использования диалоговых окон HTML. Расширитель может быть связан с любым элементом, инициирующим любые из следующих событий клиентской стороны: onfocus, onclick, onkeydown

PopupControl

ResizableControl

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

RoundedCorners

Добавляет фоновую панель к любому элементу ASP.NET, в результате чего возникает иллюзия скругления углов исходного элемента. При этом общая высота исходного элемента слегка изменяется

TextBoxWatermark

ToggleButton

Связывается с элементами TextBox. Расширитель помещает в поле текст-подсказку, поясняющую, какие данные должны вводиться в этом поле (например, «Введите свое имя»). Подсказка исчезает, как только пользователь начинает вводить текст, и снова появляется, когда текстовое поле остается пустым Связывается с элементами CheckBox. Расширитель позволяет использовать нестандартные изображения при выводе флажков. Например, можно использовать разные изображения для установленных и сброшенных флажков

Как нетрудно предположить, для одних расширителей необходима специальная поддержка со стороны браузера, а другие представляют собой обычный фрагмент кода JavaScript, присоединенный к блоку элементов разметки. Помните, что все функции являются кросс-платформенными, хотя в сопроводительной документации к выпуску ACT упоминается о возможных проблемах с браузером Safari (из-за известных ошибок в его подсистеме разбора JavaScript). Для трех самых популярных браузеров (Microsoft Internet Explorer, Firefox и Netscape) пока никаких проблем не выявлено. Мы вернемся к расширителям позднее, с примерами кода и более подробными описаниями.

Элементы ACT Наряду с расширителями, перечисленными в табл. 4.1, ACT также поддерживает несколько традиционных серверных элементов с расширенными возможностями - Accordion, Rating и ReorderList. Элемент Accordion позволяет создать несколько свертываемых панелей и отображать только одну из них. Когда пользователь щелкает на новой панели, текущая панель сворачивается и освобождает место для вывода новой панели.


Элемент Accordion

117

Элемент Rating обеспечивает интуитивно понятный интерфейс для формирования рейтинговых оценок, выраженных в количестве звездочек. Элемент представляет собой упакованную версию пользовательского интерфейса, используемого на некоторых веб-сайтах для оценки публикуемых материалов. Элемент ReorderList ориентирован на работу с данными. Он позволяет изменять порядок своих дочерних элементов посредством перетаскивания. Чтобы переместить объект в списке, пользователь захватывает мышью его манипулятор и перетаскивает в нужную позицию. В конце операции элемент выдает обращение к серверу для сохранения нового состояния источника данных. Сначала мы рассмотрим новые серверные элементы, а затем перейдем к расширителям.

Элемент Accordion Свертываемые панели часто встречаются на многих современных, передовых сайтах. На странице выводится короткий заголовок, а основной текст остается скрытым и вызывается в случае необходимости. Расширитель CollapsiblePanel (см. далее) позволяет скрывать и отображать произвольные блоки разметки. Но что делать, если понадобилось создать иерархию панелей? С помощью элемента Accordion можно сгруппировать несколько свертываемых панелей в один элемент и управлять состоянием каждой панели так, чтобы в любой момент времени могла быть развернута только одна панель.

Общие сведения об элементе Accordion Элемент Accordion содержит коллекцию дочерних элементов AccordionPane, у каждого из которых имеется свойство-шаблон для определения заголовка и содержимого. Каждая дочерняя панель может содержать произвольный код HTML, ASP.NET или Atlas.

Свойства элемента В табл. 4.2 перечислены основные свойства элемента Accordion. Таблица 4.2. Свойства элемента Accordion

Свойство

Описание

AutoSize

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

ContentCssClass FadeTransitions

FramesPerSecond

Частота смены кадров при анимации перехода к новой дочерней панели. По умолчанию задается равной 30 кадрам в секунду продолжение ■&


118

Глава 4. Элементы Microsoft ASP.NET AJAX

Таблица 4.2 (продолжение) Свойство HeaderCssClass SelectedIndex TransitionDuration

Описание Класс CSS, используемый для определения стилей заголовков дочерних панелей (чтение и запись) Панель, развернутая в данный момент (чтение и запись) Продолжительность переходной анимации в миллисекундах (по умолчанию 250)

Как видите, элемент не обладает визуальными свойствами, кроме базовых свойств ASP.NET, определенных в родительском классе WebControl (BackColor, ForeColor и т. д.). В частности, у него нет стилевого свойства для дочерних панелей. Стили их заголовков и содержимого определяются классами CSS. Если бы элемент работал с использованием обычного возврата данных, то свойства классов CSS, вероятно, были бы заменены объектами Style, как во многих классических элементах ASP.NET.

Анимация элемента Элемент Accordion поддерживает анимацию в двух контекстах. При выборе новой панели содержимое текущей панели может «растворяться». Этот эффект не является обязательным, а его применение определяется логическим свойством FadeTransitions. На небольших панелях он мало заметен. Анимация растворения тесно связана с переходной анимацией медленного развертывания содержимого новой панели. Второй формой анимации управляют свойства TransitionDuration и FramesPerSecond. Если отключить ее, эффект растворения действовать не будет, независимо от состояния свойства FadeTransitions. Чтобы полностью отказаться от анимации и заменить ее быстрой сменой панелей, свойствам FramesPerSecond и TransitionDuration присваивается значение 0. В этом случае свойству FadeTransitions следует задать значение false, чтобы элемент не выполнял лишней работы, которая все равно не приведет к видимым результатам.

Изменение размера элемента Размер элемента Accordion зависит как от элемента-контейнера, так и от содержимого разных панелей. Это означает, что общий размер элемента может изменяться при смене выбранной панели, в результате чего другие элементы страницы могут сдвигаться вверх или вниз. Свойство AutoSize помогает контролировать общий размер элемента Accordion. Допустимые значения свойства объединены в перечисление AutoSize: None (используется по умолчанию), Limit и Edit. Когда свойство AutoSize равно None, размеры элемента свободно изменяются в соответствии с размерами текущей панели. При значении Limit элемент никогда не увеличивается сверх размеров, определяемых свойствами Width и Height. Элемент Accordion наследует эти свойства от базового класса. Если свойству AutoSize задано значение None, свойства Width


Элемент Accordion

119

и Height игнорируются. Если размеры содержимого превышают заданные размеры элемента, пользователь может полностью просмотреть его при помощи полос прокрутки. При значении Fill элемент всегда занимает прямоугольник, определяемый свойствами Width и Height; содержимое элемента разворачивается или прокручивается по мере необходимости. Развертывание содержимого всего лишь означает увеличение дочерней панели.

Использование элемента Accordion Элемент Accordion состоит из набора дочерних панелей, каждая из которых представляет собой экземпляр класса AccordionPane. Панели добавляются на уровне разметки, а для программного получения коллекции панелей используется метод GetAccordionPanes: public IList<AccordionPane> GetAccordionPanesО

Расширенные возможности (такие, как программное определение панелей и управление видимостью) пока не реализованы.

Элемент Accordion в действии Следующий фрагмент кода демонстрирует использование элемента Accordion: <div style="width:ЗООрх;"> <act:Accordion ID="Accordionl" runat="server" Height="400px" SelectedIndex="0" ContentCssClass="accordionContent" HeaderCssClass="accordionHeader" r adeTransitions="true" AutoSize="Fill"> <act:AccordionPane ID="AccordionPanel" runat="server"> <Header>One</Header> <Content>This is the first pane</Content> </act:AccordionPane> <act:AccordionPane ID="AccordionPane2" runat="server"> <Header>Two</Header> <Content> <div style="height:400px"> This is the second pane</div> </Content> </act:AccordionPane> <act:AccordionPane ID="AccordionPane3" runat="server"> <Header>Three</Header> <Content>This is the third pane</Content> </act:AccordionPane> <act:AccordionPane ID="AccordionPane4" runat="server"> <Header>Four</Header> <Content>This is the fourth pane</Content> </act:AccordionPane> </act:Accordion> </div>


120

Глава 4. Элементы Microsoft ASP.NET AJAX

Каждая свертываемая панель, включаемая в элемент Accordion, представлена тегом <act:AccordionPane>. Определение панели состоит из шаблонов <header> и <content>. В приведенном примере элемент Accordion встроен в тег <div> с фиксированной шириной. Внешний контейнер определяет ширину и высоту элемента. Свойство AutoSize=Fill заставляет элемент заполнять весь ограничивающий прямоугольник. Результат показан на рис. 4.3.

Рис. 4.3. Элемент Accordion в действии

Обратите внимание: содержимому второй панели назначена высота в 400 пикселов, однако сам элемент Accordion не может быть выше 400 пикселов и не может бесконтрольно увеличиваться, потому что его размеры ограничены свойством AutoSize. В результате для просмотра содержимого, не умещающегося в этих границах, на второй панели отображается полоса прокрутки.


Элемент Rating

121

Панели элемента Accordion Дочерняя панель элемента Accordion представляет собой простой веб-элемент с именем AccordionPane. Свойство Header этого элемента используется для определения заголовка, то есть части панели, которая остается видимой после свертывания. Стиль заголовка задается классом CSS, определяемым свойством HeaderCssClass. Аналогичным образом, содержимое панели определяется свойством Content, а его стиль — классом CSS, определяемым свойством ContentCssClass.

Элемент Rating Интерес пользователей — один из ключевых факторов, определяющих успех вебсайта. Но как его измерить? Многие сайты предлагают пользователям высказать свое мнение на специальных панелях, находящихся на страницах сайта. Для получения информации от пользователей чаще всего применяется система рейтингов. Пользователю предлагается выбрать то количество звездочек, которым он бы оценил некоторый материал или функцию сайта. Многие разработчики ASP.NET смогут легко реализовать схему рейтингов, работающую на основе классического возврата данных. Элемент Atlas Rating обеспечивает стандартный механизм для работы с рейтингами на основе обратных вызовов.

Общие сведения об элементе Rating Выходной код элемента состоит из повторяющегося тега <span>, оформленного при помощи набора классов CSS. Каждый тег <span> представляет «звездочку» в системе рейтингов, а его стиль задается в соответствии с тем статусом, который он должен представлять. Рисунок 4.4 дает представление о внешнем виде элемента Rating.

Рис. 4.4. Элемент Rating в действии


122

Глава 4. Элементы Microsoft ASP.NET AJAX

Свойства элемента В табл. 4.3 перечислены важнейшие свойства элемента Rating. Таблица 4.3. Свойства элемента Rating Свойство

Описание

CurrentRating

Текущий рейтинг, представляемый элементом (по умолчанию используется значение 3). Свойство доступно для чтения и записи Класс CSS для оформления «пустых» (не выбранных) звездочек. Свойство доступно для чтения и записи

EmptyStarCssClass FilledStarCssClass MaxRating RatingAlign RatingDirection

Класс CSS для оформления «заполненных» (выбранных) звездочек. Свойство доступно для чтения и записи Максимальный рейтинг, который может быть представлен элементом (по умолчанию 5) Тип выравнивания звездочек (по умолчанию используется горизонтальное выравнивание) Направление (ориентация) звездочек. По умолчанию при горизонтальном выравнивании используется направление слева направо, а при вертикальном — сверху вниз

ReadOnly

Свойство указывает, принимает ли элемент пользовательский ввод

StarCssClass

Класс CSS, определяющий стиль всего элемента (чтение и запись)

Tag

Строка, которая передается серверному коду, обрабатывающему щелчки на элементе Класс CSS, используемый при выводе «выбранных» звездочек на время возврата данных сервером после изменения состояния элемента пользователем

WaitingStarCssClass

Разметка, генерируемая элементом Rating, выглядит примерно так: <div id="Ratingl" style="float: 1 eft;"> <span id="Ratingl_Star_l" class="ratingStar filledRatingStar" style="f 1 oat:1 eft;"> </span> <span id="Ratingl_Star_2" class="ratingStar filledRatingStar" style="f1 oat:1 eft;"> </span> </div>

Как видно из листинга, каждая «звездочка» обладает уникальным идентификатором и представляется тегом <span>, содержащим пустую строку. Однако из рис. 4.4 ясно видно, что звездочка представлена графическим изображением. Как такое возможно?

Определение стиля элемента С элементом Rating ассоциируются четыре класса CSS. Класс StarCssClass определяет стиль всего элемента. Классы EmptyStarCssClass и FilledStarCssClass определяют стиль тега <span>. Наконец, WaitingStarCssClass определяет стиль


Элемент Rating

123

заполненных звездочек в то время, пока элемент обращается с обратным вызовом к серверу (обычно заполненные звездочки выводятся желтым цветом, но на время обновления они могут выводиться красным). Для заполнения может использоваться графическое изображение или контрастный цвет фона. Возьмем следующие классы CSS: .filledRatingStar { background-color: #2E4d7B; } .emptyRatingStar { background-image: url(images/NotSelected.png); }

Эффект от их применения показан на втором объекте на рис. 4.4. Выбранная часть ряда выводится в виде закрашенной полосы, а в остальной части элемента рисуются пустые звездочки. Редактируя классы CSS, можно выбрать изображения звездочек, соответствующие ожиданиям пользователей вашего приложения.

Использование элемента Rating Элемент Rating относительно прост в использовании. Он не имеет дочерних элементов или шаблонов, а разработчику достаточно задать значения нескольких свойств (классы CSS).

Элемент Rating в действии Следующий код демонстрирует использование элемента Rating: <h2>Rate this item:</h2> <div> <act:Rating ID="Ratingl" runat="server" CurrentRating="3" MaxRating="10" StarCssClass="ratingStar" WaitingStarCssClass="savedRatingStar" r i11edStarCssClass="fi11edRatingStar" EmptyStarCssClass="emptyRatingStar" OnChanged="Rating_Changed" /> </div>

Свойство MaxRating определяет максимальное количество звездочек. Первые (или последние, в зависимости от выбранного направления) CurrentRating звездочек в этом ряду оформляются с использованием стиля класса, имя которого задается свойством FilledStarCssClass. Стили остальных звездочек определяются атрибутами класса, заданного свойством EmptyStarCssClass. Очень важно, чтобы в свойствах элемента были заданы правильные имена классов CSS. Скажем, если опустить стиль пустых звездочек, соответствующий тег <span> будет выведен без каких-либо графических изменений. Так как в исходном варианте этот тег содержит пустую строку, визуальный вывод для него не генерируется.


124

Глава 4. Элементы Microsoft ASP.NET AJAX

Модель событий Элемент Rating внедряет в поток данных клиентского браузера фрагмент сценарного кода, который фактически решает две задачи: отслеживание перемещений мыши в пределах ограничивающего прямоугольника элемента и обработка щелчков. По мере того как пользователь перемещает мышь над пустыми звездочками, сценарный код автоматически переключает имя класса нижележащего тега <span>. Чтобы изменить текущее значение рейтинга, достаточно щелкнуть на звездочке, соответствующей новому значению. Например, в рейтинговой системе с пятью звездочками щелчок на четвертой звездочке устанавливает рейтинг 4. Когда это происходит, элемент Rating инициирует внеполосный вызов к серверу и выдает событие Changed: protected void Rating_Changed(object sender, RatingEventArgs e) { // Выполнение содержательных действий на стороне сервера // (например, сохранение нового значения в базе данных) }

Класс RatingEventArgs содержит три основных свойства — Tag, Value и CallbackResult. Свойство Tag передает произвольную строку от клиента серверу. Свойство Value обозначает текущее значение, выбранное в элементе. В свойстве CallbackResult передается любая дополнительная информация, которую сервер возвращает клиенту. Например, если событие Changed используется для сохранения рейтинга в базе данных, в строке CallbackResult может передаваться сообщение об ошибке в том случае, если при выполнении операции произошел сбой.

Элемент Reorder List Содержимое баз данных, представленное в виде списка, часто выводится на вебстраницах. Обычно такие списки доступны только для чтения, то есть не могут напрямую изменяться пользователем. Тем не менее в некоторых ситуациях может потребоваться переупорядочить содержимое списка. Представьте страницу, на которой пользователь выбирает несколько городов и получает информацию о текущей погоде. Города перечислены на странице в определенном порядке, но в какой-то момент пользователь захотел этот порядок изменить. Конечно, разработчик может разместить на странице кнопку Move Up; чтобы в списке из 4 городов переместить последний город на первое место, придется трижды щелкнуть на кнопке. Такое решение легко программируется, но удобным его не назовешь. Более естественно было бы дать пользователю возможность выделить нужный город и перетащить его в нужное положение. Именно эту задачу и решает элемент ReorderList. Давайте познакомимся поближе с этим новым элементом ASP.NET Atlas.

Общие сведения об элементе ReorderList Содержимое элемента ReorderList задается серией шаблонов, описывающих структуру каждого объекта в списке, пользовательский интерфейс манипулятора перетаскивания и пользовательский интерфейс точки вставки, в которую перемещается


Элемент ReorderList

125

перетаскиваемый объект. Впрочем, в программном интерфейсе элемента участвует ряд других свойств и событий.

Свойства элемента В табл. 4.4 п е р е ч и с л е н ы основные свойства элемента ReorderList. В частности, в список включены свойства, значения которых могут задаваться атрибутами на веб -страницах A S P . N E T . Элемент я в л я е т с я п р о и з в о д н ы м от DataBoundControl и реализует интерфейс IRepeatlnfollser — типичный для элементов, поддерживающих разнообразные варианты выравнивания и структуры (таких, как CheckBoxList). Базовый класс и интерфейс содержат ряд дополнительных свойств, относящихся к привязке данных и макету элемента. Таблица 4.4. Свойства элемента ReorderList Свойство

Описание

AllowReorder

Указывает, поддерживает ли элемент изменения порядка объектов перетаскиванием. Свойство автоматически задается равным true при задании шаблона ReorderTemplate

DataKeyField

Имя поля источника данных, выполняющего функции первичного ключа

DataSourceID

Идентификатор элемента-источника данных, используемого для заполнения элемента ReorderList

DragHandleAlignment

Положение манипулятора относительно перетаскиваемого объекта. Допустимые значения: Top, Left, Bottom и Right

DragHandleTemplate

Шаблон манипулятора, используемого для перетаскивания и упорядочения содержимого списка

EditItemTemplate

Шаблон, указывающий, что запись находится в режиме редактирования

EmptyListTemplate

Шаблон списка, не содержащего данных

InsertItemTemplate

Шаблон, используемый для добавления новых объектов в список

ItemInsertLocation

Указывает позицию для размещения нового элемента, когда свойство InsertItemTemplate используется для включения нового объекта в список. Допустимые значения — Beginning и End

ItemTemplate

Шаблон для объектов списка

ReorderTemplate

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

SortOrderField

Поле, определяющее порядок сортировки объектов

Важнейшие свойства элемента — ItemTemplate, DragHandleTemplate и ReorderTemplate. Свойство ItemTemplate позволяет заполнить список любыми данными и макетом по вашему усмотрению. DragHandleTemplate определяет графические элементы, н е о б х о д и м ы е п о л ь з о в а т е л я м д л я п е р е т а с к и в а н и я объектов списка. Наконец, ReorderTemplate определяет шаблон визуальной обратной связи, отображаемый в ходе операции.


126

Глава 4. Элементы Microsoft ASP.NET AJAX

События элемента Элемент ReorderList связывается с данными, а его расширенный пользовательский интерфейс в основном формируется из шаблонов. Это означает, что элемент может содержать любое количество дочерних элементов, способных инициировать возврат данных и изменять содержимое элемента. Соответственно не приходится удивляться тому, что с элементом связываются многочисленные события, перечисленные в табл. 4.5. Таблица 4.5. События элемента ReorderList

Событие

Описание

CancelCommand

Происходит при щелчке на кнопке со свойством CommandName=«Cancel» в пределах элемента Происходит при щелчке на кнопке со свойством CommandName=«Delete» в пределах элемента Происходит при щелчке на кнопке со свойством CommandName=«Edit» в пределах элемента Происходит при щелчке на кнопке со свойством CommandName=«Insert» в пределах элемента

DeleteCommand EditCommand InsertCommand ItemCommand

Происходит при щелчке на кнопке в шаблоне объекта

ItemCreated

Происходит при создании объекта в списке

ItemDataBound

Происходит при связывании объекта списка с данными

ItemReorder

Происходит при перемещении объекта в новую позицию в конце операции переупорядочения списка Происходит при щелчке на кнопке со свойством CommandName=«Update» в пределах элемента

UpdateCommand

Большинство командных событий относится к возможностям связанного источника данных. Например, событие InsertCommand происходит в том случае, если элемент ReorderList содержит шаблон с элементами для сохранения данных и вызывает команду Insert для связанного элемента-источника данных. Простой пример: <act:ReorderList ...> <InsertItemTemplate> <div> <asp:TextBox ID="TextBoxl" runat="server" Text='<^# BindC'Title") £>'></asp:TextBox> <asp:LinkButton ID="LinkButtonl" runat="server" CommandName="Insert">Add</asp:LinkButton> </div> </InsertItemTemplate> </act:ReorderList>

По щелчку на кнопке элемент ReorderList вызывает команду Insert для связанного источника данных (если он имеется). Новый объект со свойством Title


Элемент ReorderList

127

добавляется в начало или в конец источника данных в зависимости от значения свойства ItemlnsertLocation. Событие ItemReorder происходит на сервере перед отсылкой на сторону клиента нового элемента с новым порядком объектов. С событием передается структура ReorderListltemReorderEventArgs: public class ReorderListltemReorderEventArgs : EventArgs { public ReorderListltem Item { get; set; } public int Newlndex { get; set; } public int Oldlndex { get; set; } }

Свойство Item определяет перемещаемый объект, а свойства Newlndex и OldIndex — новую и старую позиции соответственно (индексы позиций начинаются с 0).

Использование элемента ReorderList Рассмотрим простую страницу, на которой используется элемент ReorderList. Как вскоре будет показано, элемент ReorderList связывается с источником данных и встраивается в элемент UpdatePanel.

Настройка элемента ReorderList В следующем фрагменте представлен пример простого переупорядочиваемого списка, связанного с элементом ObjectDataSource: <act:ReorderList runat="server" ID="list" DataSourceID="ObjectDataSourcel"> <ItemTemplate> <asp:Label ID="Labell" runat="server" Text='<^# Eval("lastname") %>' /> </ItemTemplate> <ReorderTemplate> <asp:Panel ID="Panel2" runat="server" CssClass="reorderCue" /> </ReorderTemplate> </act:ReorderList> <asp:ObjectDataSource ID="ObjectDataSourcel" runat="server" TypeName="IntroAtlas.EmployeeManager" SelectMethod="LoadAl I"> </asp:ObjectDataSource>

В данном примере свойство DragHandleTemplate не задано; это означает, что в его пользовательском интерфейсе отсутствует визуальный манипулятор для перетаскивания. Содержимое элемента выглядит как маркированный список. Чтобы изменить порядок следования пунктов списка, пользователь щелкает и перетаскивает текст рядом с маркером (рис. 4.5). Из рисунка видно, что перемещаемый элемент отображается с применением отдельного стиля. В общем случае при этом используется шаблон, заданный свойством ReorderTemplate, состоящий из текста объекта и каскадного стиля. Если шаблон отсутствует, но для списка разрешено переупорядочение (логическое свойство AllowReorder), то в качестве визуального признака выполняемой операции используется текст объекта, выведенный серым цветом.


128

Глава 4. Элементы Microsoft ASP.NET AJAX

Рис. 4.5. Перестановка содержимого в элементе ReorderList

Построить список с возможностью переупорядочения объектов совсем несложно. Тем не менее разработчик должен учесть пару важных аспектов.

Переупорядочение и события возврата данных В конце каждой операции перетаскивания элемент ReorderList производит возврат данных. Чтобы избежать полного обновления страницы, элемент ReorderList следует разместить в элементе UpdatePanel. Как объяснялось в главе 3, таким образом гарантируется обновление только пользовательского интерфейса списка (вместо полного обновления всей страницы). Задача решается несколькими строками кода разметки: <atlas:UpdatePanel runat="server" ID="UpdatePanell"> <ContentTemplate> <!-- Здесь размещается ReorderList --> </ContentTemplate> </atlas:UpdatePanel>

Почему элемент инициирует собственный возврат данных? ReorderList пытается сохранить изменения, внесенные пользователем, чтобы при любом другом возврате данных из страницы (скажем, вызванном щелчком на кнопке) поддерживалось измененное состояние ReorderList, полученное в результате перетаскивания. Любые изменения, вносимые на стороне клиента, должны сохраняться на сервере; в противном случае они будут потеряны при первом же обновлении страницы.


Серверные расширители Atlas

129

Сохранение измененного порядка Запустив любую страницу с элементом ReorderList, вы заметите, что через несколько секунд после перетаскивания объекта в новую позицию он восстанавливается в исходной позиции. Почему это происходит? В конце операции перетаскивания элемент ReorderList производит возврат данных, в результате на сервере инициируется событие ItemReorder, а список строится заново. Но список связан с данными, и если в процессе связывания ничего не изменилось, он будет связан с прежним набором записей, соответствующим старому порядку. Соответственно, страница с элементом ReorderList должна обработать событие ItemReorder и позаботиться о том, чтобы изменения на стороне клиента были отражены в источнике данных, связанном с элементом. Событие ItemReorder обладает следующей сигнатурой: void OnItemReorder(object sender, ReorderListltemReorderEventArgs e)

В обработчике события выполняется «реальная» работа по переупорядочению, то есть объект перемещается из старой позиции в новую позицию, используемую для заполнения списка. Реализация зависит от механизма связывания. Если был выбран вариант с элементом-источником данных (например, ObjectDataSource), можно попробовать сортировку по полю, отражающему новый порядок. Если источник данных задается присваиванием объекта lEnumerable свойству DataSource, получите объект и внесите необходимые изменения. Например, для DataTable это означает перестановку двух записей. ПРИМЕЧАНИЕ

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

Серверные расширители Atlas Кроме полноценных серверных элементов вроде ReorderList и Accordion, ACT также включает подборку серверных элементов, предназначенных для расширения существующих элементов страницы и наделения их новыми аспектами поведения. Такие компоненты называются расширителями элементов (control extenders) или просто расширителями.

Что такое расширитель? Расширитель представляет собой часть функциональности клиентской стороны, которую вы можете добавить к серверному элементу ASP.NET. Серверный элемент создает собственную разметку, а расширитель генерирует фрагмент кода JavaScript, специально адаптированный для работы поверх элемента. Расширитель представляет функциональность общего назначения, которая теоретически может совместно использоваться несколькими элементами ASP.NET. Классический пример — тень, отбрасываемая элементом. Фрагмент кода JavaScript


130

Глава 4. Элементы Microsoft ASP.NET AJAX

создает иллюзию тени для заданного элемента ASP.NET. Исходному элементу о выводе тени ничего не известно, но установленная на стадии выполнения связь между расширителем и элементом решает задачу.

Общие сведения об аспектах поведения Atlas Расширитель представляет собой серверную «обертку» для кода JavaScript, определяющего клиентский аспект поведения Atlas. Расширитель задает конфигурацию и генерирует код JavaScript, характеризующий требуемый аспект поведения Atlas. Аспект поведения Atlas представляет собой файл .js с определением функции JavaScript. Функция может регистрировать обработчики для событий клиентской стороны и изменять DOM элементов разметки, с которыми она связана. Чтобы лучше понять цели и характеристики аспектов поведения Atlas, давайте в общих чертах рассмотрим поведение, инкапсулированное в расширителе TextBoxWatermark. Этот расширитель управляет выводом подсказки — справочного текста, который отображается в пустом текстовом поле. Подсказка автоматически удаляется, когда пользователь начинает вводить данные в поле, и не используется при отправке данных. Если пользователь полностью удаляет текст из поля, подсказка автоматически восстанавливается. В реализации подсказок задействованы три события HTML — onfocus, onblur и onkeypress. На стадии инициализации аспект поведения подсказки также задает новый стиль и текст по умолчанию для целевого текстового поля, если оно не содержит данных. Когда текстовое поле получает фокус ввода, обработчик события удаляет текст подсказки и восстанавливает исходный стиль. Во время ввода данных обработчик onkeypress обеспечивает вывод подсказки в случае необходимости. Наконец, когда текстовое поле теряет фокус (событие onblur), обработчик возвращает подсказку, если содержимое поля представляет собой пустую строку. Чтобы связать это поведение с элементом ASP.NET ТехШох, вам потребуется расширитель. Впрочем, для специалистов по XML Script и JavaScript существует альтернативный вариант — для достижения тех же результатов можно написать фрагмент клиентского кода. ПРИМЕЧАНИЕ

Концепция аспектов поведения Atlas близка к концепции аспектов поведения DHTML (Dynamic HTML). Аспекты DHTML, впервые появившиеся в Internet Explorer, представляли собой сценарный файл (или откомпилированный объект COM), который обрабатывал события HTML и модифицировал DOM тега HTML для реализации желаемого поведения. Аспекты поведения DHTML использовались для расширения возможностей отдельных тегов HTML. Аспекты поведения Atlas действуют более универсально: они расширяют возможности блока разметки, сгенерированного отдельным элементом ASP.NET, который может содержать один или несколько тегов HTML. Кроме того, расширитель Atlas не ограничивается Internet Explorer и работает в разных браузерах.


Серверные расширители Atlas

131

Целевые свойства Расширители обладают набором свойств для настройки поведения. Класс расширителя наследует от обобщенного класса с именем ExtenderControlBase: public class TextBoxWatermarkExtender : ExtenderControlBase<TextBoxWatermarkProperties, TextBox> { }

Класс расширителя должен получать «извне» два ключевых параметра: коллекцию используемых свойств (целевые свойства) и класс элемента, к которому он применяется. В следующем примере класс TextBoxWatermarkProperties предоставляется системой и группирует все свойства, используемые при расширении функциональности целевого элемента. Класс, представляющий целевые свойства, включает общее свойство с именем TargetControllD. Свойство определяет элемент страницы, к которому применяется расширитель с заданной конфигурацией. Как правило, на странице размещается один расширитель определенного типа, снабженный одним или несколькими наборами свойств — по одному для каждого целевого элемента, как в следующем примере: <act:TextBoxWatermarkExtender ID="Watermarkl" runat="server"> <act:TextBoxWatermarkProperties TargetControlID="TextBoxl" WatermarkText=" ... " WatermarkCssClass=" ... " /> <act:TextBoxWatermarkProperties TargetControlID="TextBox2" WatermarkText=" ... " WatermarkCssClass=" ... " /> </act:TextBoxWatermarkExtender>

Вооружившись этой информацией, перейдем к описанию программного интерфейса расширителей ACT и базовых библиотек Atlas. Расширители будут сгруппированы в соответствии с возможностями их целевых элементов.

Привязка данных Свойства компонентов-расширителей также могут инициализироваться посредством декларативной привязки данных, по аналогии со свойствами классических серверных элементов ASP.NET. <act:TextBoxWatermarkExtender ID="Watermarkl" runat="server"> <act:TextBoxWatermarkProperties TargetControlID="TextBoxl" WatermarkText='<^# Eval("SomeColumn") %>' /> </actToolkit:TextBoxWatermarkExtender>

В этом фрагменте продемонстрировано заполнение свойства WatermarkText расширителя TextBoxWatermark содержимым заданного столбца в связанном источнике данных. Для выполнения привязки используется метод Eval. Но чтобы этот способ работал, целевой элемент — текстовое поле TextBoxl в данном случае — должен быть встроен в контейнер, связанный с источником данных.


132

Глава 4. Элементы Microsoft ASP.NET AJAX

Например, декларативная привязка будет работать при использовании расширенного элемента внутри шаблона. ПРИМЕЧАНИЕ

К числу интересных особенностей расширителей относится управление клиентским состоянием. Чем больше сложных и интерактивных функций реализуется на стороне клиента, тем внимательнее нужно следить за тем, чтобы действия пользователя не были потеряны при полном обновлении страницы посредством классического возврата данных ASP.NET. Не забывайте, что страницы Atlas, прежде всего, являются страницами ASP.NET; следовательно, они могут содержать обычные кнопки отправки, которые инициируют возврат данных (такие же, как в любой классической странице, никак не связанной с Atlas). При обработке возврата элементы генерируются на полученной странице с учетом их текущего состояния. По этой причине некоторые расширители сохраняют критическую информацию состояния на стороне клиента так, чтобы эта информация не терялась между обновлениями. Информация сохраняется в строке, называемой клиентским состоянием, а для ее хранения используется скрытое поле. Получение и сохранение клиентского состояния осуществляется через интерфейс JavaScript. В классах целевых свойств также существует серверный программный интерфейс для чтения клиентского состояния. Например, элемент Accordion использует строку клиентского состояния для хранения индекса текущей панели. Клиентское состояние не отражается на использовании элементов и расширителей ACT, но о нем необходимо знать при написании пользовательских элементов и расширителей.

Расширители панелей На страницах ASP.NET часто встречаются блоки разметки, которые пользователи могут перемещать, сворачивать или разворачивать по мере надобности. Следовательно, идеальный панельный элемент ASP.NET должен поддерживать как перетаскивание, так и свертку. Специально для этой цели в Atlas определены два серверных аспекта поведения, которые позволяют легко создавать сворачиваемые секции и перетаскивать панели по странице.

Расширитель DragPanel Расширитель DragPanel принадлежит к числу простейших расширителей Atlas. Он обладает всего двумя целевыми свойствами; первое определяет перетаскиваемую панель, а второе — панель, которая должна использоваться в качестве манипулятора перетаскивания: <act:DragPanelExtender ID="DragPanelExtenderl" runat="server"> <act:DragPanelProperties TargetControlID="CustomerPanel" DragHandleID="CustomersDragHandle" /> </act:DragPanelExtender>

Как подсказывает само имя, свойство TargetControllD определяет идентификатор перемещаемого панельного элемента страницы. С другой стороны, свойство DragHandlelD определяет идентификатор панельного элемента, используемого в качестве манипулятора перетаскивания. Другими словами, чтобы перетащить


Серверные расширители Atlas

133

целевую панель, пользователь захватывает и перетаскивает панель-манипулятор. Несмотря на функциональные различия, эти две панели логически связаны и генерируются с использованием вложенных тегов: <asp:Panel ID="CustomersPanel" runat="server" > <asp:Panel ID="CustomersDragHandle" runat="server"> <div style="background-color:yellow">Customers</div> </asp:Panel> <asp:Panel runat="server"> <asp:gridview runat="server" DataSourceID="ObjectDataSource1"> <Columns> … </Columns> </asp:gridview> <asp:ObjectDataSource ID="ObjectDataSource2" runat="server" TypeName="IntroAtlas.CustomerManager" SelectMethod="LoadAll"> </asp:ObjectDataSource> </asp:Panel> </asp:Panel>

Целевая панель обычно содержит панель манипулятора как дочерний элемент. Таким образом достигается эффект перемещения всей панели так, как если бы она была традиционным окном Microsoft Windows (рис. 4.6).

Рис. 4.6. Расширитель DragPanel в действии

Расширитель CollapsiblePanel Расширитель создает на странице сворачиваемую секцию, объединяя две панели — панель содержимого и контроллер сворачивания. В простейшей форме расширитель CollapsiblePanel выглядит примерно так: <act:CollapsiblePanelExtender ID="CollPanel" runat="server"> <act:Col Iapsi Ы ePanelProperties TargetControlID="ContentPanel"


134

Глава 4. Элементы Microsoft ASP.NET AJAX

ExpandControlID="HeaderPanel" CollapseControlID="HeaderPanel" </act:Col 1apsi Ы ePanelExtenders

Как обычно, свойство TargetControllD задает целевую панель. Свойства ExpandControllD и CollapseControllD задают панели, используемые при развертывании или свертывании панели содержимого. Обратите внимание на исключительную гибкость архитектуры компонента — она позволяет использовать разные панели для управления развертыванием и свертыванием панели содержимого. Вероятно, на практике вы будете обычно использовать одну панель заголовка с графической кнопкой, изменяющейся в соответствии с состоянием панели содержимого. В следующем фрагменте кода представлен более полный пример использования расширителя: <act:CollapsiblePanelExtender ID="cpe" runat="server"> <act:Col 1apsi Ы ePanelProperties TargetControlID="Col1apsi Ы eCustomersPanelContent" ExpandControlID="Col1apsi Ы eCustomersPanel" CollapseControlID="CollapsibleCustomersPanel" Collapsed="true" ExpandDirection="Vertical" ImageControlID="ToggleImage" Expandedlmage="~/images/collapse.jpg" ExpandedText="Col1 apse" Collapsedlmage="~/images/expand.jpg" CollapsedText="Expand" /> </act:Col 1apsi Ы ePanelExtender>

Необязательное свойство ImageControllD определяет элемент Image, щелчок на котором приводит к сворачиванию или разворачиванию панели. Свойства Expandedlmage и Collapsedlmage задают URL изображений, используемых для разворачивания и сворачивания. Свойства CollapsedText и ExpandedText задают текст всплывающих подсказок для изображений. Свойство Collapsed задает состояние панели, а свойство ExpandDirection — направление разворачивания (горизонтальное или вертикальное). На рис. 4.7 показано, как выглядит элемент в действии. В следующем примере продемонстрировано использование типичной пары элементов Panel с расширителем: <asp:Panel ID="CollapsibleCustomersPanel" runat="server"> <asp:Image ID="ToggleImage" runat="server" Imagellrl="-7 images/collapse.jpg" /> <t»Customers</t» </asp:Panel> <asp:Panel ID="Col1apsibleCustomersPanelContent" runat="server" Height="0" CssClass="collapsePanel"> </asp:Panel>

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


Серверные расширители Atlas

135

Рис. 4.7. Расширитель CollapsiblePanel в действии ПРИМЕЧАНИЕ

Чтобы избежать мерцания при отображении сворачиваемой панели, проследите за тем, чтобы для нее были правильно определены стили. Свойство Height должно быть равно 0, а стиль CSS overflow должен быть равен hidden.

Расширители кнопок Несомненно, кнопки принадлежат к числу самых распространенных элементов страниц ASP.NET. Но по мере расширения функциональности страниц возникает потребность в дополнительных функциях кнопок, чтобы кнопки не отставали от ожиданий пользователя. В Atlas входят два расширителя, применяемых к кнопкам отправки данных и псевдокнопкам, образующим элемент CheckBox.

Расширитель ConfirmButton Когда пользователь щелкает на кнопке, иногда бывает безопаснее запросить у него подтверждения на выполнение операции. В стандартном варианте реализации этого поведения с кнопкой на странице ASP.NET связывается сценарный код, который вызывает диалоговое окно JavaScript с подтверждающим сообщением. Расширитель ConfirmButton значительно упрощает эту стандартную задачу и позволяет решать ее на декларативном уровне: <asp:Button runat="server" ID="Buttonl" Text="Click me" /> <act:ConfirmButtonExtender ID="cbe" runat="server"> <act:ConfirrnButtonProperties


136

Глава 4. Элементы Microsoft ASP.NET AJAX

TargetControlID="Button1" ConfirmText="Are you sure you want to click this?" /> </act:ConfirmButtonExtender>

Свойство ConfirmText определяет текст в окне сообщения, выводимом при щелчке на кнопке. Форматные теги (такие, как <Ь> и <i>) запрещены, но в тексте могут использоваться примитивы HTML. Например, конструкция &#10 позволяет прервать текущую строку и продолжить сообщение в следующей строке вывода. Запрет на использование форматных тегов обусловлен кодом JavaScript элемента ConfirmButton — в нем задействован вызов JavaScript window.confirm, поэтому форматирование HTML не имеет смысла (диалоговое окно подтверждения создается функцией Windows MessageBox, не поддерживающей HTML). Во внутренней реализации расширитель ConfirmButton назначает обработчик для события onsubmit формы и поглощает событие, если пользователь не подтвердит операцию. Расширитель может использоваться только с элементами, реализующими интерфейс IButtonControl, в том числе с элементами LinkButton и ImageButton.

Расширитель ToggleButton Флажки (check boxes) представляют собой графические элементы HTML, обычно представленные парой небольших графических изображений (для установленного и сброшенного состояния) и сопроводительным текстом. Каждый браузер может использовать свою пару изображений, в результате чего флажки слегка различаются по внешнему виду. Расширитель ToggleButton имитирует флажки с применением нестандартных изображений. Он применяется к элементу CheckBox и заменяет его совершенно новым блоком разметки, использующим пользовательскую графику и обладающим таким же поведением, что и стандартный флажок: <act:ToggleButtonExtender ID="ToggleButtonExtenderl" runat="server"> <act:ToggleButtonProperties TargetControlID="CheckBoxl" ImageWidth="19" ImageHeight="19" UncheckedImagellrVDontLike.gif" CheckedImageUrl="Like.gif" /> </act:ToggleButtonExtender>

Свойства ImageWidth и ImageHeight определяют желательный размер изображений. Учтите, что эти атрибуты являются обязательными. Свойства UncheckedImageUrl и CheckedlmageUrl задают изображения, используемые соответственно для сброшенного и установленного состояния.

Расширители временных окон Практически любой веб-разработчик имеет собственную точку зрения по поводу временных окон (popup windows). Нельзя отрицать, что временные окна — особенно модальные диалоговые окна — значительно упрощают выполнение многих


Серверные расширители Atlas

137

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

Расширитель ModalPopup Расширитель ModalPopup отображает в модальном режиме все содержимое, связанное с элементом, определяемым свойством PopupControllD. В этом случае свойство TargetControllD ссылается на элемент с поддержкой щелчков: <act:ModalPopupExtender ID="mpe" runat="server"> <act:ModalPopupProperties TargetControlID="LinkButtonl" PopupControlID="PopupContent" OkControlID="Buttonl" CancelControlID="Button2"> </act:ModalPopupExtender>

Расширитель ModalPopup активизируется по событию onclick целевого элемента. Таким образом, в качестве целевого может выступать только элемент, поддерживающий щелчки мышью. Элемент PopupControllD не обязан быть панелью; в общем случае он может быть каким угодно элементом. Тем не менее обычно для отображения во временных окнах используются контейнеры, содержащие другие элементы — чаще всего Panel. На панели временного окна необходимо идентифицировать элементы, выполняющие две операции: подтверждения и отмены. Идентификаторы таких элементов (как правило, кнопки) задаются свойствами OkControllD и CancelControllD. Естественно, открытие временного окна является действием клиентской стороны, поэтому щелчки на элементе подтверждения или отмены должны обрабатываться кодом JavaScript. Свойство OnOkScript задает функцию JavaScript, запускаемую при щелчке на кнопке подтверждения; при щелчке на кнопке отмены запускается функция, определяемая свойством OnCancelScript. Следующая разметка демонстрирует фрагмент типичной модальной панели. Обратите внимание: элемент Panel должен задать свой атрибут CSS display равным попе, чтобы все его содержимое сначала оставалось невидимым. <asp:LinkButton ID="LinkButtonl" runat="server" text="Click me" /> <asp:Panel runat="server" ID="PopupContent" BackColor="Yellow"> <div style="margin:10px"> Take note of this message and tell us if you strongly agree. <br /><br /> <asp:Button ID="Buttonl" runat="server" Text="Yes" width="40px" /> <asp:Button ID="Button2" runat="server" Text="No" width="40px" /> </div> </asp:Panel>

На рис. 4.8 показано модальное диалоговое окно в действии.


138

Глава 4. Элементы Microsoft ASP.NET AJAX

Рис. 4.8. Расширитель ModalPopup в действии

Расширитель дополняется парой графических свойств: DropShadow и ВаскgroundCssClass. Логическое свойство DropShadow указывает, нужно ли имитировать тень от временного окна, как на рис. 4.8. Свойство BackgroundCssClass определяет стиль, временно применяемый к базовой странице: modalBackground { background-color:Gray; filter:alpha(opacity=70); opacity:0.7; }

Этот стиль окрашивает страницу в серый цвет и слегка затушевывает ее для пущего эффекта.

Расширитель PopupControl Расширитель PopupControl можно связать с любым элементом HTML, генерирующим события onclick, onfocus или onkeydown. Конечной целью расширителя является отображение временного окна с дополнительной информацией — например, календарем или текстовым полем, в котором пользователь должен ввести дату. Содержимое панели представляется элементом Panel и может включать серверные элементы ASP.NET, статический текст и элементы HTML: <asp:textbox runat="server" ID="InvoiceDateTextBox" /> <asp:panel runat="server" ID="Panel1"> <asp:Calendar ID="Calendarl" runat="server">


Серверные расширители Atlas

139

</asp:Calendars </asp:panel> <act:PopupControlExtender ID="PopupExtenderl" runat="server"> <act:PopupControlProperties TargetControlID="InvoiceDateTextBox" PopupControlID="Panel1" Position="Bottom" /> </act:PopupControlExtender>

Свойство TargetControllD указывает на элемент, открывший временное окно, тогда как PopupControllD указывает на панель с отображаемыми элементами. Свойство Position задает позицию панели — сверху, слева, справа или снизу от родительского элемента (рис. 4.9).

Рис. 4.9. Расширитель PopupControl в действии

Дополнительные свойства OffsetX и OffsetY обозначают смещение временного окна от выбранной позиции в пикселах. Обычно временное окно содержит интерактивные элементы и выполняет возврат данных. По этой причине содержимое временного окна можно вставить в элемент UpdatePanel, чтобы оно могло выполнять серверные операции без обновления всей страницы. Как правило, временное окно закрывается после возврата данных — например, окно на рис. 4.9 настроено таким образом, что оно закроется после выбора даты пользователем. При этом календарь генерирует на сервере событие SelectionChanged. protected void Calendarl_SelectionChanged(object sender, EventArgs e) {

PopupExtenderl.Commit( Calendarl.SelectedDate.ToShortDateString());


140

Глава 4. Элементы Microsoft ASP.NET AJAX

Метод Commit присваивает указанное значение свойству по умолчанию ассоциированного элемента. Если вы хотите управлять тем, какому свойству (отличному от свойства по умолчанию) должно присваиваться значение при закрытии временного окна, воспользуйтесь свойством CommitProperty. Свойство CommitScript определяет функцию JavaScript, выполняемую на стороне клиента после присваивания результата. ПРИМЕЧАНИЕ

Помните, что при использовании элемента UpdatePanel в элементе ScriptManager должен быть включен режим частичного обновления (EnablePartialRendering). В противном случае при выборе пользователем даты в календаре будет выполняться полный возврат данных, сопровождающийся мерцанием страницы. Что еще хуже, из текстового поля исчезнет выбранная дата, и в нем восстановится исходное содержимое (в нашем примере это пустая строка).

Расширители пользовательского интерфейса Самую многочисленную группу расширителей Atlas составляют расширители пользовательского интерфейса — то есть специальные компоненты, упрощающие реализацию расширенных функций для удобства пользователя.

Расширитель HoverMenu Расширитель HoverMenu, как и PopupControl, может связываться с любым элементом ASP.NET. Оба расширителя отображают временную панель с дополнительным содержимым, но активизируются по разным событиям. HoverMenu отображает свою панель тогда, когда пользователь задерживает указатель мыши над целевым элементом. Панель отображается в позиции, заданной разработчиком: слева, справа, снизу или сверху от целевого элемента. При этом элементу можно назначить дополнительный стиль CSS, обеспечивающий его визуальное выделение (рис. 4.10). Расширитель HoverMenu хорошо подходит для реализации автоматически отображаемых контекстных меню для практически любых элементов ASP.NET, а также для вывода рекомендаций по заполнению некоторых полей. Например, когда на рис. 4.10 пользователь задерживает указатель мыши над текстовым полем, появляется список рекомендуемых значений, упрощающих ввод. <asp:TextBox ID="TextBoxl" runat="server" /> <asp:Panel ID="Panel1" runat="server" CssClass="popupMenu"> <asp:RadioButtonList ID="RadioButtonListl" runat="server" AutoPostBack="true" OnSelectedIndexChanged="RadioButtonListl_SelectedIndexChanged"> <asp:ListItem Text="Dino Esposito"></asp:ListItem> <asp:ListItem Text="Nancy Davolio"></asp:ListItem> <asp:ListItem Text="Andrew Fuller"></asp:ListItem> <asp:ListItem Value="" Text="None of the above"></asp:ListItem> </asp:RadioButtonList> </asp:Panel>


Серверные расширители Atlas

141

<act:HoverMenuExtender ID="HoverMenul" runat="server"> <act:HoverMenuProperties TargetControlID="TextBoxl" HoverCssClass="hoverPopupMenu" PopupControlID="Panel1" PopupPosition="Right" /> </act:HoverMenuExtender>

Рис. 4.10. Расширитель HoverMenu в действии

Элемент Panel 1 определяет список переключателей с рекомендуемыми вариантами заполнения текстового поля. Расширитель HoverMenu связывается с целевым текстовым полем и определяет Panel 1 как динамическую панель для временного отображения информации. Свойство PopupPosition определяет позицию панели по отношению к целевому элементу. Другие свойства (такие, как OffsetX и OffsetY) определяют смещение панели. Свойство PopDelay определяет задержку (в миллисекундах) между остановкой указателя мыши и появлением панели.


142

Глава 4. Элементы Microsoft ASP.NET AJAX

Необязательное свойство HoverCssClass изменяет стиль текстового поля при появлении меню подсказки. Интересно взглянуть на класс CSS, связанный с панелью: .popupMenu { position:absolute; visibility:hidden; background-color:#F5F7F8; } .hoverPopupMenu { background-color:yellow; }

Очень важно, чтобы атрибут visibility панели был установлен равным hidden; в противном случае панель появится при загрузке страницы и будет скрыта впоследствии. Как и в случае с расширителем PopupControl, для полноценного использования расширителя HoverMenu необходимо разместить расширенные элементы внутри элемента UpdatePanel. Предыдущий фрагмент разметки вставляется в тег ContentTemplate следующего определения элемента UpdatePanel: <atlas:UpdatePanel ID="UpdatePanell" runat="server" Mode="Conditional"> <ContentTemplate> </ContentTemplate> <Triggers> <atlas:ControlEventTrigger ControlID="RadioButtonListl" EventName="SelectedIndexChanged" /> </Triggers> </atlas:UpdatePanel>

Когда пользователь щелкает на переключателе, панель производит асинхронный возврат данных и генерирует на сервере событие SelectedlndexChanged: void RadioButtonListl_SelectedIndexChanged(object sender, EventArgs e) { TextBoxl.Text = RadioButtonListl.SelectedValue; }

Серверный обработчик события обновляет содержимое текстового поля, как показано на рис. 4.10.

Расширитель DropShadow Расширитель DropShadow имитирует эффект тени от элементов-панелей, что придает им более профессиональный вид. Степень прозрачности и ширина тени задаются свойствами элемента: <asp:Panel runat="server" ID="Panel1"> <div style="padding:8px"> <asp:TextBox ID="TextBoxl" runat="server" /> </div> </asp:Panel>


Серверные расширители Atlas

143

<act:DropShadowExtender ID="DropShadowExtenderl" runat="server"> <act:DropShadowProperties TargetControlID="Panell" Opacity=".65" Width="5" Rounded="true" /> </act:DropShadowExtender>

Свойство TargetControllD задает элемент, снабжаемый тенью. Как правило, им является элемент Panel; впрочем, тень может применяться к любому элементу ASP.NET — например, TextBox (при условии, что при этом не используется закругление углов). Степень прозрачности тени регулируется свойством Opacity, принимающим значения из диапазона от 0 до 1 (0 означает полную прозрачность). Чем ближе значение к 1, тем темнее будет тень. Логическое свойство Rounded указывает, должна ли панель и прилегающая к ней тень иметь закругленные края. По умолчанию используется значение false. На рис. 4.11 показан расширитель DropShadow в действии.

Рис. 4.11. Расширитель DropShadow в действии

Расширитель RoundedCorners Расширитель RoundedCorners является частным случаем расширителя DropShadow, так как его функциональность ограничивается закруглением углов дочерних панелей: <asp:Panel runat="server" ID="Panel1" BackColor="LightBlue" Width="170px"> <div style="margin-left:2px"> <asp:TextBox ID="TextBoxl" runat="server" BackColor="LightBlue" BorderWidth="Opx" /> </div> </asp:Panel> <act:RoundedCornersExtender ID="RoundedCornerExtenderl" runat="server"> <act:RoundedCornersProperties TargetControlID="Panell" Radius="6" Color="LightBlue" /> </act:RoundedCornersExtender>

Аргументы задают целевой элемент, радиус закругления и цвет границы. Закругленные углы создаются рисованием дополнительной границы вокруг


144

Глава 4. Элементы Microsoft ASP.NET AJAX

целевого элемента. Если удалить все границы вокруг текстового поля и использовать одинаковые цвета фона для панели и текстового поля, можно создать симпатичный эффект, показанный на рис. 4.12, — закругленное текстовое поле.

Рис. 4.12. Расширитель RoundedCorners в действии

Расширитель AlwaysVisibleControl Расширитель AlwaysVisibleControl закрепляет элемент (или панель с элементами) у одного из углов страницы, чтобы при прокрутке или изменении размеров страницы элемент «плавал» над фоном. Он может использоваться практически с любым элементом ASP.NET. <span style="background-color:yellow;" runat="server" id="Msg"> Need a bit of dummy text? Look at <b>http://www.loremipsum.net</b></span> <act:AlwaysVisibleControlExtender ID="avl" runat="server"> <act:AlwaysVisi Ы eControlProperties TargetControlID="Msg" HorizontalSide="Left" VerticalSide="Top" /> </act:AlwaysVisi Ы eControlExtender>

Свойства HorizontalSide и VerticalSide определяют угол страницы, в котором закрепляется целевой элемент. Свойство HorizontalSide принимает допустимые значения Left и Right, а свойство VerticalSide — Тор и Bottom. Смещение от каждого из краев определяется свойствами VerticalOffset и HorizontalOffset. Наконец, свойство ScrollEffectDuration указывает продолжительность эффекта прокрутки при перемещении целевого элемента (в секундах) — рис. 4.13. Учтите, что расширитель не может применяться к простым элементам HTML. Если потребуется обеспечить постоянную видимость блока HTML (например, тега <span> из предыдущего примера), добавьте атрибут runat="server" и присвойте уникальный идентификатор.


Серверные расширители Atlas

145

Рис. 4.13. Расширитель AlwaysVisibleControl в действии ПРИМЕЧАНИЕ

При тестировании некоторых элементов, рассматривавшихся до настоящего момента, возможно мерцание экрана и перемещение элементов при загрузке страницы. Чтобы этого не происходило, перед подключением расширителя разместите целевые элементы в нужных позициях при помощи элемента CSS position.

Расширитель DragOverlay В отличие от других расширителей, рассмотренных ранее, DragOverlay не определяется в ACT. Он реализуется в базовой библиотеке Atlas, а для его работы необходим префикс <atlas>. Расширитель переводит один или несколько серверных элементов в режим свободного перемещения; пользователь может свободно перетаскивать их по странице и установить в новом положении, которое будет сохранено на будущее. Для каждого элемента, переводимого в режим свободного перемещения, необходимо добавить блок DragOverlayProperties и задать его свойству TargetControllD идентификатор элемента. Также необходимо убедиться в том, что его свойство Enabled равно true. <span style="background-color:yellow;" runat="server" id="Msg"> Enter some text:</span> <br /> <asp:TextBox ID="TextBoxl" runat="server" /> <asp:Button ID="Buttonl" runat="server" text="Click" /> <atlas:DragOverlayExtender ID="DragOverlayl" runat="server"> <atlas:DragOverlayProperties Enabled="true" TargetControlID="Msg" /> /> </atlas:DragOverlayExtender>


146

Глава 4. Элементы Microsoft ASP.NET AJAX

В этом фрагменте мы видим тег <span>, преобразованный в перетаскиваемый элемент страницы. Все элементы первоначально отображаются в своей исходной позиции; простым перетаскиванием их можно переместить в другую позицию на странице. Обратите внимание: ни один элемент нельзя вывести за физические пределы страницы. Другими словами, перетаскивание за пределы последнего фрагмента содержимого запрещено. Если вы попытаетесь это сделать, перетаскиваемый компонент быстро возвращается на исходное место (впрочем, выглядит довольно эффектно). ПРИМЕЧАНИЕ

Расширитель DragOverlay, как и DragPanel, генерирует код для вызова аспекта поведения Floating клиентской стороны (этот и другие клиентские аспекты поведения рассматриваются в главе 5). Чем же DragPanel отличается от DragOverlay? В отличие от DragPanel расширитель DragOverlay не ограничивается перетаскиванием элементов ASP.NET Panel и тегов <div>. Тем не менее при перетаскивании элементов, обрабатывающих перемещения мыши (кнопки, текстовые поля и т. д.), могут возникнуть проблемы. Следовательно, в большинстве случаев он все же будет применяться для перетаскивания панелей. Элемент DragPanel позволяет явно задать манипулятор для перетаскивания; у расширителя DragOverlay в роли манипулятора используется тело перетаскиваемого элемента. Необязательное свойство ProfileProperty позволяет сохранить текущую позицию свободно перемещаемого элемента в пользовательском профиле заданного свойства. Тем самым обеспечивается сохранение изменений, вносимых пользователем, и их автоматическое восстановение при обновлении страниц. Однако для этого на форме необходимо разместить компонент ProfileScriptService (он будет рассмотрен наряду с другими клиентскими элементами Atlas в главе 6). Пока достаточно сказать, что он представляет собой внутреннюю веб-службу, которая предоставляет клиентскому сценарию серверный объект HttpContext.Profile. ПРИМЕЧАНИЕ

Всеми параметрами свойств целевых элементов расширителей Atlas можно управлять на программном уровне. Разработчик использует коллекцию TargetProperties, предоставляемую каждым расширителем, и получает нужное свойство по индексу (нумерация индексов начинается с 0). protected void Page_Load(object sender, EventArgs e) { DragOverlayl.TargetProperties[0].Enabled = true; }

Этот фрагмент разрешает свободное перемещение целевого элемента первого свойства DragOverlay.

Расширитель CascadingDropDown Расширитель CascadingDropDown присоединяется к элементу DropDownList и обеспечивает его автоматическое заполнение на основании текущего состояния выделения одного или нескольких родительских элементов DropDownList.


Серверные расширители Atlas

147

Расширитель CascadingDropDown проектировался для весьма распространенной ситуации, в которой содержимое одного раскрывающегося списка зависит от того, какая строка выделена в другом списке. При такой схеме вам не нужно пересылать клиенту весь набор данных, чтобы дочерний список выбирал подмножество строк, отображаемых в соответствии с текущим выделением родителя. Предположим, вы хотите, чтобы пользователь выбрал страну и город в этой стране. Расширитель CascadingDropDown упрощает решение этой задачи: для этого он внедряет в клиентскую страницу связующий код, а также делает некоторые предположения относительно структуры кода страницы. Предполагается, что вся логика, определяющая содержимое элементов DropDownList, находится в веб-службе. В свою очередь, веб-служба может использовать любой подходящий механизм сохранения и поиска данных. Впрочем, она в некоторой степени ограничивается схемой, определяемой контрактом. В частности, веб-служба должна содержать метод со следующей сигнатурой: [System.Web.Services.WebMethocT public CascadingDropDownNameValue[] GetDropDownContents( string knownCategoryValues, string category) { }

Конечно, имя метода может быть другим. Внутренний тип коллекции CascadingDropDownNameValue предназначен для хранения пар «имя/значение», отображаемых в раскрывающемся списке. Каждому раскрывающемуся списку, связанному с расширителем, назначается некоторая категория: <act:CascadingDropDown ID="CascadingDropDownl" runat="server"> <act:CascadingDropDownProperties TargetControlID="DropDownListl" Category="Country" PromptText="Please select a country" ServiceMethod="GetDropDownContentsPageMethod" /> <act:CascadingDropDownProperties TargetControlID="DropDownList2" Category="City" PromptText="Please select a city" _oadingText="Please, wait ..." ServicePath="CityFinderService.asmx" ServiceMethod="GetDropDownContents" ParentControlID="DropDownListl" /> </act:CascadingDropDown>

Содержимое свойства Category представляет собой любое имя, по которому метод веб-службы может узнать, какие данные он должен получить, а также определить смысл входных аргументов. Свойство PromptText определяет текст, который должен выводиться в раскрывающемся списке при отсутствии выделения (как правило, элемент при этом заблокирован). Свойство LoadingText определяет текст, отображаемый в процессе заполнения раскрывающегося списка. Свойство ServiceMethod указывает, какой метод должен вызываться для заполнения списка. Если свойство ServicePath не задано, предполагается, что должен использоваться метод страницы. Наконец, свойство ParentControllD создает


148

Глава 4. Элементы Microsoft ASP.NET AJAX

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

Рис. 4.14. Расширитель CascadingDropDown с двумя раскрывающимися списками

Расширитель ResizableControl Большинству пользователей нравятся страницы, в которых они могут динамически изменять размеры некоторых элементов HTML — например, панелей с текстом или графикой. Расширитель ResizableControl связывается с элементом веб-страницы и предоставляет графический интерфейс для изменения размеров этого элемента. При помощи манипулятора, расположенного в правом нижнем углу элемента, пользователь изменяет размеры элемента так, как если бы он был обычным окном. Возьмем следующий пример разметки: <asp:Panel ID="Panel1" runat="server" Style="overflow:hidden" Width="130px" Height="65px"> <asp:Image ID="Imagel" runat="server" ImageUrl="-7images/atlas.gif" Style="width:100^; height:100X;" /> </asp:Panel> <asp:Panel ID="Panel2" runat="server" Style="overflow:auto" Width="130px" height="100px"> This text resizes itself to be as large as possible within its container. </asp:Panel>

Как видите, первая панель содержит графическое изображение, а вторая — простой текст. Для правильной работы расширителя необходима установка некоторых атрибутов CSS. В частности, для графики атрибут overflow обычно задается равным hidden, а для текста — auto. Атрибут overflow управляет отображением полос прокрутки в том случае, если содержимое элемента выходит за пределы зарезервированного пространства. В приведенном примере изображение растягивается или сокращается, а текст прокручивается. Очень важно назначить панелям правильный исходный размер. В частности, для изображений размер окружающей панели должен совпадать с размером изображения. Размер панелей, окружающих текст, также должен задаваться явно. <act:ResizableControlExtender ID="Resizablel" runat="server"> <act:ResizableControlProperties


Серверные расширители Atlas

149

TargetControlID="Panel1" ResizableCssClass="resizingStyle" HandleCssClass="handleStyle" /> <act:ResizableControlProperties TargetControlID="Panel2" ResizableCssClass="resizingStyle" HandleCssClass="handleStyle" /> </act:ResizableControlExtender>

Расширитель ResizableControl обладает двумя обязательными свойствами: TargetControllD и HandleCssClass. Первое свойство определяет элемент, размеры которого требуется изменить, а второе — имя класса CSS, применяемого к манипулятору. Также имеется дополнительное свойство ResizableCssClass, которое определяет класс CSS, применяемый к элементу в процессе изменения размеров. Например, с его помощью можно изменить цвет границы для визуального выделения операции. Типичный класс CSS манипулятора выглядит примерно так: .handleStyle { width:16рх; height:16px; background-image:url(-/images/HandleGrip.gif); overflow:hidden; cursor:se-resize; }

На рис. 4.15 показан расширитель в действии.

Рис. 4.15. Расширитель ResizableControl в действии


150

Глава 4. Элементы Microsoft ASP.NET AJAX

Расширитель ResizableControl также поддерживает два клиентских события (onresizing и onresize), которые могут активизировать код JavaScript для выполнения более сложных операций (например, увеличения размера шрифта для заполнения области текстом). Расширитель позволяет определить минимальный и максимальный размеры при изменении размера элемента. Все изменения, связанные с изменением размеров элемента, автоматически сохраняются между обновлениями страницы.

Расширитель DynamicPopulate Расширитель DynamicPopulate заменяет разметку заданного элемента разметкой, полученной при вызове метода веб-службы. Его можно рассматривать как упрощенную версию элемента UpdatePanel, описанного в главе 3. DynamicPopulate отслеживает клиентское событие и генерирует удаленный вызов. Возвращаемая строка вставляется в модель DOM страницы в качестве потомка целевого элемента. Пример: <input type="button" id="Buttonl" runat="server" value="Refresh ..." /> <hr /> <t»Last updated: </t» <asp:Panel runat="server" ID="Msg" Style="padding:2px;height:2em;"> </asp:Panel> <act:DynamicPopulateExtender ID="mpe" runat="server"> <act:DynamicPopulateProperties TargetControlID="Msg" ClearContentsDuringUpdate="true" PopulateTriggerControlID="Buttonl" ServiceMethod="GetTimeOnServer" UpdatingCssClass="updating" /> </act:DynamicPopulateExtender>

Когда пользователь щелкает на заданном объекте (в данном случае на кнопке с именем Button"!), расширитель берется за дело. Он вызывает метод GetTimeOnServer и заменяет поддерево, корнем которого является элемент Msg, результатом вызова. Метод GetTimeOnServer является методом веб-службы. URL службы задается свойством ServicePath. Если это свойство не задано, предполагается, что метод является методом страницы, определяемым либо в кодовом файле страницы, либо встроенным в теге <script>: [System.Web.Services.WebMethod; public string GetTimeOnServer(string contextKey) { // Использовать параметр contextKey для приема данных от клиента.

}

// Получение вывода - вероятно, разметки HTML return DateTime.UtcNow.ToStringO;

Логическое свойство ClearContentsDuringUpdate управляет стиранием содержимого целевого элемента во время обновления. Чтобы во время операции отображался специальный стиль или графическое изображение, задайте стиль CSS в свойстве UpdatingCssClass. Атрибут CSS background-image задает выводимое фоновое изображение. На рис. 4.16 показан результат работы приведенного кода.


Серверные расширители Atlas

151

Рис. 4.16. Расширитель DynamicPopulate в действии

Для активизации динамического заполнения целевого элемента может использоваться любой элемент HTML. Он вовсе не обязан быть кнопкой отправки данных (например, LinkButton или Button). В этом случае страница будет выполнять обычный возврат данных, и вы лишитесь преимуществ платформы Atlas. ПРИМЕЧАНИЕ

Для динамического заполнения элемента DOM также может использоваться фрагмент кода JavaScript. В этом случае свойству CustomScript задается имя глобальной функции JavaScript. При использовании как сценария, так и серверного метода можно воспользоваться свойством ContextKey расширителя для передачи коду произвольной строки.

Расширитель PagingBulletedList Представьте страницу, на которой должен выводиться длинный список — скажем, перечень клиентов. Стандартное решение основано на использовании многостраничной сетки. Сетки ASP.NET выполняют возврат данных для каждой новой страницы, но упаковка сетки в элементы UpdatePanel позволяет изящно решить проблему. Но если вам всего лишь требуется перечислить набор записей в виде маркированного списка, сетка оказывается излишне тяжеловесным элементом.


152

Глава 4. Элементы Microsoft ASP.NET AJAX

Элемент ASP.NET BuiletedList выводит содержимое источника данных с использованием разнообразных интерфейсов, основанных на маркировке пунктов списка. Тем не менее постраничного вывода данных он не поддерживает. Расширитель PagingBulletedList оказывается на удивление простым и эффективным средством постраничного перебора содержимого элемента BulletedList. Рассмотрим следующий код: <asp:BulletedList ID="BulletedListl" runat="server" DisplayMode="Text" DataSourceID="ObjectDataSourcel" DataTextField="CompanyName" /> <asp:ObjectDataSource ID="ObjectDataSourcel" runat="server" TypeName="IntroAtlas.CustomerManager" SelectMethod="LoadAll"> </asp:ObjectDataSource>

Страница генерирует более 80 маркированных пунктов — по одному для каждого клиента в базе данных Northwind. Применяя расширитель к элементу BulletedList, вы получаете результат, показанный на рис. 4.17.

Рис. 4.17. Расширитель PagingBulletedList в действии

Код расширителя выглядит так: <act:PagingBul I etedListExtender ID="PagingBulIetedListl" runat="server"> <act:PagingBulIetedListProperties TargetControlID="BulIetedListl" ClientSort="true" IndexSize="2" Separator^' - " SelectIndexCssClass="selectedPage" /> </act:PagingBulIetedListExtender>


Серверные расширители Atlas

153

Расширитель делит все связанные элементы на страничные блоки и создает ссылку на каждый блок. Страница может содержать фиксированное количество записей или все записи с заданным префиксом. Свойство IndexSize указывает, сколько букв в отображаемом тексте должно использоваться для создания страницы. Если задать его равным 1, будет построено простое алфавитное меню. При увеличении значения до 2 представление данных становится более детальным — каждая страница содержит записи с именами, начинающимися с двухбуквенного префикса. Вместо IndexSize можно воспользоваться свойством MaxltemsPerPage. В этом случае каждый страничный блок (кроме последнего) будет содержать заданное количество записей. Свойство Separator определяет символ, используемый для разделения элементов меню. Свойства Class и UnselectlndexCssClass задают классы CSS соответственно для выделенных и невыделенных элементов меню. Наконец, если свойство ClientSort равно true, данные перед выводом автоматически сортируются на стороне клиента.

Расширители полей ввода Веб-страницы все еще зависят от встроенных возможностей языка разметки HTML, обеспечивающих возможность ввода пользователями данных на формах и в полях ввода. Однако элементы ввода HTML порой оказываются слишком ограниченными и простыми для современных приложений и пользователей. По этой причине старый добрый элемент <input type=«text»> и его аналог ASP.NET TextBox нуждаются в дополнительных возможностях. Atlas предоставляет пару расширителей, преобразующих классическое поле ввода в более интерактивное, дружественное к пользователю текстовое поле.

Расширитель AutoComplete Ввод данных в текстовых полях иногда бывает утомительным делом, особенно когда вам приходится снова и снова повторять один и тот же текст. По этой причине в браузерах уже довольно давно появилась функция автозаполнения. Традиционно в контексте браузера под этим термином понимается отслеживание всех URL, вводившихся пользователем. Когда пользователь начинает вводить длинный и плохо запоминаемый URL, браузер поможет ему своевременной подсказкой. В некоторых браузерах (таких, как Internet Explorer 5 и более новых версий) автозаполнение также используется для любых текстовых полей на формах HTML. Браузер сохраняет на локальном компьютере любые данные, веденные в поле ввода с некоторым именем, и предоставляет эту информацию на всех страницах сайта, содержащих одноименный элемент HTML. Функция автозаполнения интегрируется в браузер и является полностью прозрачной для пользователей и разработчиков. Список предлагаемых вариантов ввода строится постепенно и не может контролироваться авторами страниц. Расширитель Atlas AutoComplete распространяет функцию автозаполнения на любой элемент ASP.NET TextBox; что еще важнее, он дает авторам страниц


154

Глава 4. Элементы Microsoft ASP.NET AJAX

возможность программного определения списка рекомендуемых вариантов заполнения (получаемого в результате вызова веб-службы). <asp:TextBox ID="CustomerName" runat="server" /> <atlas:AutoCompleteExtender runat="server" ID="AutoCompleteExtenderl"> <atlas:AutoCompleteProperties TargetControlID="CustomerName" Enabled="true" MinimumPrefixLength="l" ServicePath="Suggestions.asmx" ServiceMethod="GetSuggestions" /> </atlas:AutoCompleteExtender>

Текстовое поле из этого фрагмента автоматически заполняется данными, возвращаемыми методом GetSuggestions заданной веб-службы. Свойства расширителя ServicePath и ServiceMethod определяют службу, предоставляющую данные для автозаполнения; свойство MinimumPrefixLength указывает, сколько символов должен ввести пользователь для активизации функции автозаполнения. В приведенном примере автозаполнение включается с первого введенного символа. ПРИМЕЧАНИЕ

Веб-служба должна быть локальной, то есть установленной на том же сервере и в одном приложении с используемой страницей. Учтите, что это условие действует для всех веб-служб, используемых расширителями Atlas. Метод веб-службы, используемый для получения вариантов заполнения, должен обладать фиксированной сигнатурой: [System.Web.Services.WebMethod[ public string[] GetSuggestions(string prefixText, int count) { }

Аргумент prefixText обозначает текст, введенный пользователем до настоящего момента; аргумент count задает желательное количество вариантов. Результат отображается на раскрывающейся панели под текстовым полем. Пользователь может выбрать один из предложенных вариантов при помощи мыши или клавиатуры. В следующем фрагменте показан метод веб-службы, который возвращает подмножество имен клиентов, начинающихся с заданного префикса (рис. 4.18). public class SuggestionService : System.Web.Services.WebService { [System.Web.Services.WebMethod[ public string[] GetSuggestions(string prefixText, int count) { int i=0; DataView data = GetDataO; data = FilterData(data, prefixText); string [] suggestions = new string[data.Count]; foreach (DataRowView row in data) suggestions[i++] = row["companyname"].ToStringO ;


Серверные расширители Atlas

155

return suggestions;

private DataView GetData() { DataView view = (DataView)HttpContext.Current.Cache["Suggestions"]; if (view == null) { SqlDataAdapter adapter = new SqlDataAdapter( "SELECT * FROM customers", " . . . " ) ; DataTable table = new DataTable(); adapter.Fill(table); view = table.DefaultView; // Сохранение всего набора данных в кэше ASP.NET // для повторного использования в будущем. HttpContext.Current.Cache["Suggestions"] = view;

return view;

private DataView FilterData(DataView view, string prefix) { // Фильтрация нежелательных строк. view.RowFilter = String.Format("companyname LIKE '{0}%'", prefix); return view;

Рис. 4.18. Расширитель AutoComplete в действии

Расширитель AutoComplete не определяется в библиотеке ACT, а интегрирован в инфраструктуру Atlas.


156

Глава 4. Элементы Microsoft ASP.NET AJAX

Расширитель TextBoxWatermark Расширитель TextBoxWatermark определяет текст, который отображается по умолчанию при отсутствии данных в текстовом поле. Расширитель отображает и скрывает текст по мере необходимости. <act:TextBoxWatermarkExtender runat="server" ID="TextBoxWatermarkl"> <act:TextBoxWatermarkProperties TargetControlID="TextBoxl" WatermarkText="Type First Name Here" WatermarkCssClass="watermarked" /> <act:TextBoxWatermarkProperties TargetControlID="TextBox2" WatermarkText="Type Last Name Here" WatermarkCssClass="watermarked" /> </act:TextBoxWatermarkExtender>

Расширитель определяется в ACT и использует свойство TargetControllD для определения целевого текстового поля. Свойство WatermarkText определяет отображаемый текст, а свойство WatermarkCssClass — стиль CSS, используемый для оформления текста подсказки (рис. 4.19).

Рис. 4.19. Расширитель TextBoxWatermark в действии

Расширитель FilteredTextBox Расширитель FilteredTextBox отфильтровывает некоторые символы из буфера заданного текстового поля. Он отличается от элемента проверки данных тем, что просто запрещает пользователю вводить недопустимые символы, тогда как элемент проверки данных активизируется на более поздней стадии, когда пользователь выходит из поля ввода. <strong>How old are you?</strong> <asp:TextBox ID="TextBoxl" runat="server" /> <act:FilteredTextBoxExtender ID="Filteredl" runat="server" > <act:FiIteredTextBoxProperties TargetControlID="TextBoxl" r ilterType="Numbers" /> </act:FiIteredTextBoxExtender>


Серверные расширители Atlas

157

Текстовое поле в этом примере предназначено для ввода числа, обозначающего возраст пользователя. Естественно, в нем могут вводиться только цифры. Для выполнения этого ограничения расширитель добавляет к текстовому полю фрагмент кода JavaScript, отфильтровывающий нежелательные символы. Расширитель поддерживает несколько свойств, в том числе свойство FilterType, которое определяет тип фильтра, применяемого к вводу. Свойство принимает следующие значения: Numbers (числа), UppercaseLetters (прописные буквы), LowercaseLetters (строчные буквы) и Custom (пользовательский). Смысл первых трех значений очевиден. При указании типа фильтра Custom свойству ValidChars задается строка, разделенная запятыми, в которой каждое поле определяет разрешенный символ. Например, следующий код разрешает пользователю вводить только символы A и B в любом регистре: <act:FilteredTextBoxExtender ID="Filteredl" runat="server" > <act:FiIteredTextBoxProperties TargetControlID="TextBoxl" r ilterType="Custom" ValidChars="A,a,B,b" /> </act:FiIteredTextBoxExtender>

Объединение нескольких фильтров для одного текстового поля не допускается. Например, если вы хотите отфильтровывать все символы, кроме букв, вам не удастся добавить два расширителя FilteredTextBox для одного элемента. Вместо этого придется воспользоваться пользовательсим фильтром, у которого в свойстве ValidChars перечислены все допустимые символы. ВНИМАНИЕ

Фильтрация на стороне клиента, как и проверка данных, обеспечивается работой кода JavaScript. Это означает, что запрет JavaScript в клиентском браузере приведет к отключению фильтрации или проверке данных. В любом случае не стоит безоговорочно доверять данным, введенным пользователем в текстовом поле, — используйте фильтрацию и проверку на стороне сервера, прежде чем использовать данные в критических операциях.

Расширитель NumericUpDown Расширитель NumericUpDown добавляет к элементу TextBox пару кнопок со стрелками, при помощи которых пользователь увеличивает и уменьшает отображаемое значение. Интересно заметить, что возможность увеличения и уменьшения применима не только к числовым данным, но и к перечисляемым типам, определяемым пользователем. <strong>How old are you?</strong> <asp:TextBox ID="TextBoxl" runat="server" Width="lOOpx" /> <act:NumericlIpDownExtender ID="UpDownl" runat="server"> <act:NumericUpDownProperties Width="100" TargetControlID="TextBoxl" /> </act:NumericUpDownExtender>


158

Глава 4. Элементы Microsoft ASP.NET AJAX

Для управления размером поля ввода следует задать свойство Width как для текстового поля, так и для расширителя. Обратите внимание: свойство Width расширителя обозначает общую ширину элемента вместе со стрелками (рис. 4.20).

Рис. 4.20. Расширитель NumericUpDown в действии

По умолчанию приращение равно +1 или - 1 . Впрочем, свойству RefValues позволяет задать последовательность значений при переборе. Свойство представляет собой строку с полями, разделенными символом «;» (точка с запятой): <act:NumericUpDownProperties Width="100" RefValues="Sun;Mon;Tue;Wed;Thu;Fri;Sat" TargetControlID="TextBoxl" />

Расширитель также может воспользоваться веб-службой для вычисления следующего или предыдущего значения. В этом случае свойства ServiceUpPath и ServiceUpMethod определяют службу и метод для выполнения операции увеличения (то есть определения следующего значения), а аналогичные свойства ServiceDownPath и ServiceDownMethod предназначены для операции уменьшения. Клиент также может передавать произвольные данные методам служб в свойстве Tag. ПРИМЕЧАНИЕ

Использование расширителя NumericUpDown само по себе еще не гарантирует удобства пользователя. Дело в том, что расширитель NumericUpDown не заставляет текстовое поле принимать только те значения, которые были сгенерированы в процессе перебора. Например, ничто не помешает ввести слово в числовом текстовом поле со связанным расширителем NumericUpDown. Чтобы этого не произошло, объедините расширитель NumericUpDown с расширителем фильтрации символов и воспользуйтесь свойством MaxLength элемента TextBox для ограничения максимального количества символов.

Расширитель PasswordStrength Даже самая защищенная система не сможет ничего сделать для защиты вашего сервера, если авторизованные пользователи будут использовать слабые, легко


Серверные расширители Atlas

159

подбираемые пароли. В последнее время появился целый ряд методов для создания сильных паролей. Расширитель PasswordStrength присоединяется к элементу TextBox и измеряет силу (то есть устойчивость) текущего содержимого поля при его использовании в качестве пароля. С точки зрения синтаксиса расширитель может присоединяться к любым элементам TextBox; однако с точки зрения семантики его стоит присоединять только к элементам TextBox, предназначенным для ввода пароля. Расширитель PasswordStrength вряд ли принесет реальную пользу на странице входа в систему, где пользователь должен ввести пароль для получения доступа к некоторой функциональности. Скорее, он пригодится на форуме, где пользователи вводят информацию о себе для получения доступа к системе. <h2>Choose your password</h2> <asp:TextBox ID="TextBoxl" runat="server" /> <act:PasswordStrength runat="server"> <act:PasswordStrengthExtenderProperties TargetControlID="TextBoxl" /> </act:PasswordStrength>

Расширитель проверяет текущий текст по набору критериев, установленных автором страницы. Результат проверки выводится либо в текстовом виде, либо в виде полосы-индикатора, обозначающей уровень сложности выбранного пароля. Работа расширителя в конфигурации по умолчанию показана на рис. 4.21.

Рис. 4 . 2 1 . Расширитель PasswordStrength в действии

Обратная связь выводится в виде простого текста и изменяется динамически по мере заполнения буфера. Свойства расширителя условно делятся на две категории: внешний вид и поведение. К первой категории относится свойство DisplayPosition, задающее позицию текста обратной связи (сверху, снизу, слева или справа), и свойство StrengthlndicatorТуре, выбирающее тип визуальной обратной связи. Допустимые значения свойства StrengthlndicatorType — Text и Barlndicator. Если задано значение Text, свойство PrefixText определяет текст, используемый при составлении сообщения обратной связи. В свойстве TextCssClass указывается класс CSS, определяющий стиль сообщения обратной связи. Если для обратной связи выбран тип Barlndicator, она отображается в виде индикаторной полосы, заключенной в рамку. Стиль полосы определяется свойствами BarBorderCssClass и BarlndicatorCssClass.


160

Глава 4. Элементы Microsoft ASP.NET AJAX

Критерии проверки пароля определяются свойствами RequiresUpperAndLowerCaseCharacters, PreferredPasswordLength, MinimumNumericCharacters и MinimumSymbolCharacters. По умолчанию длина пароля равна 10 символам и другие проверки содержимого поля не применяются. Расширитель вычисляет оценку пароля на основании заданных критериев. Результат отображается в виде полосы индикатора или текстового сообщения, как на рис. 4.21. Текстовые сообщения задаются в свойстве TextStrengthDescriptions в виде перечня описаний, разделенных символом «;». Можно задать от 2 до 10 описаний, упорядоченных по возрастанию устойчивости пароля. Обратная связь от расширителя делится на две части — оценку и сообщение состояния. Оценка используется для обновления индикатора или отображения описания. Сообщение состояния содержит справочную информацию и отображается на вспомогательном элементе, определяемом свойством HelpStatusLabellD. <asp:TextBox ID="TextBoxl" runat="server" /> <br /> <asp:Label runat="server" ID="Label1" /> <act:PasswordStrength runat="server"> <act:PasswordStrengthExtenderProperties TargetControlID="TextBoxl" DisplayPosition="RightSide" PreferredPasswordLength="12" HelpStatusLabelID="Labell" /> </act:PasswordStrength>

Элемент Label 1 получает сообщение с предложениями относительно того, как обеспечить соответствие пароля поставленным требованиями. Сообщение может оставаться скрытым и отображаться по щелчку на специальной кнопке. В этом случае следует задать значения двух новых свойств: HelpHandlePosition и HelpHandleCssClass. Первое свойство задает позицию отображения кнопки, а второе — ее стиль. <act:PasswordStrength runat="server"> <act:PasswordStrengthExtenderProperties TargetControlID="TextBoxl" DisplayPosition="BelowRight" PreferredPasswordLength="12" HelpHandlePosition="RightSide" HelpHandleCssClass="helpHandle" /> </act:PasswordStrength>

Оформление кнопки задается стилем CSS: .helpHandle { width:16px; height:14px; background-image:url(images/Question.png); overflow:hidden; cursor:help; }

На рис. 4.22 показано, как выглядит полученная страница.


Заключение

161

Рис. 4.22. Расширитель PasswordStrength с кнопкой вызова справки

Заключение Многие разработчики ASP.NET стремятся к тому, чтобы их страницы обладали более широкими возможностями и стали более удобными для пользователей. С технологической точки зрения эту задачу невозможно решить без использования JavaScript и кода клиентской стороны. Практически все возможности, находящиеся под контролем клиента, от нестандартных аспектов поведения HTML до асинхронных вызовов, требуют применения кода JavaScript или других аналогичных средств — таких, как язык Atlas XML Script. Что делать, если вы недостаточно хорошо владеете JavaScript и вам не хочется изучать новый диалект XML? Серверно-центрическая модель программирования Atlas представляет в ваше распоряжение расширители элементов, которые позволяют наделять существующие элементы новыми функциями и возможностями. Расширитель определяет нестандартный аспект поведения целевого элемента. Поведение клиентской функциональности выражается с использованием кода JavaScript, но связывается с серверными элементами с применением классической модели программирования ASP.NET. Абсолютное большинство расширителей элементов, упоминавшихся в этой главе, реализуется в виде нестандартных серверных элементов, а их расширенное поведение определяется посредством фрагмента кода JavaScript, внедряемого в виде ресурса. Другие аспекты поведения также могут определяться как автономные элементы клиентской библиотеки Atlas, которая будет рассматриваться в следующей главе.


ГЛАВА 5

Библиотека Microsoft AJAX В этой главе: i Расширения JavaScript I Основные компоненты I Элементы пользовательского интерфейса Чтобы переход с классической платформы Microsoft ASP.NET на платформу ASP.NET Atlas проходил как можно проще и естественнее, в предыдущих главах я описывал возможности платформы Atlas в основном с точки зрения серверного программирования. Однако следует помнить, что мощь Atlas в значительной мере сосредоточена на стороне клиента и тесно связана с клиентской функциональностью браузеров и платформ. Серверо-центрическая модель разработки, проанализированная в главах 3 и 4, представляет собой всего лишь набор «оберток», генерирующих на сервере весь необходимый код клиентской стороны, который разработчику иначе пришлось бы писать вручную. Чтобы в полной мере понять мощь платформы Atlas и научиться создавать интерактивные приложения, необходимо поближе познакомиться с инфраструктурой и широким диапазоном компонентов, упрощающих клиентское программирование в Atlas. Для обеспечения совместимости браузеров клиентская инфраструктура Atlas написана на JavaScript, но в объектно-ориентированном ключе. Язык JavaScript поддерживает работу с объектами и позволяет создавать пользовательские объекты. Тем не менее его никак нельзя назвать современным, объектно-ориентированным механизмом построения библиотек классов в духе .NET Framework. Atlas поднимает язык JavaScript на новый уровень, с расширением системы типов и введением концепций пространств имен и наследования. Кроме того, Atlas JavaScript поддерживает интерфейсы, перечисляемые типы и ряд вспомогательных функций для работы со строками и массивами. Эти расширения программируются на основе базового набора команд языка объекта JavaScript и хранятся в файлах .js, образующих runtime-среду Atlas. В этой главе мы сначала разберемся, как использовать Atlas-расширения JavaScript (такие, как пространства имен, интерфейсы и наследование), а затем подробнее займемся пространствами имен и классами, из которых складывается клиентская среда Atlas.


Расширения JavaScript

163

Расширения JavaScript Вероятно, написание широкофункциональных и сложных сред — таких, как Atlas — на обычном JavaScript выходит за рамки человеческих возможностей. Однако использование любого другого языка создает серьезные проблемы совместимости браузеров и подрывает главную причину успеха парадигмы AJAX.

Runtime-среда Atlas Хотя практически любой современный браузер поддерживает JavaScript и модель DOM (Document Object Model) стандарта HTML 4.0, между разными версиями DOM существует ряд тонких различий. Расширения Atlas JavaScript должны абстрагировать различия между версиями DOM и предоставить среду программирования более высокого уровня — более простую в использовании, с лучше читающимся кодом и снижающую вероятность ошибок.

Основной механизм Atlas Как упоминалось в главе 2, для работы приложений Atlas необходим ряд сценарных файлов .js. Эти файлы содержат реализацию библиотеки Microsoft AJAX и предоставляют в распоряжение разработчика новые объектно-ориентированные конструкции. Элемент ScriptManager отвечает за выбор стандартных сценарных файлов, внедряемых в страницу. Как минимум внедряется файл Atlas.js. Включение файла Atlas.js в клиентскую страницу упрощает программирование на JavaScript и автоматически обогащает клиентскую среду новым, расширенным набором объектов и объектно-ориентированных возможностей. Ссылки на файлы Atlas JavaScript включаются в сборку Microsoft.Web.Atlas в виде ресурсов, но устанавливаются как файлы с исходным кодом (как в отладочной, так и в окончательной версии) для отладки и изучения. ПРИМЕЧАНИЕ

Исходный код всех файлов Atlas JavaScript находится в папке ScriptLibrary основного пути установки Atlas. По умолчанию используется путь Program Files\Microsoft ASP.NET\Atlas.

Инфраструктура расширений JavaScript В целом JavaScript является скорее объектно-базированным, нежели в полной мере объектно-ориентированным языком (в нем отсутствуют некоторые ключевые возможности, самая заметная из которых — наследование, хотя и существует механизм псевдонаследования на базе prototype). Язык включает набор встроенных вспомогательных объектов, в том числе Function, Object, Boolean, Array, Number и String. Все встроенные объекты обладают свойством с именем prototype, доступным только для чтения. Свойство prototype представляет базовую функциональность, общую для всех новых элементов объекта данного класса. Новая функциональность добавляется в прототип класса в приложениях для расширения и улучшения возможностей данного класса. Именно это происходит в Atlas.


164

Глава 5. Библиотека Microsoft AJAX

В файле Atlas.js все встроенные объекты JavaScript наделяются новыми возможностями. Например, Atlas расширяет объект JavaScript Function набором методов для реализации делегирования, абстрактных методов и наследования. Следующий краткий фрагмент Atlas.js демонстрирует сказанное: // Определение абстрактного метода Function.abstractMethod = function() { throw 'Abstract method should be implemented'; } // Определение функции обратного вызова Function.createCallback = function(method, context) { return function() { return method(context); } } // Определение делегата (указателя на функцию) Function.createDelegate = function(instance, method) { return function() { return method.apply(instance, arguments); } } // Расширение прототипа для поддержки базовых методов Function.prototype.getBaseMethod = function(instance, methodName, baseTypeName) { } // Расширение прототипа для получения информации о базовом типе Function.prototype.getBaseType = function() { return this._baseType; }

В файле Atlas.js также определяется ряд переменных для хранения специфической информации типов (например, базовый тип и имя типа) — все эти концепции не поддерживаются напрямую в браузерных реализациях JavaScript. Все методы, добавленные в прототип, «наследуются» всеми функциями, определяемыми в контексте любого приложения Atlas. Кроме того, файл Atlas.js содержит код, определяющий новые объекты и расширяющий существующие объекты JavaScript дополнительной функциональностью. В табл. 5.1 перечислены основные глобальные объекты, определяемые в файле Atlas.js. Таблица 5 . 1 . Глобальные объекты runtime-среды Atlas

Тип

Описание

Array

Расширение встроенного объекта Array. Содержит методы добавления, вставки, удаления и очистки элементов массива. Также содержит методы для проверки присутствия заданного элемента в массиве


Расширения JavaScript

165

Тип

Описание

Boolean

Расширение встроенного объекта Boolean. Определяет метод parse для получения логического значения по строке или любому выражению с логическим результатом

Error

Определяет метод createError, инкапсулирующий объект JavaScript Error, и добавляет более удобный конструктор для его создания

Function

Расширение встроенного объекта Function. Содержит методы для определения классов, пространств имен, делегатов и других объектноориентированных средств

Number

Расширение встроенного объекта Number. Определяет метод parse для получения числового значения по строке или любому выражению с числовым результатом

Object

Расширение встроенного объекта Object. Содержит методы для получения информации о типах (например, типа используемого объекта)

RegExp

Расширение встроенного объекта RegExp. Определяет метод parse для построения регулярного выражения по заданному значению

String

Расширение встроенного объекта String. Содержит методы для работы со строками — такие, как trim, endsWith и startsWith. Также определяет глобальный метод format, родственный методу String.Format управляемого типа String

Type

Содержит методы для создания перечисляемых типов и объявления пространств имен

Простое включение серверного компонента ScriptManager в страницу ASP.NET дает возможность использовать дополнительные методы «родных» объектов, определяемые в runtime-среде Atlas. Например, следующий код будет работать абсолютно нормально: var s = "Dino"; alert(s.startsWith('D')); Встроенный объект JavaScript String не поддерживает метод startWith. Тем не менее следующий код Atlas.js успешно решает проблему: String.prototype.endsWith = function(suffix) { return (this.substr(this.length - suffix.length) == suffix); } String.prototype.startsWith = function(prefix) { return (this.substr(0, prefix.length) == prefix); } Объекты JavaScript не являются полноценными объектами с сильной типизацией. В частности, не существует абсолютно надежного механизма для определения типа объекта, с которым вы работаете. Метод toString и оператор typeof лишь частично решают проблему. Метод toString предназначен для получения строкового представления объекта, поэтому он всего лишь возвращает содержимое


166

Глава 5. Библиотека Microsoft AJAX

объекта для строк и чисел. Оператор typeof для имени пользовательского объекта, с которым вы работаете, всегда возвращает строку object. var р = new PersonC'Dino", "Esposito"); alert(typeof(p));

Новый метод, добавленный в объект Object, обеспечивает точную идентификацию типов. Используйте метод Object.getTypeName: var р = new PersonC'Dino", "Esposito"); alert(Object.getTypeName(p));

На этот раз в окне сообщения выводится строка Person вместо более общей строки object. Чтобы увидеть, как это делается, достаточно заглянуть в исходный код метода getTypeName: Object.getTypeName = function(instance) { return Object.getType(instance).getNameO ; } Object.getType = function(instance) { var ctor = instance.constructor; if (!ctor || (typeof(ctor) != "function") || !ctor._typeName) { return Object; } return instance.constructor; }

В конечном итоге для получения имени типа используется метод getName объекта Function: r

unction.prototype.getName = functionO { return this.jtypeName; }

Как видите, метод getName просто возвращает значение внутренней переменной. Оказывается, каждый новый объект сохраняет в переменной _typeName строку, представляющую имя класса. Например, в Atlas.js для класса Object в качестве имени типа явно сохраняется строка «Object»: Object. jtypeName = 'Object';

Все встроенные объекты JavaScript модифицируются в файле Atlas.js с включением дополнительного свойства имени типа. Итак, Atlas формирует поверх базовых, встроенных объектов JavaScript толстую абстрактную прослойку, преобразующую сценарный язык в некое подобие объектно-ориентированной сценарной прикладной среды. Если на секунду забыть о хорошо знакомых концепциях и элементах синтаксиса JavaScript, можно представить, что перед вами совершенно новый язык. Runtime-среду Atlas можно сравнить со специализированной версией .NET Framework, а язык Atlas JavaScript — с упрощенной версией Microsoft Visual C#.


Расширения JavaScript

167

Глобальные функции Прежде чем переходить к рассмотрению более интересных объектно-ориентированных расширений вроде пространств имен и наследования, давайте потратим немного времени на пару удобных сокращений, которые можно использовать в сценарном коде Atlas для ускорения разработки. Одна из самых распространенных ошибок, встречающихся при написании сценарного кода в веб-страницах, — прямой доступ к элементам HTML вместо вызова метода getElementByld модели DOM. Предположим, на клиентской странице имеется текстовое поле с именем TextBoxl. Следующая команда работает не во всех браузерах: alert(TextBoxl.value);

Правильная форма, ратифицированная в проекте стандарта HTML DOM комитета W3C (World Wide Web), должна выглядеть так: alert(document.getElementBylcK"TextBoxl").value);

Конечно, правильная форма занимает больше места и ее неудобно писать снова и снова. На помощь приходит Atlas JavaScript со своей глобальной функцией $(). Фактически функция $() является сокращенной формой записи для функции document.getElementByld. На странице Atlas следующее выражение функционально эквивалентно приведенному ранее: alert($("TextBoxl").value);

Аналогичная сокращенная запись существует и для поиска компонентов Atlas в runtime-иерархии Atlas. Для этой цели применяется функция $object() — сокращение для Application.findObject. Application — корневой объект в runtime-иерархии Atlas. Мы вернемся к этой теме позднее, при знакомстве с библиотекой Microsoft AJAX.

Объектно-ориентированные расширения в Atlas JavaScript В JavaScript объект Function является главным инструментом, используемым для объединения кода и свойств и построения новых компонентов. В Atlas JavaScript объект Function был расширен — в него была включена поддержка информации о типах, пространств имен, наследования, интерфейсов и перечисляемых типов. Посмотрим, как это было сделано.

Поддержка пространств имен Пространство имен (namespace) является средством группировки и классификации типов, принадлежащих библиотеке. Пространство имен сам по себе не является типом, но добавляет новую информацию в определение каждого типа, содержащегося в нем, с целью его уточнения. На концептуальном уровне связь между пространством имен и содержащимся в нем типами можно сравнить со связью между каталогом и содержащимся в нем файлами. В конечном


168

Глава 5. Библиотека Microsoft AJAX

счете единицей данных, с которой вы работаете, является файл; файлы идентифицируются по именам. Тем не менее для любой среды программирования одноименные файлы, находящиеся в разных каталогах, никак не связаны друг с другом. По умолчанию все пользовательские функции JavaScript принадлежат глобальному пространству имен. В Atlas JavaScript пользовательскую функцию можно поместить в конкретное пространство имен по сугубо организационным соображениям. Следующий фрагмент показывает, как объявить пользовательский тип в Atlas JavaScript: Type.registerNamespaceO IntroAtlas"); IntroAtlas.Person = function(firstName, lastName) { var _firstName = firstName; var _lastName = lastName; this.getFirstName = functionO { return _firstName; } this.getLastName = functionO { return _lastName; } this.getName = functionO { return _firstName + ' ' + JastName; } } IntroAtlas.Person.registerClass('IntroAtlas.Person', null, null);

Метод Type.registerNamespace включает указанное пространство имен в runtimeсреду. В какой-то степени этот метод эквивалентен конструкции namespacej...} в C# или Namespace .. End Namespace в Microsoft Visual Basic .NET. ПРИМЕЧАНИЕ

Так как Atlas JavaScript представляет возможности runtime-среды, а не синтаксис языка программирования, соответствие между Type.registerNamespace и аналогичными конструкциями C# или Visual Basic .NET является чисто условным. Во внутренней реализации Type.registerNamespace ограничивается включением в текущую модель DOM информации для отслеживания текущего пространства имен. Функция IntroAtlas.Person, определяемая за объявлением пространства имен, описывает тип Person в пространстве имен IntroAtlas. Наконец, только что определенная функция должна быть зарегистрирована как класс в среде Atlas. Задача решается вызовом метода registerClass для заданной функции. Метод registerClass определяется в прототипе объекта Function, поэтому он наследуется всеми функциями. Во внутренней реализации метод registerClass задает свойству typeName функции первый параметр метода — фактическое имя класса. Метод registerClass получает три параметра: открытое имя, которое будет использоваться для представления функции JavaScript как класса, родительский класс (если есть) и все интерфейсы, реализуемые классом.


Расширения JavaScript

169

Поддержка наследования Давайте определим новый класс Citizen, который расширяет класс Person, дополняя его парой свойств: адресом и номером социального страхования. В конце фрагмента для только что определенной функции вызывается метод registerClass. IntroAtlas.Citizen = function(firstName, lastName, ssn, address) { var _ssn = ssn; var _address = address; this.getSSN = functionO { return _ssn; } this.getAddress = functionO { return _address; } } IntroAtlas.Citizen.registerClass('IntroAtlas.Citizen', IntroAtlas.Person);

Обратите внимание: первый аргумент registerClass относится к строковому типу, но во втором аргументе должна передаваться ссылка на объект. Честно говоря, от этого кода особой пользы не будет. Вам как минимум необходимо вызвать конструктор базового класса для инициализации полей, определенных в базовом классе. Метод initializeBase (определяемый в Function) получает и вызывает конструктор базового класса: IntroAtlas.Citizen = function(firstName, lastName, ssn, address) { IntroAtlas.Citizen.initializeBase(this, [firstName, lastName]); }

При вызове initializeBase передается ссылка на текущий объект и массив параметров вызываемого конструктора. Запись [...] используется для определения массива «на месте». Разработчики нередко создают производные классы, потому что им потребовалось добавить новый метод или изменить поведение существующих методов. В объектно-ориентированных языках определяется специальное ключевое слово для пометки переопределяемых методов. Как это сделать в JavaScript? Давайте изменим класс Person и включим в него переопределяемый метод GetName: Туре.registerNamespaceCIntroAtlas"); IntroAtlas.Person = function(firstName, lastName) { var _firstName = firstName; var _lastName = lastName;

this.getFirstName = function() { return _firstName; }


170

Глава 5. Библиотека Microsoft AJAX this.getLastName = function О { return _lastName; } this.getNamelnternal = functionO { return _firstName + ' ' + JastName; }

} IntroAtlas.Person.registerClass('IntroAtlas.Person', null); IntroAtlas.Person.prototype.getName = functionO { return this.getNamelnternal(); }

Метод getName выведен из основного определения класса и присоединен к прототипу функции. Метод, включенный в прототип, используется совместно производными классами, может переопределяться и вызываться из производных классов. Например, переопределение метода getName в производном классе Citizen происходит так: IntroAtlas.Citizen = function(firstName, lastName, ssn, address) { IntroAtlas.Citizen.initializeBase(this, [firstName, lastName]); var _ssn = ssn; var _address = address; this.getSSN = functionO { return _ssn; } this.getAddress = functionO { return _address; } } IntroAtlas.Citizen.registerClass('IntroAtlas.Citizen', IntroAtlas.Person); IntroAtlas.Citizen.prototype.getName = functionO { return IntroAtlas.Citizen.callBaseMethod(this, "getName") + " [" + this.getSSN() + " ] " ; }

Метод getName включается в прототип класса для дальнейшего переопределения. Вызов callBaseMethod активизирует метод родительского класса. ПРИМЕЧАНИЕ

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

Поддержка интерфейсов Интерфейс описывает совокупность логически связанных аспектов поведения, характерных для разных классов. В общем случае интерфейс может содержать методы, свойства и события; в JavaScript интерфейсы содержат только методы.


Основные компоненты

171

Чтобы определить интерфейс, вы создаете функцию, состоящую из абстрактных методов. Абстрактный метод представляется методом abstractMethod, определяемым для класса Function в Atlas.js. Метод abstractMethod — не ключевое слово, а реально существующий метод, при попытке вызова которого происходит исключение — определенно изящное решение. Туре.registerNamespaceC IntroAtlas"); IntroAtlas.IAddress = functionO { this.getFullAddress = Function.abstractMethod; this.getStreet = Function.abstractMethod; this.getCity = Function.abstractMethod; this.getZipCode = Function.abstractMethod; } IntroAtlas.IAddress.registerInterface('IntroAtlas.IAddress');

В Atlas JavaScript интерфейс представляет собой не что иное, как класс, помеченный как завершенный (sealed) и абстрактный, при его регистрации методом registerlnterface. Чтобы реализовать интерфейс, класс JavaScript предоставляет все методы интерфейса, а сам интерфейс указывается при регистрации класса. Следующая команда регистрирует класс Citizen, производный от Person и реализующий интерфейс IAddress: IntroAtlas.Citizen.registerClass('IntroAtlas.Citizen', IntroAtlas.Person, IntroAtlas.IAddress);

Однако учтите, что вы не получите никаких заблаговременных сообщений об ошибке времени выполнения, если класс, объявивший о реализации некоторого интерфейса, не поддерживает все его методы. JavaScript является интерпретируемым, а не компилируемым языком, поэтому парсер JavaScript не будет знать об отсутствующих методах интерфейса вплоть до попытки их явного вызова. ПРИМЕЧАНИЕ

Если класс реализует несколько интерфейсов, то все необходимые интерфейсы перечисляются в дополнительных параметрах метода registerClass.

Основные компоненты Библиотека Microsoft AJAX делится на три логических уровня: расширения JavaScript, фундаментальные (или базовые) классы и классы пользовательского интерфейса (рис. 5.1). Как упоминалось в предыдущих разделах, расширения JavaScript наделяют встроенные объекты JavaScript новыми методами и возможностями и добавляют методы регистрации для имитации объектно-ориентированных конструкций (таких, как классы, пространства имен, наследование и интерфейсы). В подсистему пользовательского интерфейса входят компоненты для определения клиентских аспектов поведения, операций перетаскивания, взаимодействия с моделью DOM, а также такие клиентские элементы, как текстовые поля, списки и источники данных.


172

Глава 5. Библиотека Microsoft AJAX

Классы пользовательского интерфейса Пространство имен Sys.UI

Пространство имен Sys.Data

Пространство имен Sys.UI.Data

Фундаментальные классы Пространство имен Sys

Пространство имен Sys.Net

Пространство имен Sys.Services

Расширения JavaScript Function

String

Object

Рис. 5 . 1 . Структура библиотеки Microsoft AJAX

Фундаментальные классы образуют своего рода базовую библиотеку, в которую вошли часто используемые классы для отладки, обработки событий, операций со строками, работы с веб-службами, таймерами и счетчиками. Не стоит и говорить, что все классы написаны на JavaScript и хранятся в файле Atlas.js. Как было показано ранее, Atlas JavaScript поддерживает пространства имен, поэтому классы библиотеки Microsoft AJAX принадлежат конкретным пространствам имен. Большинство классов фундаментального уровня библиотеки принадлежит пространству имен Sys. В этом разделе мы рассмотрим основные функциональные подгруппы, реализованные в пространстве имен Sys, — объект Application, действия и привязки. ПРИМЕЧАНИЕ

В электронную документацию Atlas входит утилита для просмотра клиентских классов, с помощью которой можно получить подробную информацию о классах, их членах и свойствах. Кроме того, имеется полный исходный код библиотеки. Локальную документацию Atlas можно загрузить по адресу http://atlas.asp.net.

Объект Application Класс Sys._Application играет ключевую роль в среде Atlas. Каждый раз, когда в браузере загружается страница Atlas, создается экземпляр класса _Application, который присваивается объекту Sys.Application. В самом конце файла Atlas.js находится следующая команда: Sys.Application = new Sys._Application();


Основные компоненты

173

После объявления всего кода JavaScript, входящего в библиотеку Microsoft AJAX, файл Atlas.js создает объект, представляющий текущее приложение в браузере. Задача создания экземпляра приложения зарезервирована для Atlas; разработчики никогда не должны создавать дополнительные экземпляры класса .Application.

Члены класса Sys._Application Класс Sys._Application наследует от Object и реализует два интерфейса: Sys.lTypeDescriptorProvider и Sys.lCustomTypeDescriptor: Sys._Application.registerSealedClass( 'Sys._Application', null, Sys.ITypeDescriptorProvider, Sys.ICustomTypeDescriptor);

Методы двух интерфейсов предоставляют упрощенный механизм рефлексии для объектов Atlas JavaScript. Класс Sys._Application использует методы интерфейсов для предоставления стандартизированного доступа к своим членам. Класс Sys._Application поддерживает два события и один метод. Метод называется findObject, а события — load и unload.

Поиск компонентов Atlas Метод findObject просматривает runtime-иерархию компонентов Atlas текущей страницы в поисках компонента с заданным идентификатором. Метод имеет два возможных прототипа: Sys._Application.findObject(id); Sys._Application.findObject(id, context);

Первая перегруженная версия получает идентификатор искомого компонента и просматривает всю иерархию от корневого узла. При задании аргумента context, отличного от null, поиск ограничивается поддеревом объекта context. Параметр id должен быть строковым, а параметр context — содержать объект Atlas. Метод возвращает объект с совпадающим идентификатором или null, если такой объект не найден. Что собой представляет иерархия компонентов Atlas? Каждая страница Atlas содержит разметку XML Script, сгенерированную элементом ScriptManager, и все серверные элементы Atlas, задействованные в работе страницы. Минимальный код XML Script выглядит примерно так: <script type="text/xml-script"> <page xmlns:script="http://schernas.niicrosoft.coni/xnil-script/2005"> components /> </page> </script>

Содержимое тега <components> разбирается при загрузке страницы для создания runtime-иерархии компонентов Atlas. Как упоминалось ранее, для поиска компонентов Atlas также может использоваться сокращенная запись $object(...).


174

Глава 5. Библиотека Microsoft AJAX

События жизненного цикла страницы При первоначальной загрузке страницы Atlas происходит событие load, при обработке которого клиентский код выполняет всю необходимую инициализацию. Важно заметить, что событие относится к жизненному циклу страницы, а не приложения. Таким образом, при каждом классическом возврате данных будет происходить новое событие load. Для возврата данных в стиле AJAX события не генерируются. Парное событие unload генерируется при выгрузке страницы. Событие load происходит после полного завершения загрузки и инициализации страницы Atlas. В страницах Atlas инициализацию рекомендуется выполнять при обработке события load (вместо события onload браузера). Таким образом, только после получения события Atlas load можно с полной уверенностью считать, что страница Atlas готова к взаимодействию с пользователем. Событие unload происходит непосредственно перед тем, как runtime-среда Atlas освобождает страницу и все ее ресурсы. Для стабильной работы приложения все операции зачистки следует выполнять в обработчике этого события (вместо события onunload браузера). Следующий фрагмент показывает, как добавлять обработчики событий load и unload на уровне сценария: Application.1oad.add( 1 oadHandler); Application.unload.add(unloadHandler);

Код помещается в тег <script>, импортируемый в страницу. Еще более простой способ определения обработчиков load и unload основан на использовании готовых функций pageLoad и pageUnload. Эти функции являются глобальными и вызываются без параметров: <script type="text/javascript" language="Javascript"> function pageLoadO { alertC'Being loaded"); } function pageUnloadO { alertC'Being unloaded"); } </script>

Декларативный интерфейс для Sys._Application В Atlas все, что делается посредством сценариев, также может делаться с применением блоков XML Script. Этот принцип относится и к конфигурации объекта Application. Вот как обработчик события load определяется в коде XML Script: <script type="text/xml-script"> <page xmlns:script="http://schernas.niicrosoft.coni/xnil-script/2005"> <components> <application 1oad="MyPageLoad()"> </application> </components> </page>


Основные компоненты

175

</script> <script type="text/javascript" language="Javascript"> function MyPageLoadO { alertC'Being loaded"); } </script>

Полный синтаксис тега XML Script <application> богаче, чем может показаться по предыдущему фрагменту: application load="event handler" unload="event handler"> <load> <!-- действия --> </load> <unload> <!-- действия --> </unload> </application>

В частности, он позволяет разработчикам задавать код, выполняемый при возникновении событий load и unload, на чисто декларативном уровне. Код обработки следует по возможности сводить к последовательности действий. Как определять такие действия? И еще важнее — что собой представляют запрограммированные действия Atlas и как они программируются? С точки зрения реализации действия являются компонентами Atlas, которые представляют команды, предназначенные для исполнения. Сначала мы поближе познакомимся с компонентами Atlas, а затем разберемся, как действия реализуются на декларативном уровне.

Компоненты Atlas Термином «компонент» обычно обозначается объект, предназначенный для повторного использования и способный взаимодействовать с другими объектами в контексте прикладной среды. С другой стороны, термин «элемент» обычно обозначает объект, который является специализированным компонентом. Главной характеристикой, определяющей различия между элементами и компонентами, является пользовательский интерфейс (UI, User Interface). Компоненты, в отличие от элементов, не обладают пользовательским интерфейсом. В большинстве прикладных сред, в том числе в .NET Framework и библиотеке Microsoft AJAX, компоненты и элементы наследуют от разных базовых классов и, как следствие, находятся в изолированных ветвях иерархии наследования. В Atlas иерархия компонентов начинается с корневого класса Sys.Component. Корневой класс элементов называется Control и принадлежит (кто бы мог подумать!) пространству имен Sys.UI. Сейчас мы поближе познакомимся с общими свойствами компонентов, а позднее сосредоточимся на элементах.

Класс Sys.Component Абстрактный класс Sys.Component определяет ряд свойств и событий, общих для всех компонентов прикладной среды Atlas. Свойства класса Sys.Component перечислены в табл. 5.2.


176

Глава 5. Библиотека Microsoft AJAX

Таблица 5.2. Свойства класса Sys.Component

Свойство

Описание

bindings

Возвращает привязки, принадлежащие компоненту

dataContext

Исходный объект, используемый в операциях привязки данных (свойство доступно для чтения и записи) Идентификатор, однозначно определяющий компонент (свойство доступно для чтения и записи)

id isInitialized

Указывает, прошел ли компонент инициализацию

isUpdating

Указывает, находится ли компонент в процессе пакетного обновления

У большинства компонентов Atlas имеются аналоги в модели DOM. Свойство id связывает компонент с элементом DOM, обладающим указанным идентификатором. Свойство id также позволяет проводить поиск компонентов по имени. var component = $object(id);

Функция $object предназначена для поиска компонентов в дереве Atlas. Класс Sys.Component содержит метод initialize, предназначенный для начальной инициализации компонента. Базовая реализация метода ограничивается простым созданием экземпляра коллекции bindings. В производных классах специализированные версии метода могут выполнять дополнительные операции.

Обнаружение изменений свойств Класс Sys.Component содержит встроенный механизм отслеживания изменений свойств компонентов. При изменении свойства компонента генерируется событие propertyChanged. Внутренний метод базового класса raisePropertyChanged позволяет производным компонентам генерировать событие propertyChanged, чтобы оповестить вызывающую сторону об изменении значения свойства. Например, реализация свойства компонента может выглядеть так: this.set_MyProperty = function(value) { _myProperty = value; this.raisePropertyChanged("MyProperty"); }

В общем случае производные компоненты включают вызов raisePropertyChanged в метод set заданного свойства.

Пакетные обновления В классе Sys.Component также учтена возможность одновременного обновления нескольких свойств. Чтобы свести к минимуму обновление экрана, можно приказать объекту выполнить изменение свойств в пакетном режиме. Для этого обновления группируются между вызовами методов beginUpdate и endUpdate. На время незавершенной операции пакетного обновления значение свойства isUpdating автоматически задается равным true. В зависимости от специфики компонента пакетные обновления могут повысить быстродействие и свести к минимуму обновления экрана.


Основные компоненты

177

Привязка данных Компоненты Atlas обладают встроенной поддержкой привязки данных. Свойство dataContext определяет источник данных, а в коллекции bindings хранится информация о наборе привязок компонента. В Atlas привязка представляет логическую, однонаправленную связь между элементом и компонентом. Связь соединяет источник данных компонента (свойство dataContext) с одним свойством целевого элемента. Привязки подчиняются правилам наследования; это означает, что производные компоненты наследуют родительский контекст данных, если только их свойству dataContext не будет задана другая коллекция. Привязка данных будет рассматриваться в главе 7.

Декларативные действия Декларативное программирование в Atlas встречается повсеместно и распространяется на все области программирования. Разработчик может не только сформировать пользовательский интерфейс веб-страницы в виде тегов разметки, но и задать поведение страницы клиентской стороны объединением нескольких тегов XML Script, каждый из которых содержит ссылку на блок кода JavaScript. Atlas выводит программирование на новый уровень, позволяя разработчикам задавать на декларативном уровне действия — по крайней мере для таких стандартных задач, как вызов метода, задание свойства или инициирование возврата данных. Базовым классом JavaScript для всех декларативных действий Atlas является абстрактный класс с именем Sys.Action. Класс наследует от Sys.Component и реализует интерфейс lAction: Sys.Action.registerAbstractClass('Sys.Action', Sys.Component, Sys.IAction);

Реальные компоненты, производные от класса Action, связываются с событиями и указывают, какая функция должна вызываться при возникновении того или иного события. К числу встроенных классов действий относятся InvokeMethodAction, SetPropertyAction и PostBackAction.

Класс Action В табл. 5.3 перечислены свойства, определенные для класса Sys.Action и являющиеся общими для всех производных классов. Как упоминалось ранее, действия связываются с событиями; события, в свою очередь, связываются с обработчиками. Может оказаться, что с одним событием связана серия действий и обработчик. Что должно выполняться первым? Ответ на этот вопрос зависит от свойства sequence. Допустимые значения свойства sequence объединены в перечисляемый тип Sys.ActionSequence: Sys.ActionSequence.BeforeEventHandler = G Sys.ActionSequence.AfterEventHandler = 1

Значение определяет, когда должно выполняться действие: до или после обработчика событий. По умолчанию используется значение BeforeEventHandler.


178

Глава 5. Библиотека Microsoft AJAX

Таблица 5.3. Свойства класса Sys.Action

Свойство eventArgs

Описание Свойство доступно только для чтения; возвращает аргумент, переданный методу execute

result

Свойство доступно только для чтения; возвращает значение, полученное в результате выполнения метода performAction

sender

Свойство доступно только для чтения; возвращает владельца события, инициировавшего действие

sequence

Свойство доступно для чтения и записи; указывает, должно ли действие выполняться до или после обработчиков события, инициировавшего действие

target

Свойство доступно для чтения и записи; указывает объект Atlas, являющийся целью действия

Класс Sys.Action также содержит два метода: execute и performAction. Метод performAction никогда не должен вызываться напрямую, потому что он считается внутренним методом. В полноценном объектно-ориентированном языке программирования он, вероятно, был бы определен как защищенный (protected) метод. Метод performAction дает возможность производным классам выполнить те действия, для выполнения которых они проектировались. С другой стороны, метод execute относится к открытому интерфейсу и позволяет выполнять действия управляющему коду. Метод сначала вычисляет входные привязки действия, а затем выполняет конкретное действие, определяемое классом действия. В завершение метод вычисляет выходные привязки. Возвращаемое значение performAction сохраняется в переменной result. ПРИМЕЧАНИЕ

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

Класс Invoke Met hod Act ion Класс InvokeMethodAction используется для вызова методов компонентов Atlas. Он не может использоваться для вызова методов JavaScript. Класс содержит два дополнительных поля: method и parameters. Свойство method определяет имя метода, выполняемого для объекта, на который ссылается переменная target. Аргументы метода могут передаваться в поле eventArgs класса Action, а свойство sequence определяет, должен ли метод вызываться до или после обработчиков событий. В коллекции parameters могут передаваться входные аргументы вызываемого метода. В частности, коллекция parameters используется при постоянных данных. Если ввод зависит от других свойств, используются привязки.


Основные компоненты

179

Класс SetPropertyAction Класс SetPropertyAction предназначен для присваивания нового значения произвольному свойству компонента Atlas. Свойство property определяет имя задаваемого свойства, а свойство value — присваиваемое значение. Наследуемые свойства (такие, как target и sequence) играют ту же роль, что и в уже описанном классе InvokeMethodAction. Если свойство target ссылается на объект или массив, свойство propertyKey задает ключ или индекс. Действия могут связываться с событиями как на программном, так и на декларативном уровне. Схема XML Script для класса действия выглядит так: <setProperty property="имя целевого свойства" sequence=" BeforeEventHandler\AfterEventHandler" target="объект, для которого вызывается метод" value="3tf34etfjfe"<@062> </setProperty>

Дополнительные дочерние элементы могут задавать привязки для действия и участвующих компонентов. Следующий пример страницы Atlas демонстрирует возможности класса SetPropertyAction: <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Actions</title> </head> <body> <form id="forml" runat="server"> <asp:ScriptManager ID="ScriptManagerl" runat="server" /> <div> <hl>SetProperty Action in action</hl> <hr /> <p>Message: <strong><span id="msg"></span></strong></p> <p><input id="Buttonl" type="button" value="Click" /></p> <hr /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <label id="msg" /> <button id="Buttonl" click="DoSomething" > <click> <setProperty sequence="AfterEventHandler" target="msg" property="Text" value="A new value..." /> </click> </button> </components> </page> </script> <script type="text/javascript">


180

Глава 5. Библиотека Microsoft AJAX

function DoSomethingO { alertC'Believe it or not, I'm doing something..."); } </script> </body> </html>

Разметка HTML-страницы содержит клиентскую кнопку и тег <span>. Эти два элемента связываются с компонентами Atlas при помощи фрагмента кода XML Script. А именно, тег <span> связывается с компонентом <label>, а тег <input> — с компонентом <button>. Соответствие устанавливается по значениям идентификаторов: <components> <label id="msg" /> <button id="Buttonl" click="DoSomething" /> </components>

Как будет подробно объяснено позднее, компонент Atlas Button обладает свойством click, которое содержит ссылку на фрагмент кода JavaScript, выполняемого по щелчку пользователя. Помимо обработчика события, с событием компонента можно связать дополнительные действия: <button id="Buttonl" click="DoSomething" > <click> <setProperty sequence="AfterEventHandler" target="msg" property="Text" value="A new value..." /> </click> </button>

Когда пользователь щелкнет на кнопке, строка "A new value..." будет присвоена свойству Text компонента Atlas msg. Это действие будет выполнено после возврата управления событием JavaScript. Результат показан на рис. 5.2. В этом конкретном примере обработчик события JavaScript просто отображает окно сообщения. Пока окно сообщения остается активным, текст надписи не изменяется. После того как окно сообщения будет закрыто, сигнализируя о завершении обработки события, происходит обновление компонента Atlas msg.

Класс PostBackAction Класс PostBackAction немедленно выполняет классический возврат данных ASP. NET. Свойство eventArgument задает аргумент, передаваемый серверу. Свойство target определяет имя клиентского компонента, который считается ответственным за возврат данных. Класс PostBackAction в конечном счете вызывает функцию doPostBack, лежащую в основе механизма возврата данных ASP.NET. Реализация метода performAction класса PostBackAction выглядит так: this.performAction = function О { doPostBack(this.getjtargetО, this.get_eventArgument()); }


Основные компоненты

181

Рис. 5.2. Связывание действий с обработчиком события click кнопки

Свойства eventArgument и target соответственно заполняют скрытые поля EVENTARGUMENT и EVENTTARGET запроса ASP.NET. Учтите, что вызов doPostBack не включается в страницы ASP.NET по умолчанию. Чтобы избежать ошибки времени выполнения JavaScript Object Not Found (Объект не найден), необходимо либо включить в страницу серверный элемент, генерирующий этот код автоматически (например, LinkButton), либо написать его вручную. Пример: <Й?Раде Language="C#" %> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>PostBack Actions</title> </head> <script runat="server"> void Page_Load(object sender, EventArgs e) { // Ensures that doPostBack is referenced in the page ClientScript.GetPostBackEventReference(this, " " ) ; } void Buttonl_Click(object sender, EventArgs e) { Response.WriteCClicked"); </script> <body> <form id="form1" runat="server">


182

Глава 5. Библиотека Microsoft AJAX <asp:ScriptManager ID="ScriptManagerl" runat="server" /> <div> <hl>PostBack Action in action</hl> <span id="msg">Click here and see what happens!</span> <hr /> <asp:Button runat="server" ID="Buttonl" enabled="false" onclick="Buttonl_Click" Text="Click" /> <hr /> </div> </form>

<script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <button id="Buttonl" /> <label id="msg"> <behaviors> <clickBehavior> <click> <postBack target="Buttonl" eventArgument="" /> </click> </clickBehavior> </behaviors> </label> </components> </page> </script> </body> </html>

Разметка HTML-страницы содержит тег <span> и заблокированную кнопку отправки данных. Тег <span> отображается на элемент Atlas Label с поведением click (мы вернемся к теме клиентских аспектов поведения позднее в этой главе). Аспект поведения предоставляет событие Click и выполняет заданную последовательность действий каждый раз, когда пользователь щелкает на элементе. Для приведенного выше кода XML Script страница будет выполнять возврат данных каждый раз, когда пользователь щелкает на теге <span>. Что произойдет потом? Тег <postBack> назначает Button 1 исполнителем (executor) возврата данных. Другими словами, runtime-среда ASP.NET считает, что возврат данных был инициирован Buttonl. Соответственно ASP.NET будет проверять, существует ли элемент серверной стороны Buttonl, реализующий интерфейс IPostBackEventHandler. Если такой элемент не существует, возврат просто приведет к обновлению страницы без других последствий. В противном случае будет выполнен код возврата данных, связанный с элементом. В предыдущем примере Buttonl — простая кнопка отправки данных, созданная тегом <asp:Button>. С серверным событием Click кнопки связан фрагмент кода: <asp:Button runat="server" ID="Buttonl" enabled="false" onclick="Buttonl_Click" Text="Click" />


Основные компоненты

183

<script runat="server"> void Buttonl_Click(object sender, EventArgs e) { Response.Write("Clicked"); } </script>

Когда пользователь щелкает на тексте тега <span>, происходит возврат данных с целевым элементом Button 1. В результате генерируется серверное событие Click для элемента Button"!. Таким образом, класс PostBackAction позволяет клиентскому коду осуществлять возврат данных «от имени» практически любого серверного элемента ASP.NET, даже если на стороне клиента этот элемент заблокирован. Для успешного использования класса PostBackAction необходимо учитывать пару обстоятельств. Во-первых, проследите за тем, чтобы к странице была подключена функция JavaScript doPostBack. Это заведомо произойдет при включении в Page_Load следующего кода: С1 ientScript.GetPostBackEventReference(this, " " ) ;

Во-вторых, свойство target класса PostBackAction должно ссылаться на компонент Atlas, который, в свою очередь, должен ссылаться на существующий элемент DOM. А это означает, что для инициирования возврата данных от имени Button 1 должен использоваться следующий блок XML Script: <components> <button ID="Buttonl" /> </components>

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

Другие компоненты и функциональные возможности Библиотека Microsoft AJAX содержит ряд других компонентов, предоставляющих дополнительные возможности для разработчиков Atlas. Давайте кратко познакомимся с некоторыми из них, начиная со сценарной версии популярного управляемого класса .NET Framework — StringBuilder.

Операции со строками Класс Sys.StringBuilder наделяет страницы Atlas расширенными возможностями обработки текста. Как нетрудно догадаться по имени, класс имитирует поведение управляемого класса StringBuilder, определяемого в .NETG Framework. При создании экземпляра объекта-построителя указывается исходный текст. Построитель сохраняет текст во внутреннем массиве, создавая новый элемент для каждой следующей присоединяемой строки. Методы класса Sys.StringBuilder перечислены в табл. 5.4.


184

Глава 5. Библиотека Microsoft AJAX

Таблица 5.4. Методы класса Sys.StringBuilder

Метод append

Описание Присоединяет заданный текст к внутреннему массиву

appendLine

Вызывает метод append и присоединяет символ новой строки

clear

Стирает текст, хранящийся в построителе

isEmpty

Проверяет, содержит ли построитель какой-либо текст

toString

Возвращает содержимое массива в виде простой строки

Объект StringBuilder не работает с другими объектами, кроме строк, отличных от null. Метод toString формирует текст с использованием метода join класса массива: // _parts - внутренний массив, используемый для сохранения текста this.toString = function(delimiter) { return _parts.join(delimiter || ' ' ) ; }

Класс Atlas String также дополнен методом format, имитирующим поведение метода Format класса .NET Framework String: // RuntimeVersion - глобальная переменная в Atlas.js alert(String.format("Atlas version: {0}", RuntimeVersion));

Для определения заменителей в форматных строках используется синтаксис {п}. Реальное значение, подставляемое на место заменителя, определяется n-м аргументом вызова метода format.

Класс Sys.Timer Экземпляр класса Sys.Timer, размещенный на странице Atlas, генерирует события с заданным интервалом. Класс имеет два свойства, доступных для чтения и записи — enabled и interval. Свойство enabled указывает, активен ли таймер в настоящее время; по умолчанию оно равно false. Свойство interval задает периодичность возникновения события tick. Интервалы задаются в миллисекундах. По умолчанию свойство interval равно 1000 (одна секунда). Следующий фрагмент кода показывает, как производится программная настройка объекта таймера: <script type="text/javascript" language="Javascript"> var theTimer; function CreateTimerO { theTimer = new Sys.TimerO; theTimer.initializeO; // Запуск таймера theTimer.tick.add(OnTick); theTimer.set_enabled(true); } function OnTickO { // Чтобы избежать проблем с обработчиками tick,


Основные компоненты

185

// можно блокировать таймер при входе в обработчик // и включать его заново при выходе. var builder = new Sys.StringBuiIder(""); builder.appendLineC'The tick arrives every {0} milliseconds."); builder.appendC'Click CANCEL to stop the timer"); var msg = String.format(builder.toStringO , theTimer.get_interval()); var canContinue = confirm(msg); theTimer.set_enabled(canContinue);

} </script>

Чтобы запустить клиентский таймер, следует задать свойству enabled значение true — вызовите метод set_enabled и передайте параметр true. Если потребуется проверить, активен ли таймер в настоящее время, вызовите метод get_enabled. ПРИМЕЧАНИЕ

Для чтения свойств компонентов Atlas следует использовать методы getXXX, а для присваивания — методы setXXX, где XXX — имя свойства. С компонентом Timer также можно работать на декларативном уровне с использованием следующей схемы XML Script: <timer enabled="true|false" id-"идентификатор" interval-"интервал в миллисекундах" tick="обработчик события"/>

В главе 4 был представлен компонент TimerControl, предоставляющий те же возможности. Чем отличаются эти два компонента? Компонент Timer относится к числу компонентов Atlas клиентской стороны; это означает, что его событие tick является клиентским событием. Срабатывание таймера не приводит к возврату данных на сервер. С другой стороны, TimerControl — полноценный серверный элемент, используемый при разработке на стороне сервера. Соответственно, его экземпляр создается на стороне сервера, а в клиентскую страницу внедряется код, осуществляющий возврат данных при истечении интервала.

Класс Sys.Counter Класс Sys.Counter работает как простой счетчик с функциями увеличения и уменьшения. Программный интерфейс компонента описан в табл. 5.5. Таблица 5.5. Члены класса Sys.Counter Член класса

Описание

canDecrement

Свойство указывает, возможно ли дальнейшее уменьшение счетчика при его текущем значении и нижней границе Свойство указывает, возможно ли дальнейшее увеличение счетчика при его текущем значении и верхней границе

canIncrement

продолжение ■&


186

Глава 5. Библиотека Microsoft AJAX

Таблица 5.5 (продолжение)

Член класса decrement

Описание Уменьшает счетчик

increment

Увеличивает счетчик

lowerBound

Нижняя граница счетчика (чтение и запись)

upperBound

Верхняя граница счетчика (чтение и запись)

value

Текущее значение счетчика (чтение и запись)

При вызове методов decrement и increment класс автоматически проверяет, допустимо ли новое значение счетчика, и принадлежит ли оно текущим границам. Следующий фрагмент кода демонстрирует создание объекта Counter и его использование для остановки таймера после 5 тактов: <script type="text/javascript" language="Javascript"> var trie-Timer; var theCounter; function Setup() { // Подготовка таймера и счетчика theCounter = new Sys.CounterO; theCounter.set_value(0); theTimer = new Sys.TimerO ; theTimer.initialize(); // Запуск таймера theTimer.tick.add(OnTick); theTimer.set_enabled(true); // Обновление пользовательского интерфейса $("msg").innerHTML = "Timer started...";

}

function OnTickO { // Остановка таймера после 5 тактов if (theCounter.get_value() >5) { theTimer.set_enabled(false); // Обновление пользовательского интерфейса $("msg").innerHTML = "Timer stopped..."; alertC'Timer stopped"); $("msg").innerHTML = "";

} theCounter.increment();

} </script>

В этом примере я использую обычный код JavaScript для вывода сообщений через тег <span> с именем msg (рис. 5.3).


Основные компоненты

187

Рис. 5.3. Пример страницы Atlas с таймером и счетчиком

Привязки Atlas позволяют избавиться от лишнего кода JavaScript и ограничиться стопроцентным использованием XML Script практически в любой ситуации. Привязки будут более подробно рассмотрены далее в этой главе.

Класс Sys._Debug Также стоит упомянуть класс Sys._Debug. Основные возможности этого класса уже были описаны в главе 2. Экземпляр класса присваивается свойству debug объекта DOM window: window.debug = new Sys._Debug();

Отладочный объект DOM используется для проверки контрольных условий, выхода в отладчик и вывода трассировочного текста.

Класс Sys.Net.WebRequest Растущая популярность библиотек AJAX вообще и Atlas в частности в значительной степени обусловлена внеполосными веб-запросами. Некоторые библиотеки AJAX предоставляют простой программный интерфейс на базе функций для выполнения таких вызовов, однако в Atlas используется объектно-ориентированная подсистема, в центре которой находится класс WebRequest: <script type="text/javascript"> function sendRequest(url)


188

Глава 5. Библиотека Microsoft AJAX

{

var request = new Sys.Net.WebRequestO; request.set_url(url); request.invokeO;

} </script>

Этот фрагмент показывает, как использовать класс Sys.Net.WebRequest для создания веб-запроса к заданному URL. Класс WebRequest определяет свойство url для чтения и записи целевого URL, а также свойство headers для включения строк заголовков в запрос. Также разработчик может задать приоритет (свойство priority) и тайм-аут (свойство timeoutlnterval) для завершения запроса. Для запросов типа POST тело задается свойством body. Класс WebRequest задействован во многих встроенных возможностях страниц Atlas (см. главу 6), связанных с внеполосными вызовами.

Элементы пользовательского интерфейса Подсистема пользовательского интерфейса Atlas содержит набор клиентских элементов и аспектов поведения для взаимодействия с DOM. Клиентские элементы Atlas обладают более широкими возможностями, чем стандартные элементы HTML и соответствующие серверные элементы ASP.NET. Аспекты поведения реализуют дополнительные возможности и эффекты пользовательского интерфейса — перетаскивание, обработку задержки мыши и щелчков. К числу клиентских элементов Atlas относятся типичные веб-элементы (гиперссылки, кнопки, флажки, текстовые поля и надписи) и более сложные компоненты, такие как списки, источники данных и т. д.

Клиентские элементы Классы клиентских элементов Atlas в основном представляют собой «обертки» для элементов HTML. Они добавляют ряд новых функций к «родным» возможностям элементов HTML и принимают полноценное участие в модели программирования AJAX за счет поддержки действий, аспектов поведения и операций привязки данных. Все клиентские элементы являются производными от класса Control и могут настраиваться при помощи свойств и методов, лучше выражающих возможности и ожидаемое поведение инкапсулированных элементов HTML. Элемент Atlas связывается ровно с одним элементом HTML — либо автономным элементом, либо корнем дерева элементов. Для создания экземпляра и использования клиентских элементов можно воспользоваться традиционным кодом JavaScript или же прибегнуть к коду Atlas XML Script. В JavaScript экземпляр клиентской кнопки создается следующим образом: // Объявление переменной и подключение кода JavaScript var button = new Web.UI.Button($("Buttonl")); button.click.add(DoSomething);


Элементы пользовательского интерфейса

189

Экземпляр элемента Button связывается с элементом HTML-страницы с именем Button 1. Следующий фрагмент кода XML Script приводит к тому же результату: <script type="text/xml-script"> <components> <button id="Buttonl" click="DoSomething" /> </components> </script>

Обратите внимание: все объекты, на которые вы ссылаетесь в XML Script, должны объявляться в секции <components>.

Общие члены элементов Как упоминалось ранее, все элементы Atlas наследуют от класса Sys.UI.Control. Класс содержит ряд свойств и методов, общих для всех клиентских элементов. Общие свойства класса перечислены в табл. 5.6. Таблица 5.6. Свойства класса Sys.UI.Control

Свойство

Описание

accessKey

Клавиша ускоренного доступа для элемента HTML, находящегося под управлением элемента Atlas (чтение и запись)

associatedElement Возвращает элемент DOM, связанный с элементом Atlas behaviors

Массив аспектов поведения, связанных с элементом

cssClass

Класс CSS для элемента HTML (чтение и запись)

enabled

Устанавливает или снимает блокировку со связанного элемента HTML

parent

Родитель текущего элемента (чтение и запись)

style

Возвращает объект, представляющий текущий стиль элемента DOM

tabIndex

Индекс перебора элемента HTML, находящегося под управлением элемента Atlas (чтение и запись) Режим видимости элемента DOM, связанного с элементом (чтение и запись) Видимость элемента DOM, связанного с элементом (чтение и запись)

visibilityMode visible

Свойства visible и visibilityMode управляют атрибутом CSS visibility. Если свойство visible истинно, значение visibilityMode игнорируется, а элемент просто отображается на странице. Если свойство visible ложно, вы можете дополнительно указать, должен ли браузер зарезервировать место, необходимое для отображения элемента, и оставить его пустым либо просто пропустить элемент во время построения страницы. Допустимые значения — Hide (используется по умолчанию) и Collapse — объединены в перечисляемый тип Sys.UI.VisibilityMode. При значении Hide (соответствует числу 0) браузер резервирует место для отображения элемента, а при значении Collapse (соответствует числу 1) браузер полностью игнорирует элемент при построении.


190

Глава 5. Библиотека Microsoft AJAX

В табл. 5.7 перечислены методы, поддерживаемые всеми клиентскими элементами Atlas. Таблица 5.7. Методы класса Sys.UI.Control

Метод

Описание

addCssClass

Добавляет класс CSS к связанному элементу HTML

containsCssClass

Указывает, содержит ли связанный элемент HTML заданный класс CSS

Focus

Передает фокус ввода связанному элементу HTML

onBubbleEvent

Метод переопределяется производными классами для обработки событий, инициированных дочерними элементами

raiseBubbleEvent

Пересылает события родителям элемента

removeCssClass

Удаляет класс CSS из связанного элемента HTML

scrollIntoView

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

toggleCssClass

Давайте поближе познакомимся с клиентскими элементами Atlas и посмотрим, какими свойствами и методами они обладают.

Элемент Sys.UI.Button Элемент инкапсулирует произвольный элемент HTML и добавляет к нему поддержку обработки щелчков. Чаще всего элемент Button связывается с элементом HTML <input type=button>, однако он может быть связан с любой другой разметкой HTML, поддерживающей щелчки мышью. Небольшой пример: <form runat="server"> <input id="TextBoxl" type="text" value="" /> <input id="Buttonl" type="button" value="Set Value" /> <span id="FakeButtonl">Click me</span> </form> <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <textBox id="TextBoxl" /> <button id="Buttonl"> <click> <setProperty target="TextBoxl" property="text" value="A new value..." /> </click> </button> <button id="FakeButtonl"> <click> <setProperty target="TextBoxl" property="text" value="Another new value..." /> </click>


Элементы пользовательского интерфейса

191

</button> </components> </page> </script>

Страница содержит кнопку и тег <span>. В XML Script оба элемента HTML связываются с элементом Atlas Button и наделяются аспектом поведения click. Когда пользователь щелкает на этих элементах, действие setProperty используется для изменения содержимого текстового поля. Класс Button поддерживает два дополнительных свойства argument и command, а также событие click. Свойство argument определяет параметр события click, а свойство command определяет логическую команду, инициируемую кнопкой.

Элемент Sys.UI.CheckBox Элемент инкапсулирует флажок HTML и добавляет к нему новое логическое свойство checked, обозначающее состояние элемента: <input id="TextBoxl" type="text" value="" /> <input type="checkbox" id="CheckBoxl" /> <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <textBox id="TextBoxl" /> <checkBox id="CheckBoxl" click="Check" /> </components> </page> </script> <script type="text/javascript"> function CheckO { if ($("CheckBoxl").checked) $("TextBoxl").value = "Checked"; else $("TextBoxl").value = "Unchecked"; } </script>

Щелчок на флажке обнаруживается обработкой события click либо на уровне действий, либо в простом коде JavaScript.

Элемент Sys.UI.HyperLink Элемент инкапсулирует якорный тег HTML <а> и связывает его с моделью декларативного программирования Atlas. <а id="HyperLinkl">Click here</a> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005">


192

Глава 5. Библиотека Microsoft AJAX

<components> <hyperLink id="HyperLinkl" navigatellRL="..." /> </components> </page> </script>

Свойство navigateURL задает целевой URL гиперссылки, а событие click используется для явной обработки щелчка. Обратите внимание: при обработке события click можно выполнить дополнительные операции, но не отменить перенаправление.

Элемент Sys.UI.Image Элемент Image инкапсулирует тег <img>, используемый для вывода графических изображений. Он содержит ряд дополнительных свойств, в том числе alternateText, height, width и imageURL, но не предоставляет методов или событий. Чтобы создать графическую кнопку на стороне клиента, свяжите тег <img> с элементом Button.

Элемент Sys.UI.Label Элемент представляет тег <span> или любой другой тег, используемый в веб-страницах для представления блока текста или разметки. Свойство text определяет отображаемый текст, а логическое свойство htmlEncode — режим вывода текста (кодирование HTML или отображение в исходном виде). По умолчанию текст не кодируется. Во внутреннем представлении при включенном режиме кодирования текст задается свойством DOM innerText; в противном случае используется свойство innerHTML.

Элемент Sys.UI.Select Элемент Select представляет список выбора — например, простой перечень или раскрывающийся список. Свойство selectedlndex определяет индекс текущего выделения (нумерация начинается с 0). Событие selectionChanged происходит при изменении выделения на странице пользователем. Пример: <select id="SelectNumber"> <option value="zero">Zero</option> <option value="one">One</option> <option value="two">Two</option> </select> <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <select id="SelectNumber" selectionChanged="SelectionChanged" /> </components> </page> </script> <script type="text/javascript"> function SelectionChangedO { } </script>


Элементы пользовательского интерфейса

193

Другие свойства элемента Select относятся к функциям привязки данных. На веб-страницах списковые элементы часто связываются с динамически заполняемыми коллекциями данных. Свойство data обеспечивает чтение и запись связанной коллекции данных. Свойства valueProperty и textProperty определяют пути данных к значению и тексту каждого объекта в списке. Мы вернемся к этим свойствам позднее, когда будем рассматривать привязку данных в Atlas.

Элемент Sys.Ul.TextBox Элемент представляет собой «обертку» для элементов ввода HTML типа text. Он является производным от промежуточного класса InputControl, который, в свою очередь, является производным от Control и содержит одно дополнительное свойство text. Элемент не содержит методов и событий, но может участвовать в автоматической проверке данных на стороне клиента; при этом используются члены родительского класса InputControl, которые будут рассматриваться в следующем разделе. ПРИМЕЧАНИЕ

Как вы, возможно, заметили, элементы Atlas не содержат свойств и методов для явного задания визуальных атрибутов — таких, как цвет фона, тип фона или шрифт. Эти и другие графические свойства определяются в классах CSS, для назначения и управления которыми используются методы базового класса Control.

Элементы проверки данных Проверка данных — одна из самых распространенных операций, выполняемых веб-приложениями на стороне клиента перед отправкой форм на сервер. ASP.NET Atlas определяет ряд элементов с декларативными правилами, которые убеждаются в том, что пользователь ввел правильные значения в связанные элементы. Проверка данных применима ко всем элементам Atlas, производным от базового класса InputControl. Типичным примером элемента ввода с проверкой данных является элемент TextBox. В табл. 5.8 перечислены члены, определяемые в классе InputControl. Таблица 5.8. Свойства класса Sys.UI.InputControl

Свойство

Описание

isInvalid

Указывает, является ли текущее значение, введенное в поле, недействительным Возвращает список проверочных элементов Atlas, связанных с элементом ввода Возвращает сообщение об ошибке от первого нарушенного правила проверки данных

validators validationMessage validated

Выдает событие после проверки данных


194

Глава 5. Библиотека Microsoft AJAX

Все проверочные элемент Atlas наследуют от класса Validator. Класс определяет два базовых свойства: islnvalid и errorMessage. Первое возвращает состояние элемента, отслеживаемого проверочным элементом, а второе — сообщение об ошибке, которое выдается в случае недопустимого ввода. Кроме того, в классе Validator определяется абстрактный метод с именем validate, переопределяемый производными классами для реализации специализированного поведения при проверке. Связь между проверочными элементами Atlas и элементами ввода HTML устанавливается декларативно в коде XML Script. Впрочем, базовый класс Validator также содержит метод setOwner для программного присоединения проверочного элемента к элементу ввода. Задача решается включением экземпляра в коллекцию validators элемента ввода: var textBoxl = $("TextBoxl").control; var validator = new Sys.UI.RequiredFieldValidatorO ; validator.set_errorMessage("Must enter a value."); textBoxl.get_validators().add(validator);

Как упоминалось ранее, все элементы ввода поддерживают событие validated, происходящее в конце проверки. Обработчик события устанавливается на программном уровне: textBoxl.validated.add(onPostValidation);

Обработчик представляет собой фрагмент кода JavaScript с фиксированным прототипом: <script type="text/javascript"> function onPostValidation(sender, eventArgs) { // sender references the input control if (sender.get_islnvalid()) { alert(sender.get_validationMessage()); sender.focusO; } } </script>

Обработчик validated обычно добавляется в функции pageLoad. Событие validated происходит в конце проверки. Как правило, процесс проверки инициируется некоторым действием пользователя — скажем, при выходе из поля или щелчке за пределами элемента. На программном уровне проверка запускается чтением свойства islnvalid элемента ввода: // Код связывается с событием onclick клиентской кнопки function triggerValidation О { var invalidValue = $("TextBoxl").control.get_islnvalid(); if (invalidValue) alertC'Contents of TextBoxl is INVALID"); else alertC'All right!"); }


Элементы пользовательского интерфейса

195

ВНИМАНИЕ

Опытные веб-разработчики знают, что проверка на стороне клиента еще не гарантирует правильности данных, отправляемых на сервер. Например, в браузере может быть отключена поддержка JavaScript, или злонамеренный пользователь мог обойти клиентский сценарий и специально сгенерировать вызов с потенциально вредоносными данными. Любое серьезное веб-приложение содержит уровень проверки данных, на котором объединяются проверки на стороне клиента и сервера. Классические серверные элементы ASP.NET предоставляют функции проверки данных, у которых контроль на стороне клиента является необязательным. ASP.NET Atlas предоставляет набор клиентских элементов для быстрого выполнения проверки. Никогда не полагайтесь только на эти элементы, если хотите быть уверены в том, что до сервера добрались действительные данные. Заранее определенные элементы проверки данных проверяют, что поле ввода не осталось пустым, что значения заданного типа входят в нужный диапазон или проходят проверку по регулярному выражению.

Элемент Sys.UI.RequiredFieldValidator Элемент проверяет, не остался ли связанный с ним элемент пустым. Если введенные данные отсутствуют, элемент сбрасывает внутренний флаг и возвращает сообщение об ошибке. Поведение элемента жестко запрограммировано в метод validate: this.validate = function(value) { if (lvalue) { return false; } if (String.isInstanceOfType(value)) { if (value.length == 0) { return false; } } return true; }

Как видно из листинга, метод validate получает значимые данные из отслеживаемого элемента в аргументе и выполняет свою специализированную проверку. Отслеживаемый элемент знает о существовании связанных элементов проверки и вызывает их последовательно, передавая значение, которое он считает значимым. Рассмотрим пример использования RequiredFieldValidator с текстовым полем. Допустим, на странице имеется текстовое поле с близлежащим тегом <span>. Тег <span> предназначен для вывода признака недопустимых символов в поле — как правило, это символ «звездочка» (*). <input id="TextBoxl" type="text" /> <span id="TextBoxl_Validatorl" style="color: red">*</span>

А вот как будет выглядеть код XML Script: <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <textBox id="TextBoxl">


196

Глава 5. Библиотека Microsoft AJAX

<validators> <requiredFieldValidator errorMessage="You can't leave this field empty." /> </validators> </textBox> <validationErrorLabel id="TextBoxl_Validatorl" associatedControl="TextBoxl" /> </components> </page>

Сначала необходимое количество элементов проверки включается в секцию <validators> элемента TextBox. Затем добавляется элемент <validationErrorLabel>, у которого атрибут id ссылается на элемент DOM, представляющий признак ошибки в элементе ввода. Сообщение об ошибке элемента проверки становится всплывающей подсказкой для признака ошибки, как показано на рис. 5.4. Когда текстовый элемент назначается признаком ошибки, ASP.NET Atlas автоматически скрывает его и отображает только при наличии обнаружении ошибок.

Рис. 5.4. Элемент RequiredFieldValidator в действии

Элемент Sys.UI.RangeValidator Элемент проверяет, принадлежит ли введенное значение к заданному интервалу. Определяемый для него метод validate работает следующим образом: this.validate = function(value) { if (value && value.length) { return ((value <= jjpperBound) && (value >= _lowerBound)); } return true; }


Элементы пользовательского интерфейса

197

Элемент позволяет разработчику определять границы интервала при помощи пары новых числовых свойств lowerBound и upperBound: <textBox id="TextBoxl"> <validators> <rangeValidator lowerBound="l" upperBound="10" errorMessage="Values allowed between 1 and 10." /> </validators> </textBox>

Как упоминалось ранее, поведение одного элемента ввода может зависеть от комбинации нескольких проверочных элементов.

Элемент Sys.UI.RegexValidator Элемент проверяет, соответствует ли введенное значение заданному регулярному выражению. Регулярное выражение определяется в виде объекта JavaScript RegExp, доступного через свойство regex. Метод validate реализуется следующим образом: this.validate = function(value) { if (value && value.length) { var matches = _regex.exec(value); return (matches && (matches[0] == value)); } return true; }

Пример использования элемента на веб-странице: <textBox id="TextBoxl"> <validators> <regexValidator regex="/\(\d{3}$\)(\ )\d{3}$-\d{4}$/" errorMessage="Phone number in the format '(xxx) xxx-xxxx'." /> </validators> </textBox>

Элемент Sys.Ul.TypeValidator Элемент проверяет, относится ли значение к заданному типу. Тип JavaScript, используемый при проверке, задается при помощи свойства type: <textBox id="TextBoxl"> <validators> <typeValidator type="Number" errorMessage="Enter a number." /> </validators> </textBox>

Приведенный фрагмент показывает, как настроить элемент TypeValidator в вебстранице Atlas.

Элемент Sys.UI.CustomValidator Элемент CustomValidator позволяет разработчику написать собственный код проверки входных данных. В его свойстве validateValue указывается функция JavaScript, которая должна вызываться при проверке. <textBox id="TextBoxl"> <validators> <requiredFieldValidator errorMessage="Required field." />


198

Глава 5. Библиотека Microsoft AJAX

<customValidator validateValue="onValidateValue" errorMessage="Enter a valid color." /> </validators> </textBox>

В этом фрагменте содержимое поля ввода проверяется пользовательской функцией JavaScript onValidateValue. Пример проверочной функции обратного вызова: function onValidateValue(sender, eventArgs) { var colors = [ 'Red', 'Blue', 'Green', 'White']; var valid = colors.contains(eventArgs.get_value()); eventArgs.set_isValid(valid); }

Функция получает два аргумента. Аргумент sender обозначает элемент, инициировавший вызов функции. Аргумент eventArgs представляет собой объект типа CustomValidationEventArgs. Объект обладает двумя свойствами: value и isValid. Пользовательская функция выполняет все необходимые проверки свойства value, а затем соответствующим образом задает значение логического свойства isValid.

Группы проверки данных Некоторые элементы ввода на страницах ASP.NET могут объединяться в группы проверки данных. В таких случаях результат общей проверки считается истинным только в том случае, если все сгруппированные элементы ввода содержат действительные данные. Таким образом, разработчик может разбить все элементы ввода, находящиеся на странице, на логические группы и проверять их по отдельности. В Atlas группировка элементов ввода осуществляется при помощи компонента <validationGroup>: <validationGroup id="Groupl"> <associatedControls> <reference component="TextBoxl" /> <reference component="TextBox2" /> </associatedControls> </validationGroup>

Проверка группы элементов осуществляется методом isValid объекта ValidationGroup: <script type="text/javascript"> function validateGroupK) { var group = $object("Groupl"); var invalidValue = !group.get_isValid(); if (invalidValue) alertC'Contents of Groupl are INVALID"); else alertC'All right!"); } </script>


Элементы пользовательского интерфейса

199

Объект группы проверки является объектом Atlas, поэтому для его получения используется функция $object. Выполнение приведенного фрагмента кода JavaScript обычно инициируется событием onclick клиентской кнопки.

Аспекты поведения клиентской стороны Как было показано в главе 4, аспекты поведения представляют собой особые компоненты, используемые для связывания фрагментов сценарного кода с элементами HTML. Серверные расширители (см. главу 4) предназначены для расширения клиентского поведения разметки, сгенерированной серверными элементами. Клиентские аспекты поведения, о которых речь пойдет в этом разделе, находятся под контролем клиентского сценарного кода, а их присоединение и отсоединение осуществляется исключительно средствами JavaScript. ПРИМЕЧАНИЕ

Конечной целью, как серверных расширителей, так и клиентских аспектов поведения, является расширение возможностей элементов HTML. Клиентские аспекты поведения определяют фрагмент кода JavaScript, который перехватывает некоторые клиентские события и обрабатывает их особым образом. Для определения клиентского аспекта поведения потребуется код JavaScript или XML Script. Серверный расширитель генерирует сценарный код и XML Script для привязки сценарного кода к событиям клиентской стороны. Клиентский код при этом не обязан соответствовать клиентскому аспекту поведения. Другое различие заключается в том, что серверные расширители расширяют только разметку HTML конкретного набора серверных элементов. Если вы отдаете предпочтение серверо-центрической модели программирования Atlas, сначала рассмотрите возможность применения серверных расширителей. Клиентские аспекты поведения представляют собой компоненты, производные от класса Behavior и активизируемые клиентскими событиями — такими, как перемещения мыши, нажатия клавиш, срабатывания таймера и события DOM. Активизированный аспект поведения может делать практически все что угодно: обновлять свойства, запускать анимацию, реализовывать операции перетаскивания и т. д. С одним элементом HTML может быть связано несколько аспектов поведения; один аспект поведения может быть связан с несколькими элементами. В среде Atlas определяется несколько встроенных клиентских аспектов поведения, которые будут описаны в оставшейся части этого раздела.

AutoCompleteBehavior Автозаполнение расширяет возможности элементов TextBox и автоматически предлагает пользователю возможные варианты при вводе данных в поле. Данный аспект поведения представлен классом Sys.UI.AutoCompleteBehavior. При присоединении к элементу TextBox этот аспект сохраняет текст, введенный пользователем, и посылает запрос сопутствующей веб-службе. Вызванный метод веб-службы возвращает список предлагаемых вариантов, которые упорядочиваются в раскрывающийся список. Пользователь может быстро выбрать нужный текст вместо того, чтобы вводить его полностью.


200

Глава 5. Библиотека Microsoft AJAX

Обычно аспект поведения присоединяется к элементу TextBox через коллекцию behaviors. <asp:TextBox ID="CustomerName" runat="server" />

А вот что можно сделать на XML Script: <textBox id="CustomerName"> <behaviors> <autoComplete minimumPrefixLength="l" servicellRL="Suggestions.asmx" serviceMethod="GetSuggestions"> </autoComplete> </behaviors> </textBox>

Чтобы расширить текстовое поле функцией автозаполнения, вы сначала создаете (или находите) веб-службу, содержащую метод со следующей сигнатурой: public string[] MethodName(string prefix, int count);

Имя веб-метода выбирается произвольно. Аргумент prefix определяет текст, введенный пользователем до настоящего момента. На основании анализа ввода метод возвращает предлагаемые варианты. Аргумент count задает максимальное количество возвращаемых вариантов. На рис. 5.5 показан эффект выполнения предыдущего фрагмента для веб-службы suggestions.asmx, созданной в главе 4 для расширителя автозаполнения.

Рис. 5.5. Аспект поведения AutoCompleteBehavior в действии

Класс AutoCompleteBehavior дополняется рядом дополнительных свойств. Эти свойства перечислены в табл. 5.9.


Элементы пользовательского интерфейса

201

Веб-служба вызывается в процессе ввода данных в поле, расширенном поведением автозаполнения. Такая архитектура может породить большое количество запросов. Чтобы количество асинхронных запросов оставалось под контролем, аспект поведения гарантирует, что между двумя последовательными обновлениями пройдет заданное количество миллисекунд. Таблица 5.9. Свойства класса AutoCompleteBehavior

Свойство

Описание

completionInterval

Интервал между двумя последовательными обновлениями списка предлагаемых вариантов в миллисекундах; по умолчанию равен 1000 (чтение и запись)

completionList

Блочный элемент, используемый для вывода перечня предлагаемых вариантов (чтение и запись)

completionSetCount

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

minimumPrefixLength

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

serviceMethod

Имя веб-метода (чтение и запись)

serviceURL

URL веб-службы, используемой для получения предлагаемых вариантов (чтение и запись)

Стиль раскрывающейся панели до определенной степени находится под контролем разработчика. Используя свойство completionList, можно выбрать блочный элемент с предлагаемыми вариантами (обычно тег <div>). Однако большинство графических свойств элемента переопределяется аспектом поведения в соответствии со свойствами текстового поля. Из свойств, которые можно изменять, наибольший интерес представляет шрифт: <div style="font-family:verdana;font-size:8pt;" id="Div1" />

Если присоединить этот тег <div> к аспекту поведения через свойство completionList, предлагаемые варианты будут выводиться шрифтом Verdana.

ClickBehavior Класс ClickBehavior добавляет семантику обработки щелчков к различным элементам HTML. Он выявляет событие DOM onclick связанного элемента и выполняет обработчики событий, определенные пользователем, и действия Atlas. В аспекте поведения определяется единственное событие click. Обычно поведение click используется только с элементами HTML, не обладающими собственным синтаксисом обработки щелчков. Например, его не стоит использовать с кнопкой, потому что это лишь добавит излишней сложности


202

Глава 5. Библиотека Microsoft AJAX

элементу. С другой стороны, для добавления поддержки щелчков к тегу <span> без click не обойтись. Рассмотрим следующий фрагмент кода с надписью и кнопкой: <input type="button" id="Buttonl" value="Click" /> <br /> <asp:Label runat="server" ID="Label1" Text="Click me too" />

В странице Atlas щелчки на обоих элементах можно обрабатывать следующим образом: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <label id="Labell"> <behaviors> <clickBehavior click="ClickTheLabel" /> </behaviors> </label> <button id="Buttonl" click="ClickTheButton" /> </components> </page> </script> <script type="text/javascript"> function ClickTheButtonO { alertC'Believe it or not, you clicked the button..."); } function ClickTheLabel() { alertC'Believe it or not, you clicked the label..."); } </script>

Обработчик JavaScript для события щелчка на кнопке определяется при помощи атрибута click элемента Button. Щелчки на надписи обрабатываются с использованием компонента clickBehavior. Обратите внимание: в данном случае ClickTheLabel является глобальной функцией JavaScript. Действия Atlas также можно указать в качестве потомков элемента <clickBehavior>.

HoverBehavior Компонент HoverBehavior активизируется тогда, когда указатель мыши задерживается над элементом HTML. При входе указателя мыши в ограничивающий прямоугольник элемента генерируется событие hover, а при выходе из него — событие unhover. Кроме того, свойство unhoverDelay позволяет отложить выдачу события unhover на заданное количество миллисекунд. По умолчанию задержка равна 0, но если она задана, то позволяет скрыть лишние пары событий unhover/hover, если пользователь многократно выходит и входит в одну область за короткий период времени.


Элементы пользовательского интерфейса

203

Для примера рассмотрим элемент-кнопку Button 1. Следующий фрагмент кода вызывает и удаляет всплывающую подсказку при перемещении указателя мыши над кнопкой: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <button id="Buttonl"> <behaviors> <hoverBehavior hover="SetTooltip" unhover="ResetTooltip" /> </behaviors> </button> </components> </page> </script> <script type="text/javascript"> function SetTooltipO { $("Buttonl").title = "You're on top of the button"; } function ResetTooltipO { $("Buttonl").title = ""; } </script>

Если подсказка не изменяется в зависимости от условий времени выполнения, лучше воспользоваться внутренним флагом, чтобы свойство DOM title не приходилось изменять при каждом входе и выходе в область кнопки.

PopupBehavior Компонент PopupBehavior преобразует одиночный элемент DOM в элемент верхнего уровня, отображаемый поверх остальных элементов страницы (всплывающий элемент). В общем случае поведение PopupBehavior может быть связано с любым элементом DOM. Впрочем, эта возможность не имеет особого смысла для других элементов, кроме блочных — таких, как теги <div> или элементы ASP.NET Panel. ПРИМЕЧАНИЕ

Типичная ситуация с использованием других элементов, кроме блочных, — необходимость вызова временного текстового поля, когда пользователь щелкает в заданном месте или перемещает мышь над активной зоной. Впрочем, как было показано в главе 4, расширители HoverMenu и Popup способны прекрасно справиться с этой задачей с минимальными усилиями с вашей стороны. Проблема даже не в том, чтобы отобразить элемент DOM в режиме наложения, а в добавлении кода для задания свойств целевого элемента. С расширителями этот код достается «бесплатно». С другой стороны, расширитель Atlas Popup, упоминавшийся в главе 4, не связывается с клиентским поведением Popup, о котором идет речь. Лучшая ситуация для применения клиентского поведения Popup без написания кода — отображение расширенных всплывающих подсказок (об этом чуть позже).


204

Глава 5. Библиотека Microsoft AJAX

Класс PopupBehavior обладает четырьмя свойствами, перечисленными в табл. 5.10. Таблица 5.10. Свойства класса PopupBehavior

Свойство

Описание

parentElement

Элемент DOM, используемый для позиционирования (чтение и запись). Если говорить более абстрактно, parentElement указывает на элемент DOM, являющийся целью Popup-поля

positioningMode

Якорная точка, используемая для вычисления позиции Popupполя (чтение и запись). Допустимые значения объединены в перечисляемый тип PositioningMode

X

Горизонтальное смещение относительно якорной точки, заданной свойством positioningMode (по умолчанию 0)

Y

Вертикальное смещение относительно якорной точки, заданной свойством positioningMode (по умолчанию 0)

Позиция Popup-поля по отношению к родительскому элементу выражается значениями перечисляемого типа PositioningMode, перечисленными в табл. 5.11. Таблица 5 . 1 1 . Перечисляемый тип PositioningMode

Значение

Описание

Absolute

Всплывающий элемент располагается в левом верхнем углу родительского элемента и накрывает его полностью или частично в зависимости от относительных размеров (режим используется по умолчанию). Соответствует числовому значению 0

Center

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

BottomLeft

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

BottomRight

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

TopLeft

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

TopRight

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


Элементы пользовательского интерфейса

205

Следующий фрагмент кода показывает, как определяется всплывающий элемент: <div id="RichTooltip" style="display:none; border: solid 1px; "> This block of markup was originally inserted in the page as a DIV tag. It was then transformed into a popup element using the PopupBehavior component. </div> <input type="button" id="Button1" value="I’m the target of the popup" />

<script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <control id="RichTooltip"> <behaviors> <popupBehavior id="popupBehaviorl" parentElement="Buttonl" /> </behaviors> </control> <button id="Buttonl" click="ManagePopup" /> </components> </page> </script>

Связывание всплывающего элемента с родительским элементом было лишь первым шагом. Теперь необходимо найти удобный способ отображения или скрытия всплывающего элемента. Для этой цели используются методы show и hide класса PopupBehavior. Функция JavaScript ManagePopup отображает и скрывает всплывающее поле, когда пользователь щелкает на кнопке (рис. 5.6). <script type="text/javascript"> var _popupDisplayed = false; function ManagePopupO { var popup = $object("popupBehaviorl"); if (_popupDisplayed) popup.hideO; else popup.showO; _popupDisplayed = !_popupDisplayed; } </script>

Пример позволяет увидеть, как выбор режима позиционирования из перечисляемого типа PositioningMode влияет на размещение элемента. Чтобы задать режим позиционирования на программном уровне, достаточно включить в функцию ManagePopup следующий фрагмент: var pos = $("PositionModeList").selectedlndex; popup.set_positioningMode(pos);

Предполагается, что страница содержит раскрывающийся список с именем PositionModeList.


206

Глава 5. Библиотека Microsoft AJAX

Рис. 5.6. Отображение блока пользовательского интерфейса при щелчке на кнопке

Объединяя аспекты поведения PopupBehavior и HoverBehavior, можно реализовать удобные, полнофункциональные всплывающие подсказки (рис. 5.7). Пример: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <control id="RichTooltip"> <behaviors> <popupBehavior id="popupBehaviorl" parentElement="Buttonl" positioningMode="BottomLeft" /> </behaviors> </control> <button id="Buttonl"> <behaviors> <hoverBehavior hover="SetTooltip" unhover="ResetTooltip" /> </behaviors> </button> </components> </page> </script> <script type="text/javascript">


Элементы пользовательского интерфейса

207

function SetTooltip() { $object("popupBehavior1").show(); } function ResetTooltip() { $object("popupBehavior1").hide(); } </script>

Рис. 5.7. Реализация подсказок с использованием аспектов поведения Atlas

Целевой элемент (в данном случае кнопка) связывается с обоими аспектами, PopupBehavior и HoverBehavior. Мы назначаем кнопку родителем всплывающего элемента, а затем используем события hover и unhover для отображения и скрытия всплывающего элемента. В данном примере для отображения и скрытия используется код JavaScript. Излишне говорить, что вместо него также можно воспользоваться декларативными действиями: <button id="Buttonl"> <behaviors> <hoverBehavior> <hover> <invokeMethod target="popupBehaviorl" method="show" />


208

Глава 5. Библиотека Microsoft AJAX

</hover> <unhover> <invokeMethod target="popupBehaviorl" method="hide" /> </unhover> </hoverBehavior> </behaviors> </hoverBehavior>

Opacity Аспект поведения Opacity — простой компонент, позволяющий затушевать любой элемент DOM, с которым он связан. Учтите, что для работы данного аспекта в страницу необходимо включить сценарий atlasuiglitz.js: <atlas:ScriptManager ID="ScriptManagerl" runat="server"> <Scripts> <asp:ScriptReference ScriptName="AtlasllIGlitz" /> </Scripts> </asp:ScriptManager> Допустим, имеется панель ввода следующего вида: <asp:TextBox ID="TextBoxl" runat="server"></asp:TextBox><br /> <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox><br /> <input type="button" id="Buttonl" value="Start ..." /> Если кнопка запускает продолжительную операцию, некоторые элементы стоит затушевать — тем самым вы дадите пользователю понять, что дальнейшие действия от него не ожидаются. Желаемого эффекта можно добиться при помощи поведения Opacity: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <button id="Buttonl" click="StartTask"> <behaviors> <opacity id="opacityBehaviorl" /> </behaviors> </button> <textBox id="TextBoxl"> <behaviors> <opacity id="opacityBehavior2" /> </behaviors> </textBox> <textBox id="TextBox2"> <behaviors> <opacity id="opacityBehavior3" /> </behaviors> </textBox> </components> </page> </script> <script type="text/javascript"> function StartTask()


Элементы пользовательского интерфейса

{

209

// Задайте значение 1, чтобы вернуться к прежнему виду $object("opacityBehaviorl").set_value(0.3); $object("opacityBehavior2").set_value(0.3); $object("opacityBehavior3").set_value(0.3);

} </script>

Помните, что аспект поведения Opacity является чисто графическим инструментом. Он просто применяет графический фильтр к части экрана, занимаемой элементом DOM. Даже затушеванный элемент DOM остается доступным для ввода. Возможно, Opacity стоит использовать в сочетании с другими фрагментами сценарного кода или просто для реализации графических эффектов.

FloatingBehavior Компонент FloatingBehavior преобразует подмножество элементов DOM в плавающую область, которая может перетаскиваться мышью. Он является клиентским аналогом элемента DragPanelExtender, описанного в главе 4. Код, сгенерированный расширителем, точно совпадает с кодом, внедряемым напрямую с использованием аспекта FloatingBehavior. В конечном итоге выбор между DragPanelExtender и FloatingBehavior зависит от личных предпочтений — склонности к серверо-центрической или клиенто-центрической разработке. Чтобы использовать класс FloatingBehavior, необходимо явно импортировать в страницу сценарий atlasuidragdrop.js (это одна из важных подробностей, которую от вас скрывает расширитель). <atlas:ScriptManager ID="ScriptManagerl" runat="server"> <Scripts> <asp:ScriptReference ScriptName="AtlasUIDragDrop" /> </Scripts> </asp:ScriptManager>

Допустим, у вас имеется веб-страница с двумя панелями: <asp:Panel ID="CustomersPanelContent" runat="server"> <asp:Panel ID="CustomersPanelHandle" runat="server"> </asp:Panel> <asp:Panel ID="CustomersPanel" runat="server"> </asp:Panel> </asp:Panel> <asp:Panel ID="EmployeesPanelContent" runat="server"> <asp:Panel ID="EmployeesPanelHandle" runat="server"> </asp:Panel> <asp:Panel ID="EmployeesPanel" runat="server"> </asp:Panel> </asp:Panel>


210

Глава 5. Библиотека Microsoft AJAX

Следующий фрагмент кода XML Script позволяет перетаскивать мышью все содержимое панелей: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <control id="EmployeesPanelContent"> <behaviors> <floatingBehavior handle="EmployeesPanelHandle" /> </behaviors> </control> <control id="CustomersPanelContent"> <behaviors> <floatingBehavior handle="CustomersPanelHandle" /> </behaviors> </control> </components> </page> </script>

Смысл сценария вполне прямолинеен. Мы определяем панели как компоненты Atlas при помощи элемента <control>, а затем связываем их с поведением FloatingBehavior. Свойство handle обозначает элемент DOM, используемый в качестве манипулятора для перемещения панелей. Аспект поведения также генерирует событие move при успешном завершении операции перетаскивания. Учтите, что перетаскивание возможно не для всех элементов HTML. Исключение составляют любые теги <input>, тег <select>, тег <textarea>, кнопки и надписи.

Анимация В ASP.NET Atlas также входит ряд клиентских компонентов, реализующих несложные формы анимации. Анимация делает ваши веб-приложения более эффектными и привлекательными. Как правило, анимация применяется только к отдельным элементам DOM и не может автоматически распространяться на дочерние элементы. Например, это означает, что вы не сможете анимировать все содержимое блочного элемента. Рассмотрим самую популярную форму анимации — проявление и растворение (другие типы анимации сейчас находятся в процессе разработки; они будут доступны и полностью документированы к моменту выхода ASP.NET Atlas). Для применения любого анимационного эффекта необходимо добавить в компонент ScriptManager ссылку на сценарий AtlasUIGIitz (соответствующий код уже приводился в разделе, посвященном аспекту поведения Opacity, поэтому я его не повторяю). Предположим, имеется страница с панелью ввода, на которой пользователи обязаны вводить свои учетные данные для входа на сайт. Панель остается скрытой до тех пор, пока пользователь не щелкнет на кнопке регистрации. Код такой страницы может выглядеть примерно так: <input type="button" id="Buttonl" value="Register" /> <div id="InputPanel" style="display:none;">


Элементы пользовательского интерфейса

211

<asp:TextBox ID="TextBoxl" runat="server"></asp:TextBox><br /> <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox><br /> <asp:Calendar runat="server" ID="Calendarl" /> <asp:Button runat="server" id="Button2" text="Submit" /> </div>

Каждый элемент страниц может анимироваться (то есть отображаться с применением эффекта проявления): <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <button id="Buttonl" click="OnRegister" /> <control id="Calendarl" /> <fadeAnimation id="fader" target="Calendarl" effect="fadeln" /> </components> </page> </script>

Этот фрагмент готовит эффект анимации к работе с элементом-календарем на странице. Анимация должна запускаться явно, вызовом метода play класса Behavior. Вот что происходит при щелчке на кнопке Register: <script type="text/javascript"> function OnRegisterO { // Отображение панели ввода $("InputPanel").style["display"] = ""; // Запуск анимации var fader = $object("fader"); fader.pi ay();

} </script>

Аспект поведения также содержит методы stop и pause, обеспечивающие полный контроль за ходом операции. Во внутренней реализации он запускает таймер и периодически изменяет уровень прозрачности целевого элемента. ПРИМЕЧАНИЕ

Компонент FadeAnimationBehavior может успешно применяться для создания слайдовых презентаций на веб-страницах, со сменой изображений по глобальному таймеру. В данном примере мы хотим анимировать несколько элементов, но как выясняется, анимация задается только для одного из них. Свойство target аспекта поведения может задаваться на программном уровне, поэтому следующий код на первый взгляд выглядит разумно: function OnRegisterO { // Отображение панели ввода $("InputPanel").style["display"] = ""; // Получение ссылки на объект fader


212

Глава 5. Библиотека Microsoft AJAX var fader = $object("fader");

}

// Анимация нескольких элементов fader.set_target($object("Button2")); fader.pi ay(); fader.set_target($object("Calendarl")); fader.pi ay();

К сожалению, этот код не работает так, как ожидается. Каждый последующий вызов play останавливает текущую анимацию. Это связано с особенностями асинхронной логики, используемой во внутренней реализации метода play. При таком кодировании анимация не работает так, как можно было бы ожидать. Чтобы анимировать несколько разных элементов DOM, необходимо воспользоваться аспектом поведения CompositeAnimation: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <control id="Calendarl" /> <button id="Buttonl" click="OnRegister" /> <button id="Button2" /> <compositeAnirnation id="fader"> <animations> <fadeAnimation target="Button2" effect="fadeln" /> <fadeAnimation target="Calendarl" effect="fadeln" /> </animations> </compositeAnirnation> </components> </page> </script> <script type="text/javascript"> function OnRegisterO { $("InputPanel").style["display"] = ""; var fader = $object("fader"); fader.pi ay(); } </script>

Компонент CompositeAnimation позволяет сгруппировать несколько дочерних анимаций и выполнить их как единую операцию.

Заключение Библиотека Microsoft AJAX представляет собой набор классов JavaScript, определяющих два основных программных инструмента — элементы управления и аспекты поведения. Впрочем, на пути к построению этих инструментов создается целый ряд фундаментальных классов, закладывающих основу для их построения. Во-первых, основные классы расширяют язык JavaScript объектно-ориентированными средствами (такими, как наследование, интерфейсы и пространства


Заключение

213

имен). Во-вторых, фундаментальные классы определяют базовое поведение компонентов и элементов для построения иерархии классов, напоминающей специализированную версию .NET Framework. Компоненты Atlas, как и компоненты .NET Framework, представляют собой классы без пользовательского интерфейса, рассчитанные на многократное использование. В них реализован набор общих интерфейсов и механизм обнаружения изменений свойств. Самой распространенной разновидностью компонентов Atlas являются аспекты поведения клиентской стороны. С другой стороны, элементы представляют собой многократно используемые классы, в которых возможности пользовательского интерфейса играют ключевую роль. Элементы Atlas — классы на базе JavaScript, работающие на стороне клиента и предоставляющие общую функциональность (например, текстовые поля, кнопки и надписи). В этой главе мы рассмотрели клиентские элементы и аспекты поведения Atlas и сравнили их с серверными расширителями элементов, описанными в главе 4. Тема привязки данных не рассматривалась; объекты данных, а также связанные с ними элементы и аспекты поведения будут описаны в главе 6. В следующей главе мы вернемся к теме служб, кратко упомянутой в главе 2. В частности, в ней будет показано, как вызывать веб-службы из страниц Atlas и как организовать доступ к функциональности страницы при помощи служб.


ГЛАВА 6

Обращение к серверным службам В I I I

этой главе: Встроенные прикладные службы Использование веб-служб Связь с внешними веб-службами

Главным фактором, обусловившим успех и быстрое распространение модели AJAX, является ее возможность выполнения большинства задач приложения на стороне клиента, с ограниченным обменом данными с сервером. Тем не менее некоторый обмен данными с сервером все же необходим. Как упоминалось в главах 3 и 4, приложения Atlas могут использовать асинхронный возврат данных для частичного обновления страниц и обновления состояния элементов посредством серверных событий. Однако в некоторых ситуациях простого возврата данных недостаточно для завершения желаемых действий клиента — иногда для этого требуется выполнить определенную операцию на сервере. Результаты операции должны быть переданы на сторону клиента для включения в текущую страницу. Обращение по удаленному серверному URL для выполнения некоторого кода и получения результатов — хорошо известная схема, нашедшая самую популярную реализацию в модели веб-служб. Прикладная среда Atlas предоставляет ряд механизмов для обращения к веб-службам. В наши дни термину «веб-служба» склонны придавать слишком широкое толкование. В Atlas термином «веб-служба» обозначаются веб-службы Microsoft ASP.NET (файлы .asmx), внешние веб-службы, работающие на различных платформах (например, Amazon, Google и MSN), и, наконец, службы Microsoft WCF (Windows Communication Foundation) — новейшей программной модели для построения связных систем, которая появится в .NET Framework 3.0. Несмотря на свою расширенную, улучшенную функциональность, приложения Atlas в первую очередь являются приложениями ASP.NET и веб-приложениями. Следовательно, их работа по-прежнему зависит от традиционного сервиса прикладного уровня — аутентификации, управления состоянием сеанса, кэшированием на стороне сервера, профилями пользователей, проверкой информации ролей и принадлежности к группам. Серверная среда ASP.NET предоставляет в распоряжение разработчика такие средства, как объект HttpContext (с дочерними объектами Session, Cache, Profile и User, статические классы для решения распро-


Встроенные прикладные службы

215

страненных задач вроде управления профилем или аутентификации пользователя, и т.д. Библиотека Microsoft AJAX инкапсулирует часть сервера прикладного уровня во встроенных веб-службах, с возможностью вызова и управления со стороны клиента. В этой главе мы рассмотрим связи между прикладной средой Atlas и миром веб-служб. Читатель узнает, как настроить приложение Atlas для работы с прикладными службами, как пользоваться службами ASP.NET и подключаться к веб-службам на внешних серверах.

Встроенные прикладные службы Среды серверного программирования — такие, как ASP.NET, классическая платформа ASP и Java Server Pages — формируют абстрактную прослойку, предоставляющую сервисные функции веб-приложениям. К числу таких сервисных функций относится управление состоянием сеанса, аутентификация, проверка принадлежности к группам и кэширование. Вследствие самой архитектуры эти серверные функции доступны только для серверного кода и недоступны напрямую для JavaScript и объектной модели страницы. В результате клиентская страница, которой потребуется обновить состояние сеанса или провести аутентификацию пользовательских данных, должна инициировать полный возврат данных страницы или, как минимум, обратиться с вызовом к веб-службе. Прикладные службы Atlas представляют собой обычные веб-службы ASP.NET, предоставляющие доступ к сервисным функциям со стороны сервера, которые может быть полезно вызывать со стороны клиента. Atlas предоставляет встроенные «обертки» для двух серверных служб: аутентификации форм и проверки данных пользовательского профиля.

Аутентификация форм Чтобы защитить веб-страницы от посторонних пользователей, вы помещаете в начало каждой из них относительно шаблонный код и перенаправляете пользователя на страницу входа. На странице входа пользователю предлагается ввести свои регистрационные данные. Если аутентификация прошла успешно, пользователь перенаправляется на исходную запрашиваемую страницу. Аутентификация форм и является встроенной инфраструктурой ASP.NET, реализующей описанную схему для входа в систему. Серверная аутентификация файла требует дополнительной страницы ввода и двух HTTP-команд перенаправления. Можно ли выполнить аутентификацию прямо на стороне клиента без перенаправления? Оказывается, можно. Класс JavaScript Sys.Services._AuthenticationService позволяет передать регистрационные данные встроенной веб-службе и получить логический признак, указывающий, успешно ли прошла аутентификация. Использование класса JavaScript ускоряет аутентификацию пользователей, так как форма регистрации может быть встроена в существующую страницу (вместо перенаправления на специализированную страницу ввода регистрационных данных).


216

Глава 6. Обращение к серверным службам

Веб-служба аутентификации Служба аутентификации Atlas реализована в виде веб-службы ASP.NET. Она закодирована в классе AuthenticationWebService из пространства имен Microsoft.Web. Services.Standard, входящего в сборку Microsoft.Web.Atlas. Класс AuthenticationWebService соответствует следующему URL: ScriptServices/ Microsoft/ Web/ Services/ Standard/ AuthenticationWebService.asmx

Класс JavaScript Sys.Services._AuthenticationService представляет собой «обертку» для приведенного URL. Взглянув на реализацию класса JavaScript в файле atlas.js, вы увидите следующий исходный код: Type.registerNamespace('Sys.Services'); Sys.Services._AuthenticationService = functionO { this.path = "ScriptServices/.../AuthenticationWebService.asmx"; var cm = cm(this, cm(this, cm(this,

Sys.Net.ServiceMethod.createProxyMethod; "login", "userName", "password", "createPersistentCookie"); "logout"); "validateUser", "userName", "password");

} Sys.Services._AuthenticationService.registerSealedClass( 'Sys.Services._AuthenticationService'); Sys.Services.AuthenticationService = new Sys.Services._AuthenticationService(); ПРИМЕЧАНИЕ

Имя класса JavaScript начинается с символа подчеркивания (_). Впрочем, в своих приложениях вы будете использовать автоматический созданный глобальный экземпляр этого класса с именем Sys.Services.AuthenticationService. Этот прием является своего рода имитацией статических методов в языке JavaScript. Интересно заметить, что вы не найдете файлов .asmx в своей установке Atlas, в своем приложении и вообще где-либо на серверном компьютере. Как мы вскоре увидим, приложения Atlas обрабатывают запросы .asmx с использованием специализированных обработчиков HTTP, отличающихся от обработчиков HTTP, используемых классическими приложениями ASP.NET. Специальный обработчик веб-службы распознает запрашиваемый URL и связывает его с типом сервера. Например, URL AuthenticationWebService.asmx отображается на тип AuthenticationWebService, а URL ProfileWebService.asmx - на тип ProfileWebService.

Программный интерфейс службы аутентификации В табл. 6.1 перечислены методы службы аутентификации. Обратите внимание: в таблице представлены методы веб-службы на стороне сервера, то есть методы класса AuthenticationWebService. Тем не менее «оберточный» класс JavaScript —


Встроенные прикладные службы

217

Sys.Services._AuthenticationService — обладает тем же набором методов с одинаковыми сигнатурами. Таблица 6.1. Веб-методы службы аутентификации Метод

Описание

login

Проверяет регистрационные данные и выдает аутентификационный мандат, если данные прошли проверку

logout

Заставляет сервер прекратить действие аутентификационного мандата

validateUser

Проверяет регистрационные данные и возвращает логический признак

Метод Atlas login обладает следующей сигнатурой: [WebMethod] public bool login( string userName, string password, bool createPersistentCookie)

Первые два аргумента (userName и password) задают регистрационные данные; последний логический аргумент указывает, нужно ли создавать объект cookie для долгосрочного хранения. При проверке пользователя с использованием механизма аутентификации форм cookie создается и присоединяется к ответу. В дальнейшем этот объект cookie называется «аутентификационным мандатом». Если аргументу createPersistentCookie задано значение false, срок действия выданного мандата истекает через 30 минут. Если выбран режим долгосрочного создания мандата, его срок действия истечет через 50 лет. Что делать, если потребуется создать cookie с промежуточным сроком действия — между 30 минутами и 50 годами? Для этого следует задать нужный промежуток времени в минутах в атрибуте timeout секции <authentication> файла web.config. Метод веб-службы login использует следующий код для выполнения своей работы: if (Membership.Provider.ValidateUser(userName, password)) { FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); return true; } return false;

Как видите, для проверки используется текущий провайдер принадлежности, а для создания cookie используется метод SetAuthCookie класса FormsAuthentication. Это означает, что при создании мандата аутентификации используется большинство параметров, определенных в секции <authentication> конфигурационного файла. Метод logout вызывается без аргументов и просто удаляет cookie из ответа. Во внутренней реализации он в конечном счете сводится к вызову метода SignOut класса FormsAuthentication.


218

Глава 6. Обращение к серверным службам

ПРИМЕЧАНИЕ

Служба аутентификации Atlas не работает с приложениями, использующими схему аутентификации без применения cookie. Метод SetAuthCookie работает так, как обычно в классических приложениях ASP.NET, и изменяет возвращаемый URL с включением в него аутентификационного мандата. Однако в классических приложениях ASP.NET системный компонент (фильтр ISAPI aspnet_filter.dll) вмешивается в обработку, перемещает мандат в заголовок и заменяет URL тем, который ранее запрашивался. При выполнении аутентификации через обращение к веб-службе не генерируется новый запрос, который мог бы быть перехвачен фильтром. В Atlas вызывающая страница просто получает ответ о результате аутентификации — возможно, с прикрепленным к ответу cookie. Так как замена URL не выполняется, Atlas не сможет поддерживать аутентификацию без применения cookie. Наконец, метод validateUser проверяет регистрационные данные, но ограничивается возвратом логического признака. Он не имеет ничего общего с аутентификационными мандатами. Псевдокод метода выглядит так: [WebMethocf public bool validateUser(string userName, string password) { // Проверить, доступна ли поддержка аутентификации Atlas AuthenticationWebServi се.CheckAuthenticationServicesEnabled();

}

// Проверить регистрационные данные с использованием // текущего провайдера принадлежности return Membership.Provider.ValidateUser(userName, password);

Аутентификация Atlas может производиться только через провайдера принадлежности. Если вам потребуется реализовать нестандартную логику аутентификации пользователей, постройте специализированного провайдера.

Конфигурация службы аутентификации Служба аутентификации Atlas требует задания нескольких атрибутов в файле web.config приложения. В частности, необходимо разрешить использование службы: <authenticationService enabled="true" />

По умолчанию служба аутентификации отключена. Секция <authenticationService> находится в новой секции <Microsoft.Web>, относящейся к Atlas. Обе секции должны быть правильно зарегистрированы. <configuration> <configSections> <sectionGroup name="inicrosoft.web" type="Microsoft.Web.Configuration.MicrosoftWebSectionGroup"> <section name="authenticationService" type="Microsoft.Web.Configuration.AuthenticationServiceSection" requirePermission="false"/> <section name="profileService" type="Microsoft.Web.Configuration.ProfiIeServiceSection"


Встроенные прикладные службы

219

requirePermission="false"/> </sectionGroup> <configSections> </configuration>

В приложениях Atlas, построенных с использованием шаблона Microsoft Visual Studio 2005, необходимые настройки уже включены в стандартный файл web.config. Помимо настройки секций, специфических для Atlas, для использования внеполосной аутентификации Atlas необходимо включить аутентификацию форм ASP.NET в секции <system.web>: <system.wet» <authentication mode="Forms" /> </system.wet» ПРИМЕЧАНИЕ

Почему для включения аутентификации в приложениях Atlas необходимы два разных параметра? Приложения Atlas остаются приложениями ASP.NET, хотя в них и добавилась новая функциональность. Runtime-среда, обрабатывающая запросы, остается одной и той же как в классических приложениях, так и в приложениях Atlas ASP.NET. Необходимость настройки двух разных параметров объясняется тем, что в настоящее время Atlas реализуется в виде «надстройки», использующей некоторые функции ASP.NET. В следующей версии ASP.NET функциональность Atlas будет полностью интегрирована. Вероятно, когда это произойдет, параметры службы аутентификации Atlas будут настраиваться непосредственно в подсекции <authentication> секции <system.web>.

Использование службы аутентификации в приложении В классическом приложении ASP.NET, настроенном для использования аутентификации форм, защищенные страницы группируются в одной или нескольких вложенных папках, при этом локальный файл web.config содержит следующий код: <configuration> <system.wet» <authorization> <deny users="?" /> <allow users="*" /> </authorization> </system.web> </configuration>

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


220

Глава 6. Обращение к серверным службам

упрощается системой. Конечно, на главной странице можно разместить ссылку на страницу ввода данных, чтобы пользователи могли пройти аутентификацию перед попыткой обращения к защищенным страницам. Впрочем, такой подход не является строго обязательным с функциональной точки зрения. Благодаря тому, что механизм аутентификации форм встроен в ASP.NET, такая модель будет работать в приложении Atlas без необходимости внесения каких-либо изменений. Однако основной целью технологии Atlas является сокращение количества перенаправлений и полных возвратов данных. В приложении Atlas форма ввода регистрационных данных обычно встраивается в основную страницу, а обращение к службе аутентификации производится тогда, когда пользователь щелкает на кнопке Log In. На рис. 6.1 показан пример страницы Atlas с изначально скрытой панелью ввода регистрационных данных.

Рис. 6 . 1 . Реализация аутентификации в приложениях Atlas

Как анонимные, так и проверенные пользователи могут обращаться к странице и пользоваться ее возможностями; другие функции доступны только для зарегистрированных пользователей.

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


Встроенные прикладные службы

221

с парой текстовых полей и кнопкой. Излишне напоминать, что поля ввода можно снабдить любыми расширителями по вашему усмотрению (рис. 6.2).

Рис. 6.2. Сворачиваемая форма ввода регистрационных данных

Разумеется, в приложениях Atlas кнопка Log In является клиентской кнопкой и указывает на фрагмент кода JavaScript для вызова функций аутентификации: <input type="button" id="LoginButton" value="Log In" onclick="OnLogin()" />

Функция On Log in выглядит примерно так: function OnLoginO

{

Sys.Services.AuthenticationService.login( username.value, password.value, false, OnLoginComplete); return false;

}

Как видите, метод login класса JavaScript получает четвертый аргумент — функцию обратного вызова JavaScript. В конечном счете этот класс является посредником для вызова веб-службы; классы-посредники дополняют сигнатуру метода необязательной функцией обратного вызова, которая активизируется


222

Глава 6. Обращение к серверным службам

при возврате из метода. Возможная реализация OnLoginComplete может выглядеть так: function OnLoginComplete(result) { if (result) { // UserName и Password - идентификаторы полей ввода $("UserName").value = ""; $("Password").value = ""; CloseLoginPanel(); } else alertC'Sorry, your credentials appear to be invalid."); } function CloseLoginPanel() { var behavior = $object("CollapseLoginPanell"); behavior.set_Collapsed(true); }

Возвращаемое значение метода веб-службы передается функции обратного вызова. В нашем примере это логический флаг, обозначающий результат аутентификации. Если проверка прошла успешно, мы сбрасываем пользовательский интерфейс формы ввода регистрационных данных и сворачиваем панель. <asp:Panel id="LoginPanel" runat="server" Height="0"> </asp:Panel> <act:Col 1apsi Ы ePanelExtender runat="server"> <act:Col 1apsi Ы ePanelProperties ID="CollapseLoginPanel1" TargetControlID="LoginPanel" /> </act:Col 1apsi Ы ePanelExtender>

На рис. 6.3 показано, как выглядит ответ HTTP для запроса, обращенного к URL AuthenticationWebService.asmx для выполнения метода login (в окне утилиты Web Development Helper, кратко упоминавшейся в главе 2). Как видите, ответ HTTP содержит дополнительный объект cookie с именем .ASPXAUTH. Это и есть аутентификационный мандат, выданный сервером и переданный клиенту. Начиная с этого момента пользователь может обращаться ко всем защищенным страницам приложения, вплоть до истечения срока действия мандата. Чтобы дать пользователю возможность выйти из системы «по рецепту Atlas», мы добавляем еще одну клиентскую кнопку и связываем ее со следующим кодом JavaScript: function Logout() { Sys.Services.AuthenticationService. 1 ogout(OnLogoutComplete); return false; }

При возврате из функции ответ HTTP выглядит так, как показано на рис. 6.4.


Встроенные прикладные службы

Рис. 6.3. Ответ HTTP для вызова метода login службы аутентификации

Рис. 6.4. Ответ HTTP для вызова метода logout службы аутентификации

223


224

Глава 6. Обращение к серверным службам

Тело cookie .ASPXAUTH стало пустым, и мандат уже не может использоваться для получения доступа к защищенным страницам приложения.

Типичные усовершенствования пользовательского интерфейса Любые приложения, использующие сервис аутентификации, должны предоставлять дополнительные элементы пользовательского интерфейса для вывода приветствия зарегистрированному пользователю и для выхода из системы. Кроме того, на некоторых страницах также могут размещаться условные элементы, отображаемые только для аутентифицированных запросов. Как узнать, прошел ли текущий запрос аутентификацию? В классической модели ASP.NET достаточно проверить свойство Request. IsAuthenticated в Page_Load (или там, где вам понадобится эта информация). В Atlas дело обстоит несколько иначе. При возврате управления методом login вы знаете, был ли аутентифицирован текущий пользователь. Предположим, пользователь перешел к другой странице приложения, а затем щелкнул на кнопке Back или любой ссылке, возвращающей к исходной странице. При возврате к исходной странице браузер пользователя теряет всю информацию о состоянии аутентификации — в конце концов, состояние аутентификации было всего лишь значением глобальной переменной JavaScript. Если не учесть эту возможность при проектировании приложения, пользовательский интерфейс может указывать на то, что пользователь не вошел в систему, тогда как модуль аутентификации будет правильно обрабатывать дальнейшие запросы к защищенным страницам. Рассмотрим блок кода пользовательского интерфейса для вывода имени зарегистрированного пользователя: <div runat="server" id="LoggedIn" style="display:none;"> <h2>Hey, <asp:Label id="Loggedllser" runat="server" />   <input type="button" value="Log out" onclick="Logout()" /></h2> </div> <div runat="server" id="NotLoggedIn" style="display:none;color:red;"> <h2>Not logged in</h2> </div>

Первый блок <div> используется для зарегистрированных пользователей, а второй — для всех остальных. Переключение осуществляется на основании результата вызова login и состояния аутентификации запроса. Чтобы упростить задачу, включите следующий фрагмент в событие Page_Load кодового файла: protected void Page_Load(object sender, EventArgs e) { if (Request.IsAuthenticated) { LoggedIn.Style["display"] = ""; NotLoggedIn.Style["display"] = "none"; _oggedUser.Text = User.Identity.Name; // Отобразить элементы пользовательского интерфейса // для зарегистрированных пользователей }


Встроенные прикладные службы

225

else { _oggedIn.Style["display"] = "none"; NotLoggedIn.Style["display"] = ""; _oggedUser.Text = ""; // Скрыть элементы пользовательского интерфейса // для зарегистрированных пользователей }

}

Этот код гарантирует инициализацию элементов пользовательского интерфейса при первой загрузке страницы и при всех последующих обновлениях (по ссылке или кнопкам Back/Forward). Кроме того, мы расширяем функции OnLoginComplete и OnLogoutComplete для динамического обновления пользовательского интерфейса при входе или выходе пользователя прямо со страницы. Например, можно воспользоваться следующей функцией: <script type="text/javascript"> var currentUser = $("LoggedUser"); var username = $("UserName"); var password = $("Password"); var loggedln = $("LoggedIn"); var notLoggedln = $("NotLoggedIn"); function HandleLoginLogoutState(userLogged) { if (userLogged) { notLoggedln.style.display = "none"; loggedln.style.display = ""; currentUser.innerText = username.value; // Отобразить элементы пользовательского интерфейса // для зарегистрированных пользователей } else { loggedln.style.display = "none"; notLoggedln.style.display = ""; currentUser.innerText = ""; // Скрыть элементы пользовательского интерфейса // для зарегистрированных пользователей } } </script>

Функция HandleLoginLogoutState вызывается как из OnLoginComplete, так и из OnLogoutComplete; при этом ей передаются значения true и false соответственно.

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


226

Глава 6. Обращение к серверным службам

личные настройки. Возможности персонализации в ASP.NET 2.0 строятся на основе концепции пользовательского профиля. Пользовательский профиль представляет собой совокупность свойств, которые объединяются runtime-средой ASP.NET 2.0 в динамически сгенерированный класс. Приложение определяет собственную модель данных в конфигурационном файле, а runtime-среда ASP.NET делает все остальное, разбирая и компилируя эту модель в класс. Каждое поле класса соответствует фрагменту информации, специфическому для текущего пользователя. Все данные профилей сохраняются для конкретного пользователя и существуют до тех пор, пока не будут удалены обладателем административных привилегий. При запуске приложения и отображении страницы ASP.NET динамически создает объект профиля, который содержит свойства, определенные в модели данных (с правильными типами). Созданный объект добавляется в текущий объект HttpContext и становится доступным для страниц через свойство Profile. Профильный сервис ASP.NET реализуется через модуль HTTP. Модуль вступает в дело после аутентификации запроса и загружает данные для конкретного пользователя. (Для анонимных пользователей генерируется уникальный идентификатор, сохраняемый в cookie .ASPXANONYMOUS.) В конце обработки запроса объект профиля сохраняется в хранилище данных, готовый к следующему использованию. Чтение и запись профильных свойств в серверных страницах осуществляется через объект HttpContext.Profile. Можно ли сделать то же в странице Atlas при внеполосном возврате данных? Оказывается, можно. Класс Sys._Profile из файла atlas.js выполняет функции «моста» между клиентской страницей и серверным объектом HttpContext.Profile. ПРИМЕЧАНИЕ

Хранилище данных для профильной службы настраивается в файле web.config с использованием специальных компонентов, называемых провайдерами профилей (profile providers).

Веб-служба профилей Профильный сервис Atlas реализован в виде веб-службы ASP.NET. Она закодирована в классе ProfileWebService из пространства имен Microsoft.Web.Services. Standard, входящего в сборку Microsoft.Web.Atlas. Класс ProfileWebService соответствует следующему URL: ScriptServices/ Microsoft/ Web/ Services/ Standard/ Profi1eWebService.asmx

Класс JavaScript Sys._Profile представляет собой «обертку» для приведенного URL. Класс предоставляет расширенный программный интерфейс со свойства-


Встроенные прикладные службы

227

ми и событиями, но центральное место в нем занимают два метода load и save, отображаемые на веб-методы серверного класса веб-службы ProfileWebService. В своих страницах Atlas вам не придется явно создавать экземпляры класса Sys._Profile; вместо этого вы будете использовать объект Sys.Profile, который представляет собой глобальную ссылку на единый экземпляр профильной службы: Sys.Profile = new Sys._Profile();

Кстати говоря, в файле atlas.js этот код следует непосредственно за объявлением класса Sys._Profile.

Программный интерфейс службы профилей В табл. 6.2 перечислены методы серверной веб-службы профилей. Как упоминалось ранее, класс называется ProfileWebService и находится в сборке Microsoft. Web.Atlas. Таблица 6.2. Веб-методы службы профилей Метод

Описание

GetProfile

Получает коллекцию имен свойств и возвращает хеш-таблицу, заполненную парами «имя/значение» Получает хеш-таблицу, заполненную парами «имя/значение», и сериализует ее содержимое в хранилище данных профиля

SetProfile

Точная сигнатура метода GetProfile приведена в следующем фрагменте: [WebMethocT public IDictionary GetProfi1e(string[] properties)

Аргумент properties содержит список профильных свойств, загружаемых в объект JavaScript Sys.Profile. Если аргумент равен null, загружаются все свойства профиля. Метод GetProfile возвращает хеш-таблицу с одной записью для каждого запрашиваемого свойства. Запись состоит из имени и значения. Метод SetProfile обладает следующей сигнатурой: [WebMethocT public void SetProfileGDictionary values)

Он получает хеш-таблицу и обновляет свойства в хранилище данных заданными значениями. В отличие от службы аутентификации, программный интерфейс службы профилей JavaScript шире интерфейса соответствующей веб-службы. В этом нетрудно убедиться по табл. 6.3. Поскольку загрузка и сохранение выполняются в асинхронном режиме, вы должны добавить обработчики для событий loaded и saved для выполнения операций, требующих логически целостного состояния профильной службы. Автосохранение — функция системы профилей, которая на классической платформе ASP.NET включалась по умолчанию. В системе профилей Atlas эта функция отключена; это означает, что команды, связанные с сохранением данных, должны отдаваться явно.


228

Глава 6. Обращение к серверным службам

Таблица 6.3. Члены класса Sys._Profile

Член класса autoSave

Описание Логическое свойство, показывающее, должна ли служба периодически и автоматически сохранять объект профиля на сервере. По умолчанию задается равным false

isDirty

Логическое свойство, показывающее, содержит ли текущий объект профиля незакрепленные изменения, которые еще не были сохранены на сервере

properties

Массив свойств, предоставляющий доступ к отдельным свойствам объекта профиля Метод, вызываемый для заполнения свойства properties данными профиля. Загрузка профиля производится асинхронно Событие, происходящее непосредственно после загрузки профиля

Load loaded save

Метод, вызываемый для сохранения текущего состояния объекта профиля на сервере. Если свойство autoSave истинно, метод вызывается автоматически. Сохранение профиля производится асинхронно

saved

Событие, происходящее непосредственно после сохранения профиля

Поскольку служба Atlas работает на базе подсистемы профилей ASP.NET, вы можете ограничиться частью прикладной модели данных, закодированной в файле web.config. Обычно такой подход удобен тем, что он позволяет передавать только те профильные данные, к которым необходим доступ со стороны клиента.

Конфигурация службы профилей Чтобы использовать службу профилей Atlas, вам придется немного потрудиться над ее конфигурацией. В частности, необходимо явно разрешить использование службы в подсекции <profileService> новой секции <Microsoft.Web>: <profileService enabled="true" setProperties="свойство1;свойство2; ..." getProperties="cBOHCTBol;cBOHCTBo2; ..." />

Чтобы разрешить чтение и изменение свойств профиля из приложений Atlas, перечислите их имена в атрибутах setProperties и getProperties. Атрибут getProperties содержит список имен читаемых свойств, разделенных символом «;» (точка с запятой). В атрибуте setProperties перечислены имена записываемых свойств, которые также разделяются символом «;». Чтобы сделать доступными все свойства, воспользуйтесь условным обозначением «*» или перечислите все свойства по именам. Модель данных, заложенная в основу службы профилей, определяется в подсекции <profile> секции <system.web>. Вот как выглядит модель данных, которая будет использована в следующем примере: <profile> <properties> <group name="UI">


Встроенные прикладные службы

229

<add name="BackColor" type="string" /> </group> <add name="LastPane" type="integer" /> </properties> </profile>

Соответственно, секция <profileService> принимает следующий вид: <profileService enabled="true" setProperties="LastPane;UI.BackColor" getProperties="LastPane;UI.BackColor" />

Как упоминалось ранее, атрибуты setProperties и getProperties могут содержать подмножество свойств, определенных в модели данных. Два набора свойств не обязаны совпадать; это означает, что для некоторого свойства профиля может быть разрешено чтение из страниц Atlas, но не запись (или наоборот). Если ваша модель данных включает группы, при указании свойства в секции <profileService> можно воспользоваться схемой группа.свойство.

Использование службы профилей в приложении Служба профилей тесно связана со службой аутентификации, так как объект профиля содержит информацию, относящуюся к конкретному аутентифицированному пользователю. Анонимным пользователям разрешается иметь собственные профили — при условии, что для анонимных пользователей такая возможность будет включена в конфигурационном файле. Для анонимного пользователя генерируется код Guid, однозначно идентифицирующий его; когда анонимный пользователь зарегистрируется, данные переносятся из анонимного профиля в зарегистрированный. В следующем примере рассматриваются только зарегистрированные пользователи. Приложения с поддержкой профилей должны реализовать ряд функциональных аспектов. I В страницах, использующих данные из профилей, обработчик Page_Load должен содержать серверный код, обеспечивающий правильную настройку дочерних элементов в случае возврата данных или перенаправления. I Приложение должно содержать одну или несколько страниц, на которых зарегистрированные пользователи могут отредактировать свой профиль. Внесенные изменения сохраняются в хранилище данных профилей. I На страницах должны находиться кнопки Login/Logout, позволяющие пользователям входить и выходить из системы по своему усмотрению. I При использовании Atlas пользовательский интерфейс страниц с поддержкой профилей должен обновляться сценарным кодом, чтобы в нем отражался вход новых пользователей или изменения в объектах профилей. На рис. 6.5 показан пример страницы, использующей службу аутентификации для входа пользователей, и службу профилей для чтения и обновления личных данных.


230

Глава 6. Обращение к серверным службам

Рис. 6.5. Пример страницы, использующей службу профилей

После подключения пользователя элемент Accordion (см. главу 4) используется для вывода информации. Элемент состоит из четырех панелей, которые пользователь может открывать по своему усмотрению. Индекс панели, выбранной при выходе со страницы, сохраняется в профиле, чтобы при возвращении пользователя к странице на ней была открыта та же панель (рис. 6.6). Дочерние элементы скрываются и отображаются в зависимости от состояния аутентификации запроса. Если пользователь прошел проверку, код Page_Load применяет данные из профиля. В нашем примере задача сводится к простому выбору панели элемента Accordion и настройке фона страницы: protected void Page_Load(object sender, EventArgs e) { if (Request.IsAuthenticated) { // Настройка элементов для зарегистрированного пользователя _oggedIn.Style["display"] = ""; NotLoggedIn.Style["display"] = "none"; _oggedUser.Text = User.Identity.Name; _oginBlock.Style["display"] = "none"; AccordionPanel.Style["display"] = ""; ApplyProfileO; } else { // Настройка элементов для анонимного пользователя _oggedIn.Style["display"] = "none"; NotLoggedIn.Style["display"] = "";


Встроенные прикладные службы

LoggedUser.Text = ""; LoginBlock.Style["display"] = ""; AccordionPanel.Style["display"] = "none";

protected void ApplyProfile() { // Выбор цвета фона страницы string bgColor = Profile.UI.BackColor; if (!String.IsNullOrEmpty(bgColor)) Body.Style["background-color"] = bgColor; // Выбор последней активной панели Accordion1.SelectedIndex = Profile.LastPane;

Рис. 6.6. Персонализированная страница после входа пользователя

231


232

Глава 6. Обращение к серверным службам

Приведенный фрагмент содержит чистый, классический код ASP.NET. Он гарантирует правильную работу страницы при вызове из адресной строки, при переходе по гиперссылке или переходе пользователя в прямом или обратном направлении от текущей страницы. Та же логика должна быть выражена сценарным кодом при регистрации пользователя с использованием службы аутентификации Atlas или редактировании профиля через службу профилей. Следующий сценарий активизируется после входа пользователя и загрузки данных профиля: function OnLoginO { Sys.Services.AuthenticationService.1ogin( username.value, password.value, false, OnLoginComplete); return false; } function OnLoginComplete(result) { HandleLoginLogoutState(result); if (result) { CloseLoginPanel(); _oadProfile(); } else alertC'Sorry, your credentials appear to be invalid. Try again."); } function LoadProfileO { Sys.Profile.load(); }

Учтите, что метод load работает асинхронно. Для выполнения кода, использующего информацию из профиля, потребуется обработчик события loaded. Лучшим местом для регистрации обработчика события является код инициализации страницы Atlas: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> application load="PageInit()" unload="PageEnd()"> </application> </components> </page> </script> <script type="text/javascript"> function PagelnitO { Sys.Profi1e.1oaded.add(0nProfi1eLoaded); Sys.Profi1e.saved.add(0nProfi 1 eSaved); } function PageEndO { Sys.Profi1e.1oaded.remove(OnProfi 1 eLoaded); Sys.Profi1e.saved.remove(OnProfi 1 eSaved); }


Встроенные прикладные службы

233

function OnProfileLoadedO { var panelndex = Sys.Profile.properties.LastPane; var bgColor = Sys.Profile.properties["UI.BackColor"]; var accordionBehavior = $object("Accordionl_AccordionProperties"); accordionBehavior.set_Selectedlndex(panelndex); $("Body").style.backgroundColor = bgColor; } </script>

Функция OnProfileLoaded задает фоновый цвет страницы и выбирает последнюю выбранную панель в элементе Accordion. Предполагается, что тело страницы имеет пометку runat="server" и снабжено идентификатором Body. Имя идентификатора выбирается произвольно. Тег <body> должен быть серверным тегом, чтобы фоновый цвет страницы можно было выбирать на программном уровне из страницы ASP.NET. Если бы вы хотели только иметь возможность читать и задавать цвет фона из сценария, такой тег не был бы обязательным — хватило бы одного идентификатора. Тем не менее, как упоминалось ранее, страницы с поддержкой профилей также должны содержать серверную версию любого клиентского кода, работающего с профильными данными. Во внутренней реализации серверного элемента Accordion используется расширитель, генерирующий сценарный аспект поведения. Помимо предоставления клиентского поведения, этот аспект дополняет компонент Accordion клиентской объектной моделью. В главе 5 читатель узнал, как создавать ссылки на компоненты Atlas, но элемент Accordion прежде всего является серверным элементом. Как получить ссылку на клиентский аспект поведения Accordion? Воспользуйтесь условной записью XXX_AccordionProperties, где XXX — идентификатор серверного элемента. // Получение клиентской объектной модели для элемента Accordior var accordionBehavior = $object("Accordionl_AccordionProperties"); accordionBehavior.set_Selectedlndex(panelndex);

Аспект поведения Accordion обладает теми же свойствами, что и элемент Accordion; например, свойство Selectedlndex позволяет получить или задать активную панель. Наш пример приложения завершается страницей, на которой каждый пользователь может выбрать цвет фона страницы. Для этой цели используется классическая серверная страница ASP.NET, которая читает и записывает данные профилей при помощи серверного объекта Profile. Когда пользователь щелкает на кнопке Save Page State (см. рис. 6.6), страница сохраняет профиль: function SaveProfileO { // Сохранение панели, выбранной в настоящий момент var accordionBehavior = $object("Accordionl_AccordionProperties"); Sys.Profile.properties.LastPane = accordionBehavior.get_SelectedIndex(); Sys.Profile.saveO; }


234

Глава 6. Обращение к серверным службам

Кроме того, метод save работает асинхронно, а после завершения операции происходит событие saved. Служба Profile использует встроенную веб-службу Atlas для чтения и записи профильных свойств на сервере. Она представляет собой клиентскую «обертку» для серверного объекта Profile, который, в свою очередь, использует провайдера профилей для хранения данных, относящихся к конкретному пользователю. Серверные страницы и страницы Atlas могут использоваться в одном приложении, работающем с данными профилей. Не полагайтесь только на Atlas; позаботьтесь о том, чтобы ваши страницы Atlas имели обработчик события Page_Load, применяющий данные профиля при каждой загрузке страницы.

Использование веб-служб Службы аутентификации и профилей — две встроенные веб-службы ASP.NET, для которых библиотека Microsoft AJAX предоставляет «обертки» JavaScript. В общем случае может возникнуть необходимость вызова веб-служб по Интернету. Архитектура доступа к веб-службам из Atlas делится на три уровня: I Веб-службы, принадлежащие тому же приложению, что и вызывающая страница. I Веб-службы, принадлежащие веб-приложению с поддержкой Atlas на одном сервере с вызывающим приложением, или на внешнем компьютере. I Любые другие веб-службы, для которых возможна загрузка описания WSDL (Web Services Description Language), независимо от платформы и реализации. В первом случае вам необходимо только связать файл .asmx с элементом ScriptManager. Во втором случае следует настроить файл web.config и включить поддержку междоменных вызовов веб-служб Atlas. В третьем случае необходимо создать мостовой класс для отображения внешней веб-службы на класс-посредника JavaScript. Во всех случаях доступ к веб-службам должен быть явно разрешен в файле web.config.

Работа с домашними веб-службами Веб-службы являются простейшим механизмом публикации серверного кода, который должен вызываться в ответ на действие клиента (например, щелчок на кнопке). В этом случае веб-методы службы соответствуют фрагментам кода, специфическим для данного приложения. Иначе говоря, веб-служба является частью приложения, находится на том же компьютере и в том же домене, что и приложение. ПРИМЕЧАНИЕ

Существует два способа публикации серверного кода, вызываемого со стороны клиента: веб-службы на домашнем сервере и методы страниц. Методы страниц преобразуют страницу в домашнюю веб-службу и являются еще более простым способом публикации серверного кода. Мы уже встречались с методами страниц в главе 2 и еще вернемся к ним позднее в этой главе.


Использование веб-служб

235

Разрешение вызова веб-служб Чтобы разрешить вызовы веб-служб из приложений Atlas, необходимо добавить следующий фрагмент в файл web.config приложения: <microsoft.web> <webServices enableBrowserAccess="true" /> </microsoft.web>

По умолчанию операции с веб-службами в Atlas запрещены. Помимо разрешения доступа к веб-службам, также необходимо зарегистрировать специальный обработчик HTTP для запросов .asmx: <httpHandlers> <remove verb="*" path="*.asmx"/> <add verb="*" path="*.asmx" type="Microsoft.Web.Services.ScriptHandlerFactory" /> </httpHandlers>

Обе настройки автоматически включаются в стандартный файл web.config, генерируемый шаблоном Atlas в Visual Studio 2005. Параметр ScriptHandlerFactory определяет обработчик HTTP, которому поручается обработка заданного набора запросов. Запросы .asmx отличают вызовы веб-служб Atlas от обычных вызовов веб-служб из других приложений, включая ASP.NET, Microsoft Windows и браузеры. Вызовы веб-служб Atlas обслуживаются другим обработчиком HTTP. Конечно, такие запросы обрабатываются только в том случае, если доступ к веб-службам был разрешен (позднее мы вернемся к обработчику HTTP для веб-служб Atlas).

Связывание страниц с веб-службами Для обработки вызова веб-службы Atlas необходим класс-посредник JavaScript. Как получить такой класс? Обработчик веб-служб Atlas делает это каждый раз, когда обнаруживает URL, завершающийся суффиксом /js. Предположим, у вас имеется веб-служба, находящаяся по следующему адресу: http://localhost/IntroAtlas/WebServices/MyDataServi се.asmx

В данном примере используется веб-служба MyDataService, кратко упоминавшаяся в главе 2. Попробуйте ввести URL в адресной строке браузера и добавить к нему суффикс /js: http://localhost/IntroAtlas/WebServices/MyDataService.asmx/js

Вот что вы должны получить: Type.registerNamespace('IntroAtlas.WebServices'); IntroAtlas.WebServices.MyDataService = new functionO { this.appPath = "http://localhost/IntroAtlas/"; var cm = Sys.Net.ServiceMethod.createProxyMethod; cm(this, "LookupCustomer", "id"); cm(this, "LookupAllCustomers"); }


236

Глава 6. Обращение к серверным службам

IntroAtlas.WebServices — пространство имен класса веб-службы, а LookupCustomer и LookupAIICustomers — веб-методы, определенные в классе. Полученный вывод представляет собой класс JavaScript, имя которого соответствует классу веб-службы, а прототип соответствует веб-методам службы. Приведенный выше класс JavaScript — всего лишь класс-посредник, необходимый для работы со службой MyDataService.asmx из приложения Atlas. Как видите, Atlas автоматически получает метаданные веб-службы и генерирует класс-посредник JavaScript, упрощающий вызов веб-методов без прямой работы с SOAP (Simple Object Access Protocol) и WSDL. Следующим шагом должно стать связывание класса-посредника со страницей Atlas. Эта операция может выполняться двумя способами, которые я условно назову «ручным» и «автоматическим». В ручном варианте вы берете код JavaScript, полученный в результате встроенного генерирования класса-посредника в Atlas, и сохраняете его в локальном классе .js. Далее остается отредактировать файл по вашему усмотрению и включить ссылку на него в страницу. Такой подход позволяет достаточно легко генерировать посредников JavaScript, которые затем редактируются и настраиваются вручную. По сути он мало чем отличается от предварительного построения посредников SOAP утилитой wsdl.exe, обычно используемого в классических приложениях ASP.NET и Windows. Автоматический способ основан на регистрации URL веб-службы в элементе ScriptManager при помощи элемента <asp:ServiceReference>. Кроме того, вебслужбы должны пройти программную регистрацию в событии Page_Load класса кодового файла: ServiceReference service = new ServiceReferenceO; service.Path = "-/WebServices/MyDataService.asmx"; service.GenerateProxy = true; scriptManagerl.Services.Add(service);

Какой бы путь вы ни выбрали, для вызова веб-службы создается обращение к классу-посреднику через код JavaScript. Имя класса-посредника совпадает с именем веб-службы, и он обладает тем же набором методов.

Вызов методов веб-служб Операция вызова веб-службы выполняется страницей в ответ на действие пользователя — например, щелчок на кнопке. Желательно, чтобы эта кнопка была клиентской кнопкой, связанной с функцией JavaScript, как в следующем примере: <input type="button" value="Customer details" onclick="findCustomer()" />

Она также может быть классической кнопкой отправки данных — при условии, что свойство OnClickClient связано с кодом JavaScript, возвращающим false для предотвращения стандартного действия отправки данных: <asp:Button ID="Buttonl" runat="server" Text="Button" OnClientClick="findCustomer();return false;" />

Функция findCustomer читает идентификатор текущего клиента из раскрывающегося списка и вызывает метод LookupCustomer для импортированного классапосредника, как показано в следующем фрагменте: <script 1anguage="javascript" type="text/javascript"> function findCustomer()


Использование веб-служб

237

var list = $("CustomerList"); var customerID = list.options[list.selectedIndex].value; IntroAtlas.WebServices.MyDataService.LookupCustomer( customerID, findCustomerComplete);

function findCustomerComplete(results) { $("CustomerData").style.visibility = "visible"; $("companyID").innerText = results.ID; $("companyName").innerText = results.CompanyName; $("companyContact").innerText = results.ContactName; $("companyCity").innerText = results.City; $("companyCountry").innerText = results.Country; } </script>

Метод посредника выполняется асинхронно, но вы можете задать функцию обратного вызова, получающую управление сразу же после возврата из метода. Функция обратного вызова получает единственный параметр — возвращаемое значение метода. Им может быть как примитивный тип (строка, число или дата), так и более сложный объект JavaScript. Последствия вызова показаны на рис. 6.7.

Рис. 6.7. Использование веб-службы для получения информации о клиенте


238

Глава 6. Обращение к серверным службам

Каждый метод класса-посредника JavaScript представляет собой объект класса ServiceMethod, определенного в файле atlas.js. Класс ServiceMethod, в свою очередь, является производным от WebMethod. Все классы принадлежат пространству имен Sys.Services. Кроме обычного набора параметров, поддерживаемого парным методом вебслужбы, каждый веб-метод JavaScript поддерживает ряд конфигурационных параметров. Все эти параметры перечислены в табл. 6.4. Таблица 6.4. Параметры конфигурации методов-посредников Параметр

Описание

onMethodComplete

Определяет функцию обратного вызова JavaScript, вызываемую при успешном возврате из метода веб-службы

onMethodTimeout

Определяет функцию обратного вызова JavaScript, вызываемую при возврате из метода веб-службы по тайм-ауту

onMethodError

Определяет функцию обратного вызова JavaScript, вызываемую при возникновении исключения при вызове метода веб-службы

onMethodAborted

Определяет функцию обратного вызова JavaScript, вызываемую при отмене вызова метода

userContext

Любое значение (строка, число, массив, словарь или объект), которое представляет контекстную информацию, передаваемую функциям обратного вызова

timeoutInterval

Интервал тайм-аута для вызова в миллисекундах

priority

Числовой приоритет вызова. Допустимые значения: 0 (высокий), 1 (нормальный) и 2 (низкий). Приоритеты обычно используются при пакетном объединении вызовов (об этом чуть позже)

useGetMethod

Логический признак, указывающий, следует ли использовать операцию GET при выполнении вызова. По умолчанию используется операция POST

При вызове любого метода-посредника можно задать любые из перечисленных параметров — в порядке их следования в таблице. Если требуется задать лишь некоторые параметры, можно воспользоваться следующим синтаксисом: function findCustomer()

{ var list = $("CustomerList"); var customerID = list.options[list.selectedIndex].value; IntroAtlas.WebServices.MyDataService.LookupCustomer( customerID, // Аргумент метода { onMethodComplete: findCustomerComplete, onMethodTimeout: findCustomerTimeout, onMethodError: findCustomerError, onMethodAborted: findCustomerAborted, timeoutInterval: 5000

);


Использование веб-служб

239

Вместо явного перечисления параметры конфигурации объединяются в массив, передаваемый в дополнительном аргументе вызова метода.

Обработка результатов вызова Методы веб-служб ни в коей мере не ограничиваются примитивными типами — такими, как строки и целые числа. При передаче и возврате параметров веб-служб допускается использование большинства типов .NET. Такие типы .NET автоматически сериализуются в классы JavaScript (и десериализуются) с использованием формата JSON (JavaScript Object Notation), в полной мере поддерживаемого в Atlas (см. главу 2). В двух словах, клиентская инфраструктура Atlas JSON сериализует объект JavaScript в специальный формат, предназначенный для обмена данными, и передает его по каналу связи получателю, находящемуся на стороне сервера. Получатель разбирает поток данных и строит соответствующий объект .NET. Аналогичным образом серверная инфраструктура Atlas JSON берет объект .NET и сериализует его в формат обмена данными JSON. На стороне клиента по потоку данных создается объект JavaScript. Этот чрезвычайно мощный механизм практически полностью прозрачен для разработчиков Atlas. От вас потребуется лишь позаботиться о том, чтобы Atlas успешно преобразовал ваши типы. Стандартная инфраструктура Atlas нормально справляется с большинством пользовательских и системных классов. Atlas также регистрирует конвертеры JSON для популярных типов ADO.NET. Рассмотрим следующий метод веб-службы MyDataService.asmx: [WebMethocT public CustomerCollection LookupAllCustomers() { return CustomerManager.LoadAl 1 0 ; }

Метод возвращает пользовательский класс-коллекцию, содержащий экземпляры класса Customer: public class CustomerCollection : Collection<Customer> { }

Как работать с этой коллекцией в функции JavaScript? Следующая функция findContacts присоединяется к событию onclick кнопки: function findContacts() { IntroAtlas.WebServices.MyDataService.LookupAl1 Customers( findContactsComplete); } function findContactsComplete(results) { // Получение объекта builder для построения отображаемой строки var builder = new Sys.StringBuilder("<br><br>");


240

}

Глава 6. Обращение к серверным службам

// Перебор коллекции с построением отображаемой строки for(i=0; i<results.length; i++) { // result[i] - экземпляр класса JavaScript с именем Customer. // Класс Customer обладает теми же свойствами, // что и исходный тип .NET. builder.append(results[i].ContactName); builder.append("<hr>"); } // Присоединение строки к надписи на странице $("Label1").innerHTML = builder.toStringO ;

Аргумент results представляет любой объект, возвращаемый методом. Если метод возвращает массив или коллекцию, то results представляет массив или коллекцию. В нашем примере элементами коллекции являются экземпляры класса Customer. Объект JavaScript Customer обладает такой же сигнатурой, как и исходный тип .NET. На рис. 6.8 показан результат выполнения приведенного кода.

Рис. 6.8. Использование веб-службы для получения массива клиентов

Сценарный код, сгенерированный для веб-службы, включает определение Customer. Класс CustomerCollection представляет собой массив JavaScript, и посредники для него не нужны. Если вызываемому методу должен передаваться пользовательский объект, от вас потребуется лишь создать экземпляр соответствующего посредника JavaScript. Предположим, имеется метод следующего вида:


Использование веб-служб

241

[WebMethod] public Customer UpdateCustomer(Customer cust) { }

На странице находится раскрывающийся список, в котором выбирается имя клиента. Кроме того, на ней размещено текстовое поле, в котором пользователь может ввести имя нового клиента. Следующий сценарий показывает, как реализуется создание новой записи: function updateCustomer() { var textbox = $("ContactName"); var contactName = textbox.value; var list = $("CustomerList"); var customerID = list.options[list.selectedIndex].value; var c = new IntroAtlas.Customer(); c.ID = customerID; c.ContactName = contactName; // Здесь могут задаваться другие свойства // Обновление информации о клиенте IntroAtlas.WebServices.MyDataService.UpdateCustomer( c, updateCustomerComplete);

} function updateCustomerComplete(results) { // Аргумент results представляет возвращаемое значение метода }

В конечном счете, при вызове методов веб-служб у вас найдутся более важные заботы, чем тип данных. Базовая инфраструктура JSON берет на себя большую часть проблем. При использовании сложных типов данных — таких, как DataTable и других типов ADO.NET — может потребоваться специализированный конвертер JSON. Atlas предоставляет такие конвертеры для многих распространенных типов. ПРИМЕЧАНИЕ

Конвертер JSON представляет собой класс, производный от JavaScriptConverter — базового класса, определяемого в пространстве имен Microsoft.Web.Script.Serialization основной сборки Atlas. Конвертер обычно переопределяет несколько методов для выполнения сериализации в поток данных JSON и десериализации по формату JSON.

Перехват исключений и тайм-аут при вызове методов Что если при вызове метода веб-службы произойдет ошибка с выдачей исключения? Можно ли ограничить выполнение метода по времени и отменить его, если метод занимает слишком много времени? В табл. 6.4 были представлены параметры, управляющие выполнением методов. Некоторые из них относятся к исключениям, тайм-ауту и отмене.


242

Глава 6. Обращение к серверным службам

Как упоминалось ранее, методы веб-служб могут связываться с различными функциями обратного вызова JavaScript — для успешного завершения, тайм-аута, исключений и отмены. Впрочем, сигнатуры этих функций несколько отличаются: function function function function

methodCompleted(results, response, context) methodTimeOut(results, context) methodError(results, response, context) methodAborted(results, context)

Параметры перечислены в табл. 6.5. Таблица 6.5. Параметры функций обратного вызова для методов веб-служб

Параметр

Описание

Results

Возвращаемое значение метода веб-службы, десериализованное инфраструктурой Atlas Ответ на запрос в виде объекта JavaScript Sys.Net.XmlHttpExecutor. Содержит свойства statusCode, isComplete, isTimedOut и data Объект JavaScript, переданный в качестве пользовательского контекста при вызове метода

Response Context

Параметр results берется из объекта, на который ссылается параметр response. Если метод веб-службы инициирует исключение, то он возвращает управление, а Atlas выполняет обработчик ошибки (если таковой есть). В этом случае объект response отсутствует, а в параметре results передается объект JavaScript Sys.Net.MethodRequestError с тремя свойствами: message, stackTrace и exceptionType. function methodError(results, response, context) { // Выводит сообщение, соответствующее серверному исключению alert(results.get jnessage()); }

Интервал тайм-аута при выполнении метода задается в шестом аргументе. Например, следующая команда ограничивает время выполнения метода тремя секундами. После истечения заданного интервала запрос автоматически отменяется, после чего вызывается функция обратного вызова methodTimedOut: IntroAtlas.WebServices.MyDataService.VeryLengthyTask( methodCompleted, methodTimedOut, null, null, null, 3000);

Наконец, пользователи могут отменять незавершенные запросы в интерактивном режиме: // request - глобальная переменная JavaScript request = IntroAtlas.WebServices.MyDataService.VeryLengthyTask( methodCompleted, null, null, methodAborted);

Также можно разместить кнопку отмены где-то на странице, связанной с обработчиком onclick: function abortRequestO { request.abortO; }


Использование веб-служб

243

Пакетные обращения к веб-службам Каждый вызов веб-службы требует круговой пересылки данных (отправка + прием). Хотя цикл пересылка данных Atlas облегчена по сравнению с классическим возвратом данных, она все равно остается ресурсоемкой операцией, которая может отрицательно сказаться на работе вашего приложения. В Atlas можно приказать сетевому уровню клиентской стороны упаковывать несколько вызовов в одну круговую пересылку. Библиотека Microsoft AJAX автоматически упаковывает все запросы, по отдельности выполняет их на сервере, а затем заново упаковывает результаты перед отправкой их браузеру. Для включения режима пакетных вызовов потребуется дополнительный обработчик HTTP, который должен быть активизирован в файле web.config приложения: <httpHandlers> <add verb="*" path="atlasbatchcall.axd" type="Microsoft.Web.Services.MultiRequestHandler" /> </httpHandlers>

Кроме того, в каждую страницу Atlas, в которой предполагается использование пакетных вызовов, включается следующий код XML Script: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <webRequestManager enableBatching="true" batchSize="5" batchDelay="1000" /> </components> </page> </script>

Следующий код JavaScript создает четыре вызова метода веб-службы. В данном примере вызывается один и тот же метод одной службы, однако вы можете вызывать любые методы других служб: <script 1anguage="javascript" type="text/javascript"> function findCustomersO { IntroAtlas.WebServices.MyDataService.LookupCustomer("alfki", findCustomersCompleted, null, null, null, null, null, 1); IntroAtlas.WebServices.MyDataService.LookupCustomer("bottm", findCustomersCompleted, null, null, null, null, 1); IntroAtlas.WebServices.MyDataService.LookupCustomer("godos", findCustomersCompleted, null, null, null, null, 1); IntroAtlas.WebServices.MyDataService.LookupCustomer("magaa", findCustomersCompleted, null, null, null, null, 1); } function findCustomersCompleted(results, response, context) { debug.dump(results); debug.trace("<hr>"); } </script>


244

Глава 6. Обращение к серверным службам

Для каждого вызова указывается функция обратного вызова для успешного завершения и приоритет. Чтобы вызов веб-службы мог быть включен в пакет, он должен обладать ненулевым приоритетом — то есть 1 (нормальный) или 2 (низкий). Вызовы с максимальным приоритетом (0) никогда не объединяются в пакеты, а выполняются немедленно. Обратите внимание: по умолчанию вызову веб-метода присваивается максимальный приоритет. Другими словами, для использования пакетного вызова необходимо явно задать ненулевой приоритет. Параметр batchSize в коде XML Script указывает количество вызовов, объединяемых в пакет. Если он равен 5, четыре вызова (как в findCustomers) потребуют одной круговой пересылки данных (рис. 6.9). Попробуйте изменить значение batchSize на 2 и перезапустите страницу. Результат внешне не изменится, но для завершения страницы теперь потребуются два цикла пересылки данных.

Рис. 6.9. Объединение пакетных вызовов веб-служб в один цикл пересылки


Использование веб-служб

245

При возвращении из запроса функция обратного вызова будет вызвана по одному разу для каждого пакетного вызова. С точки зрения разработчика программирование пакетных вызовов ничем не отличается от программирования одиночных вызовов веб-методов. ВНИМАНИЕ

Пакетная подсистема группирует вызовы веб-методов из любых точек приложения. В предыдущем примере четыре последовательных вызова инициировались одним щелчком на кнопке. А если бы на странице находились четыре кнопки, каждая из которых инициирует один вызов веб-службы? Что произойдет в этом случае? Будет ли пакетная подсистема неопределенно долго ожидать, пока пользователь щелкнет на всех четырех кнопках? Конечно, нет. В коде XML Script, настраивающем пакетный вызов для отдельных страниц, также можно задать значение свойства batchDelay. Свойство определяет, как долго клиентский сетевой уровень будет ожидать накопления запросов веб-служб и заполнения пакета, прежде чем приступить к пересылке. Задержка по умолчанию равна 500 мс. В ситуации с четырьмя кнопками это означает, что если пользователь щелкает, скажем, дважды менее чем за полсекунды, то в пакет будут объединены два вызова; в противном случае эти вызовы будут отправлены раздельно. Обратите внимание: в предыдущем примере свойство batchDelay было задано равным 1000 мс.

Механика вызова веб-службы Любая веб-служба, зарегистрированная в ScriptManager, заставляет Atlas загрузить класс-посредника JavaScript при загрузке веб-страницы в браузере. Рассмотрим следующий серверный сценарий: <atlas:ScriptManager ID="scriptManagerl" runat="server"> <Services> <asp:ServiceReference Path="~/WebServices/MyDataService.asmx" /> </Services> </atlas:ScriptManager>

Элемент <asp:ServiceReference> — удобная конструкция, которая вставляет в клиентскую страницу следующий код XML Script: <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <references> <add src="../WebServices/MyDataService.asmx/js" onscriptload="IntroAtlas.WebServices.MyDataService.path = '/IntroAtlas/WebServices/MyDataService.asmx'" /> </references> </page>

Атрибут OnScriptLoad указывает на фрагмент сценарного кода, выполняемый при загрузке сценария, на который указывает атрибут src. В данном примере он задает путь к веб-службе (информация, которая не была жестко закодирована в сгенерированном классе-посреднике).


246

Глава 6. Обращение к серверным службам

СОВЕТ

Ссылки на сценарные файлы в приложениях Atlas лучше всего объявлять на декларативном уровне. Для этого потребуется небольшой объем кода XML Script: <script type="text/xml-script"> <page> <references> <add src="..." onscriptload="..." /> </references> </page> </script>

Более аккуратная и абстрактная, но полностью эквивалентная запись становится возможной при использовании секции <Scripts> компонента ScriptManager. <asp:ScriptManager ID="scriptManagerl" runat="server"> <Scripts> <asp:ScriptReference path="..." onscriptload="..." /> </Scripts> </asp:ScriptManager>

В обоих случаях атрибут OnScriptLoad определяет код, который должен выполняться после полной загрузки указанного сценария. После того как ссылка на веб-службу будет создана, вы можете на программном уровне обращаться к веб-методам, вызывая методы класса-посредника JavaScript. Каждый вызов метода веб-службы приводит к тому, что на URL веб-службы отправляется запрос с дополнительным строковым параметром mn (сокращение от «method name», то есть «имя метода»), содержащим имя вызываемого метода: http://localhost/IntroAtlas/WebServices/MyService.asmx?mn=LookupCustomer

Как видно из приведенного URL, при передаче запроса в виде операции GET входные параметры присоединяются к URL. Но если запрос передается в виде операции POST, входные параметры включаются в тело запроса. На рис. 6.10 показано тело запроса POST к веб-службе. HTTP-обработчик Atlas для ресурсов .asmx перехватывает запрос и по параметру mn определяет, что перед ним вызов веб-службы Atlas. В общем случае HTTP-обработчик Atlas для веб-служб особым образом обрабатывает два типа URL: с суффиксом /js и со строковым параметром mn. В первом случае генерируется код класса-посредника; во втором случае обработчик десериализует входные параметры, выполняет метод и готовит ответ. Ответ сериализуется в представление XML или JSON, после чего отправляется. Формат сериализации выбирается при помощи атрибута WebOperation метода веб-службы (по умолчанию JSON). [WebMethcC [WebOperation(true, ResponseFormatMode.Json, true); public Customer LookupCustomer(string id) { }


Использование веб-служб

247

Рис. 6.10. Тело запроса к веб-службе, переданного в формате операции POST Конструктор атрибута WebOperation получает до трех параметров, перечисленных в табл. 6.6. Таблица 6.6. Свойства атрибута WebOperation Параметр

Описание

GetVerbEnabled

Логическое свойство, указывающее, допускает ли веб-метод вызовы с использованием команды GET. В .NET Framework GET-вызовы веб-служб по умолчанию запрещены, поэтому значение по умолчанию равно false

ResponseFormat

Формат сериализации данных для веб-метода. Допустимые значения объединены в перечисляемый тип ResponseFormatMode: Json (0, по умолчанию) и Xml (1)

SafeForCrossDomain

Логический признак, указывающий, может ли метод вызываться в том случае, если веб-служба находится под управлением другого приложения. По умолчанию используется значение false

Как выясняется, для вызова методов веб-служб из страниц Atlas потребуется некоторая инфраструктура. Основные требования заключаются в том, чтобы конечные точки «понимали» J S O N и могли распознавать вызовы с суффиксом /js и строковым параметром mn.


248

Глава 6. Обращение к серверным службам

Конечно, Atlas не сможет вызвать произвольную веб-службу без некоторой работы с вашей стороны. Вскоре тема связывания Atlas с внешними веб-службами будет рассмотрена более подробно.

Методы страниц Atlas Как упоминалось ранее, веб-службы в основном используются как механизм вызова серверного кода со стороны клиента. Это одна из причин, по которым в приложение приходится встраивать специальные функции (мостовые технологии) для вызова реально используемых служб. Создание локальной веб-службы (то есть находящейся в том же приложении) потребует некоторой дополнительной работы. Вам придется написать класс кодового файла, а также конечную точку .asmx для открытого доступа. Кроме того, необходимо знать атрибуты веб-методов и веб-служб. Наконец, ваш код станет доступен в Интернете, поэтому могут потребоваться дополнительные меры безопасности для защиты вашей системы. Методы страниц Atlas предоставляют «облегченную» альтернативу для написания серверного кода, вызываемого из клиентских страниц.

Объявление методов страниц Методы страниц представляют собой открытые методы, определенные в классе кодового файла и помеченные атрибутом WebMethod. Они преобразуют страницу Atlas в некое подобие сильно упрощенной веб-службы. Пример метода страницы: using System.Web.Services; public partial class Samples_Ch06_PageMethods : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } [WebMethod; public UserStatus CreateUser(UserData data) { } [WebMethod; public UserStatus DeleteUser(string userName)

{

}

}

Как видите, в определении метода страницы может использоваться практически любой тип данных, будь то тип .NET Framework или тип, определяемый пользователем. Все, что говорилось ранее о сериализации JSON для методов вебслужб, относится и к методам страниц.


Использование веб-служб

249

Кроме того, веб-методы могут определяться в виде встроенного кода в исходном файле .aspx: <script type="text/C#" runat="server"> [WebMethocT public UserStatus CreateUser(UserData data) { } [WebMethocT public UserStatus DeleteUser(string userName) { } </script>

Если вы используете Microsoft Visual Basic, замените значение атрибута type на text/VB. Если кодовый файл страницы Atlas содержит методы, помеченные атрибутом WebMethod, компонент ScriptManager генерирует код, который напоминает класс-посредника для обычной веб-службы, однако использует фиксированное имя класса: <script type="text/javascript"> var PageMethods = new functionO { var cm=Sys.Net.PageMethod.createProxyMethod; cm(this,"CreateUser","data"); cm(this,"DeleteUser","userName"); } </script>

В этом примере CreateUser и DeleteUser — веб-методы, объявленные в классе кодового файла, или в исходном файле .aspx. Как мы вскоре увидим, для обращения к методам страниц следует вызывать методы класса-посредника PageMethods.

Вызов методов страниц Ранее в этой главе обсуждалась служба аутентификации и операция входа в систему. Служба аутентификации Atlas использует API принадлежности (membership API) для проверки регистрационных данных. Тем не менее не существует средств Atlas (по крайней мере пока) для проверки пользователей на стороне клиента. Чтобы создать новую учетную запись пользователя без полного возврата данных, можно внедрить элемент CreateUserWizard в элемент UpdatePanel. Такое решение будет работать еще быстрее, если инкапсулировать нужную часть интерфейса принадлежности в веб-службу уровня страницы. Представьте, что у вас имеется фрагмент разметки HTML для создания формы регистрации пользователей. В ее нижней части находится кнопка, при помощи которой пользователь завершает регистрацию и создает учетную запись: <input type="button" value="Create User" onclick="createUser()" />

Пользовательский интерфейс показан на рис. 6.11.


250

Глава 6. Обращение к серверным службам

3 Creating a new user - Microsoft Internet Explorer File

Edit

View

Favorites 0

Ш

Tools 'ii

@s

_-_

if

Help

Я Search ^Favorites

■@||01-<^[Й]

Address Щ\ http://localhost: 1386/IntroAtlas/Samples/Ch06/WebServices/Crea

v

и

т

[ Links

Sign Up for Your N e w Accou nt User Name: DinoE Barely Acceptable

Password: E-mail: foo@contoso.com Security Question: What follows 123? Security Answer: 456 Create User

$s\ Done

* J Local intranet

Рис. 6 . 1 1 . Форма Atlas для создания новой учетной записи пользователя, с расширителем PasswordStrength в поле ввода пароля

Метод createUser выглядит примерно так: <script language="javascript" type="text/javascript"> function createUserO { // Получение ссылок на поля ввода var user = $("UserName"); var pswd = $("Password"); var email = $("EmaiI"); var question = $("Question"); var answer = $("Answer"); // Подготовка входных данных для метода страниць var data = new UserDataO; data.UserName = user.value; data.Password = pswd.value; data.Email = email.value; data.Question = question.value; data.Answer = answer.value; // Вызов метода страниць debug.clearTraceO; Pagefiethods.CreateUser(data, createUserComplete); } function createUserComplete(results) { debug.trace("<hr>"); debug.dump(results); </script>

>J

4


Использование веб-служб

251

Функция упаковывает входные данные в объект UserData — класс определяется в кодовом файле и сериализуется в объект JavaScript благодаря помощи ScriptManager. Метод страницы CreateUser создает нового пользователя при помощи объекта Membership и возвращает объект UserStatus с кодом статуса и описанием: public class UserData { public string UserName; public string Password; public string Email; public string Question; public string Answer; } public class UserStatus { public MembershipCreateStatus StatusCode; public string Description; } public partial class Samples_Ch06_CreateUser : System.Web.UI.Page { [WebMethod; public UserStatus CreateUser(UserData data) { MembershipCreateStatus code; Membership.CreateUser(data.UserName, data.Password, data.Email, data.Question, data.Answer, true, out code); UserStatus status = new UserStatusO; status.StatusCode = code; status.Description = code.ToStringO; }

return status;

}

При завершении метода JavaScript CreateUser функция обратного вызова получает управление и выводит содержимое возвращенного объекта. На рис. 6.12 показано, что произойдет, если введенный пароль окажется недостаточно сильным.

Разрешение и выполнение методов страниц Чтобы вызвать метод страницы, необходимо сначала включить поддержку модуля HTTP ScriptModule в файле web.config приложения: <httpModules> <add name="ScriptModulе" type="Microsoft.Web.Services.ScriptModule"/> </httpModules>


252

Глава 6. Обращение к серверным службам

Рис. 6.12. Неудачная попытка создания учетной записи

Если страница вызывает только «обычные» методы веб-служб, этот модуль не обязателен. Модуль HTTP перехватывает событие приложения PostMapRequestHandler. Данное событие указывает, что конвейер ASP.NET успешно определил объект обработчика HTTP, который, в конечном итоге, будет обслуживать текущий запрос. Если объект является производным от Page, модуль регистрирует специализированный обработчик для события страницы PreRenderComplete. Обработчик события проверяет отправленные данные и ищет в них пару скрытых полей __serviceMethodName и _serviceMethodParams. В них хранится имя и параметры метода, вызываемого для обслуживания запроса. Метод выполняется и передает возвращаемое значение; при десериализации входных данных и сериализации выходных данных используется формат JSON.


Связь с внешними веб-службами

253

ПРИМЕЧАНИЕ

Состав удаленных служб, которые могут использоваться в приложениях Atlas, не ограничивается веб-службами ASP.NET (конечные точки .asmx). Приложения Atlas также могут работать с серверными веб-службами на базе WCF (Windows Communication Foundation), одного из краеугольных камней будущей платформы .NET Framework 3.0. Технология WCF (ранее известная под кодовым обозначением «Indigo») представляется как совокупность ряда существующих коммуникационных технологий Microsoft (например, MSMQ, .NET Remoting и Enterprise Services), объединенных и расширенных в единую программную модель. Службы WCF могут использоваться в тех ситуациях, в которых сегодня используются веб-службы ASP.NET, а также в ситуациях, когда уместно применение других коммуникационных технологий. Модель WCF не зависит от нижележащих коммуникационных протоколов и позволяет приложениям взаимодействовать друг с другом на базе открытых стандартов и протоколов. Технология Windows Communication Foundation будет выпущена в составе .NET Framework 3.0 — одной из базовых частей операционной системы Windows Vista. Она также будет доступна для Windows XP и Windows Server 2003. Для проверки модели взаимодействия WCF в приложениях Atlas потребуется по меньшей мере NET Framework версии Beta 2. За дополнительной информацией о WCF обращайтесь на сайт http://wcf.netfx3.com. В любом случае помните, что поддержка WCF не является обязательной для работы ASP.NET Atlas. Даже если она недоступна на сервере, приложения Atlas все равно смогут пользоваться веб-службами ASP.NET.

Связь с внешними веб-службами До настоящего момента мы рассматривали только веб-службы, находящиеся в одном приложении с вызывающей страницей. Но что, если веб-служба является частью другого веб-приложения или работает на другой серверной платформе? Проблема имеет разные решения в зависимости от того, распространяется ли контроль разработчика на обе стороны канала связи или только на клиентскую сторону. В первом случае существует обходное решение, позволяющее использовать междоменные вызовы в Atlas. Во втором случае потребуется участие посредника, связывающего вызывающую страницу с произвольным вебсервером.

Вызов нелокальных веб-служб Atlas Ключевой фактор заключается в том, что для прямого обращения к веб-службе из Atlas веб-сервер конечной точки должен удовлетворять ряду критериев. В частности, конечная точка должна поддерживать формат JSON, а также уметь обрабатывать суффикс /js и параметр mn в строке запроса. Самый простой вариант выполнения необходимых условий — если целевая веб-служба находится под управлением другого приложения с поддержкой Atlas. Очевидно, это возможно только в том случае, если веб-служба входит в состав вызывающего приложения Atlas или если веб-сервер конечной точки принадлежит той же группе разработки.


254

Глава 6. Обращение к серверным службам

Настройка веб-служб во внешнем приложении Atlas Если вы хотите развернуть веб-службы, вызываемые из Atlas, в другом веб-приложении или сервере, вам необходимо создать второе приложение ASP.NET 2.0, в котором выполняются как минимум следующие условия: I включение поддержки Atlas в файле web.config. В частности, это означает замену обработчика HTTP .asmx и включение веб-служб; установка сборки Microsoft.Web.Atlas в папке Bin. После этого остается лишь обратиться к веб-службе из страницы по полному удаленному URL. Тем не менее попытка вызова веб-службы, скорее всего, завершится неудачей. Дело в том, что реализации XmlHttpRequest в некоторых браузерах по умолчанию запрещают междоменные вызовы. Так как объект, от которого исходит вызов, спрятан где-то в недрах прикладной среды, вам как веб-разработчику будет нелегко найти нужную точку для внесения изменений. В том, что касается Atlas, междоменные вызовы разрешаются посредством регистрации нового обработчика HTTP в целевом веб-приложении и небольших изменений в коде класса вебслужбы. HTTP-обработчик Atlas, предназначенный для междоменных вызовов, называется iframecall.axd: <httpHandlers> <add verb="*" path="iframecall.axd" type="Microsoft.Web.Services.IFrameHandler" /> </httpHandlers>

Также необходимо проследить за тем, чтобы веб-методы были помечены атрибутом WebOperation, у которого третий параметр (SafeForCrossDomain) задан равным true: [WebMethod; [WebOperation(true, ResponseFormatMode.Json, true); public Customer LookupCustomer(string id) { }

Обработчик iframecall.axd и сопутствующий класс JavaScript (Sys.Net.lFrameExecutor в файле atlas.js) вызывают веб-службу без использования объекта XmlHttpRequest. Вместо этого они выполняют междоменные вызовы, загружая URL веб-службы в скрытом фрейме как сценарный код.

Возможные изменения Класс IFrameExecutor работает в Atlas CTP (Community Technology Preview) за июль 2006 года, но может и не работать в будущих версиях. Возможно, синтаксис, основанный на iframecall.axd, сохранит работоспособность, но базовая технология междоменных вызовов, скорее всего, значительно изменится.


Связь с внешними веб-службами

255

Основная схема обращения к веб-службам за пределами локального приложения базируется на сетевых исполнителях. Сетевой исполнитель (network executor) представляет собой класс JavaScript, производный от Sys.Net.WebRequestExecutor. Этот класс знает, как организовать выполнение веб-запроса по заданному URL. На данный момент в библиотеку Microsoft AJAX включены два исполнителя: XMLHttpExecutor и IFrameExecutor. Идея заключается в создании собственного механизма междоменных вызовов, для которого вы выбираете способ упаковки данных и проведения запроса.

Подключение к сторонним веб-службам в Интернете Может оказаться, что веб-служба, к которой вы хотите подключиться из приложения Atlas, не является ни локальной, ни работающей на базе Atlas. Еще раз стоит подчеркнуть, что все описанные ранее средства и приемы не сработают, если вы захотите использовать страницу для размещения заказов через службу AmazonWeb (произвольно взятый пример типичной сторонней службы, не базирующейся на Atlas). Что делать в этом случае? Технология мостов (bridge) Atlas позволяет разработчику создавать программные шлюзы к веб-службам в Интернете. Поскольку мостовой код работает внутри вызывающего приложения объект, браузер взаимодействует только с одним сервером. Мостовой код, в свою очередь, может взаимодействовать с любой вебслужбой, доступной в Интернете, и возвращать данные клиентской странице. Мостовой код является основной технологией для создания гибридных приложений, то есть приложений, объединяющих данные от нескольких удаленных источников и представляющих их клиентам в удобной форме.

Конфигурация и настройка Шлюз Atlas к сторонним веб-службам представляет собой файл с расширением .asbx. Файл содержит данные XML с описанием вызова веб-службы. Основная причина для наличия подобных посредников заключается в том, что реальные веб-службы могут получать параметры не в виде отдельных величин, а требовать передачи объекта. В свою очередь, объект может включать свойства, также относящиеся к сложным типам. Следовательно, построение запросов и разбор возвращаемых значений могут потребовать некоторой работы по программированию. Здесь в дело вступают мостовые файлы (.asbx). Для подключения к сторонней веб-службе из Atlas необходимо зарегистрировать расширение .asbx в IIS (Internet Information Services) и поручить ASP.NET его обработку. Чтобы посмотреть, как это делается, откройте диалоговое окно конфигурации в целевой виртуальной папке IIS, перейдите на вкладку Mappings и просмотрите содержимое диалогового окна Add/Edit Application Extension Mapping (рис. 6.13). Если проект Atlas был создан на базе шаблона Visual Studio 2005, расширение .asbx уже присутствует и зарегистрировано за aspnet_isapi.dll. В противном случае отредактируйте отображения и выполните привязку ресурсов .asbx в расширении ASP.NET ISAPI (Internet Server Application Programming Interface).


256

Глава 6. Обращение к серверным службам

Рис. 6.13. Регистрация расширения .asbx в IIS

Что делать, если ваш сайт работает на внешнем хостинге? Что, если вы не обладаете правами администратора? Альтернативный способ использования мостовой технологии основан на применении туннелей. На практике вам придется настроить модуль HTTP, предоставляемый Atlas, который анализирует все входящие запросы и перенаправляет запросы, содержащие строку "bridge.axd", указанному вами ресурсу .asbx. Например, запрос MyServiceBridge.axd преобразуется в запрос к MyService.asbx при помощи модуля Atlas BridgeModule, конфигурация которого выглядит так: <httpModules> <add name="BridgeModule" type="Microsoft.Web.Services.BridgeModule" /> </httpModules>

Создание и выбор имени мостового файла (например, Xxx.asbx) происходит по обычным правилам. Затем, если включена поддержка туннелирования, мостовой файл переименовывается в XxxBridge.axd. В только что рассмотренном примере мостовому файлу было присвоено имя MyServiceBridge.axd. BridgeModule отсекает «MyService» и присоединяет суффикс «.asbx». Сформированное имя мостового файла MyService.asbx будет реально вызываться при поступлении запросов к MyServiceBridge.axd. Зачем это все нужно? При внешнем хостинге вы не обладаете доступом к административной панели IIS, а следовательно, не сможете добавить запись *.asbx в список отображаемых расширений IIS. В стандартной конфигурации IIS не обрабатывает запросы к ресурсам .asbx, и любые попытки таких запросов завершатся неудачей. Механизм туннелирования попросту «обманывает» IIS и заставляет сервер перенаправить запрос ASP.NET — где существует компонент, умеющий обрабатывать мостовые файлы. Далее также необходимо зарегистрировать обработчик HTTP для мостовых файлов: <httpHandlers> <add verb="*" path="*.asbx" type="Microsoft.Web.Services.ScriptHandlerFactory" /> </httpHandlers>


Связь с внешними веб-службами

257

Если вы планируете использовать мостовые файлы непосредственно в приложении, мостовой обработчик не потребуется. Если все страницы на базе мостов находятся в одной вложенной папке, эту настройку также можно переместить в файл web.config уровня папки.

Мостовые файлы Как упоминалось ранее, мостовой файл Atlas представляет собой файл XML с расширением .asbx. В мостовом файле вы объявляете, как следует подключаться к удаленной веб-службе и как будут обрабатываться возвращаемые данные. В наши дни многие популярные веб-службы поддерживают два протокола вызова: SOAP и POX. Хорошо известный протокол SOAP (Simple Object Access Protocol) определяет структуру сообщений, используемых в распределенной среде обмена данных, на базе XML. Протокол SOAP используется веб-службами для взаимодействия с вызывающей стороной. Формат POX (Plain Old XML) состоит из команды GET, у которой все параметры перечисляются в строке запроса. Почему многие популярные веб-службы, включая MSN Search и Amazon, в наши дни используют POX? Ответ состоит из одного слова: доступность. Избавление от сложностей, связанных с SOAP, позволяет работать с веб-службами на базе POX более широкому кругу пользователей. (Не обманывайтесь, протокол SOAP отнюдь не тривиален… Просто с ним удобнее работать, чем с его предшественниками.) В свете сказанного у приложений Atlas есть два возможных способа работы с веб-службами. I Утилита wsdl.exe из Visual Studio 2005 генерирует для веб-службы класс-посредник .NET. Разработчик помещает класс в папку App_Code приложения Atlas и создает мост, связывающий страницы с классом-посредником. Класс моста фактически определяет программный интерфейс JavaScript, который будет использоваться вашими страницами для обращений к классу-посреднику .NET. Класс-посредник использует SOAP для размещения вызовов. I Если вы предпочитаете решение на базе POX, классы-посредники .NET не понадобятся. Вы просто создаете мостовой файл и строите программный интерфейс JavaScript для страниц Atlas. Далее Atlas обрабатывает содержимое мостового файла и размещает запросы GET для вызова веб-методов. Давайте посмотрим, как создать мостовой файл, который использует POX для вызова веб-службы Amazon и получения информации о книгах.

Построение мостового файла для веб-службы Amazon Веб-служба Amazon позволяет разработчикам веб-сайтов работать на программном уровне с каталогом Amazon, поисковой системой, корзиной и коммерческим инструментарием. Необходимый пакет разработчика можно загрузить по следующему адресу: http://associates.amazon.com/exec/panama/associates/join/developer/kit.html

Для использования веб-службы вам потребуется маркер (token) разработчика; страница упрощает его получение. Чтобы получить информацию о книгах,


258

Глава 6. Обращение к серверным службам

написанных заданным автором, необходимо отправить вызов по следующему URL с добавлением нескольких параметров: http://xml.amazon.com/onca/xml2

Некоторые из часто используемых параметров перечислены в табл. 6.7. Таблица 6.7. Параметры поиска книг веб-службой Amazon

Параметр

Описание

T

Тег запроса (должен быть равен «webservices-20»)

F

Формат вывода (должен быть равен «xml»)

Page

Запрашиваемая страница (по умолчанию 1)

Sort

Тип сортировки (допустимые значения: +salesrank, +daterank и +titlerank (алфавитная)

dev-t

Маркер разработчика

Mode

Каталог, в котором ведется поиск (должен быть равен books)

Type

Объем информации, упакованной в ответе (допустимые значения: lite и heavy) Строка запроса

keywordSearch

В мостовом файле определяется программный интерфейс для возможностей веб-службы. Например, можно определить «обертку» JavaScript с методом Find, который получает ключевое слово поиска и возвращает данные. Соответствующий мостовой файл будет выглядеть так: <bridge namespace="IntroAtlas" className="AmazonPox" > <proxy type="Microsoft.Web.Services.BridgeRestProxy" serviceUrl="http://xml.amazon.com/onca/xml2" /> <method name="Find"> <input> <parameter name="t" value="webservices-20" /> <parameter name="mode" value="books" /> <parameter name="type" value="lite" /> <parameter name="f" value="xml" /> <parameter name="page" value="l" /> <parameter name="sort" value="+salesrank" /> <parameter name="dev-t" value='7 appsettings : AmazonToken %" /> <parameter name="KeywordSearch" /> </input> </method> </bridge>

Атрибуты namespace и classname тега <bridge> определяют полное имя полученного класса JavaScript. В элементе <ргоху> атрибут type обозначает компонент, ответственный за выполнение удаленного вызова по поручению вашего приложения. Атрибут serviceURL задает URL, которому адресуется вызов. Элементы <method> определяют программный интерфейс мостового класса. Все методы, определяемые в этой секции, включаются в «обертку» JavaScript


Связь с внешними веб-службами

259

IntroAtlas.AmazonPox, которая будет использоваться в ваших страницах Atlas. Конечно, количество элементов <method> может быть любым. Приведенный сценарный код определяет метод Find, получающий до восьми параметров. Все перечисленные параметры (и только они) включаются в команду GET, отправляемую на URL службы. Первые шесть параметров имеют фиксированные значения. Значение седьмого параметра (dev-t) читается из секции <appSettings> файла web.config. <appSettings> <add key="AmazonToken" value="..." /> </appSettings>

На этой стадии все готово к написанию кода JavaScript, который вызывает веб-службу Amazon и получает информацию о книгах, написанных заданным автором. Какую информацию, спросите вы? В этом-то все и дело!

Получение данных Amazon через мост Atlas В следующей странице при создании ссылки на мостовой файл используется синтаксис, уже использовавшийся ранее для локальных веб-служб. На странице расположено текстовое поле и кнопка. Пользователь вводит имя автора в текстовом поле и щелкает на кнопке, чтобы найти книги данного автора. <form id="forml" runat="server"> <asp:ScriptManager ID="scriptManagerl" runat="server"> <Services> <asp:ServiceReference Path="-7WebServices/Amazon.asbx" /> </Services> </asp:ScriptManager> <h3>Find an author: <asp:TextBox ID="TextBoxl" runat="server" /></h3> <input type="button" value="Go" onclick="findBooks()" /> <hr /> <asp:Panel runat="server" ScrollBars="Auto" Height="300px"> <asp:label runat="server" ID="0utput" /> </asp:Panel> </form>

Функция findBooks выглядит так: <script language="javascript" type="text/javascript"> function findBooks() { var author = $("TextBoxl").value; IntroAtlas.AmazonPox.Find( {KeywordSearch:author}, findBooksCompleted, null, findBooksError); } function findBooksCompleted(results, response, context) { debug.dump(results); } function findBooksError(results, response, context) { debug.trace(results.get_message()); } </script>


260

Глава 6. Обращение к серверным службам

Запустите страницу. Введите в текстовом поле строку Dino Esposito и щелкните на кнопке. На рис. 6.14 показан результат.

Рис. 6.14. Мостовой файл получает информацию от веб-службы Amazon

Такое решение работает, но можно ли честно сказать, что именно на это мы и рассчитывали? Возвращаемое значение представляет собой огромную строку XML, в которой вся запрашиваемая информация упакована в соответствии со схемой, определенной разработчиками Amazon. Как преобразовать полученную строку в формат, удобный для пользователя?

Преобразование ответа веб-службы По умолчанию считается, что ответ веб-службы представляет собой поток данных JSON, и он разбирается в объект JavaScript. Впрочем, это предположение не обязательно истинно. Amazon или любая другая сторонняя веб-служба знает о ваших предположениях. В конечном итоге данные будут приняты правильно, но это не значит, что их можно будет немедленно использовать. Добавляя секцию <transforms> в мостовой файл, вы приказываете инфраструктуре Atlas обработать возвращаемое значение и построить объект JavaScript, с которым вам будет удобнее работать для представления результатов пользователю.


Связь с внешними веб-службами

261

Для последующей обработки возвращаемого значения веб-службы могут использоваться различные преобразователи (transformers). В Atlas имеется несколько встроенных преобразователей, перечисленных в табл. 6.8. Таблица 6.8. Встроенные мостовые преобразователи

Тип

Описание

ObjectMapperBridgeTransformer

Если возвращаемое значение представляет собой объект, преобразователь отображает свойства возвращенного объекта на пользовательский объект

XmlBridgeTransformer

Преобразователь обрабатывает возвращенные данные с помощью XmlSerializer и получает строку XML, представляющую выходные данные

XPathBridgeTransformer

Преобразователь использует запросы XPath для извлечения данных из строки ответа и построения пользовательского объекта

XsltBridgeTransformer

Преобразователь применяет таблицу стилей XSLT к документу XML, представляющему возвращаемые данные

Следующий пример показывает, как разобрать строку XML, полученную от веб-службы Amazon, с извлечением содержательной информации запросами XPath. Чтобы написать преобразователь, необходимо хорошо знать формат ответа. В ответах Amazon обычно используется следующий формат: <ProductInfo> <TotalResults> ... </TotalResults> <TotalPages> ... </TotalPages> <Details> <Asin> ... </Asin> <!-- Другие теги --> </Details> <Details> </Details> </ProductInfo>

Элемент <TotalResults> определяет количество записей, полученных при поиске, а элемент <TotalPages> — количество страниц, которые для этого потребуются, с размером страницы по умолчанию. Наконец, массив блоков <Details> содержит информацию об отдельных предметах — книгах, альбомах и т. д. Каждый блок <Details> содержит, по крайней мере, один тег <Asin> c идентификатором предмета, используемым службой Amazon. Для книг он совпадает с кодом ISBN. Следующий преобразователь превращает ответ в массив более простых объектов JavaScript: <transforms> <transform type="Microsoft.Web.Services.XPathBridgeTransformer"> <data>


262

Глава 6. Обращение к серверным службам

attribute name="selector" value="Details" /> dictionary name="selectedNodes"> <item name="ISBN" value="Asin" /> <item name="Title" value="ProductName" /> <item name="Publisher" value="Manufacturer" /> <item name="CoverPicture" value="ImageUrlSniall" /> </dictionary> </data> </transform> </transforms>

Атрибут selector приказывает XPath выбрать заданный узел — в данном случае Details. Затем словарь selectedNodes выбирает множество узлов, корень которого находится в выбранном узле. В результате выбираются все узлы с именем Details. Преобразователь создает словарь JavaScript, объекты которого обладают четырьмя свойствами: ISBN, Title, Publisher и CoverPicture. Каждое свойство отображается на конкретный элемент в иерархии n-го элемента <Details>. На этой стадии значение, которое передается функции обратного вызова, назначенной для завершения метода, представляет собой массив объектов JavaScript с указанными четырьмя свойствами. Теперь на его основе можно строить пользовательский интерфейс (рис. 6.15).

Рис. 6.15. Простой, но эффективный пример гибридного приложения


Заключение

263

function findBooksCornpleted(results, response, context) { var builder = new Sys.StringBuilder("<table>"); for(i=0; i<results.length; i++) { builder.append("<tr>"); builder.append("<td><img src='"); bui1der.append(results[i].CoverPicture); builder.appendC' /></td>"); builder.append("<td>"); builder.append(results[i].ISBN); bui1der.append("<br/><b>"); builder.append(results[i].Title); bui1der.append("</b><br/>"); bui1der.append(results[i].Publisher); builder.append("</td></tr>"); } bui1der.append("</table>"); $("Output").innerHTML = builder.toStringO ; }

Кэширование данных Иногда получение ответа от сторонних веб-служб требует времени — из-за неизбежных сетевых задержек или по другим причинам. Таким образом, результаты вызова веб-служб есть смысл кэшировать, чтобы они оставались доступными в течение некоторого интервала времени. Включая следующий сценарий в мостовой файл, вы приказываете Atlas использовать для получения данных указанный вами компонент. Если компонент не возвращает данные (то есть кэшированный результат отсутствует), Atlas продолжает действовать по обычной схеме и инициирует вызов: <bridge namespace="IntroAtlas" className="AmazonPox" > <caching> <cache type="IntroAtlas.AmazonPoxCacheObject" /> </caching> </bridge>

Пользовательский класс реализует интерфейс IBridgeRequestCache, а его основные функции сводятся к простому сохранению полученных данных. Класс отвечает за выбор способа хранения (статические члены класса, файл на диске, база данных) и за определение срока, в течение которого данные считаются действительными. Интерфейс IBridgeRequestCache определяет три метода: Put, Lookup и Initialize. Метод Put сохраняет данные; метод Lookup проверяет действительность данных и возвращает их.

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


264

Глава 6. Обращение к серверным службам

по принципу «черного ящика»: их функциональность можно использовать многократно, не беспокоясь о том, как реализована данная служба. В общем случае рекомендуется проектировать веб-службы так, чтобы они работали именно как службы, а не как типичные компоненты. Тем не менее в Atlas дело обстоит несколько иначе. В приложениях Atlas веб-служба прежде всего является логически четким и эффективным способом выполнения действий на стороне сервера в ответ на действия клиента. Например, если потребуется заполнить клиентский элемент данными, сгенерированными сервером, вы используете веб-службу. Но в этом случае будет использоваться локальная веб-служба, определяемая и вызываемая из того же приложения. В Atlas определен специальный протокол для вызова веб-служб. Предполагается, что конечная точка понимает формат JSON и умеет обрабатывать суффикс /js. Atlas сначала требует у веб-службы вернуть класс-посредник JavaScript — по аналогии с тем, как в Visual Studio 2005 утилита wsdl.exe используется для получения класса-посредника C# или Visual Basic. В приложениях Atlas веб-службы работают только в контексте прикладной среды и могут представлять собой веб-службы ASP.NET, находящиеся под управлением приложений Atlas. Значит ли это, что вы не сможете вызывать реальные веб-службы (такие, как Amazon или Google)? Вовсе нет. При помощи мостовой технологии страницу Atlas можно связать практически с любой веб-службой в Интернете. Программный интерфейс остается неизменным; схема всего лишь дополняется промежуточным компонентом — мостовым файлом. Мостовой файл отвечает за предоставление информации, достаточной для построения класса-посредника JavaScript прикладной средой. Трудно представить, что реально работающая веб-служба, которой ничего не известно об Atlas, сможет сгенерировать класс-посредника JavaScript нужным вам образом. Мостовой файл решает именно эту задачу. Наконец, мостовой файл также отвечает за упаковку возвращаемого значения в объект JavaScript. В этой главе мы воспользовались мостовой технологией для приема информации о книгах с веб-сайта Amazon. После этого мы столкнулись с проблемой удобного вывода полученной информации. Я выбрал вариант с построением таблицы HTML с использованием клиентского объекта StringBuilder. Если бы информация Amazon был доступна на моем сервере, было бы естественно воспользоваться Repeater или любым другим элементом, связанным с данными. Именно клиентской привязке данных и будет посвящена следующая глава.


ГЛАВА 7

Привязка данных на стороне клиента

В этой главе: I Привязка данных к элементам I Расширенные функции Подавляющее большинство веб-страниц предназначено для отображения данных. В большинстве случаев данные читаются из серверного хранилища, оформляются в разметку HTML и передаются ожидающему пользователю. Все эти задачи обычно выполняются на сервере и требуют полного возврата данных или по меньшей мере панели Atlas с частичным обновлением. Как было показано в главе 3, для простейшей реализации некоторой интерактивности в вебстраницах существует простой прием: включите элементы, связанные с данными, в элемент UpdatePanel, а затем используйте стиль программирования, знакомый по классической платформе ASP.NET. Например, после размещения элемента GridView или Repeater на элементе UpdatePanel вы сможете легко отображать, фильтровать, выводить в страничном режиме и сортировать любые данные — и за все это придется расплачиваться лишь ценой асинхронного возврата данных Atlas. Чего еще можно пожелать? Чтобы дать содержательный ответ на этот вопрос, давайте продолжим с того, на чем мы остановились в главе 6. В этой главе читатель узнал, как загрузить данные с веб-сайта Amazon и оформить их в виде коллекции JavaScript на стороне клиента. Как организовать клиентское представление таких данных? Какие элементы необходимы для отображения перечислимых (enumerable) данных? Настоящая глава посвящена клиентской привязке данных — то есть совокупности средств и методов, используемых для формирования эффективных, управляемых данными пользовательских интерфейсов на JavaScript и HTML, в сочетании с дружественной моделью приложения классической платформы ASP.NET.

Привязка данных к элементам В комплект поставки Atlas входит ряд элементов, связанных с данными, а также источников данных, которые могут использоваться напрямую в браузерах без полного возврата данных. Механизм привязки данных (то есть процесс связывания данных с элементами) существует в двух вариантах: простом и сложном.


266

Глава 7. Привязка данных на стороне клиента

Простая привязка данных Простой привязкой данных называется прямолинейное установление связи между одним значением в источнике данных и свойством элемента. Значения свойств элементов могут задаваться как на программном, так и на декларативном уровне. Программный способ требует перехвата определенного события (например, загрузки страницы или изменения состояния элемента) и явного присваивания нужных значений одному или нескольким свойствам элемента. При декларативном способе разработчик должен «объявить» о своих намерениях в фрагменте кода XML Script. Каждый элемент отслеживает свой набор привязок и использует эту информацию при выводе. Декларативная привязка выражается посредством XML Script, и в ней задействуются компоненты Atlas.

Коллекция <Bindings> Во всех компонентах Atlas определяется коллекция bindings. Элементы коллекции описывают связь между одним свойством компонента и одним свойством указанного источника данных. Источником данных может быть как тот же, так и другой компонент. Привязки пересчитываются автоматически (по умолчанию) или программно. В последнем случае вы должны вызвать метод evaluateln объекта binding. Каждая привязка представляется экземпляром класса Sys.Binding. Привязки определяются элементами <binding> в секции <bindings>. Атрибуты элементов <binding> перечислены в табл. 7.1. Таблица 7.1. Атрибуты элемента binding

Атрибут

Описание

automatic

Логическое значение, указывающее способ пересчета привязок (автоматический или программный). По умолчанию используется значение true

dataContext

Компонент-источник для операций привязки данных

dataPath

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

direction

Направление привязки (допустимые значения — In, Out и InOut). По умолчанию используется значение In Идентификатор привязки. Задается только в том случае, если привязка должна использоваться в программном коде

id property

Имя свойства компонента, получающего связанные данные

propertyKey

Индекс или ключ целевого свойства (если применяется индексирование)

transformerArgument

Необязательный аргумент, передаваемый преобразователю

transform

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


Привязка данных к элементам

267

Обратите внимание: привязка работает только между компонентами Atlas и только через свойства, определенные для компонентов Atlas. Таким образом, хотя вы можете инкапсулировать текстовое поле HTML или тег <span> в компоненте Atlas, значения, относящиеся к пользовательскому интерфейсу (такие, как свойства стиля), не могут быть использованы для привязки. Возможности привязки ограничиваются копированием значения исходного свойства в целевое свойство. К исходному значению можно применить простые преобразования, но нельзя выполнять действия или реализовать пользовательскую логику. При настройке в автоматическом режиме (по умолчанию) привязка чрезвычайно полезна, поскольку ее пересчет гарантируется при каждом изменении свойства. Впрочем, при изменении свойства можно только задать значение. Для выполнения более сложных действий (скажем, обращения к стилю объекта или выполнению пользовательской логики) придется прибегнуть к классическим обработчикам событий.

Использование привязки для назначения свойств элемента Допустим, на странице находится раскрывающийся список и надпись. Фрагмент разметки ASP.NET: <asp:DropDownList runat="server" ID="DropDownListl"> <asp:Listltem Text="Red" /> <asp:ListItem Text="Blue" /> <asp:Listltem Text="Brown" /> <asp:Listltem Text="Orange" /> </asp:DropDownList> <hr /> <asp:label runat="server" id="Label1" />

Чтобы определить привязку таким образом, чтобы текст надписи автоматически обновлялся при любых изменениях состояния раскрывающегося списка, придется написать небольшой фрагмент XML Script: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <select id="DropDownListl" /> <label id="Labell"> <bindings> <bitiding dataContext="DropDownListl" dataPath="selectedValue" property="text" /> </bindings> </label> </components> </page> </script>

Атрибут property означает, что свойство text компонента Label должно обновляться при каждом изменении свойства selectedValue компонента DropDownListl. В приведенном примере текст надписи изменяется сразу же после смены выделенной строки в раскрывающемся списке, отражая новое состояние выделения.


268

Глава 7. Привязка данных на стороне клиента

ВНИМАНИЕ

Если к тегам HTML, оформляемым в виде компонентов Atlas, применяются какие-либо расширители, в дереве компонентов Atlas появятся дубликаты. В этом случае не следует добавлять определения компонентов самостоятельно; воспользуйтесь тем, что могут сделать за вас расширители. Допустим, надпись связана с содержимым текстового поля. В странице должен присутствовать следующий код: <textBox id="TextBoxl" />

Но если поле TextBox1 также связывается с расширителем, то приведенный текст появится дважды в клиентском исходном коде (там, где его вставите вы, и там, где его вставит расширитель) и runtime-среда Atlas выдаст сообщение об ошибке. Чтобы исправить ситуацию, опустите приведенное объявление; расширитель сгенерирует его за вас. Если позднее расширитель будет удален, вставьте объявление текстового поля вручную, чтобы избежать ошибки «Object Not Found». Что, если значение свойства-источника обновляется программным способом? Будут ли свойства связанного элемента обновляться автоматически? Давайте добавим кнопку на страницу из нашего примера и посмотрим, что произойдет: <asp:Button ID="Buttonl" runat="server" Text="Click" OnClientClick="SelectItem();return false;" />

Элемент ASP.NET Button инкапсулирует традиционную кнопку отправки данных HTML. Однако возврат false в атрибуте OnClientClick позволяет обойти стандартное поведение инкапсулированной кнопки отправки данных и пропустить отправку формы на сервер. В данном случае серверный элемент Button ведет себя как простая (не выполняющая отправку данных) клиентская кнопка. Для программной смены выделения в раскрывающемся списке потребуется следующий код: <script type="text/javascript"> function SelectItern О { // Выделение любой строки (например, 3) $("DropDownListl").selectedlndex = 3; } </script>

Интересный результат: выделение в раскрывающемся списке изменилось, но связанный элемент не обновился. Почему это произошло? Все компоненты Atlas используют метод raisePropertyChange для оповещения всех связанных элементов об изменении своего состояния. В результате происходит пересчет привязок. Для компонента Select оповещения об изменениях состояния происходят при выдаче события onchange, инициируются нижележащим объектом DOM и при обновлении свойств, специфических для Atlas (например, data). Программное изменение selectedlndex не инициирует события onchange в модели DOM и, как следствие, не обновляет привязки.


Привязка данных к элементам

269

Учтите: речь идет об особенности раскрывающихся списков и их реализации в DOM. В аналогичной ситуации элемент ТехШох будет работать иначе. При программном задании содержимого в текстовом поле инициируется событие, обновляющее все привязки. В конечном итоге привязки пересчитываются тогда, когда источник данных оповещает об изменении состояния отслеживаемого свойства. Каждый компонент Atlas организует оповещение об изменениях состояния в соответствии со своей внутренней реализацией.

Использование преобразователей Ранее уже говорилось о том, что привязка в основном сводится к присваиванию исходного значения целевому свойству, к исходному значению можно применить некоторые преобразования. Для этой цели используются специальные обработчики событий, называемые преобразователями (transformers). Преобразователь определяется со следующей сигнатурой: function MyTransformer(sender, eventArgs) { }

Аргумент sender определяет объект Binding, инициировавший пересылку данных. Аргумент eventArgs является экземпляром класса BindingEventArgs и инициализируется значением, хранящимся в целевом элементе. Свойства класса BindingEventArgs перечислены в табл. 7.2. Таблица 7.2. Свойства класса BindingEventArgs Свойство

Описание

direction

Направление текущей привязки: In, Out или InOut

targetPropertyType

Тип целевого свойства в привязке

transformerArgument

Аргумент, передаваемый преобразователю. Значение задается в элементе <binding> и должно быть постоянной величиной

value

Значение, задаваемое в результате привязки

Преобразователь может изменить значение свойства value в объекте BindingEventArgs. При выходе из преобразователя содержимое value будет присвоено целевому свойству привязки. Следующий преобразователь применяет форматирование к строке, выбранной в раскрывающемся списке: <script type="text/javascript"> function FormatString(sender, eventArgs) { eventArgs.set_value("<t»Selected color is: </b>" + eventArgs.get_value()); } </script>

В Atlas определяется несколько встроенных преобразователей; они перечислены в табл. 7.3.


270

Глава 7. Привязка данных на стороне клиента

Таблица 7.3. Встроенные преобразователи Функция

Описание

Add

Прибавляет аргумент преобразователя к исходному значению. Свойство direction указывает, должно ли значение прибавляться или вычитаться (вычитание применяется при direction=Out)

Compare

Сравнивает исходное значение с аргументом преобразователя, если он задан; в противном случае исходное значение вычисляется как логическая величина. Преобразователь не является двусторонним и может использоваться только при direction=In

CompareInverted

Проверяет, отличается ли исходное значение от аргумента преобразователя, если он задан; в противном случае исходное значение вычисляется как логическая величина, а полученный результат инвертируется. Преобразователь не является двусторонним и может использоваться только при direction=In

Invert

Применяет оператор JavaScript ! к исходному значению. Учтите, что инвертирование null дает результат true

Multiply

Умножает исходное значение на аргумент преобразователя

ToLocaleString

Преобразует исходное значение в строку с учетом локального контекста. Преобразователь не является двусторонним и может использоваться только при direction=In

ToString

Преобразует исходное значение в строку. Преобразователь не является двусторонним и может использоваться только при direction=In

Будет полезно кратко рассмотреть исходный код преобразователя Add. Это даст вам более четкое представление о принципах работы преобразователей и об их потенциальных возможностях. Sys.BindingBase.Transformers.Add = function(sender, eventArgs) { var value = eventArgs.get_value(); if (typeof(value) != 'number') { value = Number.parse(value); } var delta = eventArgs.get_transformerArgument(); if (!delta) { delta = 1; } if (typeof(delta) != 'number') { delta = Number.parse(delta);

if (eventArgs.get_direction() == Sys.BindingDirection.Out) { delta = -delta;

var newValue = value + delta;


Привязка данных к элементам

271

if (eventArgs.get_targetPropertyType() != 'number') { newValue = newValue.toStringO; } eventArgs.set_value(newValue); }

Как видите, преобразователь разбирает параметры, пытаясь получить значение нужного типа — в данном случае это число. То же самое происходит и при возврате преобразованного значения. При несоответствии свойства целевого типа новое значение сериализуется в строку. ПРИМЕЧАНИЕ

Если преобразователь используется для выполнения математических операций со связанным значением, убедитесь в том, что вы работаете с допустимыми числами. Кроме вызова Number.parse() для преобразования значения в число, сравните его с NaN — специальным значением класса Number, указывающим, что значение (как это ни парадоксально) не является числом. Для проверки следует использовать глобальную функцию isNaN, определенную в языке JavaScript.

Сложная привязка данных В терминологии .NET Framework сложной привязкой данных называется ситуация, при которой с элементом связывается коллекция записей данных. Привязка влияет на весь интерфейс элемента, а не только на отдельное свойство. Как правило, элемент при этом поддерживает шаблоны и предназначается для хранения дочерних элементов. В классической среде ASP.NET сложная привязка данных поддерживалась элементами Repeater, DataList и DataGrid. В ASP.NET 2.0 к ним присоединились новые элементы GridView, DetailsView и FormView. Как отобразить содержимое коллекции объектов JavaScript, находящихся на стороне клиента, — например, результаты вызова веб-службы? В главе 6 было представлено обходное решение с динамическим построением таблицы HTML при помощи объекта Sys.StringBuilder. Трюк отлично сработал. Но является ли такое решение единственным? Должен существовать более изящный способ.

Макетные шаблоны В конечном счете, динамическое построение строк является единственным способом построения пользовательских интерфейсов в HTML с использованием команд JavaScript. Проблема в том, что возможности построителей строк ограничены, и такое не назовешь простым и удобным. В фрагменте кода, приведенном в главе 6, не использовались стили, а решение ограничивалось объединением тегов <tr> и <td>. А если вы захотите добавить стилевое оформление и выбирать его на основании условий времени выполнения? Что, если в какой-то момент потребуется изменить структуру полученной таблицы? При поддержке аналогичных (и действительно реалистичных) схем логика работы с построителями строк получается довольно сложной и запутанной. А «сложный и запутанный» также означает «подверженный ошибкам»; подобные ситуации требуют досконального тестирования для всех возможных случаев


272

Глава 7. Привязка данных на стороне клиента

использования. Конечно, мы разработчики и тестирование — наш хлеб с маслом. И все же именно из-за того, что мы разработчики и программирование является нашей профессией, мы должны искать более качественные и эффективные способы выполнения своей работы. На сцену выходят макетные шаблоны Atlas. Макетные шаблоны представляют собой компоненты Atlas, которые помогают организовать <div> и другие теги HTML в периодическую структуру с заготовками для данных. Макетные шаблоны обрабатываются специализированным клиентским элементом Atlas — ListView. Для примера возьмем следующую разметку HTML: <div id="searchResults"></div> <div style="display: none;"> <div id="LayoutTemplate"> <ul id="ItemTemplateParent"> <li id="ItemTemplate"> <span>...</span><br /> <span>...</span>,  <span>...</span><hr /> </li> </ul> </div> </div>

Верхний элемент <div> представляет точку, в которой будет вставляться динамически сгенерированная разметка, связанная с данными. Тег <div> с именем LayoutTemplate описывает структуру полученной разметки. Блок скрывается, потому что он служит лишь для передачи информации, а его содержимое разбирается и обрабатывается для получения реальной разметки. Элемент с именем ItemTemplateParent обозначает блок разметки, содержащий повторяющиеся записи. Наконец, элемент с именем ItemTemplate представляет шаблон отдельных записей, каждая из которых связана с объектом данных. Имена элементов, представляющих шаблоны макета, родительского элемента и записи данных, выбираются произвольно. Не стоит полагать, будто ключевые слова LayoutTem plate, ItemTemplateParent и ItemTemplate имеют для Atlas какой-то особый смысл. Однако при отображении шаблонов на элемент ListView имена шаблонов назначаются специальным свойствам, обозначающим шаблоны макета, родителя и записи данных. Макет, определяемый предшествующим фрагментом, обеспечивает представление связанного источника данных в виде маркированного списка, при этом каждая позиция списка превращается в две строки: Company ID Company Name, ContactName

Для преобразования шаблона в разметку, связанную с данными, потребуется код XML Script. Прежде всего необходимо создать компонент ListView для тега <div>, обозначающего заготовку для разметки (searchResults в приведенном примере). Но для начала стоит поближе познакомиться с программным интерфейсом клиентского элемента Atlas ListView.


Привязка данных к элементам

273

Элемент ListView Элемент Sys.UI.Data.ListView принадлежит к числу элементов Atlas, связанных с данными, и наследует от Sys.UI.Data.DataControl. Класс DataControl является родительским для всех элементов Atlas, связанных с данными. Потомки DataControl поддерживают списковую привязку, а также привязку отдельных записей и средства перемещения (навигации). Свойства класса перечислены в табл. 7.4. Таблица 7.4. Свойства элемента DataControl

Свойство

Описание

canMoveNext

Логическое свойство, указывающее, поддерживает ли элемент перемещение в прямом направлении (по умолчанию True)

canMovePrevious

Логическое свойство, указывающее, поддерживает ли элемент перемещение в обратном направлении (по умолчанию True)

data

Источник данных (чтение и запись). Значение представляет собой коллекцию данных

dataIndex

Индекс элемента связанного источника данных, выбранного для отображения (чтение и запись)

dataItem

Текущая запись источника данных, если задано свойство dataIndex (только чтение)

length

Количество записей данных в текущем связанном объекте источника данных

Также базовый класс DataControl содержит четыре метода: addltem, deleteCurrentItem, moveNext и movePrevious. В табл. 7.5 перечислены специфические свойства элемента ListView. Таблица 7.5. Свойства элемента ListView

Свойство

Описание

alternatingItemCssClass

Имя стиля CSS, применяемого к каждой второй записи данных (чтение и запись)

layoutTemplate

Шаблон HTML, определяющий макет всего представления (чтение и запись)

itemCssClass

Имя стиля CSS, применяемого к каждой записи данных (чтение и запись)

itemTemplate

Шаблон HTML, определяющий макет отдельных записей данных (чтение и запись)

itemTemplateParentElementId

Идентификатор элемента HTML, который является родителем по отношению к шаблону записи данных (чтение и запись). Как правило, используется для определения заголовка разметки

separatorCssClass

Имя стиля CSS, применяемого к разделителям, если таковые имеются (чтение и запись) продолжение ■&


274

Глава 7. Привязка данных на стороне клиента

Таблица 7.5 (продолжение) Свойство

Описание

separatorTemplate

Шаблон HTML, определяющий макет разделителя (чтение и запись)

emptyTemplate

Шаблон HTML, определяющий макет всего спискового представления при отсутствии связанных данных (чтение и запись)

Шаблон Atlas является экземпляром класса Sys.UI.ITemplate. Для заполнения элемента ListView используется код XML Script. Этот код должен установить соответствие между разметкой HTML и шаблонами, поддерживаемыми элементом ListView.

Использование элемента ListView Давайте начнем настройку элемента ListView с определения небольшого фрагмента разметки HTML, который должен быть связан с шаблонами (фактически это та же разметка, которую я приводил ранее, с другими идентификаторами и заполненными элементами <span>). <div id="searchResults"></div> <div style="display: none;"> <div id="search_LayoutTemplate"> <ul id="search_ItemTemplateParent"> <li id="search_ItemTemplate"> <span id="search_ID"></span><br /> <span id="search_Company"></span>,  <span id="search_Contact"></span><hr /> </li> </ul> </div> </div>

Тег searchResults указывает, в каком месте страницы должна быть вставлена разметка. С другой стороны, скрытый тег <div> содержит метаданные, а не реальную разметку HTML для страницы. Скрытое содержимое этого блока описывает способ построения разметки для списка. Элемент с именем searchJtemTemplateParent определяет разметку, которую должен сгенерировать элемент ListView перед тем, как приступать к перебору связанных данных. Блок, помеченный как родительский шаблон, генерируется только один раз. Он используется в Atlas практически так же, как вы используете свойства HeaderTemplate и FooterTemplate в серверных шаблонных элементах ASP.NET. Элемент с именем searchJtemTemplate определяет шаблон HTML, который заполняется данными и повторяется для каждой связанной записи данных. Шаблон может содержать произвольную комбинацию тегов HTML с уникаль-


Привязка данных к элементам

275

ным идентификатором. В конечном итоге приведенный ранее шаблон сгенерирует следующий код HTML: <ul> <li> <span>...</span> <br/> <span>...</span> Snbsp; <span>...</span> <hr /> </li> </ul>

Для получения этого блока разметки потребуется следующий код XML Script: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <listView id="searchResults" itemTemplateParentElementId=''search_itemTemplateParent''> <layoutTemplate> <template layoutElement="search_layoutTemplate" /> </layoutTemplate> <itemTemplate> <template layoutElement="search_itemTemplate"> <label id="search_ID"> <bindings> <bitiding dataPath="ID" property="text" /> </bindings> </label> <label id="search_Company"> <bindings> <binding dataPath="CompanyName" property="text" /> </bindings> </label> <label id="search_Contact"> <bindings> <binding dataPath="ContactName" property="text" /> </bindings> </label> </template> </itemTemplate> </listView> </components> </page> </script>

Компонент <NstView> связывается с тегом <div> searchResults и использует элемент DOM с именем searchJtemTemplateParent в качестве родительского. На этом уровне можно задать классы CSS для оформления содержимого. Окончательный макет элемента ListView определяется блоком searchJayoutTemplate управляющей страницы. Наконец, в шаблоне записи данных перечислены все


276

Глава 7. Привязка данных на стороне клиента

элементы DOM блока searchJtemTemplate управляющей страницы. Элементы связываются с данными при помощи компонентов <label> и привязок Atlas: <label id="search_ID"> <bindings> <binding dataPath="ID" property="text" /> </bindings> </label>

Например, тег <span> с именем search_ID ассоциируется с клиентским элементом Label. Свойство text надписи связывается со свойством ID записи данных. Как связать данные с клиентскими элементами? Есть два основных способа. Во-первых, можно написать обработчик клиентского события и вызвать метод веб-службы для загрузки данных. Когда это будет сделано, задается свойство data элемента, связанного с данными. Во-вторых, можно воспользоваться клиентским компонентом-источником данных.

Программная привязка данных Чтобы опробовать привязку данных, рассмотрим еще один пример. Он продолжается с того места, на котором мы остановились в главе 6. Допустим, имеется страница, подключенная к веб-службе Amazon. Страница получает коллекцию динамически созданных объектов JavaScript, которые необходимо отобразить в табличном виде. Вот как выглядит код для создания спискового представления на базе таблицы HTML: <div id="tableResults"></div> <div style="display: none;"> <div id="table_layoutTemplate"> <table cellpadding="2"> <thead class="tableHeader"> <tr> <td><b>Book</b></td> <td><b>Info</b></td> </tr> </thead> <tbody id="table_itemTemplateParent" class="tableContent"> <tr id="table_itemTemplate" > <td> <img id="table_Cover" src="" alt="" /> </td> <td> <span id="table_ISBN"></span><br /> <span id="table_Title"></span><br /> <span id="table_Publisher"></span><br /> </td> </tr> </tbody> </table> </div> </div>


Привязка данных к элементам

277

Таблица состоит из двух столбцов. В левом столбце выводится обложка книги, а в правом — информация о ней (название, код ISBN и издатель). Необходимый код XML Script выглядит так: <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <listView id="tableResults" itemTemplateParentElementId="table_itemTemplateParent"> <1ayoutTemplate> <template layoutElement="table_layoutTemplate" /> </layoutTemplate> <itemTemplate> <template layoutElement="table_itemTemplate"> <label id="tableResults_ISBN"> <bindings> <binding dataPath="ISBN" property="text" /> </bindings> </label> <label id="tableResults_Title"> <bindings> <binding dataPath="Title" property="text" /> </bindings> </label> <1 abel id="tableResults_Publisher"> <bindings> <binding dataPath="Publisher" property="text" /> </bindings> </label> <image id="tableResults_Cover"> <bindings> <binding dataPath="CoverPicture" property="imageURL" /> </bindings> </image> </template> </itemTemplate> </listView> </components> </page>

На странице находится клиентская кнопка, щелчок на которой приводит к вызову веб-службы. Обработчик щелчка на кнопке называется findBooks. <script type="text/javascript"> function findBooks() { ShowProgress(true); var author = $("TextBoxl").value; IntroAtlas.AmazonPox.Find( {KeywordSearch:author}, findBooksCompleted, findBooksTimeout, findBooksError); } function findBooksCompleted(results, response, context) { var tableResults = $("tableResults"); tableResults.control.set_data(results); ShowProgress(false); }


278

Глава 7. Привязка данных на стороне клиента

function findBooksTimeout(results, context) { alert(results); ShowProgress(false); } function findBooksError(results, response, context) { alert(results.getjnessageO); ShowProgress(false); } function ShowProgress(busy) { if (busy) { $("ProgressBar").style.display = ""; $("Buttonl").disabled = "disabled"; } else { $("ProgressBar").style.display = "none"; $("Buttonl").disabled = ""; } } </script>

Ключевая операция выполняется в функции обратного вызова findBooksCompleted. При возврате управления веб-службой мы получаем данные и задаем свойство data компонента ListView: function findBooksCompleted(results) { var tableResults = $("tableResults"); tableResults.control.set_data(results); }

На рис. 7.1 показана страница в действии. СОВЕТ

Запустив страницу, вы заметите, что получение данных сопровождается анимацией. Чтобы добиться этого эффекта, достаточно определить пользовательский класс CSS со свойством background-image, которому назначен анимированный GIF-файл. Далее остается лишь присоединить стиль к элементу на странице и отображать/скрывать его по мере надобности.

Клиентские компоненты источников данных Как и в случае с серверными элементами, для заполнения клиентских элементов Atlas, связанных с данными, могут использоваться специальные компоненты, называемые источниками данных. Связь между клиентскими элементами и источниками данных устанавливается на декларативном уровне в коде XML Script посредст-


Привязка данных к элементам

279

вом элемента <binding>. При использовании источников данных функция привязки становится полностью декларативной и совершенно не требует кода JavaScript.

Рис. 7.1. Привязка данных от веб-службы Amazon

Класс Sys.Data.DataSource Компоненты-источники данных Atlas являются экземплярами класса Sys.Data. DataSource. В отличие от классической модели ASP.NET с несколькими различными компонентами источников данных (ObjectDataSource, SqIDataSource и т. д.), в Atlas существует только один объект источника данных верхнего уровня — класс DataSource, делегирующий задачи доступа к данным пользовательским службам данных. Иерархия показана на рис. 7.2. Программный интерфейс класса DataSource определяется в табл. 7.6. Источник данных инициализируется либо явно заданными исходными данными, либо настраивается на автоматическую загрузку данных (autoLoad). Источник данных хранит данные в своем внутреннем представлении в виде объекта Sys.Data. DataTable. Впрочем, массивы и коллекции объектов также допустимы; после присваивания компоненту они загружаются в клиентский объект DataTable.


280

Глава 7. Привязка данных на стороне клиента loadi Sys.Data DataSource

Клиент Atlas ASP.NET Server

Для операций загрузки вызывается GetData

Метод с пометкой ■* SELECT

Для операций записи вызывается SaveData

YourDataService.asmx

I

+

Метод с пометкой DELETE, UPDATE, INSERT

MicrosoftWeb.Services.DataService Внутренний вызов

1

""■GetData

SaveData -

Внутренний вызов

System.Web.Services.Webservice

Рис. 7.2. Источники данных и службы данных в Atlas Таблица 7.6. Члены класса DataSource

Член класса

Описание

autoLoad

Логическое свойство, указывающее, что источник данных должен начать загрузку данных сразу же после того, как будет загружен в странице (по умолчанию false)

data

Данные, хранящиеся в источнике (чтение и запись). Допустимые типы — массивы и объекты Sys.Data.DataTable

dataAvailable

Событие, которое происходит при наличии в источнике данных, готовых к отображению

initialData

Строка JSON, предназначенная для чтения и записи исходного содержимого источника данных. Не задается при истинном свойстве autoLoad

isDirtyAndReady

Логическое свойство, указывающее, что источник данных готов к пакетному обновлению — то есть данные доступны, и имеются необработанные изменения

isReady

Логическое свойство, указывающее, что источник данных готов к выводу

load

Метод для загрузки данных в компонент. Вызывает метод службы данных, помеченный как метод SELECT. Если один атрибут совместно используется несколькими методами, для определения нужного метода используется свойство loadMethod

loadMethod

Имя метода службы данных, вызываемого для получения данных. Свойство задается при наличии у службы нескольких методов SELECT

parameters

Массив параметров, передаваемых вызываемому методу службы данных

rowCount

Количество записей в источнике данных (только чтение)


Привязка данных к элементам

281

Член класса

Описание

serviceURL

URL используемой службы данных (чтение и запись)

save

Метод для сохранения необработанных изменений в службе данных. Реализует своего рода режим пакетного обновления с активной обработкой данных, содержащих незавершенные изменения — такие, как операции удаления, обновления и вставки

Механика работы источников данных Класс Sys.Data.DataSouce представляет собой клиентский компонент, способный подключаться к домашнему серверу и загружать обновленные данные для клиентских операций. Подключение производится через механизм веб-служб, описанный в главе 6. «Партнером» клиентского источника данных на стороне сервера является компонент службы данных. Служба данных представляет собой серверный класс, производный от DataService — класса, определенного в пространстве имен Microsoft.Web.Services. Класс DataService в конечном счете наследует от System. Web.Services. WebService; фактически он является особой разновидностью вебслужбы ASP.NET (см. рис. 7.2). Чтобы организовать привязку данных Atlas через источник, вы создаете собственную службу данных и открываете доступ к ней как к домашней веб-службе (вскоре я объясню более подробно). URL службы данных передается источнику данных в свойстве serviceURL. Настройка источника данных определяется следующим фрагментом XML Script: <dataSource id="dataSourcel" serviceURL="MyDataSource.asmx" />

Атрибут autoLoad обеспечивает автоматическую загрузку данных при загрузке страницы. Если он не используется, загрузка данных осуществляется методом load источника данных. Следующий фрагмент кода функционально эквивалентен заданию истинного свойства autoLoad: <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <application> <load> <invokeMethod target="dataSourcel" method="load" /> </load> </application> <dataSource id="dataSourcel" serviceURL="MyDataSource.asmx" /> </components> </page>

Для дополнительного контроля за ходом операции можно воспользоваться кодом JavaScript: <script type="text/javascript"> function findCustomers(dataSource) {


282

Глава 7. Привязка данных на стороне клиента ShowProgress(true); $object("dataSource1").load();

} </script>

На рис. 7.3 показана страница в действии.

Рис. 7.3. Управление загрузкой данных в Atlas

В некоторых случаях требуется организовать оповещение о появлении доступных данных с целью обновления некоторых фрагментов пользовательского интерфейса. Для этого перехватывается событие dataAvailable: <dataSource id="dataSourcel" serviceURL="MyDataSource.asmx" dataAvailable="DataIsHere" />

Объект источника данных передается обработчику события в аргументе: function DatalsHere(dataSource) { ShowProgress(false); alert(dataSource.get_rowCount() + " rows ready. Click to display."); }

Событие dataAvailable задействовано в обратном вызове по завершении метода при обращениях к веб-службам. Метод load источника данных организует вызов веб-службы, направленный к конечной точке службы данных — свойству serviceURL. При обращении будет вызван метод GetData службы. В соответствии с архитектурными требованиями класс службы данных должен быть производным от серверного класса DataService, который предоставляет всего лишь два метода: GetData и SaveData. Соответственно, эти методы присутствуют во всех пользовательских службах данных. Внутренняя реализация конструктора DateService использует механизм рефлексии для получения и сохранения информации о методах, помеченных атрибутом DataObjectMethod. Значения атрибута - Select, Update, Delete и Insert - объединены в перечисляемый тип DataObjectMethodType. Таким образом, каждый метод


Привязка данных к элементам

283

службы данных предоставляет информацию о своей роли. Метод GetData принадлежит к числу так называемых «фасадных методов»: он просто выбирает метод, помеченный атрибутом Select, и вызывает его. Что, если атрибут назначен нескольким методам службы данных? В этом случае GetData выбирает первый из указанных методов, если только источник данных не дает явного указания на вызываемый метод. Атрибут loadMethod сообщает, какой из методов (Select) должен быть вызван для источника данных. Если loadMethod ссылается на метод, не помеченный атрибутом Select, происходит исключение. Аналогичным образом, метод save источника данных выбирает методы, помеченные атрибутами Delete, Update и Insert, и выполняет их в указанном порядке. Метод save инициирует своего рода пакетную операцию обновления. Он извлекает из своих данных записи, содержащие необработанные изменения, и последовательно перебирает их. Следующий псевдокод показывает, как реализована эта процедура: foreach (object row in changeList.Deleted) { DataService.InvokeMethod(DataObjectMethodType.Delete, row); } foreach (object row in changeList.Updated) { DataService.InvokeMethod(DataObjectMethodType.Update, row); } foreach (object row in changeList.Inserted) { DataService.InvokeMethod(DataObjectMethodType.Insert, row); }

Определять несколько методов с атрибутами Delete, Update или Insert в службе данных запрещается. При нарушении этого правила происходит исключение.

Создание пользовательской службы данных Пользовательская служба данных представляет собой класс, производный от DataService. Класс содержит несколько открытых методов, помеченных специальным атрибутом DataObjectMethod. Как упоминалось ранее, этот атрибут предназначается для пометки методов, выполняющих операции Select, Insert, Update и Delete. ПРИМЕЧАНИЕ

Атрибут DataObjectMethod не является специфическим для Atlas. Он определяется в пространстве имен System.ComponentModel сборки System. Этот атрибут, изначально появившийся в ASP.NET 2.0 для описания объектов данных в визуальных средах вроде Microsoft Visual Studio 2005, идентифицирует тип метода. В Atlas атрибут используется для той же цели, но применяется к другому объекту — компоненту источника данных. Доступ к классу службы данных предоставляется через открытую конечную точку .asmx. Пример класса службы данных: namespace IntroAtlas.WebServices { [WebService(Namespace = "http://introatlas.book/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfilel_l)]


284

Глава 7. Привязка данных на стороне клиента public class MyDataSource : DataService { [WebMethod] [DataObjectMethod(DataObjectMethodType.Select); public CustomerCollection LookupAllCustomers() { return CustomerManager.LoadAl 1 0 ; } [WebMethod; [DataObjectMethod(DataObjectMethodType.Select) [ public CustomerCollection CustomersInSouthAmericaO { return CustomerManager.LoadFromSouthAmerica(); } public CustomerCollection LookupCustomers(string query) { return CustomerManager.LoadBylnitial(query); }

}

}

[WebMethod; [DataObjectMethod(DataObjectMethodType.Update); public Customer UpdateCustomer(Customer cust) { return CustomerManager.Save(cust); }

Класс связывается с конечной точкой .asmx следующим образом: <%& WebService Language="C#" CodeBehind="-/App_Code/MyDataSource.cs" Class="IntroAtlas.WebServices.MyDataSource" %> Давайте проанализируем исходный код класса более подробно. Класс содержит два метода Select и один метод Update. Это означает, что служба не может использоваться с элементом данных, поддерживающим удаление и вставку. Атрибут DataObjectMethod независим от атрибута WebMethod. Атрибут WebMethod не является строго обязательным для методов источника данных. Тем не менее, как вам, вероятно, уже известно, методы без атрибута WebMethod не могут вызываться в удаленном режиме извне (за пределами механизма источников данных Atlas), потому что такой метод даже не будет объявлен в качестве вызываемого метода веб-службы. ПРИМЕЧАНИЕ С учетом сказанного метод LookupCustomers, приведенный в коде примера, не является методом Select с точки зрения клиента, потому что он не обладает атрибутом DataObjectMethod. Более того, клиент даже не будет знать о существовании метода LookupCustomers, так как из-за отсутствия атрибута WebMethod ASP.NET не предоставит доступ к методу через Web. Таким образом, он больше напоминает вспомогательный метод для класса MyDataSource (а может, разработчик просто забыл пометить метод нужными атрибутами!)


Привязка данных к элементам

285

Какую информацию должны возвращать методы служб данных? Как упоминалось ранее, клиентский источник данных хранит свои данные в клиентском объекте DataTable — клиентском «партнере» класса ADO.NET DataTable. Это означает, что методы служб данных без каких-либо проблем принимают и возвращают объекты ADO.NET — такие, как DataTable, DataSet, DataView и DataRow. Кроме того, источники данных Atlas поддерживают массивы и коллекции. А это означает, что службы данных могут работать с пользовательскими объектами и их коллекциями, как в предыдущем примере с классами Customer и Customer-Collection. Однако обратите внимание, что источник данных преобразует объекты Customer и коллекции в клиентский объект DataTable. Пользовательский объект должен явно указать, какие из его свойств должны загружаться в столбцы таблицы. Только свойства, помеченные атрибутом DataObjectField, загружаются и становятся доступными через интерфейс источника данных. Иначе говоря, если свойство пользовательского объекта, используемого для пересылки данных источнику данных и обратно, не обладает атрибутом DataObjectField, прямая привязка результатов к ListView невозможна. Следующий класс используется для представления клиента; обратите внимание на пометку свойства CompanyName атрибутом DataObjectField, чтобы значение свойства могло использоваться для прямой привязки на стороне клиента: public class Customer { // Приватные поля private string jcompanyname; [DataObjectField(false); public string CompanyName { get { return jcompanyname; } set { jcompanyname = value; } } }

Разумно предположить, что классы представления сущностей (такие, как Customer) будут определяться на уровне бизнес-логики вашего приложения. Добавленный атрибут DataObjectField будет проигнорирован всеми верхними уровнями представления, за исключением Atlas. Атрибут DataObjectField может получать до четырех параметров: public DataObjectFieldAttribute( bool primaryKey, bool isldentity, bool isNullable, int length)

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


286

Глава 7. Привязка данных на стороне клиента

ПРИМЕЧАНИЕ

Элемент ListView воспроизводит данные «порциями» по пять записей, чтобы браузер не переставал реагировать на действия пользователя при большом объеме данных. Этот режим называется пошаговым воспроизведением.

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

Построение расширенных списковых представлений Как правило, при представлении данных в веб-приложениях используются две основных модели: списковая и детализированная. На классической платформе ASP.NET эти две модели представлены двумя исключительно популярными элементами: GridView и DetailsView. Библиотека Microsoft AJAX не предоставляет столь широкого выбора элементов, как сборка ASP.NET system.web, однако в ней имеется все необходимое для эффективного построения списковых и детализированных представлений. К настоящему моменту вы уже знакомы с основными принципами работы элемента ListView и знаете, как связать его с данными на программном или декларативном уровне. Что дальше? Процесс привязки данных к элементу можно сделать более эффективным и функциональным, наделив его некоторыми дополнительными возможностями. Давайте рассмотрим некоторые из них.

Отправка исходных данных После того как страница будет настроена для работы с данными с использованием источника данных, в ней можно включить необязательный режим автозагрузки. В этом случае разработчик избавляется от хлопот по заполнению источника данных и связанных элементов. Впрочем, не все так просто — взгляните на рис. 7.4. Если источник данных используется в режиме автозагрузки, серверу направляются два запроса: первый (управляемый пользователем) загружает страницу, а второй (управляемый компонентом источника данных) принимает ее данные. Если загружаемые автоматически данные не зависят от параметров времени выполнения, их можно внедрить в разметку страницы. В этом случае данные для инициализации источника поступают с первым запросом, и необходимость в дополнительном запросе для инициализации пользовательского интерфейса отпадает.


Расширенные функции

Браузер

Браузер

287

Загрузка страницы по запросу пользователя

Начинается построение страницы Источник данных размещает второй запрос для загрузки своих данных (autoLoad = true)

Браузер Содержимое элемента ListView на странице полностью сформировано Рис. 7.4. Для загрузки страницы и данных необходимы два запроса к серверу

Инициализационные данные для источника данных представляют собой текстовую строку, представляющую JSON-сериализованный объект. Следующий фрагмент кода демонстрирует передачу инициализационных данных компоненту-источнику: <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <components> <dataSource> <initialData> <![CDATA[ ... ]]> </initialData> <bindings> <binding dataContext="dataSourcel" dataPath="data" property="data" direction="Out" /> </bindings> </dataSource> </components> </page> </script>

Вы определяете дополнительный компонент <dataSource> и заполняете его элемент <initialData> JSON-представлением объекта, связываемого через JavaScript — например, коллекции. Вновь созданный компонент <dataSource> включает привязку к заполняемому компоненту-источнику данных. Атрибут direction="Out" означает, что источник данных, владеющий привязкой, является источником (а не приемником) при передаче данных. Во всех остальных привязках, рассматривавшихся ранее, объект, указанный в атрибуте dataContext, был владельцем данных, передававшихся объекту-владельцу привязки. С инициализационными данными все происходит в обратном направлении — собственно, именно это и означает атрибут direction компонента <binding>.


288

Глава 7. Привязка данных на стороне клиента

Как выглядит JSON-сериализованный объект? В следующем фрагменте представлен объект Sys.Data.DataTable с тремя столбцами и одной строкой: new Sys.Data.DataTable( [ new Sys.Data.DataColumn("CompanyName",String,nul 1,false,false), new Sys.Data.DataColumn("ContactName",String,nul 1,false,false), new Sys.Data.DataColumn("Country",String,null,false,false) ], [ {"CompanyName":"Alfreds Futterkiste", "ContactName":"Maria Anders", "Country":"Germany"}, } ~_

Конечно, вводить этот текст вручную было бы весьма противно, особенно если источник данных содержит несколько записей. Также присутствует и другой, гораздо более важный фактор — возможно, вы захотите управлять предоставляемыми данными на программном уровне. На помощь приходит новый серверный элемент Atlas Initial Data: <atlas:InitialData runat="server" id="InitialDatal" servicePath="~/samples/ch07/complex/MyDataSource.asmx" ClientDataSourceID="dataSourcel"> </atlas:InitialData>

При настройке элемента InitialData необходимо указать действительный виртуальный путь к службе данных. Элемент InitialData вызывает метод GetData указанной службы данных и генерирует приведенный ранее блок XML Script, вместе с привязками и JSON-сериализованными данными. Независимо от того, возвращает ли метод Select службы данных пользовательскую коллекцию или объект ADO.NET DataTable, элемент InitialData сериализует такие данные в JSON и обеспечивает их загрузку в клиентском элементе, связанном с данными. ПРИМЕЧАНИЕ

После инициализации клиентский компонент DataSource вызывает метод load, если свойство autoLoad равно true, или при наличии инициализационных данных. Внутренняя реализация load сначала проверяет инициализационные данные. Если они заданы, метод загружает данные и возвращает управление. В противном случае выдается следующий запрос на прием данных. Обратите внимание: после приема данных содержимое свойства initialData сбрасывается. Данные, присвоенные initialData на программном уровне, будут проигнорированы, если источник уже связан с данными.

Сортировка данных на стороне клиента В большинстве случаев список данных наполовину утрачивает свою полезность, если его нельзя сортировать по разным полям и направлениям. В классической модели ASP.NET элементы DataGrid и GridView обладают встроенной поддержкой


Расширенные функции

289

сортировки. Пользователь щелкает на гиперссылках, страница производит возврат данных, обновляет представление данных и перерисовывает изображение. Сортировка сетки приводит к выполнению двух высокозатратных операций. Во-первых, страница возвращает данные и полностью обновляется. Во-вторых, источник данных сетки сортируется, а сортировка не принадлежит к числу простых операций. У первой проблемы есть изящное решение: сетка вставляется в элемент UpdatePanel. В результате вам не придется осваивать новую модель программирования, а последствия от возврата данных для отображаемой страницы существенно снижаются. Что делать со второй проблемой — вычислительной сложностью сортировки? Честно говоря, вряд ли вам удастся сделать что-нибудь для заметного ускорения операций сортировки — не считая кэширования заранее отсортированных таблиц данных. Если вам потребуются расширенные средства сортировки, работающие по нескольким полям и в разных направлениях с большими таблицами данных, самым быстрым средством сортировки в конечном счете оказывается сервер баз данных. Однако обращения к серверу за сортировкой, даже без учета затрат на возвраты данных, создадут немалую нагрузку для канала связи. Как насчет сортировки на стороне клиента? Прежде, до появления Atlas, такое решение не было невозможным, но возможность скорее оставалась теоретической. Прикладная среда Atlas и язык XML Script позволяют легко справиться с этой задачей. Важнейшее изменение, которое необходимо внести в ListView для поддержки сортировки на стороне клиента, — это его связывание с объектом DataView вместо объекта DataSource. В свою очередь, DataView получает информацию от источника данных: <dataSource id="dataSourcel" servicellRL="MyDataSource.asmx" /> <listView id="tableResults" itemTemplateParentElemeriti d="table_itemTemplateParent"> <bindings> <bitiding dataContext="view" dataPath="filteredData" property="data" /> </bindings> </listView> <dataView id="view"> <bindings> <bitiding dataContext="dataSourcel" dataPath="data" property="data" /> </bindings> </dataView>

Модель в целом напоминает решение, использовавшееся для создания сортируемых сеток в классических приложениях ASP.NET. Элемент сетки связывается с объектом ADO.NET DataView, построенным на базе объекта ADO.NET DataTable. В ASP.NET и Atlas объект представления не хранит сами данные и ограничивается предоставлением отфильтрованного или отсортированного представления связанных данных. В табл. 7.7 перечислены члены класса Atlas DataView. Как видно из таблицы, объект DataView также обладает широкими возможностями для страничного вывода содержимого клиентского источника данных. Вскоре мы вернемся к теме страничного вывода.


290

Глава 7. Привязка данных на стороне клиента

Таблица 7.7. Члены класса Sys. Data. Data View Член класса

Описание

data

Данные, используемые для построения представления (чтение и запись)

filteredData

Данные текущего представления. Представление может отличаться от исходных данных из-за сортировки или фильтров

filters

Коллекция условий, ограничивающих состав записей текущего представления

hasNextPage

Логический признак, определяющий возможность перехода к следующей странице

hasPreviousPage

Логический признак, определяющий возможность перехода к предыдущей странице

length

Количество записей в текущем представлении

pageCount

Количество страниц в текущем представлении

pageIndex

Индекс страницы, выбранной в текущем представлении

pageSize

Размер страницы (чтение и запись)

sort

Метод сортировки представления

sortColumn

Имя столбца, по которому выполняется сортировка (чтение и запись)

sortDirection

Направление сортировки: по возрастанию (Ascending) или по убыванию (Descending) (чтение и запись)

Чтобы завершить пример, в котором пользователи могут сортировать набор данных на стороне клиента, необходимо включить в пользовательский интерфейс гиперссылки: <table eel 1padding="2"> <thead class="tableHeader"> <tr> <td><t» <a href="javascript:;" id="sortByCompany">Customer</a> </t»</td> <td><t» <a href="javascript:;" id="sortByContact">Contact</a> </t»</td> </tr> </thead> </table> Если списковое представление оформляется в виде таблицы, заголовки столбцов можно заключить в тег <а>. Затем в X M L Script эти гиперссылки связываются с новым аспектом поведения — sortBehavior: <control id="sortByCompany"> <behaviors> <sortBehavior dataView="view" sortColumn="CompanyName" /> </behaviors>


Расширенные функции

291

</control> <control id="sortByContact"> <behaviors> <sortBehavior dataView="view" sortColumn="ContactName" /> </behaviors> </control>

Разумеется, столбцы сортировки должны соответствовать столбцам связанного источника данных. Сортировка выполняется исключительно на стороне клиента без возвратов данных — как полных, так и ограниченных возвратов Atlas. Аспект поведения сортировки достаточно сообразителен, чтобы автоматически изменить направление сортировки при повторном щелчке на сортируемом столбце. Сортировка выполняется по алфавиту или по числовым значениям, в зависимости от типа столбца данных. ПРИМЕЧАНИЕ

Сортировка, производимая на стороне клиента, безвозвратно теряется при возврате данных страницей, а содержимое элемента строится заново. Также следует учитывать, что клиентская сортировка реализована на JavaScript. Поэтому не ожидайте, что она будет быстро работать, и применяйте ее только при небольшом количестве записей (на мой взгляд, не более 20).

Страничный вывод данных на стороне клиента Как упоминалось ранее, компонент DataView поддерживает страничный вывод данных. Но как управлять выбором отображаемой страницы и настроить пользовательский интерфейс для перемещения между страничными блоками данных? Для этого на странице размещается новый компонент DataNavigator, который связывается с представлением. У компонента DataView должен быть явно задан размер страницы. <dataNavigator id="dataNavigatorl" dataView="view" /> <dataView id="view" pageSize="10"> <bindings> <bitiding dataContext="dataSourcel" dataPath="data" property="data" /> </bindings> </dataView>

Компонент DataNavigator связывается с элементом страницы, представляющим панель перемещения. Пример: <div id="dataNavigatorl"> <input type="button" id="firstPageButton" value="<<" /> <input type="button" id="previousPageButton" value="<" /> <span id="pageIndexLabel"></span> out of <span id="pageCountLabel"></span> <input type="button" id="nextPageButton" value=">" /> <input type="button" id="lastPageButton" value=">>" /> </div>


292

Глава 7. Привязка данных на стороне клиента

Типичная панель перемещения содержит четыре кнопки: переход к первой, предыдущей, следующей и последней странице. Каждая кнопка перехода должна быть ассоциирована с компонентом Atlas и связана со свойствами объекта DataView: <button id="previousPageButton" parent="dataNavigatorl" command="previousPage"> <bindings> <bitiding property="enabled" dataPath="hasPreviousPage" /> </bindings> </button> <button id="nextPageButton" parent="dataNavigatorl" command="nextPage"> <bindings> <bitiding property="enabled" dataPath="hasNextPage" /> </bindings> </button> <button id="firstPageButton" parent="dataNavigatorl" command="firstPage"> <bindings> <binding property="enabled" dataPath="hasPreviousPage"> </bindings> </button> <button id="lastPageButton" parent="dataNavigatorl" command="lastPage"> <bindings> <binding property="enabled" dataPath="hasNextPage" /> </bindings> </button>

У кнопок, предназначенных для перехода вперед и назад, свойство enabled может связываться со свойствами hasNextPage и hasPreviousPage представления данных. В этом случае разработчику не придется беспокоиться об установлении и снятии блокировки кнопок при переходе между страницами. Атрибут parent играет ключевую роль: благодаря ему родительский компонент (в данном случае DataNavigator) получает события, поступающие от дочерних элементов. В результате DataNavigator перехватывает щелчки на кнопках и получает информацию, связанную с атрибутом command. Во внутренней реализации DataNavigator используется команда выбора switch с соответствующим назначением индекса страничного блока. DataNavigator распознает четыре команды: previousPage, nextPage, firstPage и lastPage. На рис. 7.5 показано, как выглядит страница из нашего примера в действии. В приведенном примере реализован механизм перехода к следующей/предыдущей странице. Также возможен вариант с прямым переходом, при котором для каждого страничного блока создается отдельная кнопка или гиперссылка. В этом случае кнопка ассоциируется с командой page, чтобы панель перемещения распознала команду, а индекс запрашиваемой страницы указывается в атрибуте argument: <button id="pageButton3" parent="dataNavigatorl" command="page" argument="3" />


Расширенные функции

293

Рис. 7.5. Страница с сортировкой и страничным выводом информации на стороне клиента

В схеме перехода к следующей/предыдущей странице пользователю будет полезно знать, сколько страничных блоков входит в текущий набор данных, а также индекс текущего страничного блока. Для достижения желаемого результата, показанного на рис. 7.5, используются два тега <span>: для индекса страничного блока и для общего количества блоков. <t»<span id="pagelndex"></span></t» ■4nbsp;  out of    <b><span id="pageCount"></span></b>

Также потребуется следующий фрагмент XML Script: <label id="pagelndex"> <bindings> <bitiding dataContext="view" dataPath="pageIndex" property="text" transform="Add" /> </bindings> </label> <label id="pageCount"> <bindings> <binding dataContext="view" dataPath="pageCount" property="text" /> </bindings> </label>


294

Глава 7. Привязка данных на стороне клиента

Трюк с преобразователем Add помогает решить проблему с индексированием страничных блоков, начинающимся с 0, и вывести более реалистичный номер текущей страницы.

Создание детализированных представлений Сетка не всегда является лучшим способом отображения данных. Нередко пользователю бывает удобнее просматривать данные одной записи на специальной форме. В классических приложениях ASP.NET существует два элемента, значительно упрощающих эту задачу: DetailsView и FormView. Первый предоставляет фиксированный, табличный пользовательский интерфейс с двумя столбцами: для названий полей и для их значений. Пользовательский интерфейс элемента может быть дополнен кнопками перехода и выполнения базовых операций — таких, как удаление и обновление текущей записи или создание новой записи. Элемент FormView с точки зрения функциональности служит той же цели, что и DetailsView, но он обладает полноценными шаблонными элементами, а все элементы и составляющие пользовательского интерфейса должны определяться вручную — так достигается гораздо большая гибкость, но за счет существенного увеличения объема работы по программированию. В Atlas для создания детализированных представлений используется клиентский элемент ItemView — парный по отношению к элементу ListView, описанному ранее в этой главе. Оба элемента обладают общим базовым классом (DataControl) и могут заполняться как на программном уровне через свойство data, так и на декларативном уровне через компоненты-источники данных.

Элемент ItemView Элемент ItemView обладает тремя свойствами. Два из них — itemTemplate и emptyTemplate — представляют собой шаблоны. Эти свойства играют ту же роль, как и для элемента ListView. Свойство itemTemplate содержит блок разметки HTML, используемый для построения детализированного представления записи данных. Свойство emptyTemplate определяет разметку, которая отображается при отсутствии данных, связанных с элементом. Последнее свойство элемента, CssClass, определяет стиль CSS, используемый для изменения внешнего вида элемента. Свойство itemTemplate позволяет взять под полный контроль пользовательский интерфейс, отображаемый для каждой связанной записи. Привязки используются для присоединения данных к элементам пользовательского интерфейса — таким, как текстовые поля и надписи. Разработчик также должен предоставить панель перемещения и другие кнопки, необходимые для редактирования, вставки и удаления записей данных.

Построение детализированного представления Первым шагом в построении детализированного представления должно стать определение позиции основной страницы, в которой будет вставляться динамически сгенерированная разметка: <div id="detailsView"></div>


Расширенные функции

295

Затем добавляется скрытый блок <div> для определения шаблона записи данных. Следующая разметка строит табличное представление записи данных, практически полностью идентичное тому, которое обеспечивается серверным элементом DetailsView. Каждое связанное поле представлено в таблице отдельной строкой: один столбец для имени поля и один столбец для чтения и редактирования значения: <div style="display: none"> <div id="details_Template"> <table cellpadding="2"> <thead class="tableHeader"> <tr> <td><t»Fields</t»</td> <td><t» </t»</td> </tr> </thead> <tbody class="tableContent"> <tr> <td><b>Customer ID</b></td> <td><asp:textbox runat="server" </tr> <tr> <td><b>Company Name</b></td> <td><asp:textbox runat="server" </tr> <tr> <td><b>Contact</b></td> <td><asp:textbox runat="server" </tr> <tr> <td><b>Country</b></td> <td><asp:textbox runat="server" </tr> </tbody> </table>

id="details_CustomerID" /></td>

id="details_CompanyNanie" /></td>

id="details_Contact" /></td>

id="details_Country" /></td>

</div> </div>

Шаблон позволяет просматривать и редактировать четыре поля связанной записи данных: код клиента, название компании, имя контактного лица и страну. Реализация необходимого клиентского поведения обеспечивается следующим кодом XML Script: <dataSource id="dataSourcel" serviceURL="MyDataSource.asmx" /> <itemView id="detailsView"> <bindings> <bitiding dataContext="dataSourcel" dataPath="data" property="data" /> </bindings> <itemTemplate> <template 1ayoutElement="detai1s_Template"> <textBox id="details_CustomerID"> <bindings> <binding dataPath="ID" property="text" direction="InOut" /> </bindings>


296

Глава 7. Привязка данных на стороне клиента

</textBox> <textBox id="details_CompanyName"> <bindings> <binding dataPath="CompanyName" property="text" direction^'InOut" /> </bindings> </textBox> <textBox id="details_Contact"> <bindings> <binding dataPath="ContactName" property="text" direction^'InOut" /> </bindings> </textBox> <textBox id="details_Country"> <bindings> <binding dataPath="Country" property="text" direction^'InOut" /> </bindings> </textBox> </template> </itemTemplate> </itemView> Шаблон, корневым элементом DOM которого является details_Template, содержит текстовые поля, связанные с полями источника данных. В целом код почти не отличается от того, который использовался ранее для элемента ListView, не считая заданного значения атрибута direction привязки. Вскоре мы вернемся к этому обстоятельству при обсуждении двусторонних привязок. На рис. 7.6 показано, как выглядит представление, созданное нами к настоящему моменту.

Рис. 7.6. Пример детализированного представления связанных данных


Расширенные функции

297

Объект ItemView связывается со всем источником данных, возвращенным службой; однако на экране отображается содержимое только одной записи. Следовательно, мы должны добавить кнопки, чтобы дать возможность пользователям перебирать связанные записи — уже загруженные на сторону клиента. Структура панели перемещения зависит только от вас. Как минимум на ней желательно разместить пару кнопок для перехода к предыдущей и следующей записи данных, а также кнопку для сохранения необработанных изменений. Пример возможной структуры панели: <div id="itemNavigator"> <input type="button" id="previousButton" value="<" /> ■inbsp;Snbsp;<b><span id="itemlndex"></span></b> ■4nbsp; out of   <b><span id="itemCount"x/span></b> Snbsp; <input type="button" id="nextButton" value=">" /> <hr /> <input type="button" id="saveButton" value="Save" /> </div>

На панели перемещения также находится пара надписей для вывода индекса текущей записи и общего количества связанных записей. Как обычно, эта разметка практически бесполезна без дополняющего кода XML Script. Вот как выглядит сценарный код привязки надписей: <label id="itemlndex"> <bindings> <binding dataContext="detailsView" dataPath="dataIndex" property="text" transform="Add" /> </bindings> </label> <label id="itemCount"> <bindings> <binding dataContext="detailsView" dataPath="length" property="text" /> </bindings> </label>

Согласно CTP (Community Technology Preview) за июль 2006 года, элементы данных не поддерживают методы перехода к первой или последней связанной записи данных. Впрочем, разработчик может реализовать эту возможность при помощи обработчиков событий и кода JavaScript. Для операций, поддерживаемых элементами данных, можно воспользоваться простой привязкой данных, как показано в следующем примере: <button id="previousButton"> <bindings> <binding dataContext="detai1sView" dataPath="canMovePrevious" property="enabled" /> </bindings> <click> <invokeMethod target="detailsView" method="movePrevious" /> </click> </button>


298

Глава 7. Привязка данных на стороне клиента

<button id="nextButton"> <bindings> <bitiding dataContext="detailsView" dataPath="canMoveNext" property="enabled" /> </bindings> <click> <invokeMethod target="detailsView" method="moveNext" /> </click> </button>

По событиям click происходит смещение текущей записи данных в ItemView. Управление доступностью кнопки осуществляется декларативно при помощи свойств canMovePrevious и canMoveNext элементов данных. На рис. 7.7 показана окончательная версия страницы в действии. Обратите внимание: кнопка Save доступна из-за наличия несохраненных изменений.

Рис. 7.7. Пример детализированного представления связанных данных с панелью перемещения

Двусторонняя привязка данных Класс ItemView наследует методы addltem и deleteCurrentltem от своего базового класса DataControl. Эти методы можно связать с кнопками пользовательского интерфейса, инициирующими действия по вставке и удалению данных на стороне клиента. Оба метода реализуются классом DataControl и работают напрямую


Расширенные функции

299

с данными, связанными с элементом. Простое редактирование данных во втором столбце на рис. 7.7 приведет к изменению содержимого соответствующих полей данных. Подобное поведение является следствием двусторонней привязки данных. <textBox id="details_Contact"> <bindings> <binding dataPath="ContactName" property="text" direction="InOut" /> </bindings> </textBox>

В коде XML Script страницы, показанной на рисунке, каждое текстовое поле связывается с полем данных через привязку типа InOut. В результате данные передаются в обоих направлениях: источник данных поставляет данные для текстового поля, а текстовое поле обновляет содержимое источника в соответствии с правкой пользователя. Двусторонняя привязка обеспечивает обновление клиентского кэша данных в реальном времени. Для добавления новых и удаления существующих записей создаются специальные кнопки и методы: <button id="addButton"> <bindings> <binding dataContext="dataSourcel" property="enabled"/> </bindings> <click> <invokeMethod target="detailsView" </click> </button> <button id="delButton"> <bindings> <binding dataContext="dataSourcel" property="enabled"/> </bindings> <click> <invokeMethod target="detailsView" </click> </button>

dataPath="isReady"

method="addItem" />

dataPath="isReady"

method="deleteCurrentItem" />

Как видите, мы отслеживаем свойство isReady источника данных и временно блокируем элементы, если данные не готовы или находятся в процессе обновления. Говоря о двустороннем связывании данных, необходимо обратить особое внимание на синхронизацию данных с сервером. Клиентские элементы (такие, как ItemView) могут синхронизировать пользовательский интерфейс только с копией данных из клиентского кэша. Единственным компонентом, имеющим доступ к серверному хранилищу данных, является источник данных. Как было показано ранее (см. рис. 7.2), источник данных — единственный компонент, обращающийся к серверу через указанную службу данных. Для сохранения необработанных изменений (правок, вставок и удалений) необходимо вызвать метод save источника данных. Вот как это делается при помощи кнопки Save: <button id="saveButton"> <bindings>


300

Глава 7. Привязка данных на стороне клиента

<bitiding dataContext="dataSourcel" dataPath="isDirtyAndReady" property="enabled" /> </bindings> <click> <invokeMethod target="dataSourcel" method="save" /> </click> </button>

С кнопки автоматически снимается блокировка при наличии необработанных изменений. Следующая привязка становится завершающим штрихом пользовательского интерфейса: <itemView id="detailsView"> <bindings> <binding dataContext="dataSourcel" dataPath="data" property="data" /> <binding dataContext="dataSourcel" dataPath="isReady" property="enabled" /> </bindings> </itemView>

Привязка к свойству enabled элемента ItemView заблокирует весь пользовательский интерфейс элемента при неготовности источника данных (то есть на то время, пока источник обновляет серверное хранилище данных).

Заключение Привязка данных заложена в основу большинства приложений, и веб-приложения не являются исключением. Привязка состоит из двух основных фаз: загрузки информации из хранилища данных и связывания данных с элементами пользовательского интерфейса. Долгие годы эти задачи решались на программном уровне, написанием многих строк шаблонного кода. В какой-то момент архитекторы и разработчики задумались, нельзя ли делать то же самое более эффективно. Ответом на поставленную проблему стали элементы, связанные с данными, и компоненты-источники данных, которые были по-разному реализованы в разных прикладных средах и платформах. Так возникла концепция декларативной привязки, окончательно укрепившаяся в ASP.NET 2.0. В сущности, речь идет об описании привязок на уровне разметки, без традиционного программирования. В серверной среде привязка данных не создает особых проблем независимо от того, как она реализована (на программном или декларативном уровне). В Atlas привязка данных четко разделена на два уровня: клиентский и серверный. Все, что относится к пользовательскому интерфейсу и действиям пользователя, происходит на клиентском уровне; закрепление изменений становится возможным только при передаче данных клиента на сервер. Центральное место в архитектуре привязки данных Atlas занимают домашние веб-службы. Если предпочтение отдано чисто программному подходу, разработчик строит специальную веб-службу и использует ее методы для получения


Заключение

301

и сохранения записей. Для привязки данных к элементам используется код JavaScript. Точка. Если вы предпочитаете обойтись без программирования JavaScript, выбирайте декларативное решение, при котором для реализации расширенных возможностей достаточно небольшого объема XML Script. В этом случае все операции загрузки и сохранения данных выполняются через объект источника данных. В свою очередь, источник данных использует для выполнения «черной работы» особую разновидность веб-служб — службы данных. Привязки и действия позволяют строить сложные пользовательские интерфейсы с минимальными усилиями. Atlas также обеспечивает встроенную поддержку постраничного вывода и сортировки данных. Остается ответить на один принципиальный вопрос: насколько эффективна и производительна эта схема? Atlas — не волшебство; большей частью это код JavaScript — язык, как известно, интерпретируемый. Все расширенные возможности предоставляются библиотекой JavaScript, моделирующей управляемую среду .NET Framework. В одних ситуациях это вполне допустимо — например, при заполнении отдельных страниц несколькими записями данных или при построении гибридных приложений. Однако при значительном объеме кода (например, при сортировке) стоит рассмотреть и другие, альтернативные решения.


ГЛАВА 8

Гаджеты Atlas В этой главе i Что такое гаджет? i Построение гаджета Atlas Гаджеты, или мини-приложения, приобретают все большую популярность в сообществе квалифицированных пользователей Web и Microsoft Windows. Добавление гаджетов поддерживается многими известными страницами, включая личную начальную страницу Live.com, пользовательское пространство MySpaces.com, специализированные службы типа Windows Live Mail, рабочий стол Windows Vista и даже ряд обычных блогов и веб-страниц. Гаджеты предназначены для максимального ускорения доступа к сервису и информации. Важная отличительная особенность гаджетов заключается в том, что они ориентированы на выполнение одиночных задач и прием информации в реальном времени, в сочетании с логичным, интуитивно понятным, удобным и четким интерфейсом. Сущность гаджетов хорошо выражена в следующей цитате из статьи «Windows Live Gadget Design Guide» (http://microsoftgadgets.com/livesdk/clocs/uiguicle.htm): «Гаджет не является контейнером для нескольких разнородных функций. Если для работы с гаджетом потребуется пояснительный текст, вероятно, вы сделали его слишком сложным.» Гаджет выполняет всего одну функцию, притом делает это способом, понятным и удобным для пользователя. Классические примеры гаджетов — мини-приложения для вывода прогнозов погоды, сводок веб-новостей или отображения слайдовых презентаций. За последнее время многие разработчики выпустили схожие технологии. Для примера можно упомянуть Konfabulator от Yahoo!, Desktop X от Stardock и Kapsules от Shellscape. Все перечисленные продукты спроектированы для расширения рабочего стола Windows, а для управления ими необходимо установить специализированное ядро. Какое место гаджеты занимают в Atlas, спросите вы? Прикладная среда Atlas является самым простым и самым мощным инструментом для написания новых гаджетов.

Что такое гаджет? Архитектура гаджетов открыта для применения любых технологий. Например, гаджеты можно писать на Dynamic HTML или, еще лучше, на Microsoft ASP.NET Atlas.


Что такое гаджет?

303

В конечном итоге автор сам определяет, как будет работать его гаджет. Например, можно написать гаджет, который проверяет версию нижележащей операционной системы и добавляет дополнительные эффекты GUI при работе на компьютерах с Windows Vista. Разработчики также могут писать унифицированные гаджеты, которые генерируют код Dynamic HTML и работают как в вебстраницах (скажем, Live.com), так и на боковой панели Windows Vista. Гаджет во многом похож на приложение. Он может базироваться на платформе Windows или Web; он может работать в режиме постоянного сетевого подключения, подключаться по мере необходимости или просто работать в автономном режиме. Гаджет может передавать любую информацию (сводки, прогнозы погоды, новости, фотографии и т. д.), давать пользователю возможность выполнять различные задачи или играть в игры. При этом должно выполняться лишь одно ключевое условие: гаджет должен быть направлен на решение одной первоочередной функции. Кроме того, он должен иметь интуитивно понятный интерфейс и существовать в чрезвычайно компактном пространстве.

Live.com, Windows Vista и гаджеты Гаджеты могут размещаться в двух основных местах: на веб-сайте Live.com и на боковой панели Windows Vista. В обоих случаях главной целью является персонализация личного пространства, чтобы вся необходимая функциональность была у пользователя «под рукой». Гаджеты всего лишь представляют собой разновидность приложений, которые работают на обеих платформах и загружаются из галереи (или разрабатываются собственными силами).

Рекомендации по проектированию гаджетов Гаджет — приложение узкоспециализированное, компактное, простое и обладающее намеренно ограниченной функциональностью. Гаджеты проектируются таким образом, чтобы выводимая ими информация была понятна пользователям с минимальными пояснениями, а работа требовала минимальных усилий по настройке конфигурации. Гаджет не должен быть усеченной версией более функционального, более мощного приложения; скорее, это мини-приложение с ограниченными задачами, которое иногда дополняет существующее приложение. Гаджет хорошо вписывается в окружающие условия по своему внешнему виду и поведению и эффективно использует занимаемое пространство. В идеале высота гаджета не должна превышать 200 пикселов в стандартном состоянии, и он не должен увеличиваться свыше 400 пикселов в развернутом виде. Содержимое гаджета должно храниться готовым к отображению, чтобы пользователю не приходилось ожидать появления информации. Впрочем, динамическая загрузка содержимого гаджета тоже допустима, если последний обеспечит соответствующую обратную связь для пользователя. Учтите, что некоторые гаджеты обладают ограниченным сроком жизни. Представьте, что вы установили гаджет, который выводит в реальном времени новости о некотором спортивном мероприятии. Спустя несколько дней или недель после мероприятия у гаджета уже не будет полезной информации. В таких


304

Глава 8. Гаджеты Atlas

случаях гаджет должен вывести сообщение с предложением удалить себя, вместо того чтобы продолжать выводить устаревшую информацию.

Режимы работы Гаджет может находиться в одном из трех режимов: запуска, свернутом и развернутом. Чтобы добавить свои гаджеты на боковую панель Vista или на страницу Live.com, пользователь выбирает их в каталоге. Гаджет, находящийся в каталоге, работает в режиме запуска. В этом режиме для него выводится только значок и название. Эта информация должна быть как можно более содержательной, потому что именно она становится ключевым фактором, по которому пользователи выбирают гаджет. Содержимое гаджета отображается в раскрывающемся окне. Когда окно закрыто, гаджет находится в свернутом состоянии. В это время виден только заголовок гаджета. Как правило, заголовок включает значок и имя, но в него также можно включить некоторые ключевые аспекты содержимого. Например, у гаджета с прогнозом погоды в свернутом виде может показываться только текущая температура и сводка погоды для отслеживаемого города или местности (рис. 8.1).

Рис. 8 . 1 . Гаджет с прогнозом погоды в свернутом состоянии

В развернутом режиме отображается полное содержимое гаджета. Высота окна не должна превышать 400 пикселов. На рис. 8.2 показано, как гаджет с прогнозом погоды выглядит в развернутом состоянии.

Рис. 8.2. Гаджет с прогнозом погоды в развернутом состоянии

Примеры гаджетов В конечном итоге большинство гаджетов представляет собой гибридные приложения, пользующиеся поставками данных от других сайтов и веб-служб. Так возникает пара дополнительных проблем, относящихся скорее к построению гибридных приложений нежели к гаджетам в частности. У пользователя никогда не должно возникать впечатления, что гаджет не реагирует на его действия;


Что такое гаджет?

305

пользователь должен быть в курсе всех выполняемых действий, включая аутентификацию (там, где это необходимо). Если ожидаемый формат внешних данных вдруг изменился или данные стали недоступными, мини-приложение должно быть способно корректно сократить свою функциональность. На рис. 8.3 показаны гаджеты, установленные на боковой панели Windows Vista. Боковая панель представляет собой разновидность панели задач для размещения гаджетов.

Рис. 8.3. Гаджеты, устанавливаемые по умолчанию на боковой панели Windows Vista

Гаджет с прогнозом погоды в развернутом состоянии Веб-сайт Live.com (также известный под названием Windows Live) играет роль центральной консоли, которая может настраиваться пользователями для персонализации ряда Интернетслужб. Домашняя страница Windows Live, составленная из гаджетов, позволяет получить доступ ко всем ежедневно используемым службам, апплетам и данным. Чтобы создать себе страницу, сделанную «по личной мерке», пользователь регистрируется на сайте Windows Live, а затем выбирает гаджеты из предлагаемого каталога (рис. 8.4).

Внутреннее устройство гаджетов Гаджет состоит из трех основных элементов: I манифест — XML-файл, определяющий ключевые атрибуты (значок, название и местонахождение других элементов, включая код и таблицу стилей);


306

Глава 8. Гаджеты Atlas

Рис. 8.4. Размещение гаджетов на домашней странице Windows Live

I код — файл со всем программным кодом, используемым для создания гаджета; I таблица стилей — необязательный файл в формате CSS (Cascading Style Sheet), который может использоваться для настройки пользовательского интерфейса гаджета. При разработке любого гаджета используется HTML и сценарный код. Гаджет также располагает дополнительной информацией о себе самом и о системе Windows. Страница HTML, выполняемая в формате гаджета в Windows Live или на боковой панели Windows Vista, получает доступ к объектной модели, предоставляющей дополнительную функциональность. Это позволяет гаджетам работать с файлами и папками Windows, отображать графику из пользовательской личной папки или вывести информацию о состоянии беспроводного соединения. Гаджет может работать с двоичными объектами, но чаще даже очень сложные и широкофункциональные гаджеты ограничиваются использованием сценарного кода. Чтобы читатель получил представление о внутренней структуре гаджета, рассмотрим пример гаджета, загруженный с сайта Microsoft Live Gallery по адресу http://gallery.live.com. Вот как выглядит его манифест: <?xml version="1.0" encoding="utf-8" ?> <gadget> <name>Sudoku</name>


Что такое гаджет?

307

<namespace>microsoft.windows</namespace> <versi оп>1.0.0.0</version> <author name="Microsoft Corporation"> <info url="http://go.microsoft.com/fwlink/7LinkIch55696" text="gallery.live.com" /> <logo src="logo.png" /> </author> <copyright>© 2006</copyright> <description>Play Sudoku.</description> <icons> <icon height="48" width="48" src="icon.png" /> </icons> <hosts> <host name="sidebar"> <base type="html" apiVersion="1.0.0" src="sudoku.html" /> <permissions>full</permissions> <platform minPlatformVersion ="1.0"/> </host> </hosts> </gadget>

Страница с именем sudoku.html содержит ссылку на сценарный файл, в котором определяется большая часть логики. За дополнительной информацией о разработке гаджетов на основе HTML, кода JavaScript или двоичных объектов обращайтесь по адресу http://windowssdk. msdn.microsoft.com/en-us/library/ms723694.aspx. В этой главе будет рассказано о том, как писать гаджеты на платформе Atlas, а также будут рассмотрены основные потенциальные проблемы, на которые следует обратить внимание.

Гаджеты и Atlas Поскольку гаджеты состоят из разметки и сценарного кода, Atlas становится естественным кандидатом на роль платформы разработки ¹ 1 — по крайней мере, для веб-гаджетов, работающих в Windows Live.

Почему именно Atlas? Упоминавшийся ранее гаджет Sudoku написан исключительно на JavaScript. Он должен поддерживать информацию состояния, принимать ввод и соответствующим образом обновлять пользовательский интерфейс. В каждой из перечисленных областей Atlas обладает значительными преимуществами. Как упоминалось в главе 5, Atlas дополняет язык JavaScript наследованием, пространствами имен и интерфейсами. Вполне очевидно, что сложную логику проще писать на Atlas JavaScript. Более того, службы данных, пользовательские профили, привязки, клиентские шаблоны и элементы существенно упрощают построение расширенных пользовательских интерфейсов по сравнению с «обычным» кодом Dynamic HTML. И такой подход действительно оказывается эффективным. Таким образом, данная платформа отлично подходит для построения сценарных гаджетов; можно предположить, что Atlas станет главным (если не единственным) кандидатом сразу же после его официального выпуска.


308

Глава 8. Гаджеты Atlas

Страницы Atlas и гаджеты Atlas Гаджет загружается в виде веб-страницы, поэтому создание гаджетов Atlas начинается с создания страницы Atlas. Гаджет Atlas представляет собой полнофункциональную страницу Atlas, которую можно протестировать локально при помощи веб-приложения Microsoft IIS (Microsoft Internet Information Services). Тем не менее между простыми страницами Atlas и страницами, спроектированными для выполнения в качестве гаджетов, существует ряд различий. Чтобы выявить эти различия и убедиться в том, что гаджет действительно работает именно так, как предполагалось, необходимо тщательно протестировать его в управляющей среде гаджета (на боковой панели Windows Vista или в Windows Live). Страница-гаджет Atlas должна включать специализированный элемент Gadget, причем все ссылки на сценарный код или веб-службы должны осуществляться через этот элемент, а не через ScriptManager. Элемент Gadget предоставляет шаблон содержимого, в котором определяется пользовательский интерфейс гаджета. Любые элементы, размещаемые за пределами Gadget, при предоставлении страницы в качестве гаджета отображаться не будут. Впрочем, в шаблоне гаджета могут размещаться только «натуральные» элементы, а элементы runat=server запрещены. Итак, давайте займемся построением гаджета Atlas и разберемся со всеми потенциальными проблемами.

Построение гаджета Atlas В целом написание гаджета принципиально не отличается от написания страницы Atlas, и все же остается ряд различий. В обоих случаях первым шагом должно быть написание клиентской страницы Atlas. Как упоминалось ранее, гаджеты Atlas не могут содержать серверных элементов; это главное отличие между гаджетами Atlas и веб-страницами. Следовательно, основу пользовательского интерфейса гаджетов Atlas образуют теги HTML и привязки XML Script.

Реализация гаджета Тело страницы Atlas, которая может использоваться в качестве гаджета, имеет следующую форму: <body> <form id="forml" runat="server"> <div> <atlas:ScriptManager ID="scriptManagerl" runat="server" /> <atlas:Gadget runat="server" ID="Gadgetl" Title="My First Gadget" Description^'It’s only my first try..."> <ContentTemplate> <p>Hello, Gadget.</p> </ContentTemplate> </atlas:Gadget> </form> </div> </body>


Построение гаджета Atlas

309

Серверный элемент Gadget определяет заголовок и описание гаджета — информацию, которая будет передана в манифест. Тег ContentTemplate определяет содержимое гаджета.

Серверный элемент Gadget Серверный элемент Gadget определяется в пространстве имен Microsoft.Web.Ul и реализуется в виде составного шаблонного элемента. На рис. 8.5 показан конструктор элемента Gadget в Microsoft Visual Studio 2005.

Рис. 8.5. Серверный элемент Gadget в конструкторе страницы Visual Studio 2005

Все, что мы видим внутри блока Gadget, будет отображаться во время выполнения в Windows Live или на боковой панели. С этой точки зрения конструктор элемента весьма удобен и интуитивно понятен. Свойства элемента Gadget перечислены в табл. 8.1. Обратите внимание: коллекции Scripts и Services аналогичны коллекциям, определяемым для элемента ScriptManager. В обоих случаях в коллекциях хранятся ссылки на используемые сценарные файлы и веб-службы. Однако коллекции Scripts и Services элемента ScriptManager содержат ссылки на файлы, используемые страницей, а коллекции элемента Gadget содержат ссылки на файлы, используемые гаджетом. Данное различие не принципиально, если страница используется в качестве простой страницы Atlas, вызываемой по гиперссылке или из браузера. Но если страница предоставляется пользователю как гаджет, внешние ссылки будут потеряны, если они не устанавливались непосредственно через элемент Gadget. В конечном итоге ссылки на все файлы JavaScript, веб-службы или таблицы стилей, используемые элементом Gadget, должны подключаться через свойства элемента.


310

Глава 8. Гаджеты Atlas

Таблица 8 . 1 . Свойства элемента Gadget

Свойство ContentTemplate Description Scripts Services Styles Title

Описание Шаблонное свойство, опеределяющее содержимое гаджета. Шаблон не может содержать серверных элементов Описание гаджета, которое будет храниться в манифесте (чтение и запись) Коллекция объектов ScriptReference. Содержит ссылки на все сценарные файлы, необходимые для элемента Gadget Коллекция объектов ServiceReference. Содержит ссылки на все вебслужбы и мостовые файлы, необходимые для элемента Gadget Коллекция объектов StyleReference. Содержит ссылки на все файлы CSS, необходимые для элемента Gadget Название гаджета, которое будет храниться в манифесте (чтение и запись)

Подключение веб-служб, таблиц стилей и сценарных файлов может осуществляться либо на программном, либо на декларативном уровне через дочерние теги, как показано в следующем примере: <atlas:Gadget runat="server" ID="Gadgetl" Title="My First Gadget" Description^'It’s only my first try..."> <ContentTemplate> </ContentTemplate> <Services> <atlas:ServiceReference path="~/WebServices/MyDataService.asmx" /> </Services> <Scripts> <atlas:ScriptReference path="Gadgetl.aspx.js" /> </Scripts> <Styles> <atlas:StyleReference path="Gadgetl.aspx.css" /> </Styles> </atlas:Gadget>

Интересно заметить, что элемент Gadget представляет собой некое подобие «страницы в странице» — своего рода микрокосм, который должен быть полностью самодостаточным. Например, не допускается определение встроенного сценарного кода или подключение таблиц стилей на уровне страницы в теге <head>. Ответы на все «почему?» и «с какой стати?», связанные с этим требованием, становятся очевидными, если заглянуть «под капот» элемента Gadget и больше узнать о его взаимодействии с внешней средой.

Внутреннее устройство элемента Gadget Элемент Gadget является составным (composite) элементом ASP.NET; он наследует от Control и переопределяет метод CreateChildControls. Метод создает экземпляр свойства ContentTemplate и перебирает все свои дочерние элементы. Реализация на псевдокоде выглядит так:


Построение гаджета Atlas

311

protected override void CreateChildControlsO { if (ContentTemplate != null) return; // Создание экземпляра шаблона Control container = new Control(); ContentTemplate.Instantiateln(container);

}

// Проверка содержимого шаблона foreach (Control ctl in container.Controls) { if (!(ctl is LiteralControl)) throw new InvalidOperationExceptionC . . . " ) ; } // Включение реализованного шаблона в дерево элемента Controls.Add(container);

Как видно из листинга, элемент Gadget выявляет серверное содержимое и в случае обнаружения чего-либо с пометкой runat=server выдает исключение. Помните, что все содержимое страницы ASP.NET, не помеченное атрибутом runat, компилируется как LiteralControl. В основном это делается по соображениям быстродействия. Впрочем, отсутствие элементов runat=server не ограничивает мощи программного кода. Вы по-прежнему можете строить интерактивные страницы, принимающие ввод и отвечающие на действия пользователей, — просто необходимо понять, что эти возможности должны кодироваться на уровне простого сценарного кода и тегов HTML. Вот почему Atlas со своей расширенной моделью JavaScript оказывает здесь огромную помощь. Следующее крупное событие в жизненном цикле элемента Gadget связано со стадией предварительного построения. Взгляните на псевдокод переопределенной версии OnPreRender: protected override void OnPreRender(EventArgs e) { if (IsGadgetRequestO) { if (IsGadgetContentRequestO) { Page.SetRenderMethodDelegate( new RenderMethod(RenderGadgetContents)); } else { Page.SetRenderMethodDelegate( new RenderMethod(RenderManifest)); } } else { AddScriptsToScriptManager(); AddServicesToScriptManagerO; AddStylesToPageO; } }


312

Глава 8. Гаджеты Atlas

Метод сначала проверяет, поступил ли запрос от «хозяина» гаджета. Если нет, Gadget ведет себя как элемент-заполнитель и выдает свое содержимое на страницу. Что еще важнее, его стили, службы и сценарии просто включаются в компонент ScriptManager страницы. Если запрос страницы действительно адресован элементу Gadget, метод ОпРгеRender регистрирует метод, который будет активизироваться посредством обратного вызова в процессе непосредственного построения элемента. Зарегистрированный метод заменяет стандартный и полностью определяет вывод элемента. Иначе говоря, когда запрос страницы поступает от «хозяина» гаджета, все содержимое в теле страницы игнорируется, за исключением содержимого элемента Gadget. Существует два варианта обращения к гаджету со стороны «хозяина»: он может запросить манифест гаджета или только его содержимое. Такие специальные запросы создаются посредством включения параметров в строку запроса, как показано в следующем примере: http://www.contoso.com/gadgets/gadgetl.aspx?gadget=true http://www.contoso.com/gadgets/gadgetl.aspx?gadget=true&render=true

Оба URL ссылаются на страницу Atlas с именем gadgetl .aspx, содержащую элемент Gadget. Первый URL запрашивает манифест гаджета, а второй — разметку, внедряемую в боковую панель Windows Vista или сайт Windows Live. В предыдущем фрагменте псевдокода функция IsGadgetRequest ищет параметр gadget, а IsGadgetContentRequest ищет оба параметра, gadget и render. Разметка, посылаемая элементом Gadget своему хозяину, выглядит так: <root><![CDATA[ <!-- Разметка элемента Gadget --> <script type="text/xml-script"> <page xmlns:script="http://schemes.microsoft.com/xml-script/2005"> <references> <add src="http://.../IntroAtlas/atlasglob.axd" /> <add src="http://.../IntroAtlas/Samples/Ch08/gadgetl.aspx.js" /> <add src="http://.../IntroAtlas/Services/MyDataService.asmx/js" /> </references> components /> </page></script> ]]></root>

Как видите, для подключения всех необходимых внешних ссылок вставляется специальный тег <script>. Если, допустим, оформить сценарный код как встроенный, страница будет нормально работать при вызове из браузера, но выдаст ошибку «Object not found» при ее размещении в полноценном «хозяине» гаджета. Код JavaScript, в котором определяется объект, не включается без явного связывания через тег <Scripts>. В конечном итоге элемент Atlas Gadget позволяет определить шаблон с содержимым и собирает воедино всю необходимую информацию для «хозяина», чтобы вам не приходилось создавать манифест самостоятельно.


Построение гаджета Atlas

313

Несколько гаджетов Страница Atlas может содержать несколько элементов Gadget. В этом случае каждый элемент Gadget должен обладать уникальным идентификатором, а синтаксис загрузки манифеста гаджета или содержимого слегка изменяется: необходим дополнительный параметр GadgetID, идентифицирующий элемент на странице. http://www.contoso.com/gadgets/test.aspx?gadget=true&gadgetid=MyGadgetl

Если параметр GadgetID не задан, происходит исключение типа «недопустимая операция».

Гаджет Amazon Давайте разработаем содержательный гаджет на основе примера с поиском книг на сайте Amazon, созданного в главе 7. Страница гаджета должна содержать компонент ScriptManager, но все ссылки должны быть перемещены в элемент Gadget, как показано ниже: <atlas:Gadget runat="server" ID="AmazonFeed" Title="Amazon Gadget" Description="Book information from Amazon"> <Services> <atlas:ServiceReference Path="~/WebServices/Amazon.asbx" /> </Services> <Scripts> <atlas:ScriptReference Path="amazongadget.aspx.js" /> </Scripts> <Styles> <atlas:StyleReference Path="amazongadget.aspx.css" /> </Styles> <ContentTemplate> </ContentTemplate> </atlas:Gadget>

Содержимое гаджета копируется «один в один» из примеров главы 7. Для подключения к веб-службе Amazon используется мостовой файл amazon.asbx, а для построения таблицы с результатами — клиентский макетный шаблон. На рис. 8.6 показан гаджет Amazon в действии на странице Windows Live. Как загрузить гаджет в Windows Live? Именно эта тема будет рассматриваться в следующем разделе. С другой стороны, Windows Vista требует предварительной загрузки гаджета в каталог http://gallery.live.com. Windows Vista позволяет добавлять гаджеты сторонних разработчиков, но вы не сможете просто ввести URL для загрузки гаджета, как это делается в Windows Live. ПРИМЕЧАНИЕ

В сущности, гаджеты являются гибридными приложениями, потребляющими данные от внешних поставщиков. Свяжитесь с поставщиком данных и сообщите ему, как используются данные и какой объем дополнительного трафика следует ожидать. Гаджет должен содержать упоминание о поставщике данных и быть готовым среагировать, если данные вдруг станут недоступными. Наконец, не забудьте получить у поставщика разрешение на использование содержимого.


314

Глава 8. Гаджеты Atlas

Рис. 8.6. Гаджет Amazon в действии

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

Требования IIS Гаджет должен быть установлен на общедоступном веб-сайте, работающем из виртуального каталога IIS. Естественно, у приложения IIS должна быть установлена поддержка ASP.NET 2.0 и Atlas. Если для гаджета создается отдельная виртуальная папка, то от вас потребуется лишь обеспечить присутствие сборки Microsoft.Web.Atlas в папке Bin и включение необходимых конфигурационных данных в файле web.config. Что должно находиться в файле web.config? В двух словах, все настройки, необходимые содержащимся в гаджете средствам Atlas (таким, как конвертеры и мостовые файлы). Как упоминалось в главе 6, последние версии Firefox и Microsoft Internet Explorer 7.0 не поддерживают междоменные запросы XmlHttpRequest от доверенных сайтов. Для работы гаджетов (и приложений Atlas вообще) необходимо включить обработчик HTTP IFrameHandler, как показано ниже: <httpHandlers>


Построение гаджета Atlas

315

<add verb="*" path="iframecall.axd" type="Microsoft.Web.Services.IFrameHandler" /> </httpHandlers>

Впрочем, учтите, что этот аспект может измениться в будущих версиях Atlas.

Установка в Windows Live Установка гаджета в Windows Live выполняется очень просто: щелкните на ссылке Add Stuff (см. рис. 8.4) и перейдите на вкладку Advanced Options. Введите URL гаджета в текстовом поле и щелкните на кнопке (рис. 8.7). Для гаджетов Atlas необходимо ввести URL страницы с параметром gadget=true.

Рис. 8.7. Добавление нового гаджета на домашнюю страницу Windows Live

Страница Windows Live загружает манифест сборки, после чего предлагает пользователю просмотреть и установить его (рис. 8.8). После выполнения всех действий новый гаджет появляется на домашней странице, готовый к работе.

Утечка памяти Основной проблемой типичных веб-сайтов на базе AJAX (Asynchronous JavaScript + XML) является утечка памяти. Поскольку такие приложения в основном строятся на сценарном уровне, возникает впечатление, что они лучше защищены от ошибок.


316

Глава 8. Гаджеты Atlas

Рис. 8.8. Подтверждение установки только что загруженного гаджета

Тем не менее утечка памяти в них возможна, причем отследить ее бывает очень трудно. Написание гаджетов на Atlas или в других прикладных средах позволяет большей частью избавиться от подобных неприятностей, так как используемый код Atlas хорошо написан и свято соблюдает все рекомендации. Если вы не используете Atlas, загляните на страницу http://microsongadgets.com/livesdk/docs/default.htrn с рекомендациями по грамотному программированию гаджетов.

Заключение Гаджеты представляют собой миниатюрные Windows-приложения, написанные на HTML и сценарном коде. Они устанавливаются локально и размещаются на рабочем столе или боковой панели Windows Vista. Гаджеты также могут использоваться для расширения домашних страниц службы Windows Live. Процесс разработки гаджетов хорошо знаком любому веб-разработчику; он включает создание HTML-страницы, выполняющей полезные функции, и манифеста XML для хранения некоторых свойств гаджета (в том числе названия, значка и описания).


Об авторе Дино Эспозито (Dino Esposito) — преподаватель и консультант в области программного обеспечения; живет в Риме (Италия). Является членом группы Solid Quality Learning (www.solidqualitylearning.com). Дино специализируется на технологиях Microsoft .NET и проводит большую часть времени за преподаванием и консультациями в Европе и Соединенных Штатах. За прошедшие годы Дино накопил практический опыт и квалификацию в области проектирования и построения распределенных систем для банков и страховых компаний и в других областях с особенно высокими требованиями к безопасности, оптимизации, производительности, масштабируемости и функциональной совместимости. Ежемесячно по меньшей мере в пяти журналах и веб-сайтах по всему миру публикуются статьи Дино по различным темам: от веб-разработки до доступа к данным, от современных методов программирования до веб-служб. Дино ведет ежемесячную рубрику «Cutting Edge» в MSDN Magazine (www. msdn.microsoft.com/ msdnmag), рубрику «CoreCoder» в журнале asp.netPRO (www.aspnetpro.com) и бюллетень ASP.NET-2-The-Max для Dr. Dobb’s Journal (www.ddj.com/dept/windows). Пользуясь репутацией авторитетного и признанного эксперта в области веб-приложений, построенных с применением технологий .NET, Дино вносит свой вклад в создание материалов Microsoft для разработчиков и IT-консультантов. Его статьи можно найти в выпусках MSDN Developer Center по таким темам, как ASP.NET, безопасность (www.msdn.net.microsoft.com/aspnet), Microsoft Windows Vista и доступ к данным. Дино написал целую серию книг, многие из которых считаются лучшими в своих областях — как, например, «Applied XML Programming for Microsoft® .NET» (Microsoft Press, 2002). До сих пор пользуется спросом, и что еще важнее — применяется разработчиками со всего мира, книга «Visual C++ Windows Shell Programming» (Apress, 1998). Она остается уникальным источником информации для всех, кто пожелает расширить возможности операционной системы Windows. Две последние книги Дино — «Programming Microsoft ASP.NET 2.0 Core Reference» и «Programming Microsoft ASP.NET 2.0 Applications: Advanced Topics» (Microsoft Press, 2006). Обладатель звания «наиболее значимого профессионала» (MVP, Most Valuable Professional), Дино регулярно выступает на отраслевых конференциях по всему миру (Microsoft TechEd, Microsoft DevDays, DevConnections, DevWeek и WinDev), а также локальных технических конференциях и собраниях в Европе и США. Дино живет со своей семьей неподалеку от Рима (Италия). Столкнувшись с особенно сложной технической проблемой, он обычно играет в теннис или занимается пробежками на природе. Решение, словно по волшебству, появляется через несколько минут после душа, завершающего тренировку.


Алфавитный указатель А

InvokeMethodAction, класс, 178 ItemView, элемент, 294

Action, класс, 177 Application, объект findObject, метод, 173 общие сведения, 167 asbx, файлы, 255 atlas.js, файл, 163 AutoCompleteBehavior, класс, 199

Label, элемент, 192 ListView, элемент, 273 Live.com, 302 load, события, 174

В

P

L

CheckBox, элемент, 191 ClickBehavior, класс, 201 CustomValidator, элемент, 197

PopupBehavior, класс, 203 PositioningMode, перечисление, 204 PostBackAction, класс, 180 POX (Plain Old XML), 257 ProfileWebService, класс, 216 propertyChanged, событие, 176

D

R

DataControl, элемент, 273 DataObjectMethod, атрибут, 282 DataSource, класс, 279 DataView, класс, 285

RangeValidator, элемент, 196 RegexValidator, элемент, 197 RequiredFieldValidator, элемент, 195

Button, элемент, 190

С

F findObject, метод, 173 FloatingBehavior, компонент, 209 Function, объект, 164

G GetProfile, метод, 227

Н HoverBehavior, компонент, 202 Hyperlink, элемент, 191

I IFrameExecutor, 254 IIS (Internet Information Services), 308 Image, элемент, 192 InputControl, класс, 193

S Select, элемент, 192 SetProfile, метод, 227 SetPropertyAction, класс, 179 SOAP (Simple Object Access Protocol), 257 StringBuilder, класс, 183 Sys.Application, класс, 172 Sys.Debug, класс, 187 Sys.Profile, класс, 227 Sys.Action, класс, 177 Sys.Binding, класс, 266 Sys.Component, класс, 175 Sys.Counter, класс, 185 Sys.Data.DataSource, класс, 279 Sys.Data.DataView, класс, 285 Sys.Net.WebRequest, класс, 187 Sys.Net.WebRequestExecutor, 255


Алфавитный указатель Sys.StringBuilder, класс, 183 Sys.Timer, класс, 184 Sys.UI.AutoCompleteBehavior, класс, 199 Sys.UI.Button, элемент, 190 Sys.UI.Control, класс, 189 Sys.UI.CustomValidator, элемент, 197 Sys.UI.Data.ListView, элемент, 273 Sys.UI.Image, элемент, 192 Sys.UI.Label, элемент, 192 Sys.UI.RangeValidator, элемент, 196 Sys.UI.RegexValidator, элемент, 197 Sys.UI.RequiredFieldValidator, элемент, 195 Sys.UI.Select, элемент, 192 Sys.UI.TextBox, элемент, 193 Sys.UI.TypeValidator, элемент, 197 T TextBox, элемент, 193 TypeValidator, элемент, 197 U unload, событие, 174 W WebMethod, атрибут, 217 WebOperation, атрибут, 246 wsdl.exe, утилита, 257 X XMLHttpExecutor, 255 A анимация, 210 аспекты поведения автозаполнение, 199 анимация, 210 прозрачность, 211 аутентификация, служба web.config, файл, 217 конфигурация, 217 общие сведения, 215 ответы HTTP, 217 В веб-службы доступ, 234 мостовая технология, 255 нелокальные, 253 терминология, 215 вызов нелокальных веб-служб, 253

Г гаджеты HTML, 306 Windows Live, 305 Windows Vista, 305 общие сведения, 302 развернутое состояние, 304 свернутое состояние, 304 утечка памяти, 315 Д декларативные действия Action, класс, 177 InvokeMethodAction, класс, 178 PostBackAction, класс, 180 SetPropertyAction, класс, 179 Sys.Action, класс, 177 общие сведения, 177 И источники данных Sys.Data.DataSource, класс, 279 механика работы, 281 общие сведения, 279 К клиентская библиотека Action, класс, 177 findObject, метод, 173 InvokeMethodAction, класс, 178 SetPropertyAction, класс, 179 StringBuilder, класс, 183 Sys.Application, класс, 172 Sys.Debug, класс, 187 Sys.Action, класс, 177 Sys.Component, класс, 175 Sys.Counter, класс, 185 Sys.Net.WebRequest, класс, 187 Sys.Timer, класс, 184 основные компоненты, 171 пакетные обновления, 176 привязка данных, 177 клиентские элементы Sys.UI.Button, 190 Sys.UI.CheckBox, 191 Sys.UI.Control, 189 Sys.UI.HyperLink, 191 Sys.UI.Image, 192 Sys.UI.Label, 192 Sys.UI.Select, 192 Sys.UI.TextBox, 193

319


320

Алфавитный указатель

М макетные шаблоны, 271 мандат, 217 методы страниц WebMethod, атрибут, 217, 248 объявление, 248 Н наследование, поддержка, 169 нелокальные веб-службы, 253 П пакетное обновление, 176 преобразователи, 261 привязка Amazon, пример, 276 bindings, коллекция, 266 DataControl, 273 DataNavigator, 291 DataObjectMethod, атрибут, 282 ItemView, элемент, 294 Sys.Binding, класс, 266 Sys.Data.DataSource, класс, 279 Sys.Data.DataView, класс, 285 встроенные преобразователи, 269 общие сведения, 177

проверка данных Sys.UI.CustomValidator, 197 Sys.UI.InputControl, 193 Sys.UI.RangeValidator, 196 Sys.UI.RegexValidator, 197 Sys.UI.RequiredFieldValidator, 195 Sys.UI.TypeValidator, 197 общие свещения, 193 программный интерфейс, 227 простая привязка данных, 266 пространства имен, 167 С сериализация, 30 сетевые исполнители, 255 сложная привязка данных, 271 событие триггерное, 28 состояние отображения, 30 страницы и гаджеты, 306 Т триггерное событие, 28 У утечка памяти в гаджетах, 315


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.