Showing posts with label Bing Maps. Show all posts
Showing posts with label Bing Maps. Show all posts

Sunday, March 13, 2011

Bing Maps на Windows Phone 7. Полные исходники

Я планировал сделать целую серию заметок о создании простого Bing Maps приложения на Windows Phone 7, но жизнь вносит свои коррективы. Большое количество работы, сдача сертификации Microsoft, а также планирование отпуска отбирают все свободное время. К тому же, судя по отсутствию комментариев, эта тема никого особо не трогает. Поэтому эта серия останется незавершенной, но я все-таки хочу выложить все исходники проекта – может, кому-то пригодятся для самостоятельного изучения.

Исходники лежат на Google Code. Вот ссылка на папку Downloads проекта: http://code.google.com/p/wp7-wikimapia-client/downloads/list.

Если будут вопросы по коду или теме в целом – обращайтесь.

Tuesday, February 1, 2011

Bing Maps на Windows Phone 7. Часть 2. Масштабирование карты

Первая часть серии была посвящена созданию простейшего Bing Maps приложения для Windows Phone 7. Во второй части мы рассмотрим улучшение контролов изменения масштаба карты (zoom) и коснемся интерфейса INotifyPropertyChanged.

Изменение ViewModel

Может быть, вы заметили, что в прошлой части мы установили свойству ZoomBarVisibility значение Visible. Стандартный Zoom Bar выглядит достаточно грубо, кроме того, отображается в нижней части карты, что не очень удобно. Поэтому мы его заменим на наш собственный.

Для начала устанавливаем свойству ZoomBarVisibility значение Collapsed (или просто удаляем его из списка свойств).

Далее нам нужно расширить наш MapViewModel, добавив туда свойство Zoom, которое будет источником данных для свойства ZoomLevel у контрола Map. Добавляем в MapViewModel свойство и некоторые константы для удобства:

private const double DefaultZoomLevel = 4.0;
private const double MaxZoomLevel = 21.0;
private const double MinZoomLevel = 3.0;
public double Zoom
{
    get { return zoom; }
    set
    {
        var coercedZoom = Math.Max(MinZoomLevel, Math.Min(MaxZoomLevel, value));
        if (zoom != coercedZoom)
        {
            zoom = value;
            NotifyPropertyChanged("Zoom");
        }
    }
}

Здесь вроде бы все понятно за исключением NotifyPropertyChanged. NotifyPropertyChanged – это метод-реализация интерфейса INotifyPropertyChanged. Этот интерфейс используется для оповещения классов, которые привязываются (binding) к источнику данных, об изменениях в этом источнике. В нашем случае контрол Map нашей View будет таким образом оповещаться об изменениях в свойстве Zoom.

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

Добавляем в папку ViewModel проекта новый класс BaseViewModel, который будет выглядеть следующим образом:

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null && !String.IsNullOrEmpty(propertyName))
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

И наследуем класс MapViewModel от BaseViewModel:

public class MapViewModel : BaseViewModel

Работа с представлением

Теперь нам нужно перейти во View и привязать свойство ZoomLevel контрола Map к свойству Zoom модели представления:

ZoomLevel="{Binding Zoom, Mode=TwoWay}"

Добавляем кнопки для масштабирования карты внутрь <canvas>:

<Button x:Name="ButtonZoomIn" 
    BorderThickness="0" Margin="8,200,0,0" Padding="0"
    HorizontalAlignment="Left" VerticalAlignment="Top" 
    Width="72" Height="72"
    Click="ButtonZoomIn_Click">
        <Image Source="Images/Menu/ZoomIn_White.png" />
</Button>
<Button x:Name="ButtonZoomOut" 
    BorderThickness="0" Margin="8,280,0,0" Padding="0"
    HorizontalAlignment="Left" VerticalAlignment="Top" 
    Height="72" Width="72"
    Click="ButtonZoomOut_Click">
        <Image Source="Images/Menu/ZoomOut_White.png" />
</Button>

Соединяем все вместе

Теперь нжуно реализовать обработку нажатий на кнопки. Для этого добавляем обработчики событий для кнопок, которые будут изменять свойство Zoom модели представления, в code-behind файл MainPage.xaml.cs:

private void ButtonZoomIn_Click(object sender, RoutedEventArgs e)
{
    Model.Zoom++;
}

private void ButtonZoomOut_Click(object sender, RoutedEventArgs e)
{
    Model.Zoom--;
}

Добавляем свойство Model в тот же code-behind файл:

protected MapViewModel Model
{
    get { return (MapViewModel)Resources["ViewModel"]; }
}

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

Если говорить строго, то написание логики представления (а в нашем случае это обработка событий и устновка свойства Zoom) – это уже отход от правильной реализации паттерна MVVM. Но в то же время, code-behind страницы – это все еще View, поэтому мы можем сами решать, как оповещать наш ViewModel о событиях UI. Поэтому для начальной реализации приложения мы будем использовать code-behind страницы как прокси к ViewModel для упрощения реализации. В реальном же приложении нам нужно было бы использовать т.н. команды для привязки событий ко View, а не обычные обработчики. Но поскольку Silverlight до сих пор не обзавелся официальной поддержкой команд (за исключением интерфейса ICommand), а писать свои с нуля мы еще не готовы, то пока что отложим этот вопрос на будущее. Если кому-то интересно узнать больше уже сейчас, вот несколько ссылок:

http://weblogs.asp.net/nmarun/archive/2009/12/02/using-icommand-silverlight-4.aspx
http://johnpapa.net/silverlight/5-simple-steps-to-commanding-in-silverlight/
http://blogs.southworks.net/jdominguez/2008/08/icommand-for-silverlight-with-attached-behaviors/
http://houseofbilz.com/archives/2009/05/22/adventures-in-mvvm-commands-in-silverlight/

Добавляем картинки

Итак, логика готова. Нам осталось лишь добавить картинки для кнопок. Для этого создаем папку Images\Menu в проекте и добавляем туда файлы ZoomIn_White.png и ZoomOut_White.png. Скачать все файлы картинок, включая эти две, можно отсюда. Напоследок нам нужно открыть окно Properties для каждой из этих картинок и установить там свойство Build Action в Content вместо Resource. Этим мы убиваем двух зайцев: уменьшаем размер сборки, тем самым сокращая время загрузки приложения, и упрощаем доступ к файлам из приложения.

Запускаем приложение и видим, что у нас появились кнопки масштабирования. Их можно покликать – они работают :)

В следующей части мы реализуем получение нашего положения в пространстве при помощи GeoLocation API и научимся центрировать карту в это положение.

Вся серия:

Часть 1. Введение
Часть 2. Масштабирование карты
Часть 3. Полные исходники

Sunday, January 30, 2011

Bing Maps на Windows Phone 7. Часть 1. Введение

Эта статья содержит примеры кода, подсвеченные с помощью JS SyntaxHighlighter, который почему-то не отрабатывает в Google Reader, а возможно и в других RSS ридерах. Чтобы нормально увидеть код, вам придется перейти в блог. Извините за неудобство.

Недавно я делал code session на встрече харьковской UNETA на тему создания картографического приложения для Windows Phone 7. По моим субъективным ощущениям все прошло неплохо, слушатели помогали мне в написании кода, активно задавали вопросы и поправляли меня, когда я где-нибудь лажал :), а также стойко терпели технические проблемы, связанные со взаимной нелюбовью моего ноутбука и местного проектора, за что им отдельное спасибо. Так как тема программирования под Windows Phone сейчас интересна многим людям, хотелось бы также выложить материалы этого code session в блог.

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

Требования

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

  1. отображение карты
  2. определение текущего местоположения, центрирование карты
  3. отображение различных объектов вокруг текущего местоположения
  4. отображение деталей объекта при клике на нем
  5. фильтрация объектов по типам

При этом мы попытаемся реализовать приложение в более-менее правильном стиле, с использованием паттерна MVVM.

Начнем с создания проекта и отображения простой карты.

Создание проекта

Перед тем, как начать программировать для Windows Phone 7, вам нужно убедиться, что у вас есть вся инфраструктура:

  • Visual Studio 2010 (есть бесплатная версия VS2010 Express for Windows Phone)
  • Windows Phone Developer Tools (Silverlight for Windows Phone, эмулятор, библиотеки и т.д.)
  • Bing Maps Silverlight control for Windows Phone
  • (опционально) XNA Game Studio 4.0
  • (опционально) Expression Blend (есть бесплатная версия Blend for Windows Phone)

Все это добро бесплатно и его можно скачать с сайта http://create.msdn.com/en-us/home/getting_started. После того, как установите основной пакет, не забудьте также установить October 2010 update to the Windows Phone Developer Tools.

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

Немного о платформе разработки

Как вы уже догадались, писать мы будем на Silverlight. Только не на обычном Silverlight 4, а на т.н. Silverlight for Windows Phone. Эта версия SL отличается от своего настольного брата немного урезанными возможностями и оптимизированной производительностью. В общем-то производительность для телефона – это все, потому что по сравнению с native-приложениями у нас и сам .NET довольно прожорливый, и тем более Silverlight.

Кроме программирования бизнес-приложений (и в более редких случаях игр) на SL, у нас также есть возможность программировать игры (и в более редких случаях бизнес-приложения) на XNA. XNA – это платформа и набор инструментов для разработки игр для Windows, Xbox, Zune и вот теперь еще Windows Phone 7.

image

Поддержка XNA в Windows Phone – это очень полезная вещь, т.к. разработчики игр под Xbox теперь могут использовать привычные инструменты и модель разработки для программирования игр под мобильную платформу, а также с меньшими трудозатратами перенести существующие игры на WP7.

Отображение карты

Вернемся к практике. После установки необходимых дополнений в Visual Studio появятся новые типы проектов. Поэтому идем в File -> New -> Project и создаем Windows Phone Application проект.

Начнем мы с отображения карты. Здесь все просто. Сначала добавим в проект reference на сборку Microsoft.Phone.Controls.Maps. Затем добавим namespace для Bing Maps контрола, а также сам контрол в XAML главной страницы приложения, попутно заменив основной Grid на Canvas, чтобы нам было проще позиционировать элементы в будущем:

xmlns:maps="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"
<!--LayoutRoot is the root grid where all page content is placed-->
<Canvas x:Name="LayoutRoot">
    <Grid Background="Transparent" Height="768" Width="480">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <TextBlock x:Name="ApplicationTitle" Grid.Row="0"  Text="My Places" Style="{StaticResource PhoneTextNormalStyle}"/>

	<maps:Map Grid.Row="1" Name="mapPlace" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
            ScaleVisibility="Visible"
            ZoomBarVisibility="Visible"
            CopyrightVisibility="Collapsed">

            <maps:Map.Mode>
                <maps:AerialMode ShouldDisplayLabels="True" />
            </maps:Map.Mode>

        </maps:Map>

    </Grid>
</Canvas>

Свойства контрола ScaleVisibility, ZoomBarVisibility и CopyrightVisibility определяют видимость соответствующих стандартных элементов карты. Надо отметить, что стандартный Zoom Bar располагается снизу (что не всегда удобно) + выглядит очень плохо, поэтому немного позже мы его улучшим.

Запустим приложение в эмуляторе. Как видите, приложение показывает карту:

image

Если присмотреться, то можно заметить, что посередине экрана белым шрифтом написан следующий текст:

Invalid Credentials. Sign up for for a developer account.

Этот текст обозначает, что нам нужно получить Bing Maps Developer Account и сообщить о нем контролу карты. Для этого идем на http://www.bingmapsportal.com/, логинимся туда при помощи Live ID (или сначала регистрируем его), и затем создаем ключ при помощи пункта меню Create or view keys.

Для того, чтобы карта заработала, достаточно установить свойство CredentialsProvider контрола:

CredentialsProvider="AoYOaLG9nor0LXJzvYrL18U8URccNTyacL-qfGUMBdvFseoLbF384hct4JxGVvI9"

Напомню, что ключ у вас должен быть свой. Это не важно для тестового проекта, но не уверен, что не будет проблем в случае публикации вашего приложения на MarketPlace.

Однако мы же пытаемся все делать правильно, используя паттерн MVVM, не так ли? Поэтому данный способ не совсем для нас. Этот ключ – данные, а данные должны связываться с View (представление, наша страница) через ViewModel (модель представления), специальный объект, содержащий весь набор данных, необходимых View для работы, а также логику представления. Выглядеть это должно где-то так:

image

Дополнительную информацию по паттерну MVVM и его примеры для Silverlight можно прочитать на страничке MSDN и в статье в MSDN Magazine.

Итак, нам нужно сделать ViewModel класс и как-то привязать его к View. Создадим в проекте папку ViewModel, в которой будут лежать все модели представления, а следом класс MapViewModel, который будет связываться с представлением нашей главной страницы). На данном этапе класс будет выглядеть следующим образом:

public class MapViewModel
{
    private const string Id = "AoYOaLG9nor0LXJzvYrL18U8URccNTyacL-qfGUMBdvFseoLbF384hct4JxGVvI9";
    private readonly CredentialsProvider credentialsProvider = new ApplicationIdCredentialsProvider(Id);

    public CredentialsProvider CredentialsProvider
    {
        get { return credentialsProvider; }
    }
}

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

Далее привязываем MapsViewModel к представлению в XAML файле страницы. Добавляем namespace:

xmlns:model="clr-namespace:WP7Test.ViewModel"

Говорим нашей View, что ее ViewModel будем класс MapsViewModel, установив свойство PhoneApplicationPage.Resources (перед началом тега Canvas):

<phone:PhoneApplicationPage.Resources>
    <model:MapViewModel x:Key="ViewModel" />
</phone:PhoneApplicationPage.Resources>

Устанавливаем свойство DataContext у Canvas:

<Canvas x:Name="LayoutRoot" DataContext="{StaticResource ViewModel}">

Теперь, когда ViewModel привязан к представлению, можно связать установить свойство CredentialsProvider контрола Map через стандартный data binding:

CredentialsProvider="{Binding ViewModel.CredentialsProvider}"

Вот и все. Запускаем, и видим, что наше приложение больше не показывает белую строчку.

В следующей части мы рассмотрим кастомизацию Zoom Bar.

Вся серия:

Часть 1. Введение
Часть 2. Масштабирование карты
Часть 3. Полные исходники