33CD_ASP3-5-C#2008

63
ГЛАВА 33 Silverlight Х отя Web несомненно является наиболее популярной средой для программного обеспечения для бизнеса, существуют вещи, которые Web-приложения делать не могут или делают недостаточно хорошо. Даже если вы оснастите ваши Web-страницы ASP.NET последним передовым JavaScript-кодом, то все равно не сможете воспроизвести многих возможностей настольных приложений, таких как анимация, воспроизведение звука и видео, а также трехмерная графика. И хотя вы можете использовать JavaScript для реагирования на пользовательское перемещение фокуса, движения мыши и прочие события “реального времени”, тем не менее, вы не можете строить сложные интерфей- сы, которые хоть в какой-то мере приблизились бы к реактивности окон в приложении “толстого” клиента. (Сдержанное изящество Web-программирования состоит в том, что обычно вам и не нужны подобные украшения. Зато вы получаете такие преимущества, как широкая совместимость, высокая безопасность, отсутствие затрат на развертыва- ние и масштабируемая модель серверной стороны — неплохая компенсация за потерю некоторых “украшательств”.) Тем не менее, разработчики постоянно расширяют границы возможностей Web-при- ложений. В наши дни уже нередко можно увидеть анимированный сайт электронной коммерции или сыграть в простую, но богато оформленную игру непосредственно в ва- шем браузере. Очевидно, что такие возможности не являются частью обычных стандар- тов HTML, CSS и JavaScript. Вместо этого они обеспечиваются подключаемыми модуля- ми (plug-in) браузеров, иногда — аплетами Java, но чаще всего — Flash-содержимым. Новая технология от Microsoft, называемая Silverlight — прямой конкурент Flash. Подобно Flash, Silverlight развертывается с использованием легковесного браузерного подключаемого модуля и поддерживает широкое разнообразие браузеров и операционных систем. На данный момент Flash превосходит Silverlight из-за его широкого распростра- нения и зрелости. Однако Silverlight может похвастаться несколькими архитектурными особенностями, которых нет у Flash, и самая важная из них — тот факт, что основана она на масштабируемой версии многоязыковой исполняющей системы .NET (CLR), а потому позволяет пользователям писать код клиентской стороны на простом C#. В данной главе мы предпримем детальную экскурсию по Silverlight. Вы узнаете, как это работает, какие средства поддерживает и какие — пока нет. Вы также узнаете, как использовать Silverlight для оснащения Web-сайтов или даже интегрировать содержи- мое Silverlight в существующие страницы ASP.NET. Понятие Silverlight Технология Silverlight использует знакомую технику, позволяющую выйти за преде- лы стандартных Web-страниц — она применяет легковесный браузерный подключае- мый модуль.

Transcript of 33CD_ASP3-5-C#2008

Page 1: 33CD_ASP3-5-C#2008

Г Л А В А 33

Silverlight

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

могут или делают недостаточно хорошо. Даже если вы оснастите ваши Web-страницы ASP.NET последним передовым JavaScript-кодом, то все равно не сможете воспроизвести многих возможностей настольных приложений, таких как анимация, воспроизведение звука и видео, а также трехмерная графика. И хотя вы можете использовать JavaScript для реагирования на пользовательское перемещение фокуса, движения мыши и прочие события “реального времени”, тем не менее, вы не можете строить сложные интерфей-сы, которые хоть в какой-то мере приблизились бы к реактивности окон в приложении “толстого” клиента. (Сдержанное изящество Web-программирования состоит в том, что обычно вам и не нужны подобные украшения. Зато вы получаете такие преимущества, как широкая совместимость, высокая безопасность, отсутствие затрат на развертыва-ние и масштабируемая модель серверной стороны — неплохая компенсация за потерю некоторых “украшательств”.)

Тем не менее, разработчики постоянно расширяют границы возможностей Web-при-ложений. В наши дни уже нередко можно увидеть анимированный сайт электронной коммерции или сыграть в простую, но богато оформленную игру непосредственно в ва-шем браузере. Очевидно, что такие возможности не являются частью обычных стандар-тов HTML, CSS и JavaScript. Вместо этого они обеспечиваются подключаемыми модуля-ми (plug-in) браузеров, иногда — аплетами Java, но чаще всего — Flash-содержимым.

Новая технология от Microsoft, называемая Silverlight — прямой конкурент Flash. Подобно Flash, Silverlight развертывается с использованием легковесного браузерного подключаемого модуля и поддерживает широкое разнообразие браузеров и операционных систем. На данный момент Flash превосходит Silverlight из-за его широкого распростра-нения и зрелости. Однако Silverlight может похвастаться несколькими архитектурными особенностями, которых нет у Flash, и самая важная из них — тот факт, что основана она на масштабируемой версии многоязыковой исполняющей системы .NET (CLR), а потому позволяет пользователям писать код клиентской стороны на простом C#.

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

Понятие Silverlight Технология Silverlight использует знакомую технику, позволяющую выйти за преде-

лы стандартных Web-страниц — она применяет легковесный браузерный подключае-мый модуль.

Page 2: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны2

Преимущество такой модели в том, что пользователю нужно инсталлировать один-единственный компонент, чтобы увидеть информационное содержимое, созданное мно-гими людьми и компаниями. Инсталляция подключаемого модуля требует небольшой загрузки и подтверждения операции пользователем, как минимум, в одном диалоговом окне безопасности (а обычно — больше). Это требует небольшого, но заметного времени, и не представляет никаких неудобств. Однако, как только подключаемый модуль инстал-лирован, ваш браузер может обрабатывать любое содержимое, использующее этот под-ключаемый модуль совершенно гладко, без каких-либо дальнейших предупреждений.

На рис. 33.1 показаны два представления страниц с содержимым Silverlight. Страницу слева вы увидите, если у вас не инсталлирован подключаемый модуль Silverlight. В этой точке вы можете щелкнуть на изображении Get Microsoft Silverlight (Получить Microsoft Silverlight), чтобы попасть на Web-сайт Microsoft, где вам будет предложено инсталли-ровать подключаемый модуль и затем вернуться на исходную страницу. Справа видна страница, которую вы увидите после инсталляции подключаемого модуля Silverlight.

На заметку! Silverlight предназначен для преодоления ограничений обычного HTML, чтобы позволить разработчикам создавать более интерактивные приложения с богатой графикой. Однако Silverlight не позволит разработчикам разрушить стены “песочницы” безопасности браузера. В основном приложения Silverlight ограничены в той же мере, что и обычные Web-страницы. Например, при-ложению Silverlight разрешено создавать файлы и обращаться к ним, но только к тем файлам, ко-торые находятся в специально защищенной области изолированного хранилища. Концептуально изолированное хранилище работает подобно cookie-наборам на обычной Web-странице. Файлы разделяются по Web-сайту и текущему пользователю, и их размер строго ограничен.

Рис. 33.1. Инсталляция подключаемого модуля Silverlight

Сравнение Silverlight и FlashНаиболее успешным браузерным подключаемым модулем является Adobe Flash, ко-

торый инсталлирован в более чем 90% браузеров по всему миру. Flash имеет длинную историю, охватывающую период свыше 10 лет. Все начиналось с простого инструмента для добавления анимированной графики и постепенно эволюционировало в платформу для разработки интерактивного содержимого.

Page 3: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 3

Совершенно целесообразно разработчикам ASP.NET расширять свои Web-сайты, используя Flash-содержимое. Однако это требует отдельного инструмента проектиро-вания, совершенно иного языка программирования (ActionScript) и среды программи-рования (Flex). Более того, не существует прямого пути генерации содержимого Flash посредством кода .NET серверной стороны, что означает сложности в интеграции со-держимого ASP.NET и Flash — они живут на разных островах.

На заметку! Существуют некоторые решения от независимых разработчиков, которые помогают разрушить барьер между ASP.NET и Flash. Примером может служить SWFSource.NET (http://www.activehead.com/SWFSource.aspx), который предоставляет набор классов .NET, позволяющих вам динамически генерировать файлы Flash (.swf). Однако такие инструмен-ты работают на относительно низком уровне. Они еще далеки от полноценной платформы разработки.

Целью Silverlight является предоставление разработчикам .NET лучшего выбора для создания богатого Web-содержимого. Silverlight представляет браузерный подключае-мый модуль, оснащенный многими средствами, подобными Flash, но при этом, будучи основанным на фундаменте .NET, Silverlight органично поддерживает язык C# и исполь-зует широкий диапазон концепций .NET. В результате разработчики могут писать код клиентской стороны для Silverlight на том же языке, которые они применяют и для раз-работки кода стороны сервера (таком как C# и VB), и использовать многие одинаковые абстракции (включая потоки, элементы управления, коллекции, обобщения и LINQ).

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

Широкая поддержка браузеров. Пока еще слишком рано говорит о том, насколько хорошо работают оснащенные Silverlight браузеры на разных платформах. В на-стоящее время бета-сборки Silverlight 1.1 работают в Windows Vista и Windows XP (в мире ПК), на OS X 10.4.8 и более поздних (в мире Mac). Минимальные версии браузеров, поддерживаемых Silverlight 1.1, — это Internet Explorer 6, Firefox 1.5.0.8 и Safari 2.0.4. Хотя Silverlight 1.1 пока не работает под Linux, команда разработ-чиков проекта Mono трудится над реализацией открытого кода Silverlight 1.0 и Silverlight 1.1 для Linux. Этот проект известен как Moonlight, и он разрабатывает-ся с ключевой поддержкой со стороны Microsoft. За дополнительной информацией обращайтесь по адресу http://www.mono-project.com/Moonlight.

Легковесность. Для того чтобы поощрять адаптацию, Silverlight инсталлируется из маленькой установки (около 4 Мбайт), которую легко загрузить. Это позволяет ему обеспечить возможность гладкой установки — почти так же, как Flash (но не-сколько иначе, чем Java).

Двухмерное рисование. Silverlight предлагает богатую модель для двухмерной гра-фики. Лучше всего то, что содержимое, которое вы рисуете, определяется как фигуры и точки, так что вы можете манипулировать им на клиентской стороне. Вы можете даже реагировать на события (вроде щелчков на части графики), что облегчает добавление интерактивности к вашим рисункам.

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

Page 4: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны4

Медиа. Silverlight обеспечивает воспроизведение Windows Media Audio (WMA), Windows Media Video (WMV7–9), MP3 аудио и VC-1 (поддерживающего высокое разрешение). Вы не привязаны к элементу управления ActiveX Windows Media Player или браузерному подключаемому модулю. Вместо этого вы можете созда-вать любой фасад, который вы хотите, и даже показывать видео в полноэкран-ном режиме. Microsoft также предлагает бесплатную службу хостинга (на http://silverlight.live.com), которая предоставляет вам 4 Гбайт пространства для хранения медиа-файлов.

CLR. Больше всего впечатляет, что Silverlight включает масштабируемую версию CLR, оснащенную полным наборов классов ядра, сборщиком мусора, JIT-компи-лятором (just-in-time — оперативный), поддержкой обобщений и т.д. Во многих случаях разработчики могут взять код, написанный для полной .NET CLR, и при-менять его в приложении Silverlight с минимальными изменениями.

Взаимодействие с Web-службами. Приложения Silverlight могут вызывать Web-службы ASP.NET старого стиля (.asmx) или Web-службы WCF (Windows Communication Foundation). Также они могут пересылать через HTTP вручную созданный код XML.

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

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

Распространение SilverlightSilverlight — это очень новая технология, настолько новая на момент написания на-

шей книги, что существует всего лишь в двух версиях (1.0 и 1.1), и обе они являются бета-версиями. По этой причине трудно предсказать, насколько хорошо Silverlight смо-жет противостоять основной сильной стороне Flash — распространенности.

В данный момент Sulverlight установлен лишь на небольшой части компьютеров. Однако Microsoft считает, что если появится ценное информационное содержимое для Silverlight, то пользователь загрузит подключаемый модуль. Существует много факто-ров, поддерживающих этот аргумент. Flash значительно распространился за короткий период времени, и Microsoft обладает очевидным опытом разработки других Web-ори-ентированных приложений, которые начинались с малого и со временем получили ши-рокое распространение. (В первую очередь на ум приходит Windows Messenger, наряду с множеством подключаемых модулей ActiveX, предназначенных для решения задач, начиная с многопользовательской координации в MSN Games, и заканчивая верифика-цией Windows на MSDN.)

Ключевой момент, который следует иметь в виду при рассмотрении модели разра-ботки Silverlight, состоит в том, что вы будете использовать Silverlight для улучшения су-ществующего содержимого вашего Web-сайта (которое все еще основано на HTML, CSS и JavaScript). Например, вы можете добавить содержимое Silverlight, отображающее рек-ламу или же позволяющее улучшить впечатление от части Web-сайта (такой как игры,

Page 5: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 5

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

Хотя можно создавать Web-сайты, основанные только на Silverlight, маловероятно, что вы предпочтете такой подход. Тот факт, что Silverlight — относительно новая тех-нология, и что она не поддерживает унаследованные клиенты (прежде всего, стоит от-метить, что она не предусматривает поддержки пользователей Windows ME, Windows 2000 и Windows 98) означает, что она не имеет того же богатства, что и обычный HTML. Многие компании, применяющие Silverlight, делают это для того, чтобы отличаться от конкурентов изощренным содержимым Web-сайтов.

Silverlight 1.0 и 1.1

Silverlight существует в двух версиях.

Первая версия Silverlight 1.0 предоставляет довольно скромные возможности. Она включает средства двухмерной графики и средства воспроизведения медиа. Однако она не включает механизма CLR или поддержки языков .NET, так что любой код, который вы пишете, должен использовать JavaScript.

Вторая версия, Silverlight 1.1, добавила средства .NET, которые произвели наибольшее впе-чатление на разработчиков. Она включает CLR, подмножество классов .NET Framework, и мо-дель пользовательского интерфейса, основанную на WPF (как описано в следующем разделе “Silverlight и WPF”).

Хотя Silverlight 1.1 — менее зрелая версия из двух, она наиболее привлекательна для разработ-чиков .NET. Эта глава в основном сосредоточена на Silverlight 1.1.

Silverlight и WPFОдним из наиболее интересных аспектов Silverlight является тот факт, что он заим-

ствует модель WPF (Windows Presentation Foundation) для разработки богатых пользова-тельских интерфейсов на стороне клиента.

WPF — недавно представленная технология следующего поколения для создания приложений Windows, имеющих встроенную поддержку таких развитых средств, как трехмерная графика, анимация, отображение документов и многое другое. Она поя-вилась в .NET 3.0 как наследник Windows Forms. Технология WPF достойна внимания, потому что она не только упрощает разработку за счет мощного набора высокоуров-невых средств, но также повышает производительность за счет того, что визуализа-ция осуществляется через конвейер DirectX. Чтобы узнать побольше о WPF, можете об-ратиться к книге WPF: Windows Presentation Foundation в .NET 3.0 для профессионалов (ИД “Вильямс”, 2008 г.).

Очевидно, что Silverlight не может дублировать все средства WPF, поскольку многие из них полагаются на возможности операционной системы, включая специфичные для Windows дисплейные драйверы и технологию DirectX. Однако вместо того, чтобы изо-бретать полностью новый набор элементов управления и классов для разработки кли-ентской стороны, Silverlight использует подмножество модели WPF. Если у вас нет ни-какого опыта работы с WPF, вас поразит, насколько Silverlight похож на своего старшего брата. Вот несколько общих деталей.

Чтобы определить пользовательский интерфейс Silverlight (коллекцию элементов, составляющих страницу Silverlight или окно содержимого), вы используете раз-метку XML — так же, как делаете это с WPF.

Page 6: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны6

На заметку! XAML (Extensible Application Markup Language — расширяемый язык разметки прило-жений) — это язык разметки, используемый для создания экземпляров объектов .NET. В данной главе вы увидите, как он применяется для создания содержимого Silverlight.

При создании вашего пользовательского интерфейса вы применяете элементы, также имеющиеся в WPF, включая контейнер компоновки Canvas, фигуры вроде Rectangle, Ellipse, Line, Polyline и Polygon, элементы TextBox, Image и т.д.

На заметку! В терминологии WPF каждый графический “виджет” (widget), который появляется в пользовательском интерфейсе и представлен классом .NET, называется элементом (element). Термин элемент управления (control) обычно резервируется для элементов, предусматриваю-щих взаимодействие с пользователем.

Чтобы рисовать двухмерную графику в Silverlight, вы используете пути (paths), трансформации, геометрии и кисти — все они близко соответствуют их WPF-эк-вивалентам.

Silverlight предоставляет декларативную модель анимации, основанную на рас-кадровках (storyboards) и работающую точно так же, как система анимации WPF.

Чтобы показывать видео и воспроизводить аудиофайлы, вы используете класс MediaElement — как делаете это в WPF.

Microsoft не скрывает своих намерений продолжать расширять возможности Silverlight, происходящие от модели WPF. В будущих выпусках Silverlight вы, вероятно, увидите средства вроде привязки данных, больше контейнеров компоновки, элементы, дублирующие распространенные элементы управления Windows, и т.д.

Другими словами, Silverlight — это основанный на .NET конкурент Flash. Он наце-лен на конкуренцию Flash сегодня, но также прокладывает путь гораздо более богатому набору средств в будущем. В отличие от модели разработки Flash, которая ограничена в нескольких отношениях, ввиду пути развития, пройденного за годы, Silverlight — это попытка построить с нуля инструмент, полностью основанный на .NET и WPF, и потому имеющая все шансы обеспечить более высокую продуктивность разработчикам .NET. Во многих отношениях Silverlight — это кульминация двух тенденций: стремления рас-ширить Web-страницы, чтобы они включали все больше и больше средств “толстого” клиента, а также стремления обеспечить .NET Framework более широкое применение.

Инсталляция Silverlight и расширения Visual StudioХотя вы можете создавать страницы Silverlight вручную, все же это нелегко (и не

стоит хлопот). Вместо этого имеет смысл применить инструмент дизайна, такой как Visual Studio или Expression Blend.

В настоящий момент Visual Studio не имеет поддержки времени проектирования для создания содержимого Silverlight. Однако Microsoft выпустила бесплатное приложение с расширениями для разработки содержимого Silverlight 1.1 в Visual Studio 2008. Эти расширения, которые находятся на стадии бета-версий на момент написания нашей книги, включают полезные шаблоны для создания Web-сайтов и страниц ASP.NET, ис-пользующих содержимое Silverlight. Однако эти расширения пока не включают визу-альный дизайнер. Другими словами, нет способа увидеть ваше содержимое Silverlight в Visual Studio, или перетаскивать и устанавливать его, как это делается в других визу-альных редакторах. Вместо этого вам придется кодировать разметку вручную и затем запускать ее в браузере.

Page 7: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 7

Вы можете загрузить расширения Visual Studio и все, что вам еще может понадо-биться для начала работы с Silverlight, с сайта http://silverlight.net/GetStarted. Вот что вы там найдете.

1. Исполняющую систему Silvetlight 1.1. Это подключаемый модуль для браузера, по-зволяющий запускать содержимое Silverlight. (Также вы увидите там полностью отдельную исполняющую систему Silverlight 1.0.)

2. ASP.NET Futures. Это дополнение к ASP.NET, включающее средства, которые долж-ны стать частью будущих выпусков ASP.NET. (Конечно, эти средства могут слег-ка измениться до того момента, когда попадут в официальный выпуск. ASP.NET AJAX — пример технологии, которая сначала появилась, как дополнение (add-in), а затем была интегрирована в .NET Framework.) ASP.NET Futures включает Web-элемент управления Xaml, что необходимо, если вам нужен простой способ помес-тить содержимое Silverlight в область страницы ASP.NET.

3. Silverlight Tools for Visual Studio 2008. Это дополнение к Visual Studio, позволяющее создавать Web-сайты Silverlight и страницы Silverlight для использования с Web-элементом управления Xaml.

4. Silverlight 1.1 Software Development Kit. SDK предоставляет дополнительную доку-ментацию и примеры. Это не обязательно, но полезно.

Прежде чем вы продолжите изучать примеры Silverlight в оставшейся части этой главы, убедитесь, что вы инсталлировали все следующие компоненты: исполняющую систему Silverlight 1.1, ASP.NET Futures и Silverlight Tools for Visual Studio 2008.

Будущие версии Visual Studio, несомненно, предложат лучшую поддержку времени проектирования для содержимого Silverlight. Есть еще один вариант выбора: вы може-те создавать приложения Silverlight, используя Microsoft Expression Blend 2 — профес-сиональный инструмент проектирования, который обладает многими возможностями Visual Studio, но предназначен в основном для проектировщиков пользовательского ин-терфейса и графически-ориентированной разработки (вместо простого кодирования). Expression Blend 2 позволяет создавать настольные приложения, использующие WPF, и Web-страницы, применяющие Silverlight, но не позволяет создавать Web-страницы ASP.NET. Поэтому, если вы используете Expression Blend 2, то вам придется создавать Web-страницы ASP.NET для остальной части вашего сайта в Visual Studio.

В настоящее время Expression Blend 2 находится на ранней бета-стадии и в этой главе не обсуждается. Однако вы можете найти книги, посвященные Expression Blend 1, которые научат вас создавать полноценные пользовательские интерфейсы WPF (но не содержимое Silverligt), применяя ту же модель.

Создание проекта SilverlightСуществуют два способа интегрирования содержимого Silverlight в приложение

ASP.NET.

Создание HTML-файлов с содержимым Silverlight. Вы помещаете эти файлы в пап-ку вашего Web-сайта ASP.NET, как делаете это с любым обычным HTML-файлом. Единственное ограничение этого подхода состоит в том, что ваш HTML-файл, очевидно, не может включать элементы управления ASP.NET, потому что не будет обрабатываться на сервере.

Помещение содержимого Silverlight внутри Web-формы ASP.NET. Чтобы проделать этот трюк, вам понадобится помощь Web-элемента управления Xaml. Также вы можете добавлять другие элементы управления ASP.NET в разные области стра-

Page 8: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны8

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

Конечно, вы также вольны смешивать оба эти подхода и использовать содержимое Silverlight в выделенных HTML-страницах и внутри Web-страниц ASP.NET в пределах од-ного сайта.

В Visual Studio предусмотрено два способа разработки содержимого Silverlight, поч-ти равноценных (но не точно).

Создать проект Silverlight. Проект Silverlight состоит из файлов Silverlight (XAML-файлов), определяющих ваше содержимое Silverlight и код, а также обычных Web-страниц HTML, представляющих это содержимое. Поскольку Silverlight — технология клиентской стороны, вы можете запрашивать эти HTML-страницы непосредственно в Web-браузере. Visual Studio не нужно использовать свой интег-рированный Web-сервер, потому что никакого кода на сервере не запускается.

Использовать элемент управления в Web-форме ASP.NET. Вы можете использо-вать элемент управления Xaml в существующей Web-форме ASP.NET на сущест-вующем Web-сайте ASP.NET. Web-элемент управления Xaml — это элемент ASP.NET, но с одним отличием — вместо выдачи обычного HTML и JavaScript, он генерирует разметку XAML, определяющую пользовательский интерфейс Silverlight. Однако разметка XAML не кодируется напрямую в вашей странице ASP.NET (что было бы несколько утомительно). Вместо этого она извлекается из отдельного файла Silverlight, являющегося частью вашего Web-сайта.

В этом разделе мы начнем с применения первого подхо-да. После того, как вы ознакомитесь с окружением Silverlight и увидите, как интегрировать содержимое Silverlight в обыч-ные Web-страницы, мы рассмотрим, как можно включить его в Web-форму ASP.NET.

Чтобы создать проект Silverlight, выберите команду File�New�Project (Файл�Создать�Проект) в меню Visual Studio и укажите шаблон Silverlight Project (Проект Silverlight). Как обычно, вы должны задать имя проекта и его местопо-ложение на диске перед тем, как щелкнуть на OK для соз-дания проекта.

Каждый проект Silverlight начинается с небольшого на-бора важных файлов, как показано на рис. 33.2. Эти файлы описаны в последующих разделах.

Входная HTML-cтраница Эта страница служит точкой входа в ваше содержимое Silverlight — другими сло-

вами, страница, запрашиваемая пользователем в Web-браузере. Visual Studio именует этот файл TestPage.html, хотя вы наверняка переименуете ее в нечто более соответ-ствующее.

Входная HTML-страница в действительности не содержит разметки Silverlight или отделенного кода. Вместо этого она создает его, используя небольшой объем JavaScript. (По этой причине браузеры, в которых отключена поддержка JavaScript, не могут ото-бражать содержимое Silverlight.)

Вот как выглядит входная HTML-страница:

Рис. 33.2. Проект Silverlight

Page 9: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 9

<html xmlns="http://www.w3.org/1999/xhtml"><head> <title>Silverlight Project Test Page </title> <script type="text/javascript" src="Silverlight.js"></script> <script type="text/javascript" src="TestPage.html.js"></script> <style type="text/css"> .silverlightHost { width: 640px; height: 480px; } </style></head><body> <div id="SilverlightControlHost" class="silverlightHost"> <script type="text/javascript"> createSilverlight(); </script> </div></body></html>

Как видите, эта страница ссылается на два других файла JavaScript (TestPage.html.js и Silverlight.js). Хотя вы можете добавить и другое содержимое HTML к этой странице, данный пример включает единственный элемент <div>, куда может быть помещен элемент управления Silverlight. Когда обрабатывается этот элемент <div>, выполняется код из блока сценария JavaScript. Этот код вызывает функцию createSilverlight() (определенную в файле TestPage.html.js) для генерации содер-жимого Silverlight. После того, как содержимое создано и браузер обработал всю раз-метку входной HTML-страницы, событие onload элемента <body> гарантирует передачу клавиатурного фокуса элементу SilverlightControl.

На заметку! Visual Studio устанавливает TestPage.html в качестве стартовой страницы вашего проекта. В результате, когда вы запускаете проект, эта страница загружается в браузер. Вы можете выбрать другую стартовую страницу, выполнив щелчок правой кнопкой мыши на HTML-файле в Solution Explorer и выбрав в контекстном меню команду Set As Start Page (Установить как стартовую страницу).

Сценарий инициализации SilverlightПо умолчанию Visual Studio генерирует функцию JavaScript по имени

createSilverlight() для инициализации вашей области SilverLight. Он помещает этот код в файл по имени TextPage.html.js (хотя вы можете переименовать этот файл или переместить код JavaScript куда-нибудь еще — до тех пор, пока не забываете обновлять ссылки на входной HTML-странице).

Вот как выглядит функция createSilverlight():

function createSilverlight(){ Silverlight.createObjectEx({ source: "Page.xaml", parentElement: document.getElementById("SilverlightControlHost"), id: "SilverlightControl", properties: { width: "100%", height: "100%", version: "1.1", enableHtmlAccess: "true" }, events: {} });

Page 10: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны10

// Передать клавиатурный фокус элементу управления Silverlight по умолчанию document.body.onload = function() { var silverlightControl = document.getElementById('SilverlightControl'); if (silverlightControl) silverlightControl.focus(); }}

Функция createSilverlight() вызывает функцию Sys.Silverlight.createObjectEx(), которая определена в файле Silverlight.js. Файл Silverlight.js — ба-зовая часть инфраструктуры Silverlight, которую вы вряд ли станете модифицировать. Он включает JavaScript-код, проверяющий, инсталлирован ли Silverlight (и предлагаю-щий перенаправить пользователя на другую страницу, если нет), и JavaScript-код, соз-дающий элемент управления Silverlight. Если у вас есть несколько приложений Silverlight на одном Web-сервере, они могут с успехом использовать один файл Silverlight.js.

С другой стороны, код в функции createSilverlight() внутри файла TestPage.html.js более важен. Когда функция createSilverlight() вызывает createObjectEx(), она специфицирует несколько ключевых деталей относительно эле-мента управления Silverlight, включая имя XAML-файла, в котором хранится разметка, имя элемента <div>, куда будет помещен элемент Silverlight, минимальную требуемую версию Silverlight и также, хотите ли вы разрешить взаимодействие между Silverlight и HTML DOM. Поскольку эта информация варьируется, каждая страницы Silverlight имеет свою собственную функцию createSilverlight() для установки области содержимого Silverlight.

В этом примере (который демонстрирует код по умолчанию, сгенерирован-ный Visual Studio) файл XAML называется Page.xaml, элемент <div> называет-ся SilverlightControlHost, а элемент управления Silverlight будет называться SilverlightControl.

Ширина и высота установлены в 100%, что означает, что содержимое Silverlight все-гда будет занимать полностью всю поверхность содержащего его элемента. В данном случае таким элементом является <div>, помещаемый непосредственно в тело вашей Web-страницы. Обычно подобным образом размещенному элементу <div> разрешено расти без ограничений. Однако когда вы создаете вашу страницу Silverlight, то Visual Studio добавляет правило стиля, которое ограничивает размер <div>. Правило стиля выглядит следующим образом:

<style type="text/css"> .silverlightHost { width: 640px; height: 480px; }</style>

Это устанавливает элементу <div> точный размер в 640×480 пикселей. Вы можете изменить правило стиля, указав другой размер. (Вы можете также изменить параметры в вызове createObjectEx(), чтобы использовались фиксированные значения пиксе-лей, но будет понятнее, если поместить эти детали в файл HTML.)

Вы можете удалить свойство height, чтобы область содержимого Silverlight заполня-ла свой контейнер, что в данном случае даст высоту и ширину окна браузера. Обычно это не то поведение, которое вас устроит, и это определенно неправильный выбор, если у вас на странице присутствует другое содержимое HTML. (Например, если у вас есть некоторое содержимое HTML после элемента <div>, это содержимое не будет видно, по-тому что оно всегда остается под содержимым Silverlight, заполняющим окно браузера.) Если вы не используете свойства width и height, то, вероятно, хотите ограничить эле-мент <div>, размещая его в определенном месте вашей компоновки, таком как между другими элементами <div>, в другом элементе фиксированного размера или в ячейке таблицы.

Page 11: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 11

Совет. Вы можете также применить стиль <div> для рисования рамки вокруг вашей области со-держимого Silverlight, так чтобы ясно видеть его границы. Чтобы сделать это, установите CSS-свойства border в этом стиле.

После информации width и height в вызове createObjectEx() следуют еще две де-тали. version указывает минимальную требуемую версию Silverlight, которая должна быть инсталлирована на клиенте, чтобы можно было увидеть содержимое. Если версия не соответствует, пользователю будет предложено инсталлировать правильную версию Silverlight с сайта Microsoft.

И, наконец, последнее свойство, которое вы передаете createObjectEx() — флаг enableHtmlAccess. Укажите true, если хотите иметь возможность взаимодействовать с элементами HTML на входной странице через ваш код Silverlight. Чуть позже в этой главе вы узнаете, как это делается.

Страница XAMLКогда ваша входная страница вызывает createObjectEx(), она специфицирует имя

файла XAML. Этот XAML-файл содержит разметку, используемую для генерации набора элементов, которые появляются в области содержимого Silverlight. По умолчанию Visual Studio называет этот файл Page.xaml.

Ниже приведен код разметки, который Visual Studio добавляет к файлу Page.xaml, с одной дополнительной строкой — элементом <TextBlock>, выделенной полужирным:

<Canvas x:Name="parentCanvas" xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Loaded="Page_Loaded" x:Class="SilverlightProject1.Page;assembly=ClientBin/SilverlightProject1.dll" Width="640" Height="480" Background="White"> <TextBlock FontSize="20">Hello, World!</TextBlock></Canvas>

Этого вполне достаточно, чтобы протестировать ваш проект Silverlight. Если вы за-пустите ваше приложение, то Visual Studio откроет ваш браузер по умолчанию и перей-дет на страницу TestPage.html. Тестовая страница создает новый элемент Silverlight и инициализирует разметку в Page.xaml. На рис. 33.3. показан довольно скромный окон-чательный результат.

Рис. 33.3. Страница с содержимым Silverlight

Page 12: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны12

Чтобы понять, как работает разметка и как создать нужный пользовательский ин-терфейс Silverlight, придется немного углубиться в стандарт XAML.

Понятие о XAML

XAML (Extensible Application Markup Language — расширяемый язык разметки при-ложений) — язык разметки, используемый для создания объектов .NET. Хотя XAML — это технология, которая может быть применена ко многим разным предметным облас-тям, в первую очередь он был спроектирован в качестве способа для разработчиков конструировать пользовательские интерфейсы для “толстых” приложений Windows. Поскольку Silverlight — это маленькое подмножество WPF, оно использует тот же стан-дарт разметки XAML.

Концептуально XAML играет роль, аналогичную HTML. Язык HTML позволяет опреде-лять элементы, которые составляют обычную Web-страницу. XAML позволяет вам опре-делять элементы, которые составляют блок содержимого XAML. Для манипулирования элементами HTML вы применяете JavaScript клиентской стороны (или просто визуали-затор целой страницы, как поступает ASP.NET после каждой обратной отправки). Чтобы манипулировать элементами XAML, вы пишете код C# клиентской стороны. И, наконец, XAML и HTML выглядят одинаково. Подобно XHTML, XAML — это основанный на XML язык, состоящий из элементов, которые могут быть вложены любым подходящим вам образом, чтобы выразить включение.

Вы можете писать разметку XAML вручную или же использовать инструмент, гене-рирующий требуемый код XAML. В настоящее время Visual Studio не включает под-держки для создания страниц XAML с визуальной поверхностью дизайна, хотя в ско-ром будущем можно ожидать ее появления (в конце концов, Visual Studio позволяет вам проектировать окна XAML для полноценных приложений WPF).

Элементы Silverlight

Каждый элемент в документе XAML отображается на экземпляр класса Silverlight. Имя элемента соответствует имени класса в точности. Например, элемент <Canvas> указывает Silverlight создать объект Canvas, а <TextBox> — объект TextBox. Так как элемент <TextBox> вложен внутри элемента <Canvas>, и поскольку Canvas — элемент-контейнер, TextBox помещается внутри Canvas.

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

Таблица 33.1. Элементы Silverlight

Класс Описание

Canvas Контейнер компоновки, который может содержать любое количество дру-гих элементов Silverlight, расположенных по фиксированным координатам.

TextBox Элемент, содержащий однострочное или многострочное текстовое содержимое.

Image Элемент, демонстрирующий файл с картинкой, — обычно в формате GIF, JPG или PNG.

Rectangle, Ellipse, Line, Polygon, Polyline, Path

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

MediaElement Элемент, управляющий воспроизведением аудио- или видео-файла, и, необязательно, отображающий окно видео.

Page 13: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 13

Пространства имен XAML

Когда вы используете элементы вроде <Canvas> в файле XAML, то анализатор Silverlight понимает, что вы хотите создать экземпляр класса Canvas. Однако он не обязательно знает — какой класс Canvas использовать. В конце концов, даже если про-странства имен Silverlight просто включают единственный класс с таким именем, нет никаких гарантий, что вы не создадите собственный класс с похожим именем. Ясно, что необходим способ указать информацию о пространстве имен Silverlight, чтобы ис-пользовать этот элемент.

В Silverlight классы разрешаются посредством отображения пространств имен XML на пространства Silverlight. В примере документа, показанном ранее, определены два пространства имен:

<Canvas x:Name="parentCanvas" xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Вы найдете эти два пространства имен в каждом документе XAML, созданном для Silverlight.

http://schemas.microsoft.com/client/2007 — центральное пространство имен Silverlight 1.1. Оно включает все классы Silverlight 1.1, в том числе Canvas. Обычно это пространство имен объявляется без префикса — наименования про-странства, так что оно становится пространством имен по умолчанию для всего документа. Другими словами, каждый документ автоматически помещается в это пространство, если только вы не укажете иначе.

http://schemas.microsoft.com/winfx/2006/xaml — пространство имен XAML. Оно включает разные служебные средства XAML, позволяющие вам повлиять на интерпретацию вашего документа. Это пространство имен отображается на пре-фикс x.

Во многих ситуациях вы захотите обращаться к своим собственным пространствам имен в файле XAML. Наиболее распространенный пример — если вы хотите применить пользовательский элемент управления Silverlight, созданный вами (или другим разра-ботчиком). В этом случае вы должны определить новый префикс пространства имен XML и отобразить его на вашу сборку. Вот необходимый для этого синтаксис:

<Canvas x:Name="parentCanvas" xmlns:w="clr-namespace:Widgets;assembly=ClientBin/Widgets.dll" ...Объявление пространства имен XML устанавливает следующие три части инфор-

мации.

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

Пространство имен .NET. В этом случае классы располагаются в пространстве имен Widgets. Если у вас есть классы, которые вы хотите использовать в несколь-ких пространствах имен, вы можете отобразить их на разные пространства имен XML или на одно пространство имен XML (до тех пор, пока нет никаких конфлик-тов между именами классов).

Сборка. В данном случае классы являются частью сборки Widgets.dll. Вы всегда предваряете эту сборку именем папки ClientBin. Это — вложенная папка ваше-го Web-сайта, куда Visual Studio помещает скомпилированную сборку Silverlight и

Page 14: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны14

все библиотечные сборки, используемые ею. (Чуть позже в этой главе мы рассмот-рим модель компиляции Silverlight более подробно.) Если вы хотите использовать класс, определенный в сборке вашего проекта Silverlight, то имя сборки базирует-ся на имени вашего проекта, как в случае SilverlightProject1.dll.

На заметку! Напомним, что Silverlight применяет усеченную версию CLR. По этой причине прило-жение Silverlight не может использовать сборку полной библиотеки классов .NET. Вместо этого нужно применять библиотеку классов Silverlight. Вы можете легко создать библиотеку классов Silverlight в Visual Studio, выбрав шаблон проекта Silverlight Class Library (Библиотека классов Silverlight).

Как только вы отобразите ваше пространство имен .NET на пространство имен XML, то сможете использовать его в любом месте вашего документа XAML. Например, если пространство имен Widgets содержит элемент управления по имени HotButton, вы мо-жете создать его экземпляр следующим образом:

<w:HotButton Text="Click Me!" Click="DoSomething"></w:HotButton>

Отделенный код XAMLXAML позволяет вам конструировать пользовательский интерфейс, но для того, что-

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

<Canvas x:Name="parentCanvas" ... x:Class="SilverlightProject1.Page;assembly=ClientBin/SilverlightProject1.dll" ...

Префикс пространства имен x помещает атрибут Class в пространство имен XAML, а это означает, что атрибут Class — более общая часть языка XAML, а не специфиче-ский ингредиент Silverlight. Фактически атрибут Class говорит анализатору Silverlight, что он должен сгенерировать новый класс с указанным именем. Этот класс наследуется от класса, именованного элементом XML. Другими словами, этот пример создает но-вый класс по имени SilverlightProject1.Page, унаследованный от базового класса Canvas. Автоматически сгенерированная часть этого класса сливается с кодом, кото-рый вы помещаете в файле отделенного кода.

Обычно каждый файл XAML имеет соответствующий класс в отделенном коде C# клиентской стороны. Visual Studio создает класс отделенного кода для файла Page.xaml по имени Page.xaml.cs. Вот что вы увидите в этом файле:

using System;using System.Windows;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Ink;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Shapes;namespace SilverlightProject1{ public partial class Page : Canvas { private void Page_Loaded(object o, EventArgs e) {

Page 15: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 15

// Требуется для инициализации переменных. InitializeComponent(); } }}

Внимательный читатель отметит, что этот обработчик события Page_Loaded() в действительности реагирует на событие со слегка отличающимся именем, а именно — Canvas.Loaded. Обработчик прикреплен в коде разметки XAML для страницы. Имя Page_Loaded() используется потому, что оно лучше знакомо разработчикам ASP.NET и яснее передает то, что здесь происходит, а именно: завершается инициализация содер-жимого Silverlight на этой странице.

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

На заметку! Метод InitializeComponent() играет ключевую роль в содержимом Silverlight. По этой причине вы никогда не должны удалять вызов InitializeComponent() из конст-руктора. Точно так же, если вы добавите другой конструктор, он тоже должен будет вызывать InitializeComponent().

Есть еще одна деталь, которую следует учитывать. В вашем классе отделенного кода вы часто будете манипулировать элементами управления программно. Чтобы это было возможно, элемент управления должен включать атрибут Name. В предыдущем примере элемент TextBlock не включает атрибута Name, поэтому вы не сможете манипулиро-вать им в отделенном коде.

Вот как можно присоединить имя к TextBlock:

<TextBlock x:Name="txt" FontSize="20">Hello, World!</TextBlock>

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

Свойства и событияЧтобы сделать что-то практическое с Silverlight, вам нужно установить свойства ва-

ших элементов и присоединить обработчики к их событиям.Установить свойства просто — вы используете атрибуты со строчными значениями.

Silverlight применяет конвертеры типов (как на страницах ASP.NET), чтобы преобразо-вать строковые значения в соответствующие типы данных. Во многих случаях эта за-дача проста — например, нет никаких сложностей в замене строки с числом в числовое значение или строки с именем цвета в соответствующее цветовое значение. Однако в других ситуациях вы должны устанавливать свойство, используя объект, которые не мо-жет быть легко представлен в виде единственной строки.

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

Page 16: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны16

Например, следующий код разметки ус-танавливает свойство Canvas.Background, создавая RadialGradientBrush. Это де-лается посредством элемента <Canvas.Background> (вместо установки атрибу-та Background элемента <Canvas>, как в предыдущих примерах). Чтобы сконфигу-рировать RadialGradientBrush, вы долж-ны указать центральную точку градиента и крайние значения его цвета. Результат показан на рис. 33.4.

<Canvas ... > <Canvas.Background> <RadialGradientBrush Center="0.5,0.5"> <GradientStop Offset="0" Color="LightSteelBlue" /> <GradientStop Offset="1" Color="White" /> </RadialGradientBrush> </Canvas.Background> <TextBlock x:Name="txt" FontSize="20">Hello, World!</TextBlock></Canvas>

Чтобы прикрепить событие вы также используете атрибуты. Однако теперь вам нужно назначить имя обработчику событий по имени события. Это подобно подходу, применяемому на Web-страницах ASP.NET, за исключением того, что атрибуты события не начинаются со слова On.

Элементы Silverlight поддерживают относительно небольшой набор событий, включая GotFocus, KeyDown, KeyUp, Loaded, LostFocus, MouseEnter, MouseLeave, MouseLeftButtonDown, MouseLeftButtonUp и MouseMove. Здесь нет высокоуровневого события Click.

Например, вот как выглядит обработчик события, реагирующий на щелчок кнопкой мыши изменением текста в TextBlock:

public partial class Page : Canvas{ ... private void txt_Click(object o, EventArgs e) { txt.Text = "You clicked here."; }}А вот как вы можете привязать это событие к TextBlock:

<TextBlock ... MouseLeftButtonDown="txt_Click">Hello, World!</TextBlock>

Компиляция SilverlightПоскольку Silverlight использует CLR (хотя и сокращенную, облегченную версию), он

использует ту же модель компиляции, что и другие приложения .NET. Когда вы строите ваше приложение Silverlight, код компилируется в сборку, назван-

ную по имени вашего проекта (например, SilverlightProject1.dll). Эта сборка на-ходится во вложенной папке ClientBin внутри каталога вашего проекта. В отличие от кода Web-приложений ASP.NET, код в проекте Silverlight должен быть скомпилирован

Рис. 33.4. Canvas с фоном RadialGradientBrush

Page 17: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 17

во время разработки — сервер не осуществляет JIT-компиляции при запросе файла Silverlight. Это имеет смысл — в конце концов, нет причины предполагать, что Web-сер-вер для приложения Silverlight даже имеет инсталлированный .NET Framework.

Сборка проекта Silverlight содержит код из ваших классов отделенного кода, а также скомпилированный код для всех прочих файлов кода в вашем проекте. Дополнительно он может включать ресурсы — двоичные блоки данных, которые должны быть легко доступны, такие как графические изображения. Хотя эти ресурсы могут включать документы XAML, извлекаемые посредством кода, в современной модели разработки Visual Studio созданные вами файлы XAML не встраиваются в сборку. (Разработчики, создающие специальные элементы управления Silverlight, часто используют этот метод и встраивают шаблоны XAML, определяющие содержимое их элемента управления в виде ресурса сборки.)

Модель компиляции Silverlight имеет ряд преимуществ, включая легкость развер-тывания и чрезвычайно повышенную производительность по сравнению с обычным JavaScript. Однако хотя код компилируется, те же соображения применимы к сборкам Silverlight, как и к любому другому типу кода клиентской стороны. IL-код может быть легко декомпилирован или восстановлен, так что это неподходящее место для хранения секретов (таких как ключи шифров, патентованные алгоритмы и т.п.). Если вы хотите выполнять задачу, использующую ценный код, рассмотрите вариант вызова Web-служ-бы из вашего приложения Silverlight.

Как только вы постигнете модель компиляции Silverlight, останется один шаг к по-ниманию модели развертывания. Когда вы развертываете приложение Silverlight, вам нужно передать на Web-сервер следующие файлы:

входную HTML-страницу;

страницы сценариев .js;

страницы XAML;

папку ClientBin с ее сборками (отладочные файлы .pdb не нужны).

Вам не нужно копировать сырой исходный код, потому что он компилируется в сбор-ку проекта в папке ClientBin.

При размещении приложения Silverlight ваш Web-сервер должен быть настроен так, чтобы позволять запросы двух новых типов файлов: .xaml и .dll. Это позволит вход-ной HTML-странице загрузить начальную страницу XAML, которая затем, в свою оче-редь, загрузит сборку проекта, содержащую отделенный код класса.

Модель выполнения Silverlight достаточно прямолинейна. Во-первых, клиент запра-шивает входную страницу HTML. В этой точке браузер загружает HTML-файл и связан-ный с ним файл .js. Обрабатывая страницу HTML, браузер выполняет код JavaScript, включая вызов createObjectEx(), создающий область содержимого Silverlight. После выполнения этого шага в работу вступает подключаемый модуль на клиентской сторо-не. Он загружает связанный файл XAML (идентифицируемый исходным параметром, переданным методу createObjectEx()). Когда подключаемый модуль обрабатывает файл XAML, он добирается до атрибута Class, который ссылается на скомпилирован-ную сборку проекта. Браузер затем загружает эту сборку во всей ее полноте, наряду со всеми другими сборками, на которые она ссылается (такие как сборки библиотеки классов, содержащей ваши специальные типы, используемые на странице). Эти сборки кэшируются на стороне клиента, так что их не приходится потом загружать повторно всякий раз, когда пользователь посещает новую страницу, использующую ту же сборку. Весь код Silverlight исполняется на стороне клиента усеченной версией .NET Framework, встроенной в подключаемый модуль Silverlight.

Page 18: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны18

Сущности SilverlightТеперь, получив представление о модели Silverlight, вы готовы к тому, чтобы по-

ближе ознакомиться с фундаментальными концепциями, участвующими в построении содержимого Silverlight. В этом разделе мы рассмотрим версию .NET Framework, вклю-ченную в Silverlight, модель компоновки Silverlight и TextBlock. Также вы узнаете, как взаимодействовать с элементами HTML и использовать изолированное хранилище.

Классы .NET Framework в SilverlightSilverlight включает подмножество классов полного .NET Framework. Хотя было бы

невозможно уместить весь .NET Framework в Silverlight — в конце концов, это файл раз-мером 4 Мбайт для загрузки, который должен поддерживать широкое разнообразие браузеров и операционных систем — Silverlight включает порядочную его часть.

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

На заметку! Классы Silverlight спроектированы так, чтобы иметь общедоступные интерфейсы, по-хожие на их полноценные аналоги в .NET Framework. Однако действительные внутренние ме-ханизмы этих классов несколько отличаются. Все классы Silverlight были переписаны с самого нуля, чтобы достичь максимальной простоты и эффективности.

Прежде чем вы приступите к серьезному программированию Silverlight, вам сто-ит просмотреть версию .NET Framework от Silverlight. Один способ сделать это состо-ит в том, чтобы открыть проект Silverlight и затем показать браузер объектов (Object Browser) в Visual Studio (выбрав команду меню View�Object Browser (Вид�Браузер объ-ектов)). Наряду со сборкой кода вашего проекта вы увидите следующие сборки Silverlight (показано на рис. 33.5).

mscorlib.dll. Эта сборка — Silverlight-эквивалент сборки mscorlib.dll, которая включает наиболее основополагающие части .NET Framework. Версия Silverlight включает основные типы данных, исключения и интерфейсы из пространства имен System, обычные и обобщенные коллекции, классы управления файлами и поддержку глобализации, рефлексии, ресурсов, отладки и многопоточности.

На заметку! Некоторые члены в сборках Silverlight доступны только коду .NET Framework и не могут быть вызваны из вашего кода. Эти члены помечены атрибутом SecurityCritical. Однако этот атрибут не отображается в Object Browser, поэтому вы не сможете определить, доступно ли определенное средство приложению Silverlight, пока не попробуете его использовать. (Если вы попытаетесь использовать член, оснащенный атрибутом SecurityCritical, то получите исключение SecurityException.) Например, приложениям Silverlight разрешено обращать-ся к файловой системе только через интерфейс API изолированных хранилищ. По этой причи-не конструктор класса FileStream оснащен атрибутом SecurityCritical.

System.dll. Эта сборка содержит дополнительные обобщенные коллекции, клас-сы для работы с URI, а также классы, обрабатывающие регулярные выражения.

Page 19: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 19

System.Core.dll. Эта сборка содержит поддержку LINQ. (Вы, должно быть, пом-ните, что все эти новые средства .NET 3.5 реализованы в сборке под названием System.core.dll.)

System.Silverlight.dll. Эта сборка содержит классы для взаимодействия с элементами HTML, версию OpenFileDialog, работающую с изолированным хра-нилищем, и классы для отправки HTTP-запросов.

System.Xml.core.dll. Эта сборка включает абсолютный минимум классов, необ-ходимых для обработки XML: XmlReader и XmlWriter.

agclr.dll. Эта сборка включает классы Silverlight UI, унаследованные от моде-ли WPF. Например, вы найдете здесь классы для всех элементов Silverlight и ани-мации. (Говорят, что ag в названии agclr.dll происходит от символа серебра в периодической таблице Менделеева, в то время как clr указывает на усеченную версию CLR, используемую Silverlight.)

Рис. 33.5. Сборки Silverlight в Object Browser

CanvasВ примере, рассмотренном в предыдущей главе, корневым элементом страницы

XAML был Canvas. Canvas — контейнер компоновки, а это означает, что это элемент, который содер-

жит (и отображает) другие элементы. Полная платформа WPF включает несколько диспетчеров компоновки, включая такие, которые автоматически укладывают элемен-ты в стопку, выстраивают их в много строк или размещают в узлах невидимой сетки. Canvas — самый простой диспетчер компоновки; он просто размещает каждый элемент

Page 20: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны20

по фиксированным координатам. Хотя включение других диспетчеров компоновки в Silverlight планируется, на данный момент Canvas — единственно доступный.

Технически вы можете использовать любой элемент в качестве корневого в файле Silverlight XAML. Однако Canvas — наиболее распространенный и логичный выбор, благодаря его способности содержать другие элементы. Например, если вы используете Rectangle в качестве корневого элемента, то ваше содержимое Silverlight будет огра-ничено этим единственным объектом Rectangle, потому что он не может содержать внутри себя ничего другого.

На заметку! По умолчанию Visual Studio устанавливает размер Canvas в 640×480, указывая зна-чения свойств Width и Height в файле XAML. Однако существует потенциальное противоре-чие, потому что содержимое внутри Canvas может выходить за его границы. Поэтому если вы поместите Canvas размером 640×480 в область содержимого Silverlight большего размера, то увидите, что оно выйдет за отведенные ему пределы. Обычно такое поведение нежелательно, поскольку размер области содержимого Silverlight также ограничен прямоугольником 640×480 через правило стиля, как было описано ранее, в разделе “Сценарий инициализации Silverlight”.

Расположение элементов в CanvasЧтобы расположить элемент в Canvas, вы используете прикрепленные (attached)

свойства. Прикрепленные свойства — это еще одна концепция, принесенная из WPF. По сути, прикрепленное свойство — это свойство, определенное в одном классе, но ис-пользуемое в другом. Прикрепленные свойства — ключевой механизм расширяемости, потому что они позволяют классам взаимодействовать гибким образом, даже без пред-варительного планирования. Например, Canvas определяет три прикрепленных свой-ства: Left, Top и ZIndex. Элементы внутри Canvas могут использовать эти свойства для своего позиционирования. Такое решение лучше, чем определение этих свойств как части некоторых классов базовых элементов, потому что они менее тесно связаны. Элементы не обязаны быть специально спроектированными для работы с Canvas — они просто работают и все. И когда в Silverlight добавятся другие диспетчеры компоновки, они включат свой собственный набор прикрепленных свойств, относящихся к компо-новке. Концептуально также имеет больше смысла для свойств быть “прикрепленными” к Canvas, потому что Canvas, читающий эти значения и работающий с ними, сам не является содержащимся элементом.

Чтобы установить прикрепленное свойство в XAML, используется синтаксис из двух частей, разделенных точкой. Левая часть — имя класса, где определены свойства (вроде Canvas), в то время как правая часть — имя свойства (вроде Top). Приведем пример, помещающий Rectangle в определенное место в Canvas:

<Rectangle x:Name="rect" Canvas.Top="30" Canvas.Left="30" Fill="Blue" Height="50" Width="50" />

Координаты отсчитываются от левого верхнего угла, так что этот фрагмент создает фигуру в 30 пикселях от верхней и левой границы. Если вы не установите свойств Top и Left, по умолчанию принимается значение 0, что помещает элемент в левый верхний угол (как в случае TextBlock, продемонстрированном в предыдущем примере).

Если вы хотите модифицировать прикрепленное свойство программно, вам нужно использовать немного более сложный синтаксис. Трюк заключается в вызове метода SetValue<T> на вашем элементе. Вы указываете тип данных свойства как аргумент типа и передаете два параметра: свойство, которое вы хотите модифицировать и новое значение, которое хотите установить. Следующая строка кода устанавливает свойство Canvas.Top, применимое к Rectangle, в 100:

rect.SetValue<double>(Canvas.TopProperty, 100);

Page 21: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 21

При указании этого свойства используется синтаксис ИмяКласса.ИмяСвойстваProperty. Другими словами, свойство Canvas.Top представлено статическим полем по имени Canvas.TopProperty.

Послойное расположение элементов в CanvasЕсли у вас более одного перекрывающегося элемента, вы можете установить прикре-

пленное свойство Canvas.ZIndex, чтобы управлять их послойным расположением.Изначально все добавляемые элементы имеют одинаковый Zindex — 0. Когда эле-

менты имеют одинаковый ZIndex, они отображаются в том же порядке, в каком они были объявлены в разметке XAML. Элементы, объявленные в разметке позже, отобра-жаются поверх элементов, объявленных ранее.

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

<Rectangle Canvas.Left="60" Canvas.Top="80" Canvas.ZIndex="1" Fill="Blue" Width="50" Height="50" /><Rectangle Canvas.Left="70" Canvas.Top="120" Width="100" Height="50" Fill="Yellow" />

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

На заметку! Действительные значения, которые вы указываете для свойства Canvas.ZIndex, не имеют значения. Важная деталь состоит в том, что значение ZIndex одного элемента сравни-вается со значением ZIndex другого. Вы можете установить любое положительное или отри-цательное значение ZIndex.

Свойство ZIndex, в частности, удобно, если вам нужно программно изменить пози-цию элемента. Просто вызовите метод SetValue<T> на элементе, который вы хотите модифицировать с новым значением ZIndex, которое нужно применить. К сожалению, не предусмотрено методов вроде BringToFront() или SendToBack() — на вас возлага-ется задача отслеживания максимального и минимального значений ZIndex, если вы хотите реализовать такое поведение.

Перетаскивание кругов

Попробуем сложить вместе все изложенные концепции, используя простой пример.На рис. 33.6 показано приложение Silverlight, которое позволяет рисовать и переме-

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

Каждый круг представлен экземпляром объекта Ellipse. Очевидно, что вы не може-те определить все нужные вам эллипсы в разметке XAML. Вместо этого вам понадобится способ динамической генерации объектов Ellipse при каждом щелчке пользователя на Canvas.

Создание объекта Ellipse не так уж сложно — в конце концов, вы можете создавать их экземпляры подобно любому другому объекту .NET, устанавливать свойства и при-креплять обработчики событий. Вы можете даже использовать метод SetValue<T> для установки прикрепленных свойств, чтобы помещать их в правильное место на Canvas.

Page 22: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны22

Рис. 33.6. Перетаскивание фигур

Однако здесь есть одна деталь, о которой стоит позаботиться — нужен способ по-мещения Ellipse на Canvas. Это достаточно просто, поскольку класс Canvas предос-тавляет коллекцию Children, которая хранит все дочерние элементы. Как только вы добавили элемент в эту коллекцию, он появляется в Canvas.

Страница XAML для этого примера использует единственный обработчик для собы-тия Canvas.MouseLeftButtonDown. Никакие другие элементы не определяются.

<Canvas x:Name="parentCanvas" ... MouseLeftButtonDown="canvas_Click"></Canvas>

В классе отделенного кода вам понадобятся две переменных-члена, чтобы отслежи-вать, выполняется ли в данный момент операция перетаскивания эллипса:

// Отслеживает, когда выполняется перемещение эллипса.private bool isDragging = false;// Когда выполняется щелчок на эллипсе, записывает // текущую позицию, где он произошелprivate Point mouseOffset;

Рассмотрим код обработки событий, создающий эллипс при щелчке на Canvas:

private void canvas_Click(object o, MouseEventArgs e){ // Создать эллипс (если только пользователь // в данный момент не перетаскивает другой). if (!isDragging) { // Установить эллипсу диаметр в 50 пикселей и красное заполнение. Ellipse ellipse = new Ellipse(); ellipse.Fill = new SolidColorBrush(Colors.Red); ellipse.Width = 50; ellipse.Height = 50;

Page 23: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 23

// Использовать текущую позицию мыши в качестве центра эллипса. Point point = e.GetPosition(this); ellipse.SetValue<double>(Canvas.TopProperty, point.Y - ellipse.Height/2); ellipse.SetValue<double>(Canvas.LeftProperty, point.X - ellipse.Width/2); // Отслеживать щелчки левой кнопкой. ellipse.MouseLeftButtonDown += ellipse_MouseDown; // Добавить эллипс в Canvas. this.Children.Add(ellipse); }}

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

private void ellipse_MouseDown(object o, MouseEventArgs e){ // Начало режима перетаскивания. isDragging = true; Ellipse ellipse = (Ellipse)o;

// Получить позицию щелчка относительно левого верхнего угла эллипса (0,0). mouseOffset = e.GetPosition(ellipse);

// Изменить цвет эллипса. ellipse.Fill = new SolidColorBrush(Colors.Green);

// Наблюдать за событиями мыши в эллипсе. ellipse.MouseMove += ellipse_MouseMove; ellipse.MouseLeftButtonUp += ellipse_MouseUp;

// Захватить мышь. Таким образом, вы будете продолжать // получать событий MouseMOve, даже если пользователь // проведет курсором мыши за пределами эллипса. ellipse.CaptureMouse();}

В действительности эллипс не перемещается до тех пор, пока не произойдет событие MouseMove. В этой точке прикрепленные свойства Canvas.Left и Canvas.Top устанав-ливаются в эллипсе для перемещения его в новую позицию. Координаты устанавлива-ются на основе текущей позиции мыши, принимая во внимание точку, где пользователь сначала выполнил щелчок. Затем этот эллипс гладко перемещается мышью — до тех пор, пока не будет отпущена ее левая кнопка.

private void ellipse_MouseMove(object o, MouseEventArgs e){ if (isDragging) { Ellipse ellipse = (Ellipse)o; // Получить позицию эллипса относительно Canvas. Point point = e.GetPosition(this); // Переместить эллипс. ellipse.SetValue<double>(Canvas.TopProperty, point.Y - mouseOffset.Y); ellipse.SetValue<double>(Canvas.LeftProperty, point.X - mouseOffset.X); }}

Page 24: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны24

Когда левая кнопка отпускается, код изменяет цвет эллипса, освобождает за-хват (capture) мыши и прекращает прослушивание событий MouseMove и MouseUp. Пользователь снова может щелкнуть на эллипсе, чтобы начать весь процесс заново.

private void ellipse_MouseUp(object o, EventArgs e){ if (isDragging) { Ellipse ellipse = (Ellipse)o; // Изменить цвет эллипса. ellipse.Fill = new SolidColorBrush(Colors.Orange); // Больше не отслеживать события мыши. ellipse.MouseMove += ellipse_MouseMove; ellipse.MouseLeftButtonUp += ellipse_MouseUp; ellipse.ReleaseMouseCapture(); isDragging = false; }}

ТекстКак вам уже известно, для добавления текста в пользовательский интерфейс

Silverlight используется элемент TextBlock. Элемент TextBlock включает несколько ключевых свойств, которые описаны в табл. 33.2.

Таблица 33.2. Свойства TextBlock

Свойство Описание

FontFamily Указывает имя шрифта, который вы хотите использовать, такое как Times New Roman или Arial. Это имя не должно включать инфор-мации форматирования — другими словами, вы не должны применять значения вроде Times New Roman Bold. Значение по умолчанию — Portable User Interface, что является псевдонимом для шрифта Lucida Sans, включенного в Silverlight.

FontSize Указывает размер шрифта в пикселях. Значение по умолчанию — 14.666, что точно соответствует 11 точкам.

FontStyle Указывает признак курсива (Normal или Italic). По умолчанию приня-то Normal.

FontWeight Определяет относительный вес шрифта. Значение по умолчанию — Normal. Перечисление FontWeight определяет все поддерживаемые значения, такие как Thin, Light, Medium, Bold, ExtraBold и т.д.

FontStretch Описывает степень растяжения шрифта по горизонтали относитель-но его нормальных пропорций. Значение по умолчанию — Normal. Перечисление FontStretch определяет все поддерживаемые значения, такие как UltraCondensed, Condensed, Medium, Expanded и т.д.

TextDecorations Позволяет указывать дополнительные графические детали шрифта, ис-пользуя значение из перечисления TextDecorations. В настоящее время есть только два варианта: Normal (по умолчанию) и Underline.

TextWrapping Позволяет переносить текст на множество строк. Вы можете выбрать NoWrap (по умолчанию), Wrap или WrapWithOverflow. Как Wrap, так и WrapWithOverflow разрешают перенос текста. Отличие между ними в том, что WrapWithOverflow позволяет продолжать одно длин-ное слово за пределы элемента, если не остается места, в то время как Wrap просто разбивает слово на части.

Page 25: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 25

На заметку! Обычно вам не придется устанавливать Width и Height для элемента TextBlock. Этот элемент автоматически устанавливает свою высоту такой, чтобы можно было отобразить одну строку текста в текущем шрифте, а ширину — чтобы уместился указанный текст (даже если при этом TextBlock выйдет за пределы области содержимого Silverlight и станет там не-видимым). Однако если вы хотите использовать перенос текста, то должны установить ширину TextBlock, чтобы ограничить длину строки. Это заставит содержимое текста переходить на последующие строки и увеличит высоту TextBlock, чтобы вместить текст.

Хотя вы можете установить любой шрифт, который хотите, применяя свойство FontFamily, нет никаких гарантий, что нужный вам шрифт доступен на клиентском компьютере. Для получения согласованных результатов вы должны использовать один из основных шрифтов, включенных в Silverlight и представленных на рис. 33.7.

Рис. 33.7. Шрифты Silverlight

Как и при установке шрифтов для элемента управления ASP.NET, вы можете при-менить разделенный запятыми список, представляющий предпочтительные шрифты в начале и менее предпочтительные — в конце.

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

<TextBlock FontFamily="Arial" Width="400" TextWrapping="Wrap">To create the Silverlight control, you usethe <Run Foreground="Maroon" FontFamily="Courier New">createObjectEx()</Run>JavaScript function.</TextBlock>

Наряду с элементом Run вы можете также поместить внутри TextBlock элемент LineBreak, чтобы принудительно разделить строку.

На заметку! Если вы поместите дополнительные пробелы в начало или конец текста внутри <TextBlock>, они будут проигнорированы.

Page 26: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны26

Взаимодействие с HTMLSilverlight включает набор управляемых классов, которые реплицируют HTML DOM

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

Классы, которые понадобятся вам для выполнения этих фокусов, являются частью сборки System.Silverlight.dll и находятся в пространстве имен System.Windows.Browser. Эти классы включают HtmlElement, который представляет любой элемент HTML, HtmlObject, представляющий сценарный объект — часть HTML DOM (например, window или frameset), и HtmlDocument, представляющий весь документ HTML. Чтобы получить доступ к живым экземплярам этих классов, вы используете вспомогательный класс HtmlPage, который предлагает статические члены, перечисленные в табл. 33.3.

Таблица 33.3. Статические члены класса HtmlPage

Член Описание

BrowserInformation Возвращает объект BrowserInformation с информацией о вер-сии браузера, платформе, агентскую строку пользователя и под-держке cookie-наборов.

Cookies Предоставляет коллекцию всех текущих cookie-наборов HTTP. Вы можете читать или устанавливать значения этих cookie-наборов. Они предлагают простой, дешевый способ передачи информации от кода ASP.NET серверной стороны коду клиентской стороны Silverlight.

CurrentBookmark Возвращает необязательную часть строки URL из закладок, кото-рая может указывать на определенный якорь (anchor) на странице. Можно использовать NavigateToBookmark() для перехода к дру-гой закладке.

Document Возвращает объект HtmlDocument, представляющий текущий доку-мент HTML.

DocumentUri Возвращает URL текущего документа как объект Uri.

QueryString Возвращает часть URL, представляющую строку запроса как одну длинную строку, которую нужно разбирать.

Window Возвращает HtmlObject, представляющий текущее окно браузера.

Navigate() Перенаправляет браузер на другую страницу. Вы можете исполь-зовать перегруженную версию метода Navigate() для указания целевого фрейма.

NavigateToBookmark() Прокручивает к определенной закладке на текущей странице.

Submit() Отправляет страницу. Удобно, если вы разместили свой элемент управления Silverlight на странице ASP.NET, потому что инициирует обратную отправку, которая запускает код серверной стороны.

Если вы хотите взаимодействовать с содержимым HTML текущей страни-цы, то свойство HtmlPage.Document — наилучшая начальная точка. Имея объект HtmlDocument, представляющий страницу, вы можете спуститься вниз по дереву эле-ментов (начиная с HtmlDocument.DocumentElement) или искать элемент с определен-ным именем (используя метод GetElementByID() или GetElementsByTagName() клас-са HtmlDocument). Когда у вас есть определенный HtmlElement, вы можете передать

Page 27: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 27

ему фокус, добавлять или удалять дочерние элементы либо модифицировать текстовое содержимое.

Совет. Класс HttpUtility из пространства имен System.Windows.Browser можно исполь-зовать для выполнения распространенных задач вроде кодирования и декодирования HTML (защищая текст для отображения на странице), кодирования и декодирования URL (чтобы обезопасить текст в URL, например, аргумент строки запроса).

Например, предположим, что у вас есть следующий код HTML — прямо под областью содержимого Silverlight (а область Silverlight не полностью заполняет окно браузера), как показано ниже:

<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Silverlight Project Test Page </title> <script type="text/javascript" src="Silverlight.js"></script> <script type="text/javascript" src="TestPage.html.js"></script> <style type="text/css"> .silverlightHost { width: 640px; height: 480px; } </style> </head> <body> <div id="SilverlightControlHost" class="silverlightHost"> <script type="text/javascript"> createSilverlight(); </script> </div>\ <div> <hr /> <p id="paragraph"></p> </div> </body></html>

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

HtmlElement element = HtmlPage.Document.GetElementByID("paragraph");element.SetProperty("innerHTML", "This HTML paragraph has been updated by Silverlight.");

Вы заметите, что переход между Silverlight и HTML DOM нельзя назвать совершен-ным. Silverlight не включает полного HTML DOM, а только легковесную версию, стандар-тизированную в базовом классе HtmlElement. Чтобы манипулировать этим элементом осмысленным образом, часто придется устанавливать свойство HTML DOM (такое как innerHTML в предыдущем примере), используя метод SetProperty() и указывая имя свойства в виде строки.

Можно взаимодействовать и в обратном направлении — другими словами, позво-лить элементу HTML инициировать ваш код Silverlight в ответ на определенное событие. Простейший способ сделать это — использовать метод HtmlElement.AttachEvent() в вашем коде Silverlight, чтобы привязать ваш обработчик событий. Это можно сделать в любой точке, хотя имеет смысл сделать это однажды, при первоначальной инициализа-ции содержимого Silverlight, в ответ на событие Canvas.Loaded.

Приведем пример, в котором обработчик Silverlight подключается к событию onclick HTML-элемента <p>:

Page 28: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны28

public partial class Page : Canvas{ private void Page_Loaded(object o, EventArgs e) { InitializeComponent(); element.AttachEvent("onclick", paragraph_Click); } private void paragraph_Click(object o, HtmlEventArgs e) { txt.Text = "You clicked an HTML element, but this Silverlight application noticed."; }}

Опять-таки, вам нужно знать имя события HTML DOM. Другими словами, вам пона-добятся все знания JavaScript, чтобы перекинуть мост между Silverlight и HTML.

Эта техника позволяет достичь впечатляющих результатов. Используя Silverlight как посредника, вы можете оснастить HTML-страницу кодом C# клиентской стороны вместо применения JavaScript, который бы понадобился в противном случае.

На рис. 33.8 продемонстрирован этот код в действии.

Рис. 33.8. Взаимодействие Silverlight и HTML

В некоторых случаях вы можете предпочесть углубить интеграцию, добавив еще один слой. Вместо подключения обработчика событий Silverlight непосредственно к со-бытию HTML DOM, вы можете пожелать подключить ваше событие к методу JavaScript, а затем заставить этот метод JavaScript вызывать ваш код Silverlight. Это несколько бо-лее сложно. Чтобы заставить работать такой механизм, потребуется предпринять сле-дующие шаги.

1. Создать общедоступный метод в коде Silverlight, который предоставит информа-цию или функциональность, которую вы хотите использовать в Web-странице. При этом вы должны придерживаться простых типов данных, вроде строк, бу-левских значений и чисел, если только не хотите связываться с дополнительной работой по сериализации ваших объектов в более простую форму.

Page 29: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 29

2. Пометить метод атрибутом Scriptable.

3. Пометить ваш класс Silverlight (пользовательский класс, производный от Canvas) атрибутом Scriptable.

4. Чтобы предоставить ваш метод Silverlight коду JavaScript, вызвать метод WebApplication.RegisterScriptableObject() в обработчике событий Page_Load() при первоначальной загрузке вашего содержимого Silverlight.

Если вы выполните все эти шаги, то ваш код JavaScript сможет вызывать ваш метод Silverlight, как если бы это был метод элемента управления Silverlight.

Например, рассмотрим класс отделенного кода, представленный ниже, который включает сценарный метод по имени RemoveGradient():

[Scriptable]public partial class Page : Canvas{ public void Page_Loaded(object o, EventArgs e) { // Необходимо для инициализации переменных. InitializeComponent(); WebApplication.Current.RegisterScriptableObject("SilverlightPage", this); } [Scriptable] public void RemoveGradient() { this.Background = new SolidColorBrush(Colors.LightGray); } ...}

Метод RemoveGradient() зарегистрирован с именем Canvas. В результате Silverlight создаст свойство по имени Canvas и предоставит метод RemoveGradient() на этом свойстве. Вы можете использовать любое имя свойства, какое пожелаете, но в данном примере имеет смысл подчеркнуть, что метод прикреплен к Canvas, потому что его вы-зов затрагивает состояние Canvas.

Теперь все, что вам нужно — это функция JavaScript, вызывающая метод RemoveGradient():

<script type="text/javascript"> function removeGradient() { var control = document.getElementById("SilverlightControl"); control.Content.Canvas.RemoveGradient(); }</script>

Вы можете вызывать этот метод JavaScript в любой момент. Ниже приведен пример, где он вызывается при щелчке на параграфе:

<p onclick="removeGradient()">Click here to remove the gradient</p>

Теперь щелчок на параграфе инициирует JavaScript-функцию removeGradient(), которая, в свою очередь, вызовет метод RemoveGradient(), являющийся частью ваше-го класса Silverlight.

Изолированное хранилищеКоду Silverlight не разрешается писать в произвольное место файловой системы (или

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

Page 30: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны30

Silverlight, которые нуждаются в постоянном хранении данных, есть выбор. Они могут использовать изолированное хранилище.

Изолированное хранилище предоставляет виртуальную файловую систему, которая позволяет записывать данные в маленькие, специфичные для пользователя и приложе-ния фрагменты дискового пространства. Действительное их местоположение на жест-ком диске зашифровано (поэтому нет возможности точно узнать, куда перед этим были записаны данные), а общий объем доступного пространства составляет 512 Кбайт.

Типичное местоположение такого хранилища — путь в форме c:\Document and Settings\[ИмяПользователя]\Local Settings\Application Data\Isolated Storage\[Идентификатор_GUID]. Данные в изолированном хранилище одного пользователя скрыты от других пользователей, не обладающих административными правами.

На заметку! Изолированное хранилище — это .NET-эквивалент постоянных cookie-наборов в обычной Web-странице; оно позволяет сохранять небольшие кусочки информации в выделен-ном месте, которое имеет специфические элементы управления для предотвращения атак злоумышленников (таких как выполнение кода, пытающегося переполнить жесткий диск или подменить системный файл).

Применять изолированное хранилище довольно просто, потому что оно предлага-ет ту же потоковую модель, что и обычный файловый доступ. Вы просто используете типы из пространства имен System.IO.IsolatedStorage. Все начинается с вызова метода IsolatedStorageFile.GetUserStoreForApplication() для получения ссылки на изолированное хранилище для текущего пользователя и приложения. (Каждое при-ложение получает отдельное хранилище.) Затем вы можете создать виртуальный файл в этом местоположении, используя IsolatedStorageFileStream. Приведем пример, за-писывающий текущую дату в виртуальный файл по имени date.txt в изолированном хранилище. Чтобы использовать этот код, как он написан, необходимо импортировать пространства имен System.IO и System.IO.IsolatedStorage.

// Запись в изолированное хранилище.try{ IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication(); using (IsolatedStorageFileStream fs = new IsolatedStorageFileStream( "date.txt", FileMode.Create, store)) { StreamWriter w = new StreamWriter(fs); w.Write(DateTime.Now); w.Close(); } txtData.Text = "Data written to date.txt";}catch (Exception err){ txtData.Text = err.Message;}

Извлечь информацию столь же легко. Вы просто должны открыть IsolatedStorageFileStream в режиме для чтения:

// Чтение из изолированного хранилища.try{ IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();

Page 31: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 31

using (IsolatedStorageFileStream fs = new IsolatedStorageFileStream( "date.txt", FileMode.Open, store)) { StreamReader r = new StreamReader(fs); txtData.Text = r.ReadLine(); r.Close(); }}catch (Exception err){ // Исключение возникает при попытке открытия несуществующего файла. txtData.Text = err.Message;}Вы можете испробовать этот код со страницей IsolatedStorageTest.html, вклю-

ченной в примеры для этой главы. В отличие от полного .NET Framework, версия Silverlight класса IsolatedStorageTest

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

Silverlight и ASP.NETПримеры Silverlight, которые вы видели до сих пор, могут быть использованы как

базовый, отдельно стоящий Web-сайт либо в приложении ASP.NET. Если вы хотите при-менять их на Web-сайте ASP.NET, вам просто нужно добавить файлы Silverlight в папку Web-сайта или Web-проекта. При этом вы копируете те же файлы, что и при развертыва-нии приложения Silverlight — все, за исключением файлов исходного кода. (За дополни-тельной информацией о развертывании Silverlight обращайтесь к разделу “Компиляция Silverlight” ранее в этой главе.)

К сожалению, процесс разработки ASP.NET и процесс разработки Silverlight еще не интегрированы в среду Visual Studio. В результате вам придется компилировать про-ект Silverlight отдельно и копировать готовые сборки вручную. (Вы не можете просто добавить ссылку на скомпилированную сборку, потому что Visual Studio поместит ее в папку Bin, где она будет доступна коду серверной стороны ASP.NET, а это явно не то, что нужно. Вместо этого вам следует поместить ее в папку ClientBin, где входная страница HTML ожидает найти ее.)

Данный подход позволяет размещать страницы Silverlight и ASP.NET бок о бок на одном и том же Web-сайте, но они при этом никак не интегрированы между собой. Вы можете переходить от одной страницы к другой (например, использовать ссылку, чтобы переадресовать пользователя с Web-формы ASP.NET на входную страницу Silverlight), но между кодом серверной стороны и кодом клиентской стороны нет никакой интеграции. Во многих ситуациях такой дизайн вполне оправдан, потому что приложение Silverlight представляет собой отдельный “аплет”, доступный на вашем Web-сайте. В других сцена-риях вы можете пожелать разделить часть вашей модели данных или интегрировать обра-ботку серверной стороны с обработкой стороны клиента в рамках одной общей задачи.

ASP.NET FuturesВыпуск ASP.NET Futures включает два Web-элемента управления ASP.NET, которые

генерируют содержимое Silverlight: Xaml и Media (будут описаны в последующих разде-лах). Оба эти элемента помещены в сборку по имени Microsoft.Web.Preview.dll, ко-

Page 32: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны32

торую вы найдете в каталоге с именем вроде c:\Program Files\Microsoft ASP.NET\ASP.NET Futures July 2007\v1.2.61025\3.5.

Чтобы использовать элементы Xaml и Media, вам понадобится ссылка на сборку Microsoft.Web.Preview.dll. Также нужно зарегистрировать префикс дескриптора элемента управления для пространства имен Microsoft.Web.Preview.UI.Controls (где находятся элементы Xaml). Ниже приведена директива Register, которую вы мо-жете добавить к Web-странице (сразу после директивы Page), чтобы использовать зна-комый префикс дескрипторов asp с новыми элементами управления ASP.NET Futures:

<%@ Register Assembly="Microsoft.Web.Preview" Namespace="Microsoft.Web.Preview.UI.Controls" TagPrefix="asp" %>

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

<?xml version="1.0"?><configuration> ... <system.web> <pages> <controls> <add tagPrefix="asp" namespace="Microsoft.Web.Preview.UI.Controls" assembly="Microsoft.Web.Preview" /> ... </controls> </pages> ... </system.web> ...</configuration>

Вместо добавления ссылки на сборку и редактирования файла web.config вручную, вы можете использовать шаблон Web-сайта Visual Studio. Выберите в меню команду File�New�Web Site (Файл�Создать�Web-сайт) и далее выберите ASP.NET Futures Web Site (Web-сайт ASP.NET Futures). Применив такой подход, вы получите много новых ус-тановок в файле web.config, которые добавляются, чтобы включить другие средства ASP.NET Futures, не имеющие отношения к Silverlight. Завершив эти шаги конфигури-рования, вы готовы к тому, чтобы помещать элементы Xaml и Media на Web-страницу.

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

Элемент XamlКак вы узнали ранее, входная страница HTML создает область содержимого

Silverlight, используя для этого указатель места заполнения <div> и небольшой кусочек кода JavaScript. Нет причин, которые не позволили бы вам дублировать тот же подход для размещения области содержимого Silverlight на Web-форме ASP.NET. Однако есть сокращение, которое вы можете использовать. Вместо создания дескриптора <div> и добавления кода JavaScript вручную, вы можете применить элемент управления Xaml.

Элемент Xaml использует, по сути, ту же технику, что и входная страница HTML, которую мы видели ранее. Он генерирует дескриптор <div> и добавляет JavaScript (ис-пользуя экземпляр элемента управления ScriptManager, который был описан в главе 32). Преимущество в том, что вы специфицируете страницу XAML, которую хотите ис-пользовать (и конфигурируете несколько дополнительных деталей), используя свойст-

Page 33: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 33

ва на стороне сервера. Это предоставляет несколько более простую модель для рабо-ты и простой способ варьировать эти детали динамически (например, выбрать другую страницу XAML на основе информации серверной стороны — такой как идентичности пользователя).

Ниже приведена разметка ASP.NET, которую вы должны использовать, чтобы отобра-зить файл XAML по имени Page.xaml.

<form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server" /> <asp:Xaml XamlUrl="~/Page.xaml" runat="server"></asp:Xaml></form>

Вы можете устанавливать множество свойств элемента управления Xaml для кон-фигурирования создания области содержимого Silverlight, в том числе Height, Width, MinimumSilverlightVersion, SilverlightBackColor и EnableHtmlAccess. Вы може-те также прикрепить элемент управления Xaml к двум функциям JavaScript. Установите OnClientXamlError с именем функции JavaScript, которая будет инициирована, если Silverlight XAML не может быть загружен, и установите OnClientXamlLoaded с именем функции JavaScript, которая будет инициирована при успешном создании области со-держимого Silverlight.

Также вам нужно добавить страницу XAML к вашему Web-сайту. К сожалению, теку-щая сборка ASP.NET Future не включает шаблона XAML для содержимого Silverlight 1.1. Вместо этого в ней имеется шаблон XAML для содержимого Silverlight 1.0 вместе с файлом отделенного кода JavaScript. (Этот вариант предусмотрен для совместимости с Silverlight 1.0, где не поддерживается C# клиентской стороны и усеченная исполняю-щая среда CLR.)

Простейший способ использовать содержимое Silverlight 1.1 с элементом управле-ния Xaml заключается в создании страниц XAML в выделенном проекте Silverlight. Вы можете затем скопировать файлы XAML и папку ClientBin на ваш Web-сайт ASP.NET. Эта дополнительная работа не вызвана техническими ограничениями — это просто ог-раничение предварительного выпуска программного обеспечения.

Элемент MediaWeb-элемент управления Media дает вам абстракцию серверной стороны поверх

класса MediaElement из Silverlight. (Как уже объяснялось ранее, MediaElement — это элемент Silverlight, управляющий воспроизведением аудио и видеофайлов.)

Возникает очевидный вопрос: “когда нужно использовать MediaElement, а когда стоит предпочесть Web-элемент управления Media серверной стороны?”. Оба элемента предназначены для одной и той же цели — в конце концов, Web-элемент Media сервер-ной стороны выполняет визуализацию MediaElement, хотя и требует немного больше работы на сервере, чтобы сделать это. Главное преимущество использования Web-эле-мента Media заключается в том, что у вас есть возможность установить некоторые из его свойств посредством кода серверной стороны. Например, вы можете установить URL медиа, основанный на информации из базы данных, и даже извлечь ее через при-вязку данных.

Приведем пример определения элемента Media:

<asp:Media runat="server" ID="Media1" AutoPlay="true" MediaUrl="MyVideoFile.wmv" SilverlightBackColor="blue" MediaSkin="Professional" Height="240" Width="320" />

Это создаст элемент управления Silverlight с медиапроигрывателем внутри, как по-казано на рис. 33.9.

Page 34: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны34

Медиапроигрыватель пытается обратиться к файлу MyViewFile.wmv и немедленно начинает его воспроизведение. Чтобы облегчить процесс воспроизведения видео, медиапроигрыватель загружает и буферизует небольшие кусочки ви-деоданных, которые он воспроизводит. Лучше всего то, что медиапроигрыватель Silverlight ра-ботает независимо от Windows Media Player. Он более легковесный и производительный, а его пользовательский интерфейс (кнопки воспро-изведения, обложки и т.п.) в большей степени настраиваемый. Чтобы получить максимум воз-можностей воспроизведения медиа, вам стоит внимательней присмотреться к свойствам, пред-ставленным элементом Media. В табл. 33.4 пе-речислены некоторые из наиболее интересных.

Таблица 33.4. Свойства Web-элемента управления Media

Свойство Описание

MediaUrl Идентифицирует местоположение медиафайла. Вы можете использовать следующие типы файлов: .wma, .wmv, .mp3 и .asx. Можно указывать относительный путь к файлу на вашем Web-сервере (как в предыдущем примере), или же полный URL, указывающий на другое местоположение.

AutoPlay Указывает, должно ли воспроизведение начаться немедленно при ини-циализации страницы. По умолчанию принято false — это означает, что пользователю нужно использовать элементы управления воспроизведени-ем для его запуска.

AutoScale Устанавливает, должен ли XAML изменяться в размере, чтобы вместить проигрыватель. По умолчанию — true.

LoopCount Устанавливает количество циклов повтора медиафайла. Значение 0 уста-навливает бесконечное повторение.

MediaSkin Устанавливает обложку (skin), определяющую внешний вид медиапроиг-рывателя. Медиапроигрыватель включает несколько встроенных обложек, содержащих разную графику, цвета и эффекты анимации. Каждая обложка определена в ресурсе XAML, встроенном в сборки Silverlight. Обложки оп-ределяются перечислением MediaSkin и включают AudioGray, Basic, Blitz, Classic, Expression, Game, Professional и Simple.

PlaceholderImageUrl Указывает URL для изображения указателя места заполнения, которое бу-дет показано, пока открывается медиафайл. Как только файл открыт, это изображение заменяется первым кадром вашего видео.

Volume Устанавливает громкость, как значение между 0 (тишина) и 1 (максималь-ная громкость).

Muted Определяет, должен ли аудиосигнал быть изначально приглушенным. По умолчанию — false.

StartTime Специфицирует местоположение в медиафайле (как смещение в секун-дах), где должно начаться воспроизведение. По умолчанию воспроизведе-ние начинается с начала файла.

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

Рис. 33.9. Медиапроигрыватель Silverlight

Page 35: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 35

Есть еще несколько средств элемента управления Media, которые не упомя-нуты здесь.

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

Вы можете экспортировать любую из обложек медиапроигрывателя в XAML, на-страивать ее, а затем использовать новую версию на Web-странице.

Вы можете использовать методы JavaScript для управления воспроизведением ме-диа. Это дает вам другую возможность взаимодействия с ASP.NET, поскольку вы можете создавать процедуры ASP.NET, которые взаимодействуют с медиапроигры-вателем. Также вы можете устанавливать разнообразные свойства OnXxx (такие как OnClientMediaEnded и OnClientMediaFailed), чтобы инициировать функцию JavaScript при возникновении определенного события в медиапроигрывателе.

За полной информацией обо всех этих расширенных задачах обращайтесь по адресу http://quickstarts.asp.net/Futures/Silverlight/media.aspx.

Коммуникации между Silverlight и ASP.NETЕсли ваш Web-сайт включает код ASP.NET и содержимое Silverlight, вы можете пере-

давать некоторую информацию между ними. Есть несколько способов обеспечить по-добного рода взаимодействие. Вот два простых подхода.

Когда вы перенаправляете пользователя с Web-страницы ASP.NET на страницу входа Silverlight, вы можете просто применить стартовую информацию в cookie-наборе или строке запроса. Код Silverlight может обратиться к этой информации, используя классы из пространства имен System.Windows.Browser, как было опи-сано ранее в разделе “Взаимодействие с HTML” настоящей главы.

Аналогично ваше приложение Silverlight может использовать строку запроса или установить cookie-набор перед навигацией к Web-странице ASP.NET посредством вызова HtmlPage.Navigate(). Если элемент управления Silverlight находится в Web-форме ASP.NET, он может вызвать HtmlPage.Submit(), чтобы инициировать обратную отправку. Это также описано в разделе “Взаимодействие с HTML”.

Оба приема позволяют отправлять информацию при переключении с клиентской стороны на серверную и наоборот. Например, если ваш элемент управления Silverlight вызывает HtmlPage.Submit(), то вся страница отправляется обратно, приложение Silverlight завершается, и создаются объекты ASP.NET. Если вы хотите, чтобы пользо-ватель выполнил другое действие с вашим приложением Silverlight, вы должны вернуть новую страницу с содержимым Silverlight. Затем элемент Silverlight должен будет быть создан и инициализирован заново.

Другой вариант — позволить длительно выполняющемуся приложению Silverlight инициировать некоторый код серверной стороны, без действительной обратной отправ-ки страницы. Таким образом, приложение продолжит выполняться. Простейший способ сделать это — заставить ваше приложение Silverlight вызвать Web-службу ASP.NET, как вы сделали бы это со страницей ASP.NET AJAX. Модель разработки довольно удобна — когда вы добавляете Web-ссылку на Web-службу в проект Silverlight, то Visual Studio генерирует прокси-класс, необходимый для вызова Web-службы. Вам нужно просто создать экземпляр прокси-класса и вызывать его методы, как это делается в полноцен-ном приложении .NET, обращающемся к Web-службе. (Дополнительную информацию о проектировании Web-служб, генерации прокси-классов в Visual Studio и вызове их из “толстого” клиента вы можете найти в главах 35, 36 и 37.)

Page 36: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны36

На заметку! В текущей сборке Silverlight вы не можете выполнять междоменные вызовы Web-служб. Другими словами, вы можете только обращаться к Web-сайту, где расположена стра-ница Silverlight.

Двухмерное рисованиеКак вы, возможно, заметили, Silverlight включает относительно немного элементов

и ни одного высокоуровневого элемента управления из числа тех, которыми привыкли пользоваться разработчики “толстых” клиентов (вроде кнопок, текстовых полей, окон списков, линеек прокрутки и т.д.). Все эти детали планируются для будущих выпусков, но первые две версии Silverlight концептуально сконцентрированы на двух разных об-ластях — двухмерном рисовании и анимации. В оставшейся части этой главы мы рас-смотрим эти две области средств.

Поддержка двухмерного рисования в Silverlight — это базовый фундамент для многих более сложных средств, таких как рисуемые пользователем элементы управления, инте-рактивная графика и анимация. Даже если вы планируете кодировать на более высоком уровне, имея дело с более развитыми элементами управления Silverlight (когда таковые появятся), вам нужно иметь четкое представление об основах рисования — геометрии, кистях и прозрачности.

Создание графики XAML

Во многих случаях вы не будете создавать картинки Silverlight вручную. Вместо этого вы (или ди-зайнер) используете инструмент проектирования для создания векторных изображений, а затем экспортируете их в XAML. Экспортированный документ XAML, по сути, будет описывать Canvas, содержащий комбинацию элементов Shape. Вы можете поместить этот Canvas внутри сущест-вующего Canvas, чтобы показать ваш рисунок.

Примером инструмента проектирования, поддерживающего XAML, может быть Microsoft Expression Design. Однако для многих других популярных форматов уже есть подключаемые модули и сред-ства конвертирования. Например, вы можете преобразовать документ Adobe Illustrator в XAML, используя конвертер, доступный по адресу http://www.mikeswanson.com/xamlexport.

Silverlight поддерживает неожиданно большое подмножество средств рисования из WPF — его более могучего брата. В этом разделе мы рассмотрим, как можно создавать сложные геометрические конструкции, заполненные дугами и кривыми, как применять градиенты и другие эффекты с кистями, и как использовать частичную прозрачность. Но каждый рисунок Silverlight начинается с относительно простых ингредиентов — фи-гур (shapes).

Простые фигурыSilverlight содержит небольшой набор элементов, представляющих фигуры:

Rectangle, Ellipse, Line, Polyline, Polygon и Path. Вы уже встречались с Rectangle и Ellipse, но не рассматривали их в деталях. Например, мы говорили о свойстве Fill, служащем для рисования внутри прямоугольников и эллипсов, не рассматривая свой-ства Stroke, которое позволяет вам очерчивать их границы.

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

Page 37: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 37

Таблица 33.5. Свойства фигур

Наименование Описание

Fill Устанавливает объект-кисть, которая рисует поверхность фигуры (все, что находится внутри его границ).

Stroke Устанавливает объект-кисть для рисования границ фигуры.

StrokeThikness Устанавливает толщину границы в пикселях. При рисовании линии Silverlight разделяет ширину пополам на каждую сторону. Поэтому линия шириной в 10 единиц получает по 5 единиц пространства с каж-дой стороны от того места, где прошла бы линия толщиной в единицу.

StrokeStartLineCap и StrokeEndLineCap

Определяют контур границы начала и конца линии. Эти свойства имеют эффект только для фигур Line и Polyline и (иногда) для Path. Все прочие фигуры замкнуты, а потому не имеют начальных и конечных точек.

StrokeDashArray, StrokeDashOffset и StrokeDashCap

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

StrokeLineJoin и StrokeMiterLimit

Определяют контур углов фигуры. Технически эти свойства влия-ют на вершины, где встречаются разные линии, такие как углы Rectangle. Эти свойства не оказывают влияния на фигуры без углов, подобные Line или Ellipse.

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

Rectangle и EllipseRectangle и Ellipse — две простейших фигуры. Чтобы создать любую из них, ус-

тановите знакомые свойства Height и Width (унаследованные от FrameworkElement), задав размер вашей фигуры, а затем установите свойства Fill и Stroke (или оба сра-зу), сделав фигуру видимой.

Класс Ellipse не добавляет никаких свойств к тем, что унаследованы от FrameworkElement. Класс Rectangle добавляет два свойства: RadiusX и RadiusY. Когда они установлены в ненулевые значения, эти свойства позволяют создавать симпатично скругленные углы.

Вы можете воспринимать RadiusX и RadiusY как описание эллипса, используемого для заполнения углов прямоугольника. Например, если вы установите значения обеих свойств в 10, то Silverlight нарисует углы прямоугольника как дуги окружности радиу-сом в 10 пикселей. Увеличивая этот радиус, все большая часть вашего прямоугольника будет скруглена. Если вы увеличите RadiusY по отношению к RadiusX, ваши углы бу-дут больше закруглены по левой и правой сторонам и менее — по верхней и нижней. Если вы увеличите свойство RadiusX до размера ширины вашего прямоугольника, а свойство RadiusY — до его высоты, то в результате вы превратите ваш прямоугольник в обычный эллипс.

На рис. 33.10 показано несколько прямоугольников с закругленными углами.

Page 38: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны38

Рис. 33.10. Скругленные углы

LineФигура Line представляет прямую линию, соединяющую между собой две точки.

Начальная и конечная точки устанавливаются четырьмя свойствами: X1 и Y1 (для пер-вой точки) и X2 и Y2 (для второй). Например, следующая линия простирается от (0,0) до (10.100):

<Line Stroke="Blue" X1="0" Y1="0" X2="10" Y2="100"></Line>

Свойство Fill не оказывает никакого эффекта на линию. Вы должны установить только свойство Stroke.

Используемые в линии координаты отсчитываются относительно левого верхне-го угла ее расположения. Когда вы помещаете линию в определенное место в Canvas, то начальная точка линии представляет собой смещение по координатам Canvas. Рассмотрим следующую линию:

<Line Stroke="Blue" X1="0" Y1="0" X2="10" Y2="100" Canvas.Left="5" Canvas.Top="100"></Line>

Она простирается от (0,0) до (10,100), используя координатную систему, которая трактует точку (5,100) на Canvas как (0,0). Это эквивалентно следующей линии, которая не использует свойств Top и Left:

<Line Stroke="Blue" X1="5" Y1="100" X2="15" Y2="200"></Line>

Использовать ли свойства позиции при помещении Line на Canvas — выбор за вами. Часто вы можете упростить рисование линии, выбрав правильную начальную точку.

PolylineКласс Polyline позволяет рисовать последовательность соединенных между собой

отрезков прямых линий. Вы просто указываете список координат X и Y, используя свой-ство Points. Технически свойство Points требует объекта PointCollection, но вы на-полняете эту коллекцию в XAML, используя простой строковый синтаксис. Вам нужно просто указать список точек, добавляя пробел или запятую между координатами.

Polyline может иметь всего две точки. Например, следующий элемент Polyline дублирует линию из предыдущего примера, простираясь от (5,100) до (15.200):

<Polyline Stroke="Blue" Points="5,100 15,200"></Polyline>

Page 39: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 39

А вот более сложный Polyline, начинающийся с (10,150). Точки следуют слева на-право, колеблясь между большими и меньшими значениями Y:

<Polyline Stroke="Blue" StrokeThickness="5" Points="10,150 30,140 50,160 70,130 90,170 110,120 130,180 150,110 170,190 190,100 210,240" ></Polyline>

На рис. 33.11 можно видеть полученную в результате линию.

Рис. 33.11. Линия из нескольких сегментов

PolygonPolygon — это почти то же самое, что Polyline. Подобно классу Polyline, класс

Polygon имеет коллекцию Points, принимающую список координат. Единственное от-личие в том, что Polygon добавляет финальный сегмент, соединяющий конечную точку с начальной. (Если ваша финальная точка уже совпадает с начальной, то отличия от класса Polygon нет.) Вы можете заполнить внутренность этой фигуры, используя кисть Fill.

В простой фигуре, где линии никогда не пересекаются, заполнить внутренность лег-ко. Однако можно создать более сложный Polygon, где не сразу очевидно, какая часть находится “внутри” фигуры (и должна быть заполнена), а какая — снаружи.

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

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

По умолчанию FillRule установлено в EvenOdd. Чтобы решить, нужно ли заполнять область, Silverlight подсчитывает количество линий, которые должны быть пересечены для выхода за пределы фигуры. Если это число нечетное, область заполняется; если же четное, то нет. В центральной области на рис. 33.12 вы должны пересечь две линии, чтобы выйти за пределы фигуры, поэтому она не заполняется.

Page 40: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны40

Silverlight также включает правило заполнения Nonzero, которое немного хитрее. По сути, с Nonzero Silverlight следует тому же процессу подсчета линий, что и EvenOdd, но при этом принимает во внимание направление каждой линии. Если количество линий, идущих в одном направлении (скажем, слева направо), равно количеству линий, идущих в противоположном направлении (справа налево), то область не заполняется. Если раз-ница между этими двумя счетчиками не равна нулю, область заполняется. В фигуре из предыдущего примера внутренняя область заполняется, если вы установите FillRule в Nonzero. На рис. 33.13 объясняется почему. (В этом примере точки нумеруются в порядке их рисования, а стрелки показывают направление, в котором рисуется каждая линия.)

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

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

Вот как выглядит разметка, рисующая звезду, показанную на рис. 33.13:

<Polygon Stroke="Blue" StrokeThickness="1" Fill="Yellow" Canvas.Left="10" Canvas.Top="175" FillRule="Nonzero" Points="15,200 68,70 110,200 0,125 134,125"></Polygon>

Path и GeometryPath (путь) — фигура, отличающаяся от других. В отличие от простейших фигур,

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

Пересечь две линии(четное число)

Область не заполняется

Пересечь одну линию(нечетное число)

Область заполняется

Рис. 33.12. Определение заполняемых областей при значении свойства FillRule, равном EventOdd

Пересечение двух линий, идущих слева направо.

Разница счетчиков не равна нулю.

Область заполняется.

Рис. 33.13. Определение заполняемых областей при значении свойства FillRule, равном Nonzero

Page 41: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 41

Класс Path включает единственное свойство по имени Data, принимающее объект Geometry, который определяет фигуру (или фигуры), включенную в данный путь. Вы не можете создать объект Geometry непосредственно, потому что это — абстрактный класс. Вместо этого вы должны использовать один из его классов-наследников, пере-численных в табл. 33.6.

Таблица 33.6. Классы-наследники Geometry

Наименование Описание

LineGeometry Представляет прямую линию — геометрический эквивалент фигуры Line.

RectangleGeometry Представляет прямоугольник (необязательно со скругленными углами) — эквивалент фигуры Rectangle.

EllipseGeometry Представляет эллипс — геометрический эквивалент фигуры Ellipse.

GeometryGroup Добавляет любое количество объектов Geometry к одному Path, используя правило EvenOdd или Nonzero для определения заполняемой области.

PathGeometry Представляет более сложную фигуру, состоящую из дуг, кривых и линий. Может быть открытой или замкнутой.

Здесь вы можете удивиться: какая разница между путем и геометрией (Path и Geometry)? Геометрия определяет фигуру. Путь позволяет нарисовать фигуру. Таким образом, объект Geometry определяет такие детали, как координаты и размер вашей фигуры, в то время как объект Path применяет кисти Stroke и Fill, которые вы ис-пользуете для рисования. Класс Path также включает такие средства элементов управ-ления, как обработка мыши и клавиатуры.

В следующем разделе мы рассмотрим классы, унаследованные от Geometry.

На заметку! Геометрии предназначены не только для использования с Path. Вы можете также применять их с отсечением (clipping), определяя область определенной формы, ограничиваю-щую элемент. Чтобы использовать отсечение, просто установите свойство Clip элемента. Например, если вы установите свойство Clip элемента Canvas в эллипс, то все содержимое, которое обычно должно быть нарисовано вне эллипса, отображаться не будет.

LineGeometry, RectangleGeometry и EllipseGeometryКлассы LineGeometry, RectangleGeometry и EllipseGeometry отображаются непо-

средственно на фигуры Line, Rectangle и Ellipse. Например, вы можете преобразо-вать эту разметку, использующую элемент Rectangle:

<Rectangle Fill="Yellow" Stroke="Blue" Width="100" Height="50"></Rectangle>

в следующую разметку, применяющую элемент Path:

<Path Fill="Yellow" Stroke="Blue"> <Path.Data> <RectangleGeometry Rect="0,0 100,50"></RectangleGeometry> </Path.Data></Path>

Единственное реальное отличие в том, что фигура Rectangle имеет значения Width и Height, в то время как RectangleGeometry принимает четыре числа, описывающие размер и местоположение прямоугольника. Первые два числа описывают координаты X и Y точки, куда будет помещен левый верхний угол, а последние два числа описывают

Page 42: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны42

ширину и высоту прямоугольника. Вы можете начать прямоугольник в точке (0,0), что-бы получить тот же эффект, что и с обычным элементом Rectangle, или же сместить прямоугольник, указав другие значения. Класс RectangleGeometry также включает свойства RadiusX и RadiusY, позволяющие скруглить углы (как было описано ранее).

Аналогично вы можете преобразовать следующий элемент Line:

<Line Stroke="Blue" X1="0" Y1="0" X2="10" Y2="100"></Line>

в приведенный ниже LineGeometry:

<Path Fill="Yellow" Stroke="Blue"> <Path.Data> <LineGeometry StartPoint="0,0" EndPoint="10,100"></LineGeometry> </Path.Data></Path>

Также можно преобразовать такой Ellipse:

<Ellipse Fill="Yellow" Stroke="Blue" Width="100" Height="50"></Ellipse>

в следующий EllipseGeometry:

<Path Fill="Yellow" Stroke="Blue"> <Path.Data> <EllipseGeometry RadiusX="50" RadiusY="25" Center="50,25"></EllipseGeometry> </Path.Data></Path>

Обратите внимание, что два значения радиуса составляют просто половину от значе-ний ширины и высоты. Вы можете также использовать свойство Center, чтобы смещать прямоугольники и эллипсы, но это не обязательно, если вы помещаете фигуры на Canvas, который уже предоставляет вам возможность позиционировать их в определенных мес-тах. Фактически, если бы это все можно было делать с геометриями, то вам не пришлось бы возиться с элементом Path. Разница проявляется, когда вы решаете комбинировать более одной геометрии в одном и том же пути, как описано в следующем разделе.

Комбинирование фигур с помощью GeometryGroupПростейший способ комбинировать геометрии заключается в применении клас-

са GeometryGroup и вставке внутрь него других объектов — наследников Geometry. Рассмотрим пример размещения эллипса рядом с квадратом:

<Path Fill="Yellow" Stroke="Blue" Canvas.Top="10" Canvas.Left="10" > <Path.Data> <GeometryGroup> <RectangleGeometry Rect="0,0 100,100" /> <EllipseGeometry Center="150,50" RadiusX="35" RadiusY="25"/> </GeometryGroup> </Path.Data></Path>

Эффект от этой разметки будет таким же, как если бы вы применили два элемента Pasth — один с RectangleGeometry, а другой — с EllipseGeometry (и это то же самое, как если бы вы использовали вместо этого фигуры Rectangle и Ellipse). Однако у это-го подхода есть одно преимущество. Два элемента заменяются одним, что уменьшает накладные расходы вашего пользовательского интерфейса.

Но в комбинировании геометрий в одном элементе Path есть и недостаток, а имен-но: вы не сможете осуществлять обработку событий в разных фигурах по отдельности. Вместо этого элемент Path инициирует все события мыши. Однако, тем не менее, вы мо-жете манипулировать вложенными объектами RectangleGeometry и EllipseGeometry

Page 43: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 43

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

GeometryGroup становится более интересной, когда ваши фигуры пересекаются. Вместо того чтобы просто трактовать ваше рисование как комбинацию сплошных фи-гур, GeometryGroup использует свойство FillRule (которое может принимать значения EvenOdd или Nonzero, как было описано ранее), чтобы решать, какую фигуру нужно заполнять. Рассмотрим, что произойдет, если изменить разметку, показанную ранее, поместив эллипс над квадратом:

<Path Fill="Yellow" Stroke="Blue" Canvas.Top="10" Canvas.Left="10" > <Path.Data> <GeometryGroup> <RectangleGeometry Rect="0,0 100,100"/> <EllipseGeometry Center="50,50" RadiusX="35" RadiusY="25"/> </GeometryGroup> </Path.Data></Path>

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

Рисование кривых и прямых линий с помощью PathGeometryPathGeometry — наиболее значимый в мире геометрий. Этот класс может рисовать

все, что и другие геометрии, плюс многое другое. Единственный недостаток — более длинный (и несколько более сложный синтаксис).

Каждый объект PathGeometry построен из одного или более объектов PathFigure (которые сохраняются в коллекции PathGeometry.Figures). Каждый объект PathFigure — это непрерывный набор соединенных вместе отрезков прямых и кривых линий, которые могут быть замкнуты или открыты. Фигура считается закрытой, если конец последней линии в фигуре соединяется с началом первой линии.

Класс PathFigure имеет только четыре ключевых свойства, описанные в табл. 33.7.

Таблица 33.7. Свойства PathFigure

Наименование Описание

StartPoint Это точка (объект Point), которая указывает начало линии, составляющей фигуру.

Segments Это коллекция объектов PathSegment, используемых для рисования фигуры.

IsClosed Если равно true, то Silverlight добавляет прямую линию для соедине-ния начальной и конечной точек (если они не совпадают).

IsFilled Если равно true, то область внутри фигуры заполняется посредством кисти Path.Fill.

До сих пор все звучало достаточно просто. PathFigure — это фигура, кото-рая рисуется непрерывной линией, состоящей из множества сегментов. Однако трюк состоит в том, что есть несколько типов сегментов, и все они наследуются от класса PathSegment. Некоторые просты как LineSegment, рисующий прямую линию. Другие, вроде BezierSegment, рисуют кривые и, соответственно, более сложны. Вообще при ис-пользовании PathGeometry ваш код разметки принимает следующую форму:

Page 44: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны44

<Path Stroke="Blue"> <Path.Data> <PathGeometry> <PathFigure> <segment/> <segment/> ... </PathFigure> </PathGeometry> </Path.Data></Path>

Вы можете смешивать и составлять разные сегменты совершенно свободно, чтобы построить вашу фигуру. В табл. 33.8 перечислены классы сегментов, которые вы може-те использовать.

Таблица 33.8. Классы PathSegment

Наименование Описание

LineSegment Создает прямую линию между двумя точками.

ArcSegment Создает дугу эллипса между двумя точками.

BezierSegment Создает кривую Безье между двумя точками.

QuadraticBezierSegment Создает упрощенную форму кривой Безье, имеющую одну контрольную точку вместо двух, которая вычисляется быстрее.

PolyLineSegment Создает серии прямых линий. Вы можете получить тот же эф-фект, используя множество объектов LineSegment, но един-ственный PolyLineSegment удобнее.

PolyBezierSegment Создает серию кривых Безье.

PolyQuadraticBezierSegment Создает серию упрощенных квадратичных кривых Безье.

В последующих разделах вы увидите несколько примеров объектов Path, ис-пользующих эти сегменты для создания базовых фигур.

Прямые линии

Создавать простые линии с применением классов LineSegment и PathGeometry дос-таточно легко. Вы лишь устанавливаете StartPoint и добавляете LineSegment для ка-ждого сегмента линии. Свойство LineSegment.Point идентифицирует конечную точку каждого сегмента.

Например, следующая разметка начинается в точке (10, 100), рисует прямую ли-нию до (100,100), а затем рисует линию из этой точки до (100, 50). Поскольку свойство PathFigure.IsClosed установлено в true, конечный сегмент линии добавляет соеди-нение (100, 50) с (0, 0). В результате получаем прямоугольный треугольник:

<Path Stroke="Blue"> <Path.Data> <PathGeometry> <PathFigure IsClosed="True" StartPoint="10,100"> <LineSegment Point="100,100" /> <LineSegment Point="100,50" /> </PathFigure> </PathGeometry> </Path.Data></Path>

Page 45: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 45

Дуги

Дуги немного интереснее, чем прямые линии. Вы идентифицируете конечную точ-ку дуги, используя свойство ArcSegment.Point — как делали бы это с LineSegment. Однако PathFigure рисует кривую линию из начальной точки (или конечной точки предыдущего сегмента) в конечную точку вашей дуги. Эта кривая замыкающая линия в действительности представляет собой фрагмент эллипса.

Очевидно, что координаты конечной точки — недостаточная информация для ри-сования дуги, потому что существует множество кривых (и гладких, и более крутых), которые могут соединять две точки. Вы также должны указать размер воображаемо-го эллипса, который используется для рисования дуги. Это делается через свойство ArcSegment.Size, которое принимает радиусы X и Y эллипса. Чем больше размер во-ображаемого эллипса, тем более плавные описывающие его кривые.

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

Ниже приведен пример создания плавной дуги, показанной на рис. 33.14.

<Path Stroke="Blue" StrokeThickness="3"> <Path.Data> <PathGeometry> <PathFigure IsClosed="False" StartPoint="10,100" > <ArcSegment Point="250,150" Size="200,300" /> </PathFigure> </PathGeometry> </Path.Data></Path>

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

Чтобы понять, в чем проблема, нужно рассмотреть другие способы соединения тех же точек дугой. Если вы отметите две точки на эллипсе, то ясно, что соединить их можно двумя способами — по короткой дуге эллипса или же по длинной. На рис. 33.15 иллюст-рируется ситуация. Направление устанавливается посредством свойства ArcSegment.IsLargeArc, которое может быть true или false. По умолчанию принято false, что означает получение кратчайшей из двух дуг.

Даже когда вы установили направление, все равно остается некоторая неоднознач-ность, а именно: где расположен эллипс. Например, предположим, что вы рисуете дугу, соединяющую точку слева с точкой справа, используя кратчайшую из возможных дугу. Кривая, соединяющая эти две точки, может спускаться вниз, затем подниматься вверх (как это происходит на рис. 33.14), или же может сначала подниматься вверх, потом спускать-ся вниз. Дуга, которую вы получите, зависит от порядка определения двух точек и свойст-ва ArcSegment.SweepDirection, которое может принимать значения Counterclockwise (по умолчанию) или Clockwise. На рис. 33.16 можно видеть разницу.

Рис. 33.14. Простая дуга

Page 46: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны46

Кривые Безье

Кривые Безье соединяют два сегмента линий, исполь-зуя сложную математическую формулу, которая включа-ет две контрольных точки, определяющие форму кривой. Кривые Безье — компонент почти любого когда-либо соз-данного приложения векторной графики, поскольку они замечательно гибки. Не используя ничего, кроме началь-ной точки, конечной точки и двух контрольных точек, вы можете создать неожиданно широкое разнообразие глад-ких кривых (включая петли). На рис. 33.17 показана клас-сическая кривая Безье. Два маленьких кружка обозначают контрольные точки, а пунктирная линия соединяет каж-дую контрольную точку с концом линии, на который она влияет сильнее.

Даже без понимания основ математики достаточно лег-ко прочувствовать, как работают кривые Безье. По сути, всю магию осуществляют две контрольные точки. Они влияют на кривую двумя путями.

В начальной точке кривая Безье идет параллельно линии, соединяющей ее с пер-вой контрольной точкой. В конечной точке кривая идет параллельно линии, со-единяющей ее с конечной точкой. (Между ними она кривая.)

Степень кривизны определяется расстоянием к двум контрольным точкам. Если одна контрольная точка достаточно далека, она сильнее “тянет” кривую на себя.

Чтобы определить кривую Безье в коде разметки, вы указываете три точки. Первые две (BezierSegment.Point1 и BezierSegment.Point2) — это контрольные точки. Третья точка (BezierSegment.Point3) — это конечная точка кривой. Как всегда, на-чальной точкой является точка, где заканчивается предыдущий сегмент.

Пример, показанный на рис. 33.17, включает три отдельных компонента, каждый из которых использует свой тип штриха и потому требует отдельного элемента Path. Первый создает кривую, второй добавляет пунктирные линии, а третий применяет кру-ги, указывающие на контрольные точки. Вот полный код разметки:

Конечнаяточка

Большая дуга

Малая дуга

Начальнаяточка

Рис. 33.15. Два способа проведения кривой по эллипсу

Конечнаяточка

Начальнаяточка

Рис. 33.16. Два возможных изгиба дуги

Рис. 33.17. Кривая Безье

Page 47: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 47

<Canvas ...> <Path Stroke="Blue" StrokeThickness="5" Canvas.Top="20"> <Path.Data> <PathGeometry> <PathFigure StartPoint="10,10"> <BezierSegment Point1="130,30" Point2="40,140" Point3="150,150"></BezierSegment> </PathFigure> </PathGeometry> </Path.Data> </Path> <Path Stroke="Green" StrokeThickness="2" StrokeDashArray="5 2" Canvas.Top="20"> <Path.Data> <GeometryGroup> <LineGeometry StartPoint="10,10" EndPoint="130,30"></LineGeometry> <LineGeometry StartPoint="40,140" EndPoint="150,150"></LineGeometry> </GeometryGroup> </Path.Data> </Path> <Path Fill="Red" Stroke="Red" StrokeThickness="8" Canvas.Top="20"> <Path.Data> <GeometryGroup> <EllipseGeometry Center="130,30"></EllipseGeometry> <EllipseGeometry Center="40,140"></EllipseGeometry> </GeometryGroup> </Path.Data> </Path></Canvas>

Попробовать закодировать кривую Безье — прямой путь ко многим бесплодным ча-сам написания кода методом проб и ошибок. Намного вероятнее, что вы будете рисовать кривые (как многие другие графические элементы) в специальных графических про-граммах, оснащенных средствами экспорта в XAML, или в Microsoft Expression Blend.

КистиМногие элементы Silverlight поддерживают концепцию фона и переднего плана.

Обычно фон — это поверхность элемента (такого как Canvas), а передний план — текст (как в случае TextBox). В Silverlight вы устанавливаете цвета этих двух областей (но не их содержимое), используя свойства Background и Foreground. Классы фигур немного изменяют эту модель — вместо свойств Background и Foreground они предоставляют свойства Fill и Stroke, которые позволяют рисовать внутреннюю область фигуры и контур вокруг нее.

Естественно ожидать, что свойства Background, Foreground, Fill и Stroke долж-ны ожидать некоторого рода объект цвета. Однако на самом деле эти свойства ис-пользуют нечто более универсальное: объект Brush (кисть). Это обеспечивает гибкость в заполнении содержимого фона и переднего плана сплошным цветом (используя SolidColorBrush) или чем-либо более экзотичным, например, градиентом. В табл. 33.9 перечислены кисти, используемые в настоящее время Silverlight.

Градиентные кисти

Ранее в этой главе вы уже видели пример применения RadialGradientBrush, где он использовался для создания фона Canvas. И RadialGradientBrush, и LinearGradientBrush работают сходным образом. Они включают в себя коллекцию объектов GradientStop, устанавливающих цвета градиента.

Page 48: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны48

Таблица 33.9. Классы Brush

Наименование Описание

SolidColorBrush Закрашивает область одним цветом.

LinearGradientBrush Выполняет градиентное заполнение области, плавно переходя от одного цвета к другому (и, необязательно, еще к другому, затем еще, и т.д.).

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

ImageBrush Рисует область, используя графический образ, которым может быть растянут, масштабирован или многократно повторен.

VideoBrush Заполняет область видеокадрами. По мере воспроизведения видео кисть изменяется, и отображаемая область обновляется автоматиче-ски. VideoBrush работает со средствами воспроизведения медиа-файлов Silverlight.

Часто градиент состоит из двух цветов (позволяя выполнять плавный линейный пе-реход от одного к другому с помощью LinearGradientBrush либо переход от центра в направлении внешнего радиуса посредством RadialGradientBrush). Однако это не обязательно. Фактически вы можете использовать столько цветов, сколько хотите. Трюк заключается в установке свойства Offset для каждого из них, чтобы поместить его в определенное место вашего градиента. Значение Offset может изменяться от 0 (в на-чале заполнения) до 1 (в конце).

Например, следующий градиент начинается с желтого цвета в левом верхнем углу и проходит через красный, синий и, наконец, зеленый цвет — в правом нижнем углу.

<Rectangle Width="150" Height="100"> <Rectangle.Fill> <LinearGradientBrush> <GradientStop Color="Yellow" Offset="0.0" /> <GradientStop Color="Red" Offset="0.25" /> <GradientStop Color="Blue" Offset="0.75" /> <GradientStop Color="Green" Offset="1.0" /> </LinearGradientBrush> </Rectangle.Fill></Rectangle>

Изменяя значения Offset, можно модифицировать этот градиент таким образом, чтобы его переход к очередному цвету проходил быстрее или медленнее. Например, если вы хотите, чтобы цвета менялись вначале медленнее, а затем быстрее, вы можете установить значения смещений 0, 0.1, 0.3 и 1.

По умолчанию LinearGradientBrush рисует свой градиент по диагонали — от левого верхнего угла заполняемой области к ее правому нижнему углу. Однако вы можете соз-дать градиент, который меняет цвет сверху вниз или слева направо, или же использую-щий другой наклонный угол. Все эти детали управляются через свойства StartPoint и EndPoint класса LinearGradientBrush. Эти свойства позволяют выбрать точку, где начинается изменяться начальный цвет, и точку, где он завершает превращение в ко-нечный. (В промежутке между ними осуществляется равномерный цветовой переход.) Тем не менее, есть один нюанс. Используемые при этом координаты начальной и конеч-ной точек не являются реальными координатами. Вместо этого LinearGradientBrush присваивает координаты (0,0) верхнему левому углу, а (1,1) — правому нижнему углу за-полняемой области, независимо от ее реальной высоты и ширины.

Page 49: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 49

Чтобы создать горизонтальное заполнение сверху вниз, вы можете использовать начальную точку (0,0) для левого верхнего угла и конечную точку (0,1), представляю-щую нижний левый угол. Чтобы создать вертикальное заполнение слева направо (без наклона), вы можете применять начальную точку (0,0) и конечную (1,0) — для нижнего правого угла, как показано ниже:

<Rectangle Width="150" Height="100"> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> ... </LinearGradientBrush> </Rectangle.Fill></Rectangle>

RadialGradientBrush работает подобно LinearGradientBrush. Он также принима-ет последовательность цветов с разными смещениями. Как и с LinearGradientBrush, вы можете использовать столько цветов, сколько хотите. Отличие в том, как располага-ется градиент. Чтобы идентифицировать точку, где начинается первый цвет градиента, вы применяете свойство GradientOrigin. По умолчанию ее координаты выглядят как (0.5, 0.5), что представляет центр заполняемой области. Градиент распространяется от этой точке в радиальном направлении. В конечном итоге градиент достигает границ внутреннего градиентного круга (описанного свойствами Center, RadiusX и RadiusY), где и завершается. Область за пределами внутреннего круга градиента до пределов за-полняемой области заполняется сплошным цветом — последним, который определен в коллекции RadialGradientBrush.GradientStops.

Использование кистей для заполнения текста

Напомним, что применение кистей не ограничено закрашиванием фигур. Вы можете подставить экзо-тическую кисть, такую как LinearGradientBrush, RadialGradientBrush, ImageBrush или VideoBrush, в любое место, где применяете SolidColorBrush. Например, на рис. 33.18 показан пример элемента TextBlock, у которого свойство Foreground установ-лено на использование той же многоцветной кисти LinearGradientBrush, что мы применяли к элементу Rectangle в предыдущем разделе.

ПрозрачностьВ рассмотренных до сих пор примерах все фигуры

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

Существует несколько способов сделать элемент частично прозрачным.Установить свойство Opacity. Свойство Opacity принимает дробное значение от 0 до 1, где 1 означает полную непрозрачность (по умолчанию), а 0 — полную прозрачность. Свойство Opacity определено в классе UIElement (и базовом клас-се Brush), поэтому применимо ко всем элементам.

Рис. 33.18. Использование LinearGradientBrush для установки свойства TextBlock.Foreground

Page 50: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны50

Использовать полупрозрачный цвет. Любой цвет, имеющий значение альфа-ка-нала менее 255, является полупрозрачным.

Установить свойство OpacityMask. Это позволит вам делать определенные об-ласти элемента полностью или частично прозрачными. Например, вы можете ис-пользовать его для постепенного исчезновения фигуры до полной прозрачности.

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

На заметку! Silverlight поддерживает цветовой стандарт ARGB, который использует четыре значе-ния для описания каждого цвета. Эти четыре значения (каждое из которых меняется от 0 до 255) записывают компоненты альфа-канала и красной, зеленой и синей составляющих. Компонент альфа-канала определяет меру прозрачности цвета: 0 означает полную прозрачность, а 255 — полную непрозрачность. Это единственная деталь, которая имеет значение, когда вы исполь-зуете цвет (или картинку, содержащую много цветов) со свойством OpacityMask.

Кстати, Silverlight также поддерживает более сложный стандарт, именуемый scRGB, который представляет четыре цветовых компонента значениями с плавающей точкой. Стандарт scRGB использует альфа-канал для определения прозрачности — точно так же, как и ARGB.

Например, если вы применяете кисть SolidColorBrush, установленную в прозрач-ный цвет, для вашей OpacityMask, то весь элемент исчезнет. Если же вы используете SolidColorBrush с непрозрачным цветом, то ваш элемент останется полностью види-мым. Прочие детали цвета (красная, зеленая и синяя составляющие) не важны и игно-рируются при установке свойства OpacityMask.

Использование OpacityMask с SolidColorBrush не имеет особого смысла, посколь-ку вы можете достичь того же эффекта проще с помощью свойства Opacity. Однако OpacityMask становится намного удобнее, когда применяются более экзотические типы кистей — вроде LinearGradientBrush или RadialGradientBrush. Используя градиент, выполняющий переход от сплошного к прозрачному цвету, вы можете создавать эффект затухающей прозрачности по всей поверхности вашего элемента.

Например, следующий код создает два элемента на одном и том же месте (их верх-ние левые углы находятся в точке (0,0) на Canvas). В соответствии с порядком объявле-ния, синий прямоугольник перекрывает изображение. Однако он использует свойство OpacityMask для плавного затухания в прозрачности слева направо.

<Canvas ... > <Image Source="grandpiano.jpg"></Image> <Rectangle Fill="Blue" Width="300" Height="200"> <Rectangle.OpacityMask> <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> <GradientStop Offset="0" Color="Black"></GradientStop> <GradientStop Offset="1" Color="Transparent"></GradientStop> </LinearGradientBrush> </Rectangle.OpacityMask> </Rectangle></Canvas>

На рис. 33.19 можно видеть результат.

Page 51: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 51

Рис. 33.19. Прямоугольник, переходящий от сплошного цвета к прозрачному

Элементы управления Silverlight

Графические элементы Silverlight предназначены не только для статических изображений. Вы можете также применять их для создания элементов управления. Например, если использовать Canvas для размещения Rectangle в фоне и помещения TextBlock поверх, можно создать неожиданно привлекательную кнопку. Добавьте немного логики обработки событий, и вы получи-те динамическую кнопку, которая изменяет затенение при перемещении курсора мыши над ней.

Фактически Silverlight 1.1 SDK включает примеры, использующие этот подход, для создания пяти общих элементов управления: Button, Scrollbar, ScrollViewer (прокручиваемая область, в которую можно поместить содержимое), Slider и ListBox — все с тщательно затенен-ным синим градиентным наполнением. Кстати, эти элементы управления будут интегрированы в основные сборки Silverlight, но при этом могут еще существенно измениться. А пока вы мо-жете включать их в ваши собственные пробные проекты. Просто загрузите Silverlight 1.1 SDK с http://silverlight.net/GetStarted.

Возможно, вас заинтересуют элементы управления от независимых разработчиков. Одним из впечатляющих примеров может служить GOA WinForms, который предлагает элементы Silverlight, дублирующие базовые элементы управления из Windows Forms. (Есть также версия GOA WinForms, которая представляет тот же набор элементов для приложений Flash.) Дополнительную информа-цию вы можете найти по адресу http://community.netikatech.com/demos. Многие неза-висимые разработчики компонентов создают свои собственные комплекты элементов Silverlight (пример — Sapphire от ComponentOne; http://labs.componentone.com/Sapphire), и многие прикладные разработчики публикуют свои собственные эксперименты с открытым кодом (см. некоторые амбициозные примеры по адресу http://tinyurl.com/39oaul, в том числе каркас компоновки, эмулирующий WPF).

АнимацияАнимация, наряду с двухмерным рисованием — еще одна ключевая возможность

Silverlight 1.1. Анимация позволяет вам создавать действительно динамичные пользо-вательские интерфейсы. Часто она применяется для применения эффектов, например, создания пиктограмм, которые увеличиваются, когда вы перемещаете курсор над ними, вращающихся логотипов, прокручивающихся текстов и т.д.

Page 52: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны52

Анимация — интегрированная часть модели Silverlight. Это значит, что вам не при-дется использовать таймеры и код обработки событий, чтобы заставить ее работать. Вместо этого вы описываете ее декларативно, конфигурируете с применением одного из нескольких классов и запускаете в действие, не написав ни строчки кода C#.

Основы анимацииАнимация Silverlight представляет собой сокращенную версию системы анимации

WPF. Чтобы понять анимацию Silverlight, вам следует понять перечисленные ниже клю-чевые правила.

Silverlight выполняет анимацию на основе времени. То есть вы устанавливае-те начальное состояние, финальное состояние и продолжительность анимации. Частоту кадров вычисляет Silverlight.

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

Чтобы анимировать свойство, вам нужен класс анимации, поддерживающий его тип данных. Например, если вы хотите изменить свойство, использующее тип данных double (что бывает наиболее часто), вам следует применять класс DoubleAnimation. Если вы хотите модифицировать цвет, используемый для рисо-вания фона вашего Canvas, вам нужно применить класс ColorAnimation.

Silverlight имеет относительно немного классов анимации, поэтому вы ограничены в типах данных, которые можете использовать. В настоящее время анимацию можно применять для модификации свойств следующих типов: double, Color и Point.

В качестве эмпирического правила: анимация на базе свойств — замечательный способ добавления динамических эффектов к обычному в остальных отношениях при-ложению (таких как блестящие кнопки, картинки, увеличивающиеся в размере при на-ведении на них курсора, и т.д.). Однако если вам нужно использовать анимацию как часть основного назначения вашего приложения, то вам, вероятно, понадобится что-то более гибкое и более мощное. Например, если вы создаете аркадную игру или ис-пользуете сложные физические вычисления для моделирования столкновений, то вам понадобится более высокая степень контроля анимации. К сожалению, в Silverlight не предусмотрено возможности покадровой анимации, так что вам придется создавать приложения подобного рода старомодным способом — запуская бесконечный цикл, модифицируя ваши визуальные элементы и проверяя пользовательский ввод после ка-ждой итерации. Вы можете увидеть пример такой техники в эмуляторе столкновения мячей на http://bubblemark.com.

Определение анимацииСоздание анимации — многошаговый процесс. Вам нужно создать следующие от-

дельные ингредиенты: объект анимации, который будет ее выполнять, панель раскад-ровки (storyboard) для управления анимацией и триггер события для запуска панели раскадровки. В следующих разделах мы рассмотрим все эти шаги.

Класс анимации

В Silverlight предусмотрено два типа классов анимации. Каждый из этих типов ис-пользует свою стратегию для варьирования значений свойства.

Page 53: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 53

Линейная интерполяция. При линейной интерполяции значение свойства изме-няется гладко и непрерывно в течение всего периода длительности анимации. Примерами могут служить DoubleAnimation, PointAnimation и ColorAnimation.

Анимация ключевого кадра. При этой анимации значения могут спонтанно пере-прыгивать от одного к другому либо комбинировать прыжки с периоды линейной ин-терполяции. Примерами такой анимации являются классы ColorAnimationUsingKeyFrames, DoubleAnimationUsingKeyFrames и PointAnimationUsingKeyFrames.

В этой главе мы сосредоточимся на наиболее широко используемом классе анима-ции — DoubleAnimation. Он применяет линейную интерполяцию для изменения зна-чения типа double от начального до конечного.

Анимации определяются с использованием разметки XAML. Хотя классы анима-ции не являются элементами, тем не менее, они могут быть созданы посредством не-которого синтаксиса XAML. Например, приведем разметку, необходимую для создания DoubleAnimation:

<DoubleAnimation From="160" To="300" Duration="0:0:5"></DoubleAnimation>

Эта анимация длится 5 секунд (как указано свойством Duration, которое прини-мает временное значение в формате Часы:Минуты:Секунды.ДробнаяЧастьСекунды). Пока анимация выполняется, она изменяет целевое значение от 160 до 300. Поскольку DoubleAnimation использует линейную интерполяцию, это изменения проходит гладко и непрерывно.

В приведенной разметке отсутствует одна важная деталь. Анимация указывает, как будет изменяться свойство, но не говорит о том, какое свойство следует изменять. Эта деталь описывается другим ингредиентом, который представлен классом Storyboard.

Класс StoryboardПанель раскадровки (storyboard) управляет временной шкалой анимации. Вы може-

те использовать ее для группирования нескольких анимаций, кроме того, она облада-ет способностью контролировать воспроизведение анимации — приостанавливать ее, прекращать и изменять позицию. Однако основное средство, предлагаемое классом Storyboard — это его способность указывать определенное свойство и определенный элемент, через свои свойства TargetProperty и TargetName. Другими словами, панель раскадровки заполняет пробел между вашей анимацией и свойством, которое нужно анимировать.

Ниже приведен пример определения панели раскадровки, которая применяет DoubleAnimation к свойству Width элемента по имени rect:

<Storyboard x:Name="storyboard" Storyboard.TargetName="rect" Storyboard.TargetProperty="Width"> <DoubleAnimation From="160" To="300" Duration="0:0:5"></DoubleAnimation></Storyboard>

И TargetName, и TargetProperty являются прикрепленными свойствами. Это зна-чит, что вы можете применять их непосредственно к анимации, как показано ниже:

<Storyboard x:Name="storyboard"> <DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="Width" From="160" To="300" Duration="0:0:5"></DoubleAnimation></Storyboard>

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

Page 54: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны54

Если вы присвоите имя своей панели раскадровки (как это сделано в предыдущем примере), то сможете взаимодействовать с ней в коде. Класс Storyboard включает методы, позволяющие вручную управлять анимацией: Begin(), Stop(), Pause() и Resume(). Однако если вы просто хотите запустить вашу анимацию в ответ на некото-рое событие, то для этого есть решение полегче. Вы можете привязать событие непо-средственно к действию BeginStoryboard, как описано в следующем разделе.

Триггер событияОпределение объектов панели раскадровки и анимации — это первые шаги в созда-

нии анимации. Чтобы в действительности запустить в действие панель раскадровки, вам нужен триггер события. Триггер события реагирует на событие, выполняя действие панели раскадровки. Единственное действие панели раскадровки, которое поддержива-ет Silverlight в настоящее время — это BeginStoryboard, запускающее панель раскад-ровки (и, соответственно, все анимации, которые она содержит).

Следующий пример использует коллекцию Triggers объекта Canvas, чтобы при-вязать анимацию к событию Loaded. Когда впервые выполняется визуализация содер-жимого Silverlight в браузере и загружается элемент Canvas, прямоугольник начинает расти. Пятью секундами позже его ширина увеличивается от 160 до 300 пикселей.

<Canvas ... > <Canvas.Triggers> <EventTrigger RoutedEvent="Canvas.Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="Width" From="160" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Canvas.Triggers> <Rectangle Name="rect" Height="40" Width="160" Fill="Blue" Canvas.Left="10" Canvas.Top="10"></Rectangle></Canvas>

Свойство Storyboard.TargetProperty идентифицирует свойство, которое вы хоти-те изменять (в данном случае — Width). Если вы не указываете имя класса, то панель раскадровки использует родительский элемент. Если вы хотите установить прикреплен-ное свойство (например, Canvas.Left или Canvas.Top), то вам нужно взять все свой-ство в скобки:

<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" ... />

Если вы хотите использовать несколько анимаций в одной панели раскадровки, то-гда вам просто нужно добавить более одного элемента <Animation> внутрь элемента <Storyboard>. Например, вы должны использовать эту технику, чтобы заставить пря-моугольник одновременно расти в ширину и высоту.

Запуск анимации из кода

Подход на основе EventTrigger — простейший способ запустить анимацию. Однако в текущей сборке не все события Silverlight могут быть использованы в качестве тригге-ров событий. Событие Loaded поддерживается, а события, связанные с мышью, такие как MouseEnter, MouseLeave и MouseMove, — нет.

Page 55: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 55

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

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

Ниже приведен пример, определяющий анимацию увеличения прямоугольника в виде ресурса:

<Canvas x:Name="canvas" MouseLeftButtonDown="canvas_Click" ... > <Canvas.Resources> <Storyboard x:Name="growStoryboard"> <DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="Width" Storyboard.TargetName="canvas" From="160" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard> </Canvas.Resources> <Rectangle Name="rect" Height="40" Width="160" Fill="Blue" Canvas.Left="10" Canvas.Top="10"></Rectangle></Canvas>

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

Теперь вы можете просто нужно вызывать методы объекта Storyboard в обработ-чике события в файле отделенного кода Silverlight. Методы, которые вы можете приме-нять, включают Begin(), Stop(), Pause(), Resume() и Seek(); их назначение доста-точно очевидно.

private void canvas_Click(object o, EventArgs e){ growStoryboard.Begin();}

Конфигурирование свойств анимации

Чтобы получить максимальную отдачу от ваших анимаций, вам следует вниматель-ней присмотреться к классу Animation, который определяет свойства, представленные всеми классами анимации. Их описание представлено в табл. 33.10.

Пример интерактивной анимацииВ предыдущем тексте мы использовали анимацию для изменения элемента при его

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

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

Page 56: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны56

Таблица 33.10. Свойства класса Animation

Наименование Описание

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

To Устанавливает конечное значение анимации. В некоторых ситуациях вы не будете устанавливать From или To. При этом свойство возвратится в начальное значение, установленное в разметке XAML. Например, вы мо-жете использовать эту технику, чтобы вернуть прямоугольник по щелчку к его исходным размерам.

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

Duration Длительность времени выполнения анимации — от начала до конца, в виде объекта Duration.

AutoReverse Если это свойство равно true, по завершении анимации она будет за-пущена в обратном направлении, возвращая анимируемое свойство в исходное значение. Это также удваивает время выполнения анимации.

RepeatBehavior Позволяет вам повторять анимацию в течение указанного количества се-кунд. Или же, вы можете применить Forever, чтобы повторять анимацию бесконечно.

BeginTime Устанавливает задержку перед запуском анимации (как TimeSpan). Эта задержка добавляется к общему времени, поэтому 5-секундная анимация с 5-секундной задержкой займет в сумме 10 секунд. BeginTime полезно применять для синхронизации запуска разных анимаций, которые начи-наются в одно и то же время, но эффект от которых должен применяться последовательно.

SpeedRatio Увеличивает или уменьшает скорость анимации. Изначально значение SpeedRatio равно 1. Если вы увеличите его, то анимация закончит-ся быстрее (например, SpeedRatio величиной 5 завершит анимацию впятеро быстрее). Если вы уменьшаете значение этого свойства, то анимация замедляется (например, SpeedRatio, равное 0.5, удлинит анимацию вдвое). Для получения того же результата вы можете изменять длительность анимации. SpeedRatio не принимается во внимание при указании задержки BeginTime.

FillBehavior Определяет, что случится по окончании анимации. Обычно свойство ос-тается в своем конечном значении (FillBehavior.HoldEnd), но вы можете также вернуть его в исходное значение (FillBehavior.Stop).

Page 57: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 57

Рис. 33.20. Падающие прямоугольники

Когда вы щелкаете на прямоугольнике, он начинает падать вниз по Canvas и одно-временно менять цвет. Когда вы щелкаете на другом прямоугольнике, первая анимация останавливается и падать начинает новый прямоугольник.

Несмотря на простоту, этот пример демонстрирует несколько тонких концепций, присущих анимации Silverlight.

Разметка этого примера определяет одну панель раскадровки. Эта панель раскад-ровки не помещается в коллекцию Triggers, потому что изначально не привязана ни к одному конкретному прямоугольнику. Вместо этого она помещается в коллекцию Canvas.Resources, так что может быть извлечена вашим кодом при необходимости.

<Canvas ... > <Canvas.Resources> <Storyboard x:Name="fallingSquareStoryboard"> <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" To="400" Duration="0:0:2" /> <ColorAnimation Storyboard.TargetProperty="Rectangle.Fill.Color" To="Blue" Duration="0:0:2" /> </Storyboard> </Canvas.Resources></Canvas>

Эта панель раскадровки оборачивает две анимации: DoubleAnimation, перемещаю-щую прямоугольник, и ColorAnimation, изменяющую цвет. ColorAnimation исполь-зует линейную интерполяцию, что означает последовательное изменение цвета от на-чального значения (в данном примере — красного) до конечного значения (синего).

Также вы отметите, что Canvas не содержит никаких прочих элементов. Это потому, что данный пример использует более гибкий подход — он генерирует прямоугольники динамически. Когда Canvas загружается, он создает 12 прямоугольников случайного размера в случайных местах. Он привязывает событие MouseLeftButtonDown каждого из них к одному и тому же обработчику.

Page 58: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны58

public void Page_Loaded(object o, EventArgs e){ // Необходимо для инициализации переменных. InitializeComponent(); // Сгенерировать несколько прямоугольников. Random rand = new Random(); for (int i = 0; i < 12; i++) { Rectangle rect = new Rectangle(); rect.Fill = new SolidColorBrush(Colors.Red);

// Выбрать случайные места и размеры. rect.Width = rand.Next(10, 40); rect.Height = rand.Next(10, 40); rect.SetValue<double>(Canvas.TopProperty, rand.Next((int)this.Height / 2)); rect.SetValue<double>(Canvas.LeftProperty, rand.Next((int)this.Width));

// Обработать щелчки. rect.MouseLeftButtonDown += rect_Click;

// Присвоить уникальное имя, необходимое для анимации. rect.SetValue<string>(Rectangle.NameProperty, "rect" + i.ToString());

// Добавить в Canvas. this.Children.Add(rect); }}Когда выполняется щелчок на прямоугольнике, необходимо выполнить два шага.

Анимация текущего прямоугольника должна быть остановлено (вызовом метода Storyboard.Stop()), и текущая панель раскадровки должна быть прикреплена к но-вому прямоугольнику.

Однако здесь есть один нюанс. Анимации на самом деле не изменяют значение свойства — они просто временно переопределяют его. По завершении анимации свой-ство имеет значение, независимое от финального значения анимации (если только вы не установите свойство FillBehavior класса анимации в FillBehavior.Stop). Но в данном примере анимация должна останавливаться многократно. Если вы не предпри-мите дополнительных шагов, то при каждой остановке анимации падающего прямо-угольника, его позиция вернется в первоначальное значение, т.е. он “прыгнет” обратно вверх на Canvas.

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

Так выглядит код, реализующий этот дизайн:

// Отслеживание анимируемых прямоугольников.private Rectangle currentlyFallingRectangle;private void rect_Click(object o, EventArgs e){ // Получить Storyboard. Storyboard sb = (Storyboard)this.FindName("fallingSquareStoryboard"); if (currentlyFallingRectangle != null) { //Остановить текущую анимацию и переместить прямоугольник в его текущую позицию double top = (double)currentlyFallingRectangle.GetValue(Canvas.TopProperty); sb.Stop(); currentlyFallingRectangle.SetValue<double>(Canvas.TopProperty, top); }

Page 59: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 59

// Получить прямоугольник, на котором выполнен щелчок. currentlyFallingRectangle = (Rectangle)o; // Начать анимацию нового прямоугольника. sb.SetValue<string>(Storyboard.TargetNameProperty, currentlyFallingRectangle.Name); sb.Begin();}

Хотя свойство Canvas.Top устанавливается вручную после остановки анимации, цвет не устанавливается. В результате прямоугольник возвращается к своему началь-ному синему цвету, как только начинает падать другой прямоугольник.

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

В данном примере в каждый момент времени работает только одна панель раскад-ровки. Резонно спросить — можно ли создать похожий пример, в котором каждый пря-моугольник, который вы щелкнете, продолжит падение. Это возможно, но понадобится другой дизайн.

В текущей сборке Silverlight 1.1 объекты Storyboard не могут полностью конфигури-роваться программно. Поэтому вам следует определять панель раскадровки и нужные анимации в коде XAML. Это очевидно непросто, если вы создаете элементы динамиче-ски и заранее не знаете, сколько панелей раскадровки потребуется. Решение состоит в создании пользовательского элемента управления, обладающего собственным ани-мационным поведением. Чтобы реализовать этот дизайн в предыдущем примере, сле-дует создать специальный прямоугольник, имеющий собственный шаблон XAML. Этот XAML должен специфицировать анимацию, которая должна быть использована для этого прямоугольника. Таким образом, каждый раз, когда вы создаете экземпляр этого пользовательского элемента управления, он уже обладает встроенной поддержкой ани-мации. К сожалению, такой в большей степени модульный дизайн потребует сущест-венно больше кода, и его рассмотрение выходит за рамки нашей книги. Однако если вы заинтересованы узнать больше, загляните в пример Silverlight Balloons на http://tinyurl.com/398qf4. Этот пример аккуратно иллюстрирует описанный принцип в бес-конечной последовательности надуваемых шариков (каждый из которых — экземпляр пользовательского элемента управления Balloon).

ТрансформацииКак вам уже известно, анимации Silverlight работают посредством модификации

значения свойства. Элементы имеют несколько свойств, которые могут быть легко из-менены. Например, вы можете использовать Canvas.Left и Canvas.Top для переме-щения элемента. Или же, вы можете изменять установку Opacity, чтобы вызвать его постепенное появление или исчезновение. Однако не совсем понятно, как можно вы-полнить более впечатляющие изменения, такие как вращение.

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

В табл. 33.11 перечислены трансформации, поддерживаемые в Silverlight.

Page 60: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны60

Таблица 33.11. Классы трансформаций

Наименование ОписаниеВажные

свойства

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

X, Y

RotateTransform Вращает вашу координатную систему. Нарисованные обычным образом фигуры вращаются вокруг выбранной центральной точки.

Angle, CenterX, CenterY

ScaleTransform Масштабирует координатную систему в большую или меньшую сторону, так что ваши фигуры становятся боль-ше или меньше. Вы можете применять разную степень масштабирования к осям X и Y, тем самым растягивая или сжимая вашу фигуру.

ScaleX, ScaleY, CenterX, CenterY

SkewTransform Деформирует координатную систему, наклоняя ее на за-данное число градусов. Например, если вы рисуете квад-рат, он становится параллелограммом.

AngleX, AngleY, CenterX, CenterY

MatrixTransform Модифицирует координатную систему посредством матричного умножения с указанной вами матрицей. Это наиболее сложная опция, и она требует определен-ных знаний математики.

Matrix

TransformGroup Комбинирует множество трансформаций таким образом, что они могут быть применены все вместе. Порядок, в котором применяются трансформации, имеет значение — это влияет на конечный результат. Например, вращение фигуры (с помощью RotateTransform) с последующим смещением ее (посредством TranslateTransform) переместит фигуру в другом направлении, чем если вы сначала переместите ее, а потом повернете.

нет

Технически все трансформации используют вычисления над матрицами, чтобы из-менить координаты вашей фигуры. Однако применять встроенные трансформации, такие как TranslateTransform, RotateTransform, ScaleTransform и SkewTransform, намного проще, нежели применять MatrixTransform, пытаясь найти правильную матрицу для выполнения нужной вам операции. Когда вы применяете серию транс-формаций с TransformGroup, то Silverlight смешивает ваши трансформации в одну MatrixTransform, обеспечивая оптимальную производительность.

Использование трансформаций

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

Например, при вращении фигуры вы должны использовать RotateTransform для указания угла поворота. Приведем пример, поворачивающий квадрат по часовой стрел-ке на 25 градусов:

<Rectangle Width="80" Height="10" Stroke="Blue" Fill="Yellow" Canvas.Left="100" Canvas.Top="100"> <Rectangle.RenderTransform>

Page 61: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 61

<RotateTransform Angle="25" /> </Rectangle.RenderTransform></Rectangle>

При таком повороте фигуры вы делаете это вокруг ее исходной точки (левого верх-него угла). Если вы хотите повернуть фигуру вокруг другой точки, можно использовать свойство RenderTransformOrigin. Это свойство устанавливает центральную точку, применяя соответствующую систему координат, которая растягивается от 0 до 1 в ка-ждом направлении. Другими словами, точка (0,0) описывает верхний левый угол, а (1,1) — правый нижний. (Если область фигуры — не квадрат, то координатная система растягивается соответственно.)

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

<Rectangle Width="80" Height="10" Stroke="Blue" Fill="Yellow" Canvas.Left="100" Canvas.Top="100" RenderTransformOrigin="0.5,0.5"> <Rectangle.RenderTransform> <RotateTransform Angle="25" /> </Rectangle.RenderTransform></Rectangle>

Это работает, потому что точка (0.5, 0.5) означает центр фигуры, независимо от ее размера.

Совет. Вы можете использовать значения больше 1 или меньше 0, устанавливая свойст-во RenderTransformOrigin, чтобы обозначить точку, находящуюся за пределами пря-моугольника, описанного вокруг фигуры. Например, вы можете применять этот прием с RotateTransform, чтобы повернуть фигуру по большой дуге вокруг очень удаленной точки, такой как (5,5).

Анимация трансформаций

Чтобы использовать трансформацию в анимации, первым шагом должно быть опре-деление трансформации. (Анимация может изменять существующую трансформацию, но не может создать новую.) Например, предположим, что вы хотите позволить вра-щаться прямоугольнику. Это потребует RotateTransform:

<Rectangle x:Name="rect" Width="80" Height="50" Stroke="Blue" Fill="Yellow" Canvas.Left="100" Canvas.Top="100" RenderTransformOrigin="0.5,0.5"> <Rectangle.RenderTransform> <RotateTransform></RotateTransform> </Rectangle.RenderTransform></Rectangle>

Теперь у нас есть панель раскадровки, которая заставляет вращаться прямоугольник при перемещении над ним курсора мыши. Она использует целевое свойство (UIElement.RenderTransform).Angle — другими словами, читает свойство RenderTransform объекта Rectangle и модифицирует свойство Angle определенного там объекта RotateTransform. Тот факт, что свойство RenderTransform может содержать широкое разнообразие объектов трансформации, не вызывает проблем. До тех пор, пока вы при-меняете трансформацию, имеющую свойство Angle, этот триггер будет работать.

<Rectangle x:Name="rect" Width="80" Height="50" Stroke="Blue" Fill="Yellow" Canvas.Left="100" Canvas.Top="100" RenderTransformOrigin="0.5,0.5" MouseEnter="rect_Enter"> <Rectangle.RenderTransform> <RotateTransform></RotateTransform> </Rectangle.RenderTransform>

Page 62: 33CD_ASP3-5-C#2008

Часть VI. Программирование клиентской стороны62

<Rectangle.Resources> <Storyboard x:Name="rotateStoryboard"> <DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(UIElement.RenderTransform).Angle" To="360" Duration="0:0:0.8" RepeatBehavior="Forever"></DoubleAnimation> </Storyboard> </Rectangle.Resources></Rectangle>

И, наконец, обработчик событий запускает панель раскадровки:

private void rect_Enter(object o, EventArgs e){ rotateStoryboard.Begin();}

Прямоугольник вращается, делая один оборот каждые 0,8 секунды, и продолжает вращение бесконечно. В процессе его вращения он остается совершенно работоспособ-ным — например, он инициирует событие MouseLeftButtonDown при щелчке кнопкой мыши.

Чтобы остановить вращение вы можете использовать другой триггер, который от-реагирует на событие MouseLeave. В этой точке вы можете вызвать метод Storyboard.Stop(), но это вызовет прыжок кнопки на один шаг назад к ее исходному положению. Лучший подход заключается в запуске второй анимации, которая заменит первую. Вот как определяется вторая анимация:

<Storyboard x:Name="revertStoryboard"> <DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(UIElement.RenderTransform).Angle" To="0" Duration="0:0:0.2"></DoubleAnimation></Storyboard>

Эта анимация гладко повернет прямоугольник обратно в его исходное положе-ние за 0,2 секунды. Вы можете поместить эту панель раскадровки в ту же коллекцию Rectangle.Resources, что и первую. Все, что потребуется сделать — это прикрепить об-работчик к событию Rectangle.MouseLeave, который запустит панель раскадровки:

private void rect_Leave(object o, EventArgs e){ revertStoryboard.Begin();}

Совет. Вы можете легко комбинировать трансформации. Фактически это очень просто — вам нуж-но только использовать TransformGroup для установки свойства RenderTransform. Вы можете вкладывать внутрь TransformGroup столько трансформаций, сколько вам нужно.

РезюмеВ этой главе вы ознакомились с Silverlight — новой платформой, построенной на ос-

нове двух других технологий: .NET и WPF.Silverlight быстро развивается. В течение нескольких месяцев можно ожидать появ-

ления новых выпусков с новыми элементами управления и новыми средствами. В на-стоящее время Silverlight пока еще не предоставляет достаточного количества средств помимо высоконастраиваемых и в основном графических приложений. Недостаток ос-

Page 63: 33CD_ASP3-5-C#2008

Глава 33. Silverlight 63

нов (таких как элементы для ввода текста) делает его менее интересным для разработ-чиков бизнес-приложений.

Хотя пока еще слишком рано оценивать подключаемый модуль Silverlight и его про-изводительность в других браузерах и операционных системах, можно ожидать ее по-вышения. Silverlight — одна из наиболее активно продвигаемых Microsoft новых техно-логий, которые интересуют разработчиков, по сравнению с любыми другими, начиная с .NET 1.0. Разработчики, которые изучают модель Silverlight уже сейчас, получат пре-имущество в овладении более зрелыми будущими версиями.

Если вы решили погрузиться в проект Silverlight, то наилучшей отправной точкой будет изучение существующих примеров в Web. Эти примеры представляют собой наи-лучшие руководства, которые научат вас приемам, посредством которых другие разра-ботчики обходят существующие ограничения платформы Silverlight, таким как создание пользовательских элементов управления, отображение отдельных “экранов”, выполне-ние фоновых задач и управление временем жизни приложения. Вы можете найти набор блестящих примеров с полным исходным кодом по адресу http://silverlight.net/community/gallerydetail.aspx?cat=2. За дополнительной информацией об элемен-тах управления Silverlight 1.1, которые могут вам пригодиться, обращайтесь к врезке “Элементы управления Silverlight” выше в этой главе.