
Знакомство начнём с элемента управления Map, который предоставляет интерфейс к картам на Windows Phone. Давайте создадим новый проект из шаблона Windows Phone Application и назовём его ExploreMapControl.
После того, как проект будет создан, посмотрите, на какие библиотеки он ссылается.

Далее, разверните Toolbox, если он свёрнут, и перетяните элемент управления Map в дизайнер интерфейса приложения.

Обратите внимание, что теперь проект ссылается на Microsoft.Phone.Controls.Map.

Двойным щелчком перейдем к странице MainPage.xaml и посмотрим, что изменилось в XAML коде. Добавился элемент управления Map из пространства имён my:
<my:Map Height="50" HorizontalAlignment="Left" Margin="201,198,0,0" Name="map1" VerticalAlignment="Top" Width="100" />
Посмотрев на заголовок XAML документа можно увидеть, что это за пространство имён:
xmlns:my="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"
Давайте заменим my на map, чтобы название пространства имён соответствовало его содержанию:
xmlns:map="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"
<map:Map Height="50" HorizontalAlignment="Left" Margin="201,198,0,0" Name="map1" VerticalAlignment="Top" Width="100" />
Отредактируем XAML код элемента управления Map или воспользуемся панелью Properites так, чтобы элемент занимал большую часть свободного пространства и переименуем элемент в MyMap:
<map:Map Name="MyMap"/>
Запустите приложение (F5) и посмотрите, как выглядит элемент управления во время исполнения.

Обратили внимание на белый баннер в центре экрана, который говорит, что у нас неправильные авторизационные данные? Это потому что этот элемент управления использует сервис карт от Bing и для его использования требуется регистрация. Зарегистрироваться и получить ключ можно на портале Bing Maps: http://www.bingmapsportal.com.
В завершение регистрации разработчик получает строковый ключ, который надо указать в свойстве CredentialsProvider элмента управления, также его можно вынести в ресурсы или данные.
Добавим простые элементы управления картой: уменьшение/увеличение масштаба, смена режима отображения карты.
<!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <map:Map Name="MyMap"> <Button Name="ZoomIn" Content="+" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="-100,0,0,-5" Click="ZoomIn_Click" FontWeight="Bold" Padding="0,-9,0,0"/> <Button Name="ZoomOut" Content="-" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="100,0,0,-5" Click="ZoomOut_Click" FontWeight="Bold" Padding="0,-9,0,0" /> <Button Name="LayoutChange" Content="L" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="0,0,0,-5" FontWeight="Bold" Padding="0,-9,0,0" Click="LayoutChange_Click"/> </map:Map> </Grid>
И обработаем эти события в коде приложения.
private void ZoomIn_Click(object sender, RoutedEventArgs e) { MyMap.ZoomLevel += 1; } private void ZoomOut_Click(object sender, RoutedEventArgs e) { MyMap.ZoomLevel -= 1; } private void LayoutChange_Click(object sender, RoutedEventArgs e) { if (MyMap.Mode is RoadMode) { MyMap.Mode = new AerialMode(true); } else { MyMap.Mode = new RoadMode(); } }
Не забудьте добавить в блок using следующую директиву:
using Microsoft.Phone.Controls.Maps;
Запустите приложение (F5) и проверьте, что наши элементы управления работают, как предполагается. В соответствии с Metro-дизайном панель с кнопками внизу окна приложения мы должны были бы оформить в виде Application Bar, только в целях упрощения примера мы используем более простой вариант. В качестве самостоятельного упражнения можете попробовать убрать кнопки, раскоментировать пример кода Application Bar в XAML файле и переделать приложение в соответствии со стилем Metro.
Теперь перейдём к геолокационным сервисам, доступным на телефоне. Сервис предоставляет информацию, используя комбинацию информации получаемой от Wi-Fi, сотовой связи и данных от GPS приёмника. Добавим теперь в наще приложение возможности предоставляемые сервисами геолокации.
Для начала, добавим в блок using следующую директиву:
using Microsoft.Devices.Sensors;
Теперь мы готовы работать с сервисами локаций/местоположения.
Сначала напишем простое дополнение к нашей программе, которое будет центрировать карту в соответствии с гелолокационными данными, полученными от сервисов.
Добавим в класс определение переменной типа GeoCoordinateWatcher, которая позволит нам инициализировать сервисы геолокации и полчать от них данные.
private GeoCoordinateWatcher myGeoWatcher;
В конструктор класса, сразу же после кода относящегося к акселерометру добавим код инициализации и регистрации на события изменения статуса сервисов (они могут быть недоступны, могут быть не готовы и т.д.) и события изменения положения.
yGeoWatcher = new GeoCoordinateWatcher(); myGeoWatcher.MovementThreshold = 100.0f; myGeoWatcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(myGeoWatcher_StatusChanged); myGeoWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(myGeoWatcher_PositionChanged);
Хорошее приложение должно правильно обрабатывать статусы геосервисов, т.к. они не всегда могут выдавать данные и могут тратить достаточно большое время на инициализацию. Для начала мы просто оставим обработчик пустым, так как тестировать приложением мы будем на эмуляторе, и там эти проблемы отсутствуют.
Также правильнее будет поместить запуск сервиса геолокации в отдельный поток, чтобы не тормозить загрузку приложения, в нашем первом варианте приложения, с учётом использования эмулятора мы пока будем запускать сервис прямо в конструкторе класса:
myGeoWatcher.TryStart(false, TimeSpan.FromSeconds(60));
Если Visual Studio автоматически сгенерировала нам обработчики событий StatusChanged и PositionChanged, закомментируйте или сотрите код в этих методах, вызывающий исключение NotImplemented:
throw new NotImplementedException();
В обработчик события PositionChanged добавьте код, центрирующий карту при изменении позиции:
void myGeoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) { MyMap.Center = e.Position.Location; }
Запустите приложение (F5) и воспользуйтесь возможностями эмулятора по эмуляции геолкационных данных, чтобы проверить работу программы. Увеличьте масштаб так, чтобы убедиться, что позиционирование происходи правильно.

Следующим шагом, улучшения нашей программы может стать запуск сервисов в другом потоке, добавление строки статуса геолокационных данных, а также создание на карте точки, отмечающей наше местоположение.
Для использования потоков, добавим в блок using следующую директиву:
using System.Threading;
В конструкторе до запуска сервисов добавим код:
new Thread(startMyGeoWotcher).Start();
После этого создадим функцию, не принимающую и не возвращающую значений, с именем startMyGeoWotcher и перенесём в неё код запуска сервисов:
void startMyGeoWotcher() { myGeoWatcher.TryStart(false, TimeSpan.FromSeconds(60)); }
Теперь добавим элемент управление TextBlock для отображения статуса сервисов геолокации
<!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel> <TextBlock Name="GeoStatus" HorizontalAlignment="Center" VerticalAlignment="Top" Text="Geo Status .." /> <map:Map Name="MyMap" Height="580"> <Button Name="ZoomIn" Content="+" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="-100,0,0,-5" Click="ZoomIn_Click" FontWeight="Bold" Padding="0,-9,0,0"/> <Button Name="ZoomOut" Content="-" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="100,0,0,-5" Click="ZoomOut_Click" FontWeight="Bold" Padding="0,-9,0,0" /> <Button Name="LayoutChange" Content="L" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="0,0,0,-5" FontWeight="Bold" Padding="0,-9,0,0" Click="LayoutChange_Click"/> </map:Map> </StackPanel> </Grid>
И допишем вывод статусов в обработчик StatusChanged:
void myGeoWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) { switch (e.Status) { case GeoPositionStatus.Disabled: if (myGeoWatcher.Permission == GeoPositionPermission.Denied) { GeoStatus.Text = "Сервис выключен"; } else { GeoStatus.Text = "На этом устройстве сервис недоступен"; } break; case GeoPositionStatus.Initializing: GeoStatus.Text = "Сервис инициализируется"; break; case GeoPositionStatus.NoData: GeoStatus.Text = "Данные о месположении недоступны"; break; case GeoPositionStatus.Ready: GeoStatus.Text = "Данные о местоположении доступны"; break; } }
Наконец, в обработчик события изменения позиции добавим установку точки на карте.
В класс добавим переменную типа Pushpin:
private Pushpin myPushpin;
Создадим её в конструкторе класса:
myPushpin = new Pushpin();
В обработчике изменения позиции установим её на текущую позицию и добавим на карту, если её там нет:
myPushpin.Location = e.Position.Location; if (!MyMap.Children.Contains(myPushpin)) MyMap.Children.Add(myPushpin);
Запустите приложение (F5) и воспользуйтесь возможностями эмулятора по эмуляции геолкационных данных, чтобы проверить работу программы. Увеличьте масштаб так, чтобы убедиться, что позиционирование и установка точки происходит правильно; также проверьте отображаемый статус сервисов геолокации.

UPD: Как правильно написал в коментариях andrew_kane. Работать с потоками из пула — это более правльная практика.
Ниже привожу код, который необходимо поменять, чтобы работать с потоками из пула, а не вызывать
new Thread(...).Start();
Вместо
new Thread(startMyGeoWotcher).Start();
нужно написать следующий код:
ThreadPool.QueueUserWorkItem(startMyGeoWotcher, myGeoWatcher);
Мы вынуждены передвать объект в метод, потому что использование пула требует статическогом метода и у нас есть 2 варианта, либо делать объект статическим, либо передать его в качестве параметра.
Теперь нужно переписать метод
Он должен теперь выглядеть следующим образом:startMyGeoWotcher
static void startMyGeoWotcher(object GeoWatcher) { ((GeoCoordinateWatcher)GeoWatcher).TryStart(false, TimeSpan.FromSeconds(60)); }
Полезные ссылки:
Центр разработки Windows Phone на MSDN
Windows Phone SDK 7.1
Форумы по разработке под Windows Phone на русском языке
