Одна из замечательных особенностей Silverlight — поддержка динамических языков вроде IronRuby и IronPython. Это позволяет разрабатывать на платформе Silverlight полнофункциональные веб-приложения (Rich Internet Applications, RIA): XAML используется для создания презентационного уровня, а динамические языки — для написания отделенного кода (code behind). В этой статье демонстрируются возможности Silverlight в интеграции с динамическими языками и элементами управления Microsoft Bing Map. Я начну с общего обзора динамических языков, а затем детально рассмотрю поддержку таких языков в Silverlight. В заключение я покажу, как создать приложение Silverlight для поиска по картам с интерактивной трехмерной анимацией; при этом я задействую элемент управления Microsoft Bing Map и IronRuby.
Основы динамических языков
Среды Read-Eval-Print Loop (REPL) упрощают программирование за счет применения динамических языков с динамической типизацией и компиляцией в период выполнения. Вам не нужно объявлять переменные конкретных типов данных. Все обрабатывается исполняющей средой в контексте выражений.
Более привычные языки наподобие C# и Visual Basic являются языками со статической типизацией и по своей природе менее гибки. Разработка и развертывание с применением динамических языков проще, чем в случае статических языков, требующих компиляции и распространения полученного кода. Однако при использовании языков с динамической типизацией проверка безопасности типов все равно необходима.
На динамическом языке можно создать функцию и присвоить ее переменной или передать как параметр другой функции. Это резко упрощает работу с замыканиями (closures). В целом, двумя определяющими характеристиками замыканий являются возможность присваивания блока кода (функции) переменной и способность этого блока кода обеспечивать доступ к переменным, которые были доступны там, где они были созданы.
Следующий традиционный метод ShortWords на C# возвращает подмножество списка слов, удовлетворяющих критерию, согласно которому максимальная длина слова не должна превышать трех букв:
public static List<string> ShortWords(List<string> wordList) {
List<string> shortWordList = new List<string>();
int maximumWordLength = 3;
foreach(string word in wordList) {
if(word.Length <= maximumWordLength) {
shortWordList.Add(word);
}
}
return(shortWordList);
}
При использовании LINQ вы можете добиться аналогичной функциональности гораздо более эффективным способом, как показано в следующем фрагменте кода:
public static List<string> ShortWords(List<string> wordList) {
var maximumWordLength = 3;
return wordList.Where(w => w.Length <=
maximumWordLength).ToList<string>();
end
Создание того же метода на динамическом языке, например на IronRuby (реализация языка программирования Ruby для Microsoft .NET Framework), аналогично его написанию на C# с применением LINQ; при этом код гораздо короче, чем при традиционном подходе:
def ShortWords(wordList)
maximumWordLength = 3
return wordList.select {|w| w.Length <= maximumWordLength}
end
Простое сравнение этих двух реализаций одного алгоритма приоткрывает многие возможности IronRuby в частности и динамических языков в целом. Код на IronRuby очень четкий, и вы нигде не найдете в нем ключевых слов, указывающих тип данных, например string или int.
Самый интересный аспект этого блока кода на IronRuby — замыкание, расположенное между двумя фигурными скобками. В данном случае замыкание, по сути функция, передается методу select. Этот метод использует замыкание для выборки подмножества набора. Код, образующий замыкание, на самом деле выполняется в методе select (в этом примере замыкание извлекает из набора wordList строки, удовлетворяющие критерию), но исходная область видимости его переменных сохраняется (здесь — у переменной maximumWordLength).
Возможности замыканий гораздо шире, чем показано в этом простом примере. Они аналогичны использованию LINQ или передаче делегата в метод вроде Exists или Find в C# с дополнительным преимуществом сохранения исходной области видимости. Подробнее о замыканиях читайте в книге, которую я написал в соавторстве с Джеффом Скэнлоном (Jeff Scanlon), — «Accelerated Silverlight 3» (Apress, июль 2009).
Динамические языки для Silverlight
В настоящее время Silverlight поддерживает IronRuby и IronPython через Microsoft Dynamic Language Runtime (DLR) — универсальную платформу и модель хостинга динамических языков, которая работает поверх Microsoft .NET Framework Common Language Runtime (CLR).
DLR — это набор библиотек и сервисов .NET Framework, которые динамически распознают типы в период выполнения через механизм отражения, поэтому код, написанный на динамических языках, может работать на платформе .NET.
Пять DLR-сборок создают в период выполнения среду, поддерживающую сценарии и соединяющую динамические языки с Silverlight:
- Microsoft.Scripting.dll;
- Microsoft.Scripting.Core.dll;
- Microsoft.Scripting.Silverlight.dll;
- Microsoft.Scripting.ExtensionAttribute.dll;
- Microsoft.Scripting.Debugging.dll.
Microsoft.Scripting.Silverlight.dll содержит классы, позволяющие писать приложения Silverlight с применением динамических языков. Один из ключевых классов — DynamicApplication, наследующий непосредственно от System.Windows.Application. Этот класс представляет динамический объект приложения на основе Silverlight и обеспечивает доступ к визуальным элементам из кода на динамических языках, а также служит точкой входа для приложений, написанных на динамических языках и размещаемых в хост-приложениях Silverlight. Он включает дополнительные свойства, расширяющие свойства Host, Resources и RootVisual, уже предоставляемые классом Application.
IronRuby (ironruby.net) — реализация языка программирования Ruby с открытым исходным кодом, которая обеспечивает интеграцию Ruby и .NET Framework.
Текущая версия IronRuby (1.0-rc1) поддерживает .NET Framework 3.5 и бета-версию .NET Framework 4. Заметьте, что IronRuby 1.0-rc1 можно скачать в виде архивов .zip и .msi. Для приложений Silverlight лучше использовать версию, упакованную в архив .zip.
IronPython (ironpython.codeplex.com) — реализация языка программирования Python с открытым исходным кодом, которая, как и IronRuby, обеспечивает интеграцию Python и .NET Framework. В настоящее время вы можете скачать IronPython 2.6 для .NET Framework 3.5 и бета-версии .NET Framework 4.
IronRuby и IronPython имеют по две сборки, которые поддерживают конкретный язык, предоставляя такие возможности, как синтаксический разбор языка и взаимодействие со средой хостинга. Это IronPython.dll и IronPython.Modules.dll для IronPython, а также IronRuby.dll и IronRuby.Libraries.dll для IronRuby.
Заметьте, что и IronRuby, и IronPython находятся в процессе постоянного развития. Поэтому почаще заглядывайте на соответствующие сайты за самыми свежими версиями и документацией. Вы также можете получить их исходный код вместе с исходным кодом DLR на сайте dlr.codeplex.com.
Установка компонентов для разработки
Существует два подхода к разработке приложений Silverlight с использованием динамических языков:
- традиционный подход с применением средства разработки Chrion.exe;
- на основе «только текста» (just-text approach) с применением встроенной в браузер поддержки сценариев.
В этой статье я дам обзор обоих подходов, а затем покажу, как разработать приложение-пример Microsoft Bing Maps, используя более новый подход на основе «только текста».
Помимо DLR-библиотек поддержки сценариев Microsoft с момента появления Silverlight 2 предоставляет среду разработки приложений Silverlight на основе динамических языков (Chiron.exe) и шаблоны проектов Silverlight-приложений с IronRuby и IronPython.
Для начала скачайте и установите DLR и IronRuby либо IronPython с сайтов, упомянутых ранее. Примеры, документация, утилиты и некоторые дополнительные важные компоненты устанавливаются вместе с IronRuby и IronPython.
Шаблоны Silverlight для динамических языков предоставляют основные файлы приложения, размещаемые в папках Silverlight\script\templates\ruby и Silverlight\script\templates\python. В табл. 1 приведены некоторые подробности об этих файлах шаблонов приложений.
Табл. 1. Основные файлы для приложений Silverlight, использующих динамические языки
IronRuby | IronPython, установленный с IronRuby | IronPython, установленный независимо | Описание |
index.html | index.html | index.html | Служит хостом для приложения Silverlight, использующего динамический язык |
app\app.rb | app\app.py | python\app.py | Основной стартовый файл для приложения Silverlight |
app\app.xaml | app\app.xaml | python\app.xaml | Основной файл XAML UI |
css\screen.css | css\screen.css | stylesheets\screen.cs | Определяет стили приложения |
Отсутствует | Отсутствует | stylesheets\error.css | Определяет стили и формат ошибок приложения |
js\error.js | js\error.js | javascripts\error.js | Управляет необработанными ошибками приложения |
Папка Script включает файл sl.bat, который помогает создать заготовку приложения Silverlight, использующего динамический язык. Формат командной строки выглядит так:
sl [ruby|python] <ApplicationPath>
Chiron.exe, средство разработки для Silverlight, динамически упаковывает набор файлов в файл .xap для развертывания. (Подробное обсуждение Chiron.exe и процесса компиляции см. по ссылке blog.jimmy.schementi.com/2009/03/state-of-dlr-for-silverlight.html.)
Вы можете запустить приложение с помощью Chiron.exe, указав параметр /b (browser):
Chiron /b
Одна из интересных особенностей Chiron.exe заключается в том, что, как только вы модифицируете какой-либо файл внутри каталога приложения, Chiron.exe автоматически переупаковывает приложение в файл .xap и перезагружает его. Однако обновлять любые активные сеансы браузера вы по-прежнему должны самостоятельно.
Подход на основе «только текст»
Традиционный подход к разработке на основе DLR требует обязательного использования Chrion.exe и следует старой модели разработки «редактирование — компиляция — обновление».
Теперь код на IronRuby, IronPython и XAML можно писать в разметке (X)HTML прямо в браузере (детали см. по ссылке ironruby.com/browser). Это и называют подходом на основе «только текст» (just-text approach). Для создания и запуска DLR-приложения не нужно устанавливать никаких компонентов. Такой подход соответствует модели разработки «написание — сохранение — обновление» и исключает необходимость в применении Chrion.exe.
При подходе «только текст» вам не понадобятся даже локальные копии DLR-сборок поддержки сценариев, а также ранее упомянутые сборки, специфичные для языков IronRuby и IronPython. Хотя пакет-пример Gestalt, доступный по ссылке ironruby.com/browser, содержит двоичные файлы, вы можете ссылаться на dlr.js и с любого общеизвестного сервера, а значит, вам не требуется что-либо устанавливать. Однако вам все же нужен какой-то способ размещения элементов управления Silverlight и включения интеграции с DLR в HTML-странице.
Проект Gestalt использует преимущества существующего в Silverlight.js подхода, при котором с помощью JavaScript API создается тег Object, куда и помещается элемент управления Silverlight. Он также включает управление ошибками и требований к браузеру и плагинам Silverlight на клиентском компьютере. Группа Mix Online Lab расширила файл Silvelright.js, добавив поддержку подставляемых в строку сценариев (inline scripting) и интеграции DLR, и переименовала этот файл в dlr.js.
Проект Gestalt предоставляет кросс-браузерную, кросс-платформенную библиотеку, которая опирается на DLR. Вы можете получить сжатый файл библиотеки, gestalt.zip, по ссылке visitmix.com/labs/gestalt/downloads. В табл. 2 описываются основные файлы, включенные в этот ZIP-архив.
Табл. 2. Основные файлы библиотек, необходимые для применения подхода на основе «только текст»
Файл IronRuby | Описание |
dlr\dlr.js | Расширенный файл Silverlight.js для хостинга приложения Silverlight, использующего динамический язык, и включения поддержки, подставляемый в строку сценариев в HTML-страницах |
dlr\gestaltmedia.js | Поддерживает воспроизведение видео и звука HTML5 |
dlr\dlr.xap | Включает файл AppManifest.xaml, который ссылается на Microsoft.Scripting.slvx и указывает на Microsoft.Scripting.Silverlight.dll как на точку входа в сборку. Также включает languages.config, который предоставляет конфигурационную информацию DLR-языкам |
dlr\IronRuby.slvx | Включает файлы IronRuby.dll и IronRuby.Libraries.dll для поддержки разработки приложений Silverlight с применением IronRuby |
dlr\IronPython.slvx | Включает файлы IronPython.dll и IronPython.Modules.dll для поддержки разработки приложений Silverlight с применением IronPython |
dlr\ Microsoft.Scripting.slvx | Включает пять DLR-сборок для поддержки сценариев (Microsoft.Scripting.dll, Microsoft.Scripting.Core.dll, Microsoft.Scripting.Silverlight.dll, Microsoft.Scripting.ExtensionAttribute.dll и Microsoft.Scripting.Debugging.dll), которые создают исполняющую среду сценариев и «наводят мосты» между динамическими языками и Silverlight |
samples/getting.started/*.html | Примеры веб-страниц, демонстрирующие подставляемые в строку сценарии, а также возможности IronRuby, IronPython и XAML |
Transparent Silverlight Extensions (введено в Silverlight 3) позволяет упаковывать часто используемые файлы сборок в отдельную повторно применяемую библиотеку с расширением .slvx. Файлы .slvx можно развертывать на общедоступном сайте в Интернете или на сайтах, специфичных для клиентов. На необходимые файлы .slvx нужно ссылаться из файла AppManifest.xaml в разделе ExternalParts как на ExtensionPart с корректным путем.
Отличным пособием по использованию подхода на основе «только текст» является статья Джимми Скементи (Jimmy Schementi); см. по ссылке ironruby.com/browser/sl-back-to-just-text.pdf. В этой статье также подробно поясняется, как изменить исходные настройки DLR в файле dlr.js.
Вам понадобится экземпляр веб-сервера, например IIS или Apache, для хостинга и выполнения веб-приложений с поддержкой подставляемых в строку сценариев на основе DLR. Распаковав gestalt.zip, поместите папки Dlr и Samples в корень веб-сервера. Если вы установите эти папки не в корень веб-сервера, вам придется соответственно модифицировать файл dlr.js.
Затем добавьте MIME-типы для файлов с расширениями .rb, .py и .slvx files:
- для файлов .rb и .py задайте MIME-тип как text/plain;
- для файлов .slvx задайте MIME-тип как application/octet-stream.
Для проверки среды откройте папку samples/get.started и перейдите к файлу 05_final.html. Содержащаяся в нем веб-страница демонстрирует графику на основе IronPython, HTML и XAML со средствами интеграции анимаций, как показано на рис. 1.
Увеличить
Рис. 1. Выполняемое приложение-пример из проекта Gestalt
Silverlight, IronRuby и подход на основе «только текст»
Начнем с определения каркаса приложения Silverlight на основе DLR, использующего IronRuby и подход «только текст». После копирования файлов Gestalt в корень веб-сервера просто откройте текстовый редактор и пишите свой HTML-файл. Вот и все!
Приятно, что dlr.js добавляет в страницу элемент управления Silverlight и предоставляет все базовые средства для поддержки интеграции кода на динамических языках. Для этого достаточно включить файл dlr.js в HTML-страницу:
<head>
<script src="/dlr/dlr.js" type="text/javascript"></script>
</head>
Заметьте, что включение dlr.js приведет к настройке параметров по умолчанию для вашего приложения Silverlight на основе DLR. Если вы захотите изменить настройки, вам понадобится переопределить параметры по умолчанию, написав собственный сценарий в HTML-файле. Подробности см. в уже упоминавшейся статье Джимми Скементи.
Теперь вы готовы к написанию XAML- и IronRuby- или IronPython-кода в HTML-файле внутри тега script. Для подстановки кода на IronRuby нужно добавить тег script с соответствующей информацией о типе и классе приложения и поместить код на IronRuby в этот тег:
<script type="application/ruby"
class="Class Name Goes Here">
IronRuby Code Goes Here
</script>
Для подстановки кода на IronPython сделайте то же самое, но с соответствующими изменениями:
<script type="application/python"
class="Class Name Goes Here">
IronPython Code Goes Here
</script>
Для подстановки XAML-кода добавьте тег script с соответствующей информацией о типе, идентификаторе, ширине и высоте, а затем поместите XAML-код в этот тег:
<script type="application/xml+xaml" id="Place ID here"
Width="400" Height="400">
<UserControl ...>
XAML Code Goes Here
</UserControl>
</script>
Вы можете обращаться к XAML-элементам управления и реализовать интеграцию событий, используя код, который я продемонстрирую в процессе разработки приложения в следующем разделе. Закончив с кодом, просто перейдите к странице и вы должны сразу же увидеть результат работы приложения.
Интеграция с Bing Maps
Теперь, когда вы увидели базовую заготовку приложения Silverlight, использующего подход «только текст», давайте сделаем еще один шаг вперед — интегрируем карты Microsoft Bing Maps (ранее назывались Virtual Earth).
Microsoft Bing Maps Silverlight Control SDK версии 1 был выпущен в ноябре 2009 г. (msdn.microsoft.com/library/ee681884). Установщиком является BingMapsSilverlightControlv1.0.1Installer.msi. Для работы с этим элементом управления нужно установить Silverlight версии не ниже 3. В пакет установки входят Microsoft.Maps.MapControl.dll и Microsoft.Maps.MapControl.xml, Microsoft.Maps.MapControl.Common.dll и Microsoft.Maps.MapControl.Common.xml, а также документация.
Прежде чем вы начнете создавать приложения с использованием Silverlight-элемента управления Bing Maps, вы должны создать учетную запись Bing Maps Developer, чтобы получить аутентификационный ключ приложения. Для этого зайдите на сайт bingmapsportal.com.
CTP-версия Silverlight-элемента управления Microsoft Bing Maps появилась до выпуска первой версии SDK. И в версии 1 по сравнению с CTP-версией в этот элемент внесены существенные усовершенствования и расширения. Основные различия между CTP- и первой версией см. по ссылке msdn.microsoft.com/library/ee681889.
Теперь создайте файл SilverlightMap.html и включите в него файл dlr.js, как было описано в предыдущем разделе.
Вам нужно модифицировать файл AppManifest.xaml (доступный в dlr.xap) и включить в него файлы Microsoft.Maps.MapControl.dll и Microsoft.Maps.MapControl.Common.dll, чтобы они загружались вместе с приложением. С этой целью переименуйте dlr.xap в dlr.xap.zip и извлеките из него файлы AppManifest.xaml и languages.config. Затем добавьте Microsoft.Maps.MapControl.dll и Microsoft.Maps.MapControl.Common.dll как AssemblyPart (рис. 2).
Рис. 2. Модифицированный файл AppManifest.xaml
<Deployment
xmlns="http://schemas.microsoft.com/client/2007/deployment"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RuntimeVersion="2.0.31005.0"
EntryPointAssembly="Microsoft.Scripting.Silverlight"
EntryPointType="Microsoft.Scripting.Silverlight.DynamicApplication"
ExternalCallersFromCrossDomain="ScriptableOnly">
<Deployment.Parts>
<AssemblyPart Source="Microsoft.Maps.MapControl.dll" />
<AssemblyPart Source="Microsoft.Maps.MapControl.Common.dll" />
</Deployment.Parts>
<Deployment.ExternalParts>
<ExtensionPart Source="Microsoft.Scripting.slvx" />
</Deployment.ExternalParts>
</Deployment>
Теперь заархивируйте модифицированный AppManifest.xaml, а также существующие файлы languages.config, Microsoft.Maps.MapControl.dll и Microsoft.Maps.MapControl.Common.dll в ZIP-файл и переименуйте ZIP-файл в dlr.xap. Замените им существующий dlr.xap (в папке Dlr) на веб-сервере.
Далее откройте файл SilverlightMap.html, добавьте тег script для XAML-кода, а затем поместите UserControl с именем Silverlight_map и ссылку на элемент управления картой, чтобы создать необходимое пространство имен (рис. 3).
Рис. 3. Как ссылаться на элементы управления картами в HTML-файле
<Deployment
xmlns="http://schemas.microsoft.com/client/2007/deployment"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RuntimeVersion="2.0.31005.0"
EntryPointAssembly="Microsoft.Scripting.Silverlight"
EntryPointType="Microsoft.Scripting.Silverlight.DynamicApplication"
ExternalCallersFromCrossDomain="ScriptableOnly">
<Deployment.Parts>
<AssemblyPart Source="Microsoft.Maps.MapControl.dll" />
<AssemblyPart Source="Microsoft.Maps.MapControl.Common.dll" />
</Deployment.Parts>
<Deployment.ExternalParts>
<ExtensionPart Source="Microsoft.Scripting.slvx" />
</Deployment.ExternalParts>
</Deployment>
<script type="application/xml+xaml" id="sl_map"
Width="1350" Height="575">
<UserControl x:Name="silverlight_map"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="200"
Height="280" Background="Black"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl">
</UserControl>
</script>
Наконец, добавьте Canvas как основной контейнер и Map под элемент управления Grid. Заметьте, что я сохраняю ту же высоту и ширину элементов управления Canvas и Grid и задаю свойствам Width и Height элемента управления Map значения 800 и 400 соответственно:
<Canvas x:Name="container" Width="1350" Height="575">
<Grid x:Name="layout_root" Width="1350" Height="575">
<m:Map CredentialsProvider="AuthenticationKey"
Width="800" Height="400" Grid.Column="1"
HorizontalAlignment="Center"/>
</Grid>
</Canvas>
</UserControl>
Во фрагментах кода, показанных здесь, вам нужно заменить AuthenticationKey своим аутентификационным ключом для элемента управления Map.
Скопируйте этот файл в существующую на веб-сервере папку Sample/Getting.started и перейдите к странице. Вы должны увидеть карту в режиме по умолчанию — Road (рис. 4).
Увеличить
Рис. 4. Приложение Silverlight Bing Map на основе DLR с картой в режиме Road (по умолчанию)
Режимы карты и трехмерная анимация
Давайте сменим режим карты по умолчанию на Aerial с метками. А заодно введем трехмерную анимацию карты.
Чтобы сменить режим карты по умолчанию, сначала присвойте элементу Map имя (в этом примере — map_in_ironruby), чтобы на него можно было ссылаться в подставляемом в строку коде на IronRuby. Я также применю трехмерную проекцию к объекту Map. Это было новой функциональностью в Silverlight 3. Для этого я указал для свойства Projection объекта Map значение PlanProjection, а в свойстве RotationX задал значение –20. В результате объект Map трансформируется, давая угол зрения под небольшим наклоном:
<Grid x:Name="layout_root" Width="1350" Height="575" Background="Black">
<m:Map x:Name="map_in_ironruby"
CredentialsProvider="AuthenticationKey"
Width="800" Height="400">
<m:Map.Projection>
<PlaneProjection RotationX="-20"/>
</m:Map.Projection>
</m:Map>
</Grid>
Заметьте, что я также изменил цвет фона в Grid на черный.
Теперь добавьте тег script для IronRuby и напишите следующие строки кода для включения нужных сборок (в том числе MapControl.dll), а затем включите для элемента управления Map представление «с воздуха» (aerial view) с метками.
Обратите внимание, что при новом подходе «только текст» я могу ссылаться на объект Map по имени. В текущей версии библиотек Gestalt вы должны ссылаться на объекты с помощью сокращенного ключевого слова me или xaml (я использую me). В будущем к XAML-элементам с установленным x:Name вы сможете обращаться через сокращение root_visual:
<script type="application/ruby" class="sl_map">
require "Microsoft.Maps.MapControl.dll"
require "Microsoft.Maps.MapControl.Common.dll"
include System::Windows
include System::Windows::Controls
include Microsoft::Maps::MapControl
sm = me.silverlight_map
sm.map_in_ironruby.mode = AerialMode.new(true)
</script>
Имя класса в теге script, относящемся к IronRuby, — sl_map; оно похоже на идентификатор тега script, относящегося к XAML.
Теперь, если вы запустите приложение, то увидите черный фон, трехмерное представление «с воздуха» под наклоном и с метками, как показано на рис. 5.
Увеличить
Рис. 5. Режим карты установлен в Aerial с метками и трехмерной проекцией
На конференции MIX09 пользовалась большой популярностью демонстрация интеграции Silverlight и Microsoft Bing Maps с поддержкой вращения объекта Map. Давайте реализуем нечто подобное на IronRuby.
Для этого нужно сначала определить Grid с двумя столбцами, используя ColumnDefinitions:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="1100"/>
</Grid.ColumnDefinitions>
Затем добавьте три кнопки с именами Rotate Map, Pause/Stop и Reset, а также текст заголовка в пределах Border в XAML-файле. Все это нужно разместить в первом столбце Grid (рис. 6).
Рис. 6. Добавление элементов управления в объект Grid
<StackPanel Grid.Column="0" Orientation="Vertical">
<Border CornerRadius="20" Margin="0,50,0,5" Width="150"
Background="DarkBlue" HorizontalAlignment="Center">
<StackPanel Orientation="Vertical">
<TextBlock Text="3D Rotation"
HorizontalAlignment="Center"
FontSize="12" Foreground="White" Margin="0,5,0,10"/>
<Button x:Name="RotateMap" Height="25"
Content="rotate_map" Width="100" Margin="0,0,0,10"
Foreground="Black" VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Button x:Name="pause_resume" Height="25"
Content="Pause" Background="DarkGoldenrod"
Foreground="Black" Width="100" Margin="0,0,0,10"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Button x:Name="stop_reset" Height="25"
Content="Stop and Reset" Background="DarkGoldenrod"
Foreground="Black" Width="100" Margin="0,0,0,10"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</StackPanel>
Во второй столбец Grid добавьте объект Map:
<m:Map x:Name="map_in_ironruby"
CredentialsProvider="AuthenticationKey"
Width="800" Height="400" Grid.Column="1"
HorizontalAlignment="Center">
<m:Map.Projection>
<PlaneProjection RotationX="-20" RotationY="0"
RotationZ="0"/>
</m:Map.Projection>
</m:Map>
Следующий шаг — создание довольно сложного кода обновления XAML-разметки. Советую создавать XAML-файл в среде разработки наподобие Visual Studio или Expression Blend, чтобы задействовать преимущества их средств редактирования и IntelliSense. Потом законченный XAML-текст можно скопировать в файл app.xaml вашего проекта.
Создайте Storyboard с именем map_animation, предназначенным для объекта Map с именем map_in_ironruby. (Фрагмент кода приведен на рис. 7. Полный исходный код для Storyboard можно скачать с сайта MSDN Magazine.) Storyboard определяет ключевые (опорные) кадры для свойств анимации PlaneProjection: RotationZ, RotationY, GlobalOffsetX и GlobalOffsetZ. Добавьте StoryBoard как ресурс UserControl.
Рис. 7. Создание Storyboard для анимации
<StackPanel Grid.Column="0" Orientation="Vertical">
<Border CornerRadius="20" Margin="0,50,0,5" Width="150"
Background="DarkBlue" HorizontalAlignment="Center">
<StackPanel Orientation="Vertical">
<TextBlock Text="3D Rotation"
HorizontalAlignment="Center"
FontSize="12" Foreground="White" Margin="0,5,0,10"/>
<Button x:Name="RotateMap" Height="25"
Content="rotate_map" Width="100" Margin="0,0,0,10"
Foreground="Black" VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Button x:Name="pause_resume" Height="25"
Content="Pause" Background="DarkGoldenrod"
Foreground="Black" Width="100" Margin="0,0,0,10"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Button x:Name="stop_reset" Height="25"
Content="Stop and Reset" Background="DarkGoldenrod"
Foreground="Black" Width="100" Margin="0,0,0,10"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</StackPanel>
<UserControl.Resources>
<Storyboard x:Name="map_animation">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="map_in_ironruby"
Storyboard.TargetProperty=
"(UIElement.Projection).(PlaneProjection.RotationZ)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="15"/>
<EasingDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
<EasingDoubleKeyFrame KeyTime="00:00:02" Value="-15"/>
<EasingDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
<EasingDoubleKeyFrame KeyTime="00:00:04" Value="15"/>
</DoubleAnimationUsingKeyFrames>
...
</Storyboard>
</UserControl.Resources>
Далее добавьте события Click к встраиваемому коду IronRuby для запуска, приостановки, возобновления и прекращения анимации. При этом сначала придется добавить ссылку на System.Windows.Media и System.Windows.Media.Animation:
include System::Windows::Media
include System::Windows::Media::Animation
Отключайте кнопку pause_resume на время инициализации приложения:
sm.pause_resume.is_enabled = false
Теперь реализуем событие Click для каждой кнопки. Начнем с rotate_map. Указываем для объекта Map горизонтальное выравнивание по левому краю для более полного использования доступного пространства под анимацию. Устанавливаем свойство RepeatBehavior анимации StoryBoard в Forever и начинаем анимацию. Наконец, включаем кнопку pause_resume и задаем надпись на кнопке как «Pause»:
sm.rotate_map.click do |s,e|
sm.map_in_ironruby.horizontal_alignment = HorizontalAlignment.Left
sm.map_animation.repeat_behavior = RepeatBehavior.Forever
sm.map_animation.begin
sm.pause_resume.is_enabled = true
sm.pause_resume.content = "Pause"
end
Теперь реализуем событие Click для кнопки pause_resume. Здесь в зависимости от того, в каком состоянии находится анимация — приостановлена или выполняется, вы либо возобновляете, либо приостанавливаете анимацию Storyboard и изменяете надпись на кнопке:
sm.pause_resume.click do |s,e|
strbtnContent = sm.pause_resume.content.ToString
if strbtnContent == "Pause"
sm.pause_resume.content = "Resume"
sm.map_animation.pause
else
sm.pause_resume.content = "Pause"
sm.map_animation.resume
end
end
Наконец, реализуем событие Click для кнопки stop_reset. В обработчике этого события вы прекращаете анимацию Storyboard, отключаете кнопку pause_resume и меняете надпись на кнопке на «Pause». Вы также сбрасываете выравнивание объекта Map:
sm.stop_reset.click do |s,e|
sm.map_animation.stop
sm.pause_resume.is_enabled = false
sm.pause_resume.content = "Pause"
sm.map_in_ironruby.horizontal_alignment = HorizontalAlignment.Center
end
Скомпилируйте и запустите проект, используя команду Chiron /b, чтобы увидеть карту с анимацией. На рис. 8 показано вращение карты.
Увеличить
Рис. 8. Анимация трехмерной карты
Предопределенные местоположения
Теперь давайте выделим на карте три предопределенные местоположения: Нью-Йорк, Сан-Франциско и Ванкувер. Этот пример демонстрируется на C# и является частью документации на CTP-версию элемента управления Microsoft Bing Map для Silverlight. Я покажу, как реализовать его на IronRuby.
Сначала нужно изменить подставляемый в строку (встраиваемый) XAML-код, чтобы добавить еще три кнопки в новый раздел на левой стороне экрана — по одной на каждое местоположение: Нью-Йорк, Сан-Франциско и Ванкувер. Они реализуются во многом по аналогии с предыдущими кнопками. Одно заметное отличие заключается в добавлении атрибута Tag к каждому элементу Button. Атрибут Tag определяет координаты конкретного места и масштаб карты.
В следующем фрагменте кода показан XAML-код, добавляющий кнопку для Нью-Йорка:
<Button x:Name="newyork" Height="25" Width="100"
Content="New York" Margin="0,0,0,10" Foreground="Black"
VerticalAlignment="Center" HorizontalAlignment="Center"
Tag="40.7199,-74.0030,0.0000 12.0000"/>
Как видите, атрибут Tag предоставляет информацию о координатах для каждого местоположения. Когда пользователь щелкает кнопку, эта информация служит для фокусировки карты. Полный исходный код для кнопок поиска местоположений см. на сайте MSDN Magazine.
У большинства приложений есть строка заголовка, и этот пример не должен быть исключением. Я добавил заголовок «Microsoft Bing Maps Silverlight Control and IronRuby Integration» во второй столбец Grid, поместив существующий элемент Map в StackPanel вместе с элементом управления TextBlock, в котором содержится этот заголовок:
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock VerticalAlignment="Top"
HorizontalAlignment="Center" FontSize="20"
Foreground="Red" Margin="0,5,0,0"
Text="Microsoft Bing Maps Silverlight Control and IronRuby Integration" />
<m:Map x:Name="map_in_ironruby" Width="800" Height="400"
HorizontalAlignment="Center" Margin="0,50,0,20">
...
С презентационным уровнем мы закончили. Если вы сейчас запустите приложение, то должны увидеть три дополнительные кнопки в новом разделе Locate Location (найти место). Однако карта не будет фокусироваться на нужном участке при щелчке любой из только что добавленных кнопок. Для этого нужно реализовать соответствующий отделенный код для события Click каждой кнопки.
События Click одинаковы для всех трех кнопок. Исходя из значения свойства Tag нажатой кнопки, передавайте координаты и масштаб как спецификацию для создания нового представления карты. Я использую метод Split для разделения координат и масштаба и метод SetView элемента управления Map для задания представления карты. На новом представлении карты будет показан определенный участок:
sm.newyork.click do |s,e|
tag_information = s.Tag.split
location_Converter = LocationConverter.new
location_info = location_Converter.ConvertFrom(tag_information[0].ToString)
sm.map_in_ironruby.SetView(location_info, tag_information[1]);
end
>
Чтобы создавать новое представление карты, вам также понадобится добавить в программу ссылку на Microsoft.Maps.MapControl.Design:
include Microsoft::Maps::MapControl::Design
И на этом все. Как видите, настраивать представления, фокусировать карту на других участках и реализовать дополнительные функции сравнительно легко.
Идем дальше
В заключение я хотел бы кратко ознакомить вас с тем, как выделять во внешние файлы строчный код сценариев (на XAML и IronRuby/IronPython).
Чтобы добиться модульности при программировании, сначала скопируйте конечный файл SilverlightMap.html и переименуйте его в SilverlightMap-ExternalScript.html. Затем скопируйте и вырежьте подставляемый в строку XAML-код из файла SilverlightMap-ExternalScript.html в новый пустой текстовый файл, после чего сохраните его как SilverlightMap.xaml. Далее скопируйте и вырежьте код на IronRuby из файла SilverlightMap-ExternalScript.html в новый пустой текстовый файл и сохраните его как SilverlightMap.rb.
Теперь обновите теги update script для XAML и IronRuby в файле SilverlightMap-ExternalScript.html, вставив в них атрибут src, указывающий путь к внешним файлам с кодом XAML и IronRuby:
<html><head>
<script src="/dlr/dlr.js" type="text/javascript"></script>
</head>
<body>
<script type="application/xml+xaml"
src="/samples/getting.started/SilverlightMap.xaml"
id="sl_map" Width="1350" Height="575">
</script>
<script type="application/ruby"
src="/samples/getting.started/SilverlightMap.rb"
class="sl_map">
</script>
</body> </html>
Наконец, скопируйте все три новых файла — SilverlightMap-ExternalScript.html, SilverlightMap.xaml и SilverlightMap.rb — в папку веб-сервера Sample/Getting.started. После этого, если вы откроете в браузере файл SilverlightMap-ExternalScript.html, вы получите ту же вращающуюся карту с поддержкой поиска конкретных участков.
Как создать то же приложение, используя традиционный подход (с помощью Chiron.exe), читайте в моей статье на сайте SilverlightStuff.net.