Microsoft — мировой лидер в области ПО и ИТ-услуг
458,92
рейтинг
13 января 2012 в 15:17

Разработка → Использование HTML5 и JavaScript для разработки приложений под Windows Phone


Cегодня я хочу рассказать о том, какие дополнительные возможность разработки приложений под Windows Phone есть у владеющих HTML5 и JavaScript.

Прежде всего разберёмся с тем, как это возможно. Все возможности, связанные с HTML5 + JS разработкой для Windows Phone связаны с присутствием на борту браузера Internet Explorer, который по поддерживаемому функционалу аналогичен настольному Internet Explorer 9. И это понятно, потому что они имеют общую базу кода. Ниже представлены основные (но не все) возможности, поддерживаемые браузером.

Наличие мобильного браузера с такой поддержкой уже предоставляет базовую возможность присутствия на мобильной платформе: создание богатого возможностями мобильного сайта. Но ведь мы хотели писать приложения! Разрабатывать приложения позволяет доступный разработчикам элемент управления WebBrowser:
<phone:WebBrowser Grid.Row="1" Name="MyBrowser" IsGeolocationEnabled="True"  IsScriptEnabled="True" Background="{StaticResource PhoneBackgroundBrush}"/>

Наличие полнофункционально мобильного сайта и возможность вставить в своё приложение элемент управления WebBrowes приводит нас к первому типу HTML5 приложений для Windows Phone: приложениям, которое хостят мобильный веб-сайт в элементе управления WebBrowser и добавляют дополнительно сервисные возможности, связанные с платформой, например, Push Notification.

Давайте напишем пример такого приложения. Хостить будем мобильный сайт Яндекса. Для простоты не будем выдумывать дополнительные сервисы, а добавим ApplicationBar и несколько кнопок, которые будут предоставлять доступ к наиболее востребованному по нашему мнению функционалу.

Как обычно, начинаем с создания приложения на основе шаблона Windows Phone Application. Используя NuGet, добавим в приложение пакет Silverlight for Windows Phone Toolkit. После этого, на основную страницу приложения MainPaige.xaml добавим элемент управления WebBrowser и PerformanceProgressBar, чтобы показывать процесс загрузки страницы. В результате XAML код будет выглядеть следующим образом:
<toolkit:PerformanceProgressBar Name="Progress" Height="10" IsIndeterminate="False" Background="{StaticResource PhoneBackgroundBrush}"/>

<phone:WebBrowser Grid.Row="1" Name="Yandex" IsGeolocationEnabled="True" IsScriptEnabled="True" Background="{StaticResource PhoneBackgroundBrush}"/>

Не забудье доавить ссылку на пространство имён, чтобы использовать элементы управления из Silverlight for Windows Phone Toolkit:
 xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" 

Чтобы сделать приложение в стиле Windows Phone и добавить в него кнопок, я раскоментировал ApplicationBar, и воспользовался информацие из статьи Где взять иконки для Windows Phone приложений?, чтобы найти необходимые моему приложению иконки. Также я сразу добавил к кнопкам на ApplicationBar обработчики событий. В результате XAML код моей страницы MainPage.xaml моего приложения стал выглядеть следующим образом:
<phone:PhoneApplicationPage 
             x:Class="MyYandexWithBJ.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://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="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
             mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
             FontFamily="{StaticResource PhoneFontFamilyNormal}"
             FontSize="{StaticResource PhoneFontSizeNormal}"
             Foreground="{StaticResource PhoneForegroundBrush}"
             SupportedOrientations="Portrait" Orientation="Portrait"
             shell:SystemTray.IsVisible="True">   

     <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>            
        <toolkit:PerformanceProgressBar Name="Progress" Height="10" IsIndeterminate="False"  Background="{StaticResource PhoneBackgroundBrush}"/>
       <phone:WebBrowser Grid.Row="1" Name="Yandex"  IsGeolocationEnabled="True" IsScriptEnabled="True" 
 Background="{StaticResource PhoneBackgroundBrush}"/>        
     </Grid>        
     
     <!--Sample code showing usage of ApplicationBar-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/icons/appbar.back.rest.png" Text="Назад" яlick="ApplicationBarBackButton_Click"/>
            <shell:ApplicationBarIconButton IconUri="/icons/appbar.home.png" Text="Домой" Click="ApplicationBarHomeButton_Click"/>
            <shell:ApplicationBarIconButton IconUri="/icons/appbar.plane.rotated.45.png" Text="Самолёты" Click="ApplicationBarFlightsButton_Click"/>
            <shell:ApplicationBarIconButton IconUri="/icons/appbar.next.rest.png" Text="Вперёд" Click="ApplicationBarForwardButton_Click"/>            
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>    
</phone:PhoneApplicationPage>

Иконки были добавлиены в папку icons в проекте, тип содержания установлен в Content, Copy to Output в Copy if newer. Само приложение при запуске теперь выглядит следующим образом:

Добавим код, который будет при загрузке приложения открывать мобильную страницу Яндкса, по нажатии на кнопку с домиком, открыать её же, а при нажатии на самолёт – отервать мобильную страницу с расписание самолётов.
// Constructor
public MainPage()
{
    InitializeComponent();
    Loaded += new RoutedEventHandler(MainPage_Loaded);
}    

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
   Yandex.Navigate(new Uri("http://m.yandex.ru"));      
}    
private void ApplicationBarHomeButton_Click(object sender, EventArgs e)
{   
    Yandex.Navigate(new Uri("http://m.yandex.ru"));
}    

private void ApplicationBarFlightsButton_Click(object sender, EventArgs e)
{
    Yandex.Navigate(new Uri("http://m.rasp.yandex.ru/station?plane=1"));
}

Теперь приложение при запуске открывает мобильную страницу Яндекса, а при нажатии на кнопку с самолётом, открывает мобильную страницу с расписанием самолётов:

Осталось доавить обработку кнопок вперёд/назад и отображение ProgressBar при навигации на страницу. Для того, чтобы добавить отображение ProgressBar, нужно доавить оброботчики событий Navigating и Navigated элемента управления WebBrowser:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    Yandex.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(Yandex_Navigated);
    Yandex.Navigating += new EventHandler<NavigatingEventArgs>(Yandex_Navigating);
    Yandex.Navigate(new Uri(http://m.yandex.ru)); }    

void Yandex_Navigating(object sender, NavigatingEventArgs e)
{
    Progress.IsIndeterminate = true;  
}   

void Yandex_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    Progress.IsIndeterminate = false;
}

Чтобы обрабоать нажатия кнопок вперёд и назад, воспользуемся возмостями JavaScript и объекта history, вместе с возможностью вызвать его из кода приложения:
private void ApplicationBarBackButton_Click(object sender, EventArgs e)
{
    Yandex.InvokeScript("eval", "history.go(-1)");
}      

private void ApplicationBarForwardButton_Click(object sender, EventArgs e)
{
    Yandex.InvokeScript("eval", "history.go(1)");
}

Теперь у нас есть собственное приложение “мобильный Яндекс” с кнопкой домой и расписанием самолётов. Хорошим завершением упражнения будет добавление определения возможности перехода по кнопкам назад/вперёд, чтобы иметь их активными только если переход можно действительно выполнить.

Это приложение использует удалёный HTML5+JS контент, логичным следующим шагом будет второй тип HTML5 приложений для Windows Phone, которое использует локальный контент на телефоне, не выходя, или минимально выходя, за пределы элемента управления WebBrowser.

Давайте напишем пример такого приложения. Это будте локальная HTML5 страничка, активное содержание которой будет генериться JS. Чтобы упростить разработку, будем использовать библиотеку jQuery.

Как обычно, начинаем с создания приложения на основе шаблона Windows Phone Application. Чтобы не писать HTML5 приложение самостоятельно, я взял SVG пример из доклада Кости Кичинского на TechEd Russia 2011.

В проекте создадим папку content и добавим в неё файлы svgdemo.htm и jquery-1.7.min.js. В файле svgdemo.html измените путь к скрипту jquery-1.7.min.js, так, чтобы тек script выглядел следубщим образом:
<script src="jquery-1.7.min.js" type="text/javascript"></script>

Также необходимо установить у файлов svgdemo.htm и jquery-1.7.min.js Build Action в Content и Copy to Output в Copy if newer.

Теперь, добавим элемент управления WebBrowser на главную страницу приложения так, чтобы он заполнял всё пространство. В результате XAML код будет выглядеть следующим образом:
<phone:PhoneApplicationPage 
 x:Class="IE9HTML.MainPage"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://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="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://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">    
     
     <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <phone:WebBrowser Name="myBrowser" IsScriptEnabled="True" />
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

На первый взгляд, собственно, разработка закончена (с учётом того, что мы взяли готовое HTML5 приложение), однако ещё остаётся один тонкий момент. Дело в том, что наш файл HTML и JS после развёртывания приложения будут недоступны элементу управления WebBrowser, так как они будут находиться не в Isolated Storage. Поэтому нам, при запуске приложения необходимо развернуть наше HTML5 приложение в Isolated Storage. Будем для этого использовать пример кода из MSDN, изменив в нём набор файлов на собственный:
private void SaveFilesToIsoStore()
{
    //These files must match what is included in the application package,
    //or BinaryStream.Dispose below will throw an exception.
    string[] files = {
    "content/svgdemo.htm",            
    "content/jquery-1.7.min.js"
    };       

     IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();     
    
    if (false == isoStore.FileExists(files[0]))
    {
        foreach (string f in files)
        {
            StreamResourceInfo sr = Application.GetResourceStream(new Uri(f, UriKind.Relative));
            using (BinaryReader br = new BinaryReader(sr.Stream))
            {
                byte[] data = br.ReadBytes((int)sr.Stream.Length);
                SaveToIsoStore(f, data);
            }
        }
    }
}   

private void SaveToIsoStore(string fileName, byte[] data)
{
    string strBaseDir = string.Empty;
    string delimStr = "/";
    char[] delimiter = delimStr.ToCharArray();
    string[] dirsPath = fileName.Split(delimiter);        
    //Get the IsoStore.
    IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();        
    
    //Re-create the directory structure.
    for (int i = 0; i < dirsPath.Length - 1; i++)
    {
        strBaseDir = System.IO.Path.Combine(strBaseDir, dirsPath[i]);
        isoStore.CreateDirectory(strBaseDir);
    }        
    
    //Remove the existing file.
    if (isoStore.FileExists(fileName))
    {
        isoStore.DeleteFile(fileName);
    }        
    
    //Write the file.
    using (BinaryWriter bw = new BinaryWriter(isoStore.CreateFile(fileName)))
    {
        bw.Write(data);
        bw.Close();
    }
}

Метод SaveFilesToIsoStore() проверяет, что указанных файлов нет в Isolated Storage и копирует их, используя вспомогательный метод SaveToIsoStore, сохраняя иерархию директорий.

Теперь всё готово, что написать код, который закончит инициализацию нашего HTML5-приложения:
// Constructor
public MainPage()
{
    InitializeComponent();       
    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}    

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    SaveFilesToIsoStore();
    myBrowser.Navigate(new Uri("content/svgdemo.htm", UriKind.Relative));
}

Сохраняем файлы на IsolatedStorage и отображем основной HTML5 файл в элементе управления WebBrowser. Работающее приложение выглядит следующим образом:

Мне кажется, что это отличный пример. Спасибо за него Косте (kichik).

Если нам уже мало чистого HTML5 + JS и мы хотим выйти за пределы элемента управления браузера, можно говорить о третьем типе HTML5 приложений для Windows Phone, которые использует не только локальный контент на телефоне, но и выходят, за пределы элемента управления WebBrowser, используя возможности платформы (например, акселерометр). Мы можем сделать подобное приложение “с нуля “ самостоятельно, используя возможность оповестить платформу о происходящем (window.external.notify/ScriptNotify) или вызвать JS скрипт из C# кода (WebBrowser.InvokeScript). Но лучше воспользоваться уже существующим решнием – PhoneGap.

На момент написания статьи была доступна версия 1.3. Скачать его можно на сайте phonegap.com, там же есть документация. После скачивания, распакуйте архив и из папки Windows Phone скопируйте файлы PhoneGapCustom.zip и PhoneGapStarter.zip в папку Visual Studio 2010\Templates\, выгрузите все запущенные версии Visual Studio. После запуска Visual Studio появится новый тип проектов:

Cоздадим проект на основе PhoneGapStarter. Псмотрим на состав проекта:

Собственно сам PhoneGap – это JS файл phonegap-1.3.0.js и ответная ему .NET CF библиотека WP7GapClassLib. Кроме этого, вместо встроенного элемента WebBrowser используется элемент управления PGView, который находится в той же WP7GapClassLib.

Вспомогательный файл ManifestProcessor.js используется, чтобы перед сборкой приложения обновить файл GapSourceDictionary.xml, который содержит список файлов контента (директория www проекта), которые в дальнейшем, как мы уже знаем из предыдущего примера, необходимо будет развернуть в Isolated Storage.

Напишем простое приложение, которое будет считывать данные акселерометра и отображать его на странице. Для этого заменим содержимое тега файла index.html на следующее:
<h1>Accelerometer sample</h1>
<div id="valueX"></div>
<div id="valueY"></div>
<div id="valueZ"></div>

Теперь нужно заменить скрипт на этой странице на свой, который работает с акселерометром (код взят из статьи David Rousset, Tutorial: how to create HTML5 applications on Windows Phone thanks to PhoneGap):
<script type="text/javascript">        
    
    document.addEventListener("deviceready", onDeviceReady, false);        
    
    // variable to output the current x, y & z values of the accelerometer
    var valueX;
    var valueY;
    var valueZ;        
    
    // when PhoneGap tells us everything is ready, start watching the accelerometer
    function onDeviceReady() {
        valueX = document.getElementById("valueX");
        valueY = document.getElementById("valueY");
        valueZ = document.getElementById("valueZ");
        startWatch();
    }        

   // start monitoring the state of the accelerometer
    function startWatch() {
        var options = { frequency: 500 };
        navigator.accelerometer.watchAcceleration(onSuccess, onError, options);
    }            

   // if the z-axis has moved outside of our sensitivity threshold, move the aarvark's head in the appropriate direction
    function onSuccess(acceleration) {
        valueX.innerHTML = "X: " + acceleration.x;
        valueY.innerHTML = "Y: " + acceleration.y;
        valueZ.innerHTML = "Z: " + acceleration.z;
    }        function onError() {
        alert('onError!');
    }
</script>

Собтвенно, единсвенное, что требует объяснения – это объект navigator, который нам позволил подключиться к акселерометру. Это собственно часть PhoneGap. Подробнее о PhoneGap API можно прочитать на сайте phonegap.com.

Остаётся только запустить приложение и удостовериться, что оно работает:

В качестве примера возможностей платформы, хочу привести снимок экрана игры на HTML5, из той же статьи David Rousset:


Проекты примеров:
MyYandex
SVG Demo
PhoneGap Accelerometr Sample

Полезные ссылки:
Live Tiles и фоновые агенты
Центр разработки Windows Phone на MSDN (курс по Windows Phone)
Windows Phone SDK 7.1
Форумы по разработке под Windows Phone на русском языке
Автор: @stasus
Microsoft
рейтинг 458,92
Microsoft — мировой лидер в области ПО и ИТ-услуг

Комментарии (18)

  • 0
    И всем вдруг легче стало жить. Спасибо.
  • 0
    такой вопрос: к примеру у вас есть приложение на хтмл5, на котором вы хотите заработать денег — вы создаете проект, кидаете веббраузер, хардкодите в него ссылку и отправляете маркет выставляя цену, допустим, в 2$ — внимание, вопрос: что мешает мне узнать тот самый урл и просто забить его в браузере бесплатно?
    • +1
      Для того, чтобы просить 2$ нужно добавить к проложению такого типа какой-нибудь дополнительный сервис. Например, оповещения с исользованиием Push Notification и/или Background Agent.

      Либо это может быть бесплатный клиент к сайту, просто позволяющий пользователю несколько удобнее с ним взаимодейстовать. Тогда он разрабатывается «собственником» сайта.
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Приделайте аутентификацию юзера и продавайте.
    • 0
      скорее, тут применима другая модель.
      деньги нужно получать за отображаемый контент, а не за сам факт доступа к нему

      например, на Андроид маркете и Эппл Апп Сторе безумно популярны 10 игр-близнецов от Storm8, которые не делают ничего, только отображают HTML-контент с заданного адреса

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

      пользователь же ищет игрушки не в поисковике, а в Магазине приложений. держите, получите и распишитесь. а заплатите позже
    • +1
      Вы статью до конца прочитали? Вариант с локальным контентом без какой-либо ссылки на внешний источник — то, что вам нужно.
  • 0
    извиняюсь за оффтоп, но меня так удручает наличие «викинги-герои-ТЮРЬМА» на главной даже яндекса, что не смог не упомянуть об этом.

    а за тему спасибо!
  • +1
    Интересующимся phonegap: gotiggr.com
    • 0
      Там же только iOS и Android или я неправильно понял?
      • 0
        да, но если wp7 наберет хоть сколько либо значимую долю на мобильном рынке. Конечно же будет добавлен. Это лишь генератор проектов с возможностью набросать с юаем костяк, плюс набросать авторизацию рест и т.п. забиндить ответ из json скажем на нужные контролы, и забиндить что отсылать назад, быстро отредактировать js там же и скачать готовое приложение, либо заэкспортить скажем это все в эклипс.
  • 0
    На текущем проекте столкнулся с тем что mobile ie9 не совсем корректно работает с svg через jquery, а именно:
    после динамического создания некоторого количества polygon попробовал собрать их в коллекцию jquery путем присвоения им класса ( $('.className') ), так вот на выходе ничего — пусто, хотя в iOS например все четко работает.
    Больше всего разочаровал нативный браузер в Android — он тупо не отрисовывает даже polygon, уж не знаю в чем дело.
    • 0
      о! буквально сейчас понял в чем дело — он не ищет потомков через закешированного в переменную родителя :)
      • 0
        Чудотворная статья :)
  • +3
    Не могу заставить себя «любить» HTML 5
  • 0
    Подобные мобильные приложения выглядят не очень приятно на фоне нативных. К тому же зачастую в угоду кросс-платформенности нарушается рекомендованная гайдлайнами модель навигации (то, что отлично работает на iOS, например, оказывается непривычным для wp7). Непосредственно в данном случае — кнопка Home и Back/Forward на application bar. Писать для каждой платформы отдельную версию сайта — занятие глупое. Почему бы тогда уже не использовать нативные средства разработки? За статью спасибо.
    • 0
      В статье рассматривается 3 типа приложений. Для 2-х из них сайт не нужен.

      Использовать приложение — сайт имеет смысл, обычно, только если мобильный сайт уже есть. Например, приложение Одноклассники для WP.
  • 0
    Стас, кажется у тебя буква «б» на ноуте не работает — несколько раз встречается слово «доавить» :-)

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

Самое читаемое Разработка