Разработка под Windows Phone: Часть 3: Использование возможностей платформы

Мобильная платформа – это не только элементы разметки и управления. Платформа Windows Phone позволяет разработчику максимально воспользоваться своими программными возможностями через задачи выбора и  запуска, элементы управления Map и WebBrowser,  также предоставляя доступ к таким своим аппаратным возможностям, как акселерометр и гелокационным данным.

Задачи запуска и выбора

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

Задачи запуска (launchers)

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

  1. Создать экземпляр класса задачи запуска.
  2. Установить свойства полученного объекта.
  3. Вызвать метод Show объекта.

Разработчику доступны следующие задачи запуска:

Задача запуска Краткое описание
BingMapsDirectionsTask Запускается приложение Bing Maps  и отображается путь между двумя точками. Может указываться одна точка или обе. В случае указания одной точки за вторую будет взято текущее местоположение пользователя.
BingMapsTask Запускается приложение Bing Maps, можно передать строчку поиска.
ConnectionSettingsTask Отображается интерфейс сетевых настроек.
EmailComposeTask Запускается e-mail приложение c отображением интерфейса создания нового сообщения. Позволяет пользователю посылать e-mail из ваших приложений.
MarketplaceDetailTask Запускается клиентское приложение Windows Phone Marketplace с отображением страницы детальных сведений определенного приложения.
MarketplaceHubTask Запускается клиентское приложение Windows Phone Marketplace.
MarketplaceReviewTask Запускается клиентское приложение Windows Phone Marketplace с отображением страницы обзора определенного приложения
MarketplaceSearchTask Запускается клиентское приложение Windows Phone Marketplace и отображаются результаты поиска по указанному поисковому запросу.
MediaPlayerLauncher Запускается медиаплеер.
PhoneCallTask Запускается приложение Phone. Позволяет пользователю звонить из вашего приложения.
SearchTask Запускается приложение поиска.
ShareLinkTask Позволяет пользователю опубликовать ссылку в указанной социальной сети.
ShareStatusTask Позволяет пользователю опубликовать сообщение в указанной социальной сети.
SmsComposeTask Запускается приложение Messaging с отображением интерфейса создания нового сообщения.
WebBrowserTask Запускается веб-браузер.


Создайте новый проект из шаблона Windows Phone Application, как мы это уже делали в первой части и назовите его ExploreLaunchers. Обратите внимание, что весь код, для простоты, будем вставлять в страницу MainPage.xaml внутрь элемента Grid:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"></Grid>

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

<!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel Orientation="Horizontal">
                <StackPanel Width="240">
                    <Button Name="EMailMe" Width="190" Height="75" Content="E-mail" Click="EMailMe_Click"/>
                    <Button Name="AppDetails" Width="190" Height="75" Content="Программа" Click="AppDetails_Click"/>
                    <Button Name="MarketHub" Width="190" Height="75" Content="Рынок" Click="MarketHub_Click" />
                    <Button Name="MarketSearch" Width="190" Height="75" Content="Поиск по ..." Click="MarketSearch_Click"/>
                    <Button Name="MediaPlayer" Width="190" Height="75" Content="Плеер" Click="MediaPlayer_Click"/>
                </StackPanel>
                <StackPanel Width="240">
                    <Button Name="PhoneCall" Width="190" Height="75" Content="Звонок" Click="PhoneCall_Click"/>
                    <Button Name="Search" Width="190" Height="75" Content="Искать" Click="Search_Click"/>
                    <Button Name="SendSms" Width="190" Height="75" Content="SMS" Click="SendSms_Click"/>
                    <Button Name="WebBrowse" Width="190" Height="75" Content="Веб" Click="WebBrowse_Click"/>
                </StackPanel>                                
            </StackPanel>
        </Grid>
    </Grid>

Добавьте в блок using следующий модуль.

using Microsoft.Phone.Tasks;

Сразу после конструктора MainPage добавьте следующий код: private void EMailMe_Click(object sender, RoutedEventArgs e) { EmailComposeTask compose = new EmailComposeTask(); compose.To = "rush4apps@microsoft.com"; compose.Body = "Крутая иннициатива! Я хочу участовать! Продлите акцию, пожлайста!"; compose.Show(); }

    private void AppDetails_Click(object sender, RoutedEventArgs e)
    {
        //MarketplaceDetailTask marketDetails = new MarketplaceDetailTask();
        //marketDetails.ContentType = MarketplaceContentType.Applications;
        //marketDetails.ContentIdentifier = &quot;введите сюда идентфикатор приложения&quot;;
        //marketDetails.Show();
    }

    private void MarketHub_Click(object sender, RoutedEventArgs e)
    {
        MarketplaceHubTask marketHub = new MarketplaceHubTask();
        marketHub.ContentType = MarketplaceContentType.Applications;
        marketHub.Show();
    }

    private void MarketSearch_Click(object sender, RoutedEventArgs e)
    {
        MarketplaceSearchTask marketSearch = new MarketplaceSearchTask();
        marketSearch.ContentType = MarketplaceContentType.Applications;
        marketSearch.SearchTerms = &quot;GPSInfo&quot;;
        marketSearch.Show();
    }

    private void MediaPlayer_Click(object sender, RoutedEventArgs e)
    {
        //MediaPlayerLauncher player = new MediaPlayerLauncher();
        //player.Controls = MediaPlaybackControls.Stop;
        //player.Media = &quot;укажите URI с которого будет играть контент&quot;;
        //player.Show();
    }

    private void PhoneCall_Click(object sender, RoutedEventArgs e)
    {
        PhoneCallTask call = new PhoneCallTask();
        call.DisplayName = &quot;Главному разработчику&quot;;
        call.PhoneNumber = &quot;+7 555 555 5555&quot;;
        call.Show();
    }

    private void Search_Click(object sender, RoutedEventArgs e)
    {
        SearchTask search = new SearchTask();
        search.SearchQuery = &quot;rush4apps&quot;;
        search.Show();
    }

    private void SendSms_Click(object sender, RoutedEventArgs e)
    {
        SmsComposeTask sms = new SmsComposeTask();
        sms.Body = &quot;Windows Phone 7 - отличная платформа!&quot;;
        sms.Show();
    }

    private void WebBrowse_Click(object sender, RoutedEventArgs e)
    {
        WebBrowserTask web = new WebBrowserTask();
        web.Uri = new Uri(&quot;https://msdn.com/ru-ru/&quot;);
        web.Show();
    }</code></pre>

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

Соберите и запустите приложение (F5), посмотрите, как работают задачи запуска.

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

Задачи выбора (choosers)

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

  1. Создать экземпляр класса задачи запуска.
  2. Добавить обработчик события Completed.
  3. Установить свойства полученного объекта.
  4. Вызвать метод Show объекта.
  5. Получить данные от задачи выбора в обработчике события Completed.

Разработчику доступны следующие задачи выбора:

Задача выбора Краткое описание
AddressChooserTask Запускает приложение Contacts. Используется, чтобы получить адрес контакта, выбранного пользователем.
CameraCaptureTask Запускает приложение Camera. Используется, чтобы дать возможность пользователю сделать фотографию из вашего приложения.
EmailAddressChooserTask Запускает приложение Contacts. Используется, чтобы получить e-mail адрес контакта, выбранного пользователем.
GameInviteTask Отображает экран приглашения в многопользовательскую сессию игры.
PhoneNumberChooserTask Запускает приложение Contacts. Используется, чтобы получить телефон  контакта, выбранного пользователем.
PhotoChooserTask Запускает приложение PhotoChooser. Используется, чтобы дать возможность пользователю выбрать картинку.
SaveContactTask  
SaveEmailAddressTask Запускает приложение Contacts. Используется, чтобы сохранить e-mail адрес в новый или существующий контакт.
SavePhoneNumberTask Запускает приложение Contacts. Используется, чтобы сохранить номер телефона в новый или существующий контакт.
SaveRingtoneTask Позволяет пользователю сохранить аудиофайл как системный рингтон.                     


Создайте новый проект из шаблона Windows Phone Application, как мы это уже делали в первой части и назовите его ExploreChoosers. Обратите внимание, что весь код, для простоты, будем вставлять в страницу MainPage.xaml внутрь элемента Grid:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"></Grid>

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

<!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <Button Width="190" Height="75" Name="Camera" Content="Камера" Click="Camera_Click"/>
                <Button Width="190" Height="75" Name="Email" Content="EMail" Click="Email_Click"/>
                <Button Width="190" Height="75" Name="Phone" Content="Телефон" Click="Phone_Click"/>
                <Button Width="190" Height="75" Name="ChoosePhoto" Content="Фото" Click="ChoosePhoto_Click"/>
                <Button Width="190" Height="75" Name="SaveEMail" Content="Сохр. EMail" Click="SaveEMail_Click"/>
                <Button Width="190" Height="75" Name="SavePhone" Content="Сохр. тел." Click="SavePhone_Click"/>
            </StackPanel>
        </Grid>

Добавьте в блок using следующий модуль.

using Microsoft.Phone.Tasks;

Сразу после конструктора MainPage добавьте следующий код:

private void Camera_Click(object sender, RoutedEventArgs e)
        {
            CameraCaptureTask camera = new CameraCaptureTask();
            camera.Completed += new EventHandler<PhotoResult>(camera_Completed);
            camera.Show();
        }
 
        void camera_Completed(object sender, PhotoResult e)
        {
            if (e.TaskResult == TaskResult.OK)
                MessageBox.Show("Имя файла:" + e.OriginalFileName);
        }
 
        private void Email_Click(object sender, RoutedEventArgs e)
        {
            EmailAddressChooserTask email = new EmailAddressChooserTask();
            email.Completed += new EventHandler<EmailResult>(email_Completed);
            email.Show();
        }
 
        void email_Completed(object sender, EmailResult e)
        {
            if (e.TaskResult == TaskResult.OK)
                MessageBox.Show(e.Email);
        }
 
        private void Phone_Click(object sender, RoutedEventArgs e)
        {
            PhoneNumberChooserTask phone = new PhoneNumberChooserTask();
            phone.Completed += new EventHandler<PhoneNumberResult>(phone_Completed);
            phone.Show();
        }
 
        void phone_Completed(object sender, PhoneNumberResult e)
        {
            if (e.TaskResult == TaskResult.OK)
                MessageBox.Show(e.PhoneNumber);
        }
 
        private void ChoosePhoto_Click(object sender, RoutedEventArgs e)
        {
            PhotoChooserTask photo = new PhotoChooserTask();
            photo.Completed += new EventHandler<PhotoResult>(photo_Completed);
            photo.Show();
        }
 
        void photo_Completed(object sender, PhotoResult e)
        {
            if (e.TaskResult == TaskResult.OK)
                MessageBox.Show(e.OriginalFileName);
        }
 
        private void SaveEMail_Click(object sender, RoutedEventArgs e)
        {
            SaveEmailAddressTask saveEmail = new SaveEmailAddressTask();
            saveEmail.Completed += new EventHandler<TaskEventArgs>(saveEmail_Completed);
            saveEmail.Email = "rush4apps@microsoft.com";
            saveEmail.Show();
        }
 
        void saveEmail_Completed(object sender, TaskEventArgs e)
        {
            if (e.TaskResult == TaskResult.OK)
                MessageBox.Show("EMail сохранен!");
        }
 
        private void SavePhone_Click(object sender, RoutedEventArgs e)
        {
            SavePhoneNumberTask savePhone = new SavePhoneNumberTask();
            savePhone.Completed += new EventHandler<TaskEventArgs>(savePhone_Completed);
            savePhone.PhoneNumber = "+7 495 555 5555";
            savePhone.Show();
        }
 
        void savePhone_Completed(object sender, TaskEventArgs e)
        {
            if (e.TaskResult == TaskResult.OK)
                MessageBox.Show("Номер сохранен!");
        }

Мы добавили кнопок по количеству демонсрируемых задач выбора и в обработчике соответствующей кнопки регистририруем обработчик события Completed и вызываем интерфейс задачи выбора.

Соберите и запустите приложение (F5), посмотрите, как работают задачи выбора.

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

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

Чтобы познакомиться с элементом управления Map, давайте создадим новый проект из шаблона 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.

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

Чтобы познакомиться с элементом управления Map, давайте создадим новый проект из шаблона Windows Phone Application, как мы это уже делали в первой части и назовём его ExploreWebControl.

После того, как создадите проект, посмотрите, на какие библиотеки он ссылается:

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

Обратите внимание, что никаких дополнительных ссылок не добавилось:

Двойным щелчком перейдем  к странице MainPage.xaml и посмотрим, что изменилось в XAML коде. 

<phone:WebBrowser HorizontalAlignment="Left" Margin="174,169,0,0" Name="webBrowser1" VerticalAlignment="Top" />

Используется пространство имён phone, которое было добавлено в шаблон по умолчанию:

xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

Оно содержит корневой элемент нашего XAML файла – элемент управления страницу приложения:

<phone:PhoneApplicationPage 
    x:Class="ExploreWebControl.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

Отредактируем XAML код элемента управления WebBrowser или воспользуемся панелью Properites так, чтобы элемент занимал большую часть  свободного пространства и переименуем элемент в MyBrowser, а также добавим прямо в XAML файл определение свойства Source, которое указывает элементу управления,  откуда брать HTML для рендеренга. В качестве примера страницы я используют заглавную страницу русского MSDN:

<phone:WebBrowser Name="MyBrowser" Source="https://msdn.com/ru-ru/"/>             

Запустите  приложение (F5) и посмотрите, как выглядит элемент управления во время исполнения.

Сделаем теперь свой простой браузер: добавим строку ввода и кнопку Go, а также небольшой код для передачи URL браузеру и запуску навигации.

XAML код:

<!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBox Name="Url" InputScope="URL" Width="380"/>
                    <Button Name="Go" Width="82" Content="Go" Click="Go_Click"/>
                </StackPanel>
            
             <phone:WebBrowser Name="MyBrowser" Source="https://msdn.com/ru-ru/" Height="530"/>
            </StackPanel>
        </Grid>

Обработчик события нажатия кнопки:

private void Go_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Uri url = new Uri(Url.Text);
                MyBrowser.Navigate(url);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

Запустите  приложение (F5) и проверьте, что оно работает и даже выводит сообщение об ошибке, при вводе неправильного URL.

Акселерометр

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

Давайте откроем ранее созданное приложение ExploreMapControl и добавим в него возможности акселерометра.

Для работы с акселерометром к проекту надо подключить библиотеку Microsoft.Devices.Sensors. Для этого в Solution Explorer правым щелчком мыши по папке Refernce вызовите контекстное меню, выберите Add Reference и добавьте Microsoft.Devices.Sensors.  Далее, в блок using добавим следующую директиву:

using Microsoft.Devices.Sensors;

Теперь мы готовы работать с акселерометром.

Сделаем простое приложение, которое при покачивании телефона выполняло масштабирование карты. При отклонении от себя масштаб будет увеличиваться, а при отклонении телефона на себя – уменьшаться.

Добавим глобальную переменную в класс представляющую акселерометр:

private Accelerometer myAccel;

В конструктор класса добавим инициализацию акселерометра, обработчик изменения его состояний и стартуем акселерометр:

myAccel = new Accelerometer();

myAccel.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(myAccel_CurrentValueChanged);

myAccel.Start();

Новый обработчик событий использует класс Vector3 из библиотеки Microsoft.Xna.Framework. Добавим в проект ссылку на библиотеку Microsoft.Xna.Framework, также как для Microsoft.Devices.Sensors, а также в блок using директиву:

using Microsoft.Xna.Framework;

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

private Vector3 currentValues;
Теперь можно перейти к описанию логики обработчика состояния акселерометра:
void myAccel_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
{
   if (myAccel.IsDataValid)
   {
      float deltaZ = (currentValues - e.SensorReading.Acceleration).Z;
      float Z = e.SensorReading.Acceleration.Z;
 
      currentValues = e.SensorReading.Acceleration;
 
      if (Z < 0 && deltaZ > 0)
      {
           //увеличиваем масштаб
      }
      if (Z > 0 && deltaZ < 0)
      {
           //уменьшаем масштаб
      }
    }
  
 }

Мы собираемся изменять состояние пользовательского интерфейса из потока акселерометра. Напрямую это невозможно. Функция обновления должна вызываться спциальным образом через объект Dispatcher:

Dispatcher.BeginInvoke 

Создадим два обработчика в классе:

private void HandleZoomIn()
  {
      MyMap.ZoomLevel += 1; 
  }
 
  private void HandleZoomOut()
  {
      MyMap.ZoomLevel -= 1;
  }

И добавим соответствующие вызовы через Dispatcher:

Dispatcher.BeginInvoke(() => HandleZoomIn()); 

и

Dispatcher.BeginInvoke(() => HandleZoomOut());                     

Результирующий класс будет выглядеть следующим образом:

private Accelerometer myAccel;
        private Vector3 currentValues;
 
        // Constructor
        public MainPage()
        {
            InitializeComponent();
 
            myAccel = new Accelerometer();
            myAccel.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(myAccel_CurrentValueChanged);
            
            myAccel.Start();
            currentValues = myAccel.CurrentValue.Acceleration;
        }
 
        
 
        void myAccel_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
        {
            if (myAccel.IsDataValid)
            {
                float deltaZ = (currentValues - e.SensorReading.Acceleration).Z;
                float Z = e.SensorReading.Acceleration.Z;
 
                currentValues = e.SensorReading.Acceleration;
 
                if (Z < 0 && deltaZ > 0)
                {
                    Dispatcher.BeginInvoke(() => HandleZoomIn());                    
                }
                if (Z > 0 && deltaZ < 0)
                {
                    Dispatcher.BeginInvoke(() => HandleZoomOut());                    
                }
            }
            
        }
 
        private void HandleZoomIn()
        {
            MyMap.ZoomLevel += 1; 
        }
 
        private void HandleZoomOut()
        {
            MyMap.ZoomLevel -= 1;
        }
 
        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();
            }         
 
        }

Запустите  приложение (F5) и проверьте, что оно работает, как ожидается. Воспользуйтесь возможностями эмулятора, который поддерживает эмуляцию акселерометра.

Геолокационные данные

Наконец перейдём к геолокационным сервисам, доступным на телефоне. Сервис предоставляет информацию, используя комбинацию информации получаемой от Wi-Fi, сотовой связи и данных от GPS приёмника. 

Давайте откроем ранее созданное приложение ExploreMapControl и добавим в него возможности предоставляемые сервисами геолокации.

Для начала, добавим в блок using следующую директиву:

using System.Device.Location; 

Теперь мы готовы работать с сервисами локаций/местоположения.

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

Добавим в класс определение переменной типа 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) и воспользуйтесь возможностями эмулятора по эмуляции геолкационных данных, чтобы проверить работу программы.  Увеличьте масштаб так, чтобы убедиться, что позиционирование и установка точки происходит правильно; также проверьте отображаемый статус сервисов геолокации.

Итоги и следующие шаги

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

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

Файлы для загрузки

Проект ExploreLaunchers
Проект ExploreChoosers
Проект ExploreMapControl
Проект ExploreMapControl с поддержкой геолокации
Проект ExploreMapControl с поддержкой акселерометра
Проект ExploreWebControl