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

Осенью 2010 г. Microsoft заявила о себе как о важном игроке на рынке мобильных устройств, выпустив платформу Windows Phone 7. Несколько запоздав со своим ответом Apple и Google, корпорация, однако, создала систему, уже успевшую завоевать довольно большое число поклонников благодаря новизне и свежести своего пользовательского интерфейса, непохожести на все то, что существовало до нее на рынке. Осенью этого года у системы выйдет первое крупное обновление под кодовым названием Mango. Вместе с его появлением состоится и официальный запуск Windows Phone в России.

Особенно привлекательной новая платформа оказалась для разработчиков. Все приложения для Windows Phone 7 создаются в .NET Framework с помощью распространенных технологий Silverlight и XNA – а значит, большое число программистов без труда сумеет перейти на новую мобильную платформу. Работа в .NET позволяет применять любой совместимый с этой средой язык программирования. И хотя в свободно распространяемых инструментах Windows Phone SDK (ранее они назывались Windows Phone Developer Tools) в настоящее время поддерживаются только C# и Visual Basic, достаточно примеров использования и других языков (например, в разделе 7.5 книги Дмитрия Сошникова «Функциональное программирование на F#» есть пример на F#). Скачать Windows Phone SDK можно на сайте App Hub -- create.msdn.com. Кроме того, Microsoft сильно упрощает жизнь разработчикам, предъявляя единые аппаратные требования ко всем телефонам на базе своей платформы. Так, все они должны иметь одно разрешение экрана (480x800 точек), минимальную частоту процессора в 800 MГц и минимум 256-Mбайт RAM. Другими словами, программисту теперь не приходится беспокоиться о производительности своего приложения на разных устройствах, равно как и о создании нескольких версий программы для всех существующих разрешений экранов (ситуация, имеющая место при разработке продуктов, функционирующих под управлением Android). И наконец, необходимо упомянуть о том, что Marketplace (так называется интернет-магазин приложений для Windows Phone 7) сейчас находится в самом начале своего развития, которое обещает быть очень и очень бурным. Во время написания этой статьи в нем было около 20 тыс. приложений, причем их количество продолжает расти все ускоряющимися темпами. В то же время программ в Marketplace пока значительно меньше, чем в Apple AppStore или Android Market. Следовательно, у разработчика, имеющего в своем арсенале по-настоящему стоящее приложение, есть гораздо больше возможностей сделать его популярным и заработать на нем деньги.

Silverlight и XNA

Принимаясь за создание очередного приложения, каждый программист встает перед выбором, в каком из двух фреймворков он будет работать. Довольно часто при этом возникают определенные проблемы. Традиционно принято считать, что Silverlight больше годится для создания приложений со стандартным пользовательским интерфейсом – текстовыми полями, кнопками, переключателями (спектр их варьирует от простейших ToDo-менеджеров до больших клиентов для сервисов вроде Evernote), в то время как XNA следует использовать для разработки игр. Действительно, неоспоримое преимущество Silverlight заключается в том, что он позволяет быстро и легко создавать красивые приложения, обладающие богатым и привычным с точки зрения пользователя интерфейсом. Кроме того, Silverlight обладает удобными механизмами для работы с данными и их визуализации (например, так называемыми data bindings, помогающими привязывать определенную информацию к элементам управления, которые затем обновляются автоматически). Разработка же с применением XNA предполагает, что всю ответственность за то, что происходит при каждой перерисовке экрана, целиком берет на себя программист. Это приводит к тому, что разработчику приходится писать довольно большой объем рутинного кода. Однако XNA дает ему гораздо большую свободу действий, чем Silverlight. На самом деле, во многих случаях выбор зависит от личных предпочтений программиста. Так, в Marketplace есть большое количество казуальных игр, написанных на Silverlight, и не меньше программ справочного типа, сделанных в XNA.

Давайте и мы напишем какое-нибудь простенькое приложение. Скажем, пусть это будет программа, которая показывает время на аналоговых и цифровых часах и дает пользователю возможность переключаться между ними «перелистывающим» движением пальца. Причем мы создадим два приложения: одно -- на Silverlight, другое -- на XNA, и таким образом, чтобы они были как можно более похожими друг на друга. В результате нам удастся наглядно продемонстрировать различия в основных подходах при разработке в Silverlight и XNA.

Приложение Simple Watch в Silverlight

Одна из главных особенностей разработки в Silverlight -- строгое разграничение кода, отвечающего за вывод пользовательского интерфейса, и кода, реализующего логику приложения. Более того, обе части программы, вообще пишутся на разных языках. За представление интерфейса отвечает XML-подобный декларативный язык разметки XAML, а каждая созданная таким образом страница приложения поддерживается так называемым “code-behind” файлом на одном из языков .NET (в наших примерах мы будем использовать C#), содержащим в себе обработчики событий, код инициализации и прочую логику программы.

Создадим в Windows Phone SDK новый проект типа Windows Phone Pivot application. В правой части экрана будет располагаться шаблонный код главной страницы (файл MainPage.xaml), в левой – вид нашего приложения на телефоне (рис. 1). Содержимое страницы находится между открывающим и закрывающим тегами . Заменим этот код на следующий:

     Background="Transparent">

Header="analog">







 

Попробуем разобраться в том, что здесь происходит. Мы имеем корневой элемент типа Grid, в который помещаются все остальные элементы. Вообще, в приложениях для Windows Phone элемент Grid является, пожалуй, самым распространенным «контейнером». В открывающем теге задаются значения его имени и цвета фона. Так как размеры явно не указываются, Grid заполняет собой весь экран. Он является родительским элементом для следующего, Pivot (из пространства имен Microsoft.Phone.Controls, подключающегося в открывающем теге страницы под псевдонимом controls). Pivot представляет собой набор страниц (типа PivotItem), переключение между которыми осуществляется «перелистывающим» движением пальца. В нашем приложении будет две страницы с заголовками analog и digital. Их содержимое мы разместим между соответствующими открывающими и закрывающими тегами .

Начнем с аналоговых часов. Для них нам потребуется объект типа Ellipse (это будет окружность, ограничивающая область часов) и три объекта типа Line, отвечающие за стрелки. Эти четыре элемента мы расположим в новом Grid, который мы поместим в корень первой страницы нашего Pivot. Кроме того, в открывающий тег нам придется добавить ссылку на пространство имен System.Windows.Shapes, где описывается тип Line:


xmlns:shapes ="clr-namespace:System.Windows.Shapes;assembly=System.Windows"
…>




Stroke="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>

X1="228" Y1="163"
X2="228" Y2="303"
Stroke="Aquamarine"/>

Свойства HorizontalAlignment и VerticalAlignment позволяют задать расположение элемента относительно его родителя. Размеры эллипса и координаты линии в данном примере задаются явно (причем линию мы разместили так, чтобы один из ее концов находился в центре страницы, и чуть позже мы сможем легко задать вращение вокруг этой центральной точки). Наконец, свойство Stroke отвечает за цвет линий.

Теперь, когда первая страница нашего приложения готова, возникает вопрос: как оживить ее, как заставить стрелки двигаться? Мы вполне могли бы решить эту задачу в коде на C#, ежесекундно получая с помощью таймера значение системного времени и поворачивая стрелки часов на соответствующие углы. Похожий пример с аналоговыми часами, где используется такой подход, можно найти в главе 13 книги Programming Windows Phone 7 (PDF-файл с ее русским переводом доступен по ссылке http://bit.ly/k1DK9K). Однако давайте продемонстрируем всю мощь XAML. На деле данный язык оказывается способным на большее, чем на простую разметку страницы. Так, в нем легко задавать разного рода анимации, а ведь это то, что нам нужно! Например, секундная стрелка в нашей программе будет циклически осуществлять поворот на 360 о за минуту:





Storyboard.TargetProperty="Angle"
By="360" Duration="0:1:0"
RepeatBehavior="Forever"/>



Чтобы привязать эту анимацию к секундной стрелке, отредактируем объявление соответствующего элемента:

X1="228" Y1="163"
X2="228" Y2="303"
Stroke="Aquamarine"
RenderTransformOrigin="0.5 0.5">



Здесь свойство RenderTransformOrigin задает координаты точки, относительно которой будет осуществляться вращение (каждая координата может принимать значения от 0 до 1, мы же указываем центральную точку страницы), а свойство RenderTransform – тип трансформации линии. Обратите внимание на формат записи: нам пришлось разбить тег Line на открывающий и закрывающий, внутри них разместить еще пару, отвечающую за свойство RenderTransform, и уже туда поместить объект типа RotateTransform, содержащий ссылку на объявленную нами ранее анимацию.

Осталось одно – при запуске программы задать начальное положение стрелок, соответствующее текущему времени (рис. 2). Необходимо запустить и саму анимацию. Откроем файл MainPage.cs, дополняющий XAML-файл разметки. В конструктор страницы поместим следующий код:

public MainPage()
{
InitializeComponent(); //Задаем начальный поворот стрелок (углы в градусах)
DateTime now = DateTime.Now;
RotateSec.Angle = (now.Second) * 6;
RotateMin.Angle = (now.Minute) * 6 + RotateSec.Angle / 60;
RotateHour.Angle = (now.Hour % 12) * 30 + RotateMin.Angle / 12;
//Запускаем анимацию
storyboard1.Begin();
}

 

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




HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0 0 0 150"/>

Отметим использование здесь свойств Margin и Padding, вообще довольно часто встречающихся в коде разметки. Margin отвечает за отступы снаружи элемента управления (в случае кнопки это -- отступ на 50 единиц снизу). Padding же задает отступ содержимого внутри элемента. Так, для кнопки это свойство увеличивает ее размеры на 30 единиц во всех направлениях из центра.

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


using System.Windows.Threading; //Для класса DispatcherTimer
public partial class MainPage: PhoneApplicationPage
{
bool ShowTime = true; //Определяет, в каком режиме работают цифровые часы
public MainPage()
{

DispatcherTimer tmr = new DispatcherTimer();
tmr.Interval = TimeSpan.FromSeconds(1); //Обновление -- каждую секунду
tmr.Tick += delegate
{ //При каждом срабатывании таймера обновляем время или дату
tbTime.Text = ShowTime? DateTime.Now.ToLongTimeString():
DateTime.Now.ToShortDateString();
tmr.Start(); //Запускаем таймер

}

private void btSwitchMode_Click(object sender, RoutedEventArgs e)

{

ShowTime =! ShowTime;
btSwitchMode.Content = ShowTime ? «Date": "Time"

//Сразу же обновляем и основной текст, чтобы пользователь не ощутил
//задержки (период срабатывания таймера -- 1 секунда)
tbTime.Text = ShowTime ? DateTime.Now.ToLongTimeString():
DateTime.Now.ToShortDateString();

}
}

Наше небольшое приложение готово. Весь проект целиком читатель сможет найти на диске. Из Windows Phone SDK его можно запустить на эмуляторе и полюбоваться проделанной работой (рис. 3). Программа содержит минимум кода, но благодаря использованию стандартных элементов Silverlight производит довольно благоприятное и даже «стильное» впечатление. Вообще, присущая Silverlight простота и элегантность сильно облегчает жизнь программисту, позволяет ему отвлечься от мелких технических проблем и дать полет своей фантазии, да и просто делает создание приложений интересным и увлекательным занятием.

Приложение Simple Watch в XNA

Реализация той же функциональности в XNA потребует от нас куда больших усилий. По большому счету здесь все придется писать с нуля: и реакцию программы на различные касания экрана (их еще называют жестами), и код вывода всего пользовательского интерфейса. Даже такой объект, как окружность, нам целиком и полностью придется задавать программно. К сожалению, рассмотреть в этой статье все шаги создания подобного приложения не представляется возможным – сейчас мы остановимся только на ключевых моментах программирования приложения в XNA, зато на диске размещена вся программа. Она хорошо закомментирована, так что в ней довольно легко разобраться. Значительная часть кода выделена в отдельную DLL-библиотеку, некоторые классы которой, возможно, окажутся полезными читателю и в его собственных разработках на XNA.

Жизненный цикл любого XNA-приложения реализуется в классе, наследующем классу Game из пространства имен Microsoft.Xna.Framework. Создав проект типа Windows Phone Game, мы увидим его шаблон. На разных этапах работы приложения вызывается всего шесть определенных в Game методов. При загрузке приложения это будут конструктор, Initialize и LoadContent, при завершении его работы – UnloadContent. Основное же действие разворачивается в методах Update и Draw, вызываемых попеременно при каждой перерисовке экрана телефона. Update играет роль метода, «предвосхищающего» Draw. Именно в нем рекомендуется выполнять обработку жестов пользователя, изменять координаты выводимых на экран объектов и т.п. А в методе Draw следует оставлять минимум кода, отвечающего за рисование объектов (текстур, текста и др.) на экране телефона.

Мы хотим повторить в нашем XNA-приложении ранее проделаное в Silverlight, поэтому нам придется создать нечто, имитирующее Pivot. В XNA за переключение между различными «страницами» приложения отвечает код, называемый «менеджером экранов». Естественным оказывается желание реализовать этот код так, чтобы его можно было использовать многократно. Поэтому мы выделим наш менеджер экранов в отдельный класс PivotGame, который будет наследовать стандартному классу Game, а уже от него унаследуем основной класс нашего приложения. PivotGame в иерархии наследования будет играть роль «прослойки» между Game и основным классом программы. В теле его метода Update будет обрабатываться «перелистывающее» движение пальца по экрану, в Draw же будут выводиться на экран заголовки страниц, а также будет вызываться код перерисовки текущей страницы приложения.

public delegate void DrawPage(GameTime gameTime);
public class PivotGame: Microsoft.Xna.Framework.Game
{
 
List draws = new List(); //Методы, отвечающие за вывод на экран страниц приложения
int itemCount = 0; //Общее число элементов в Pivot

//Определяет, на сколько пикселов пользователь
//сдвинул страницу в горизонтальном направлении
protected float DrawOffsetX {...}
//Индекс текущей страницы
public int SelectedIndex {...}
 
protected override void Update(GameTime gameTime)
{
TouchCollection touches = TouchPanel.GetState();//Текущие жесты
if (touches.Count > 0)
{

//Если это "листающий» жест:
if (touches[0].State == TouchLocationState.Moved &&
touches[0].TryGetPreviousLocation(out pos))
DrawOffsetX += touches[0].Position.X — pos.Position.X;
//Пользователь отпустил палец – возможна смена страницы:
if (touches[0].State == TouchLocationState.Released)
{
//Индекс текущей страницы увеличивается на 1...
if (DrawOffsetX < -100)
SelectedIndex = (SelectedIndex + 1) % itemCount;
 
//...либо уменьшается на 1.
if (DrawOffsetX > 100)
{
SelectedIndex--;
if (SelectedIndex < 0)
SelectedIndex = itemCount -- 1;
}
DrawOffsetX = 0;
}
}
 
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{

//Вызываем код рисования текущей страницы Pivot.
if (draws[SelectedIndex]!=null)
draws[SelectedIndex](gameTime);

base.Draw(gameTime);
}
}

 

Следующая проблема, с которой мы неизбежно столкнемся при реализации аналоговых часов, заключается в выводе на экран простейших двухмерных объектов. Дело в том, что среди библиотек XNA нет классов, позволяющих, скажем, вывести на экран хотя бы линию на основе ее координат. Более того, в XNA сейчас вообще нет двухмерной графики. Все, что у нас есть, – это метод DrawUserPrimitives объекта типа GraphicsDevice, принимающий на вход массив трехмерных вершин и рисующий соединяющие их линии. Мы вполне можем определить для каждой из необходимых нам фигур соответствующий массив вершин (с третьей координатой, равной нулю) и вызывать при каждой перерисовке экрана упомянутый выше метод. В нашем случае, когда необходимы всего три линии и одна окружность, это вполне можно сделать; но представьте себе, что вам в своем приложении нужно не три, а сто линий! Значит ли это, что для каждой из них придется явно создавать массив, заполнять его вершинами и вызывать метод DrawUserPrimitives? К счастью, нет. В XNA предусмотрен очень удобный механизм инкапсуляции с помощью компонентов. Компонент в XNA – это объект класса GameComponent или DrawableGameComponent. Судя по названию, второй класс как раз нам и нужен; в таком случае абстрактный компонент Primitive2D будет выглядеть следующим образом:

public abstract class Primitive2D: Microsoft.Xna.Framework.DrawableGameComponent
{
protected VertexPositionColor[] vertices; //Массив вершин
protected BasicEffect effect; //Хранит основные параметры рендеринга

public Primitive2D(Game game): base(game)
{
effect = new BasicEffect(game.GraphicsDevice);
effect.VertexColorEnabled = true;
//Простейшая ортографическая проекция, позволяющая
//симулировать рисование двумерной графики в 3D
effect.Projection = Matrix.CreateOrthographicOffCenter
(0, Game.GraphicsDevice.Viewport.Width,
Game.GraphicsDevice.Viewport.Height, 0, 0, 1);
}
public override void Draw(GameTime gameTime)
{
//Применяем текущие настройки рендеринга
effect.CurrentTechnique.Passes[0].Apply();
//Последовательно соединяем все вершины линиями и рисуем примитив.
GraphicsDevice.DrawUserPrimitives
(PrimitiveType.LineStrip, vertices, 0, vertices.Length-1);
base.Draw(gameTime);
}
}

 

Все, что нам останется сделать в производных классах Line2D и Circle2D, – это соответствующим образом проинициализировать массив vertices. В Line2D он будет состоять всего из двух элементов, а в Circle2D, где мы будем имитировать окружность правильным многоугольником с большим числом сторон, число вершин будет зависеть от параметра точности детализации. После объявления этих компонентов в методе Initialize основного класса нашего приложения нам останется лишь добавить такие строки:

public class SimpleWatch: PivotGame
{
Line2D lHour;

protected override void Initialize()
{

lHour = new Line2D(new Vector2(240, 440), new Vector2(240, 440),
Color.Aqua, this);
this.Components.Add(lHour);

base.Initialize();
}
}

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

Оставшуюся часть программы – обновление позиций стрелок и вывод цифрового представления времени – здесь мы опустим; читатель легко сможет разобраться в этом коде самостоятельно.

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

Некоторые интересные возможности разработки для Windows Phone

Expression blend 4

В состав Windows Phone SDK входит Expression blend 4, удобное и простое средство разработки пользовательских интерфейсов, предназначенное для дизайнеров. Наличие такого инструмента позволяет разделять труд разработчика и дизайнера при разработке Silverlight-приложения – ведь теперь дизайнеру совершенно необязательно разбираться во всех тонкостях языка XAML, чтобы создавать богатые пользовательские интерфейсы.

Интеграция XNA и Silverlight

Windows Phone 7.1 Mango выйдет только осенью, однако новые средства разработки доступны для скачивания уже сейчас. Как говорят в Microsoft, в этой версии появилось более 1500 новых API. Одна из наиболее любопытных функций --  совмещение в одном приложении возможностей, предоставляемых Silverlight и XNA. Шаблон такого проекта называется Windows Phone 3D Graphics Application. Подробнее о создании подобных приложений можно почитать на сайте MSDN (http://bit.ly/bVMTCu).

Создание «живого» значка приложения

Одной из самых ярких черт Windows Phone 7 являются так называемые Live Tiles – «живые» значки приложений на стартовом экране телефона, постоянно обновляющие свое содержимое. Таким может быть, например, значок приложения, загружающего из Сети погоду: пользователю куда приятнее видеть данные о погоде на сегодня прямо на стартовом экране, вместо того чтобы заходить в саму программу. До выхода Mango создание таких значков было несколько затруднительным занятием; новые же API значительно расширили возможности разработчиков в этой области. Их описание опять же можно найти на сайте MSDN(http://bit.ly/bVMTCu).

Публикация приложений на Marketplace

Участие в программе Marketplace, которая дает возможность публиковать свои приложения, стоит 99 долл. в год. Для студентов, задействованных в программе DreamSpark, участие в Marketplace будет бесплатным.

Пока еще процедура регистрации довольно затянутая и непростая. Сразу после заполнения анкеты на сайте App Hub (create.msdn.com) необходимо подтвердить свою личность (обычно для этого достаточно отправить копию своего загранпаспорта на указанный адрес). Если же разработчик планирует публиковать платные приложения и получать с них доход, то придется заполнить и отправить в офис Microsoft физическую копию формы W-8BEN; для нее же потребуется получить Employer Identification Number (EIN), позвонив в американскую службу IRS.

Недавно ставшая партнером Microsoft компания Softkey предлагает услуги по публикации приложений на Marketplace. При этом разработчику не придется самостоятельно проходить всю процедуру регистрации. Цена такой услуги – 10% от оборота приложений. Стоит ли оно того или нет, каждый разработчик сам решает для себя. Ясно одно: публиковаться на Marketplace в любом случае стоит. Ведь именно сейчас этот магазин как никогда нуждается в свежих идеях, и у каждого разработчика есть уникальная возможность проявить себя – и, быть может, сделать на своем приложении неплохие деньги.