Новые фичи C#

C# 6 поставляется с Visual Studio 2015 Preview. Философия этой версии проста: улучшить простые повседневные сценарии кодирования, не добавляя много концептуальных новых вещей. Новые возможности должны сделать процесс коддинга легче, при этом не делая язык тяжелее.

Сейчас я проведу вас через каждую новую особенность языка. Мы начнем на уровне экспрессии и будем двигаться «наружу». Можно быть уверенным, что это тот набор, который будет в конечном итоге, но уже известно, что некоторые из них несколько изменятся.

 

nameof выражения — новая форма строки, для которой потребуется больше синтаксиса и она более ограничена. Неужели что-то не нравится? 🙂

На самом деле есть много причин для того, чтобы полюбить данный функционал. Часто необходимо задать строку с именем некоторых элементов: например, когда бросаем ArgumentNullException и необходимо передать имя “виновного” в ошибке; при вызове события PropertyChanged и т.д.

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

(if x == null) throw new ArgumentNullException(nameof(x));

Вы можете задать более сложное выражение в nameof, но это только для того, чтобы сообщить компилятору, где искать:

WriteLine(nameof(person.Address.ZipCode)); // выведет "ZipCode"

String interpolation позволяет вам более легко формировать строки. String.Format и его двоюродные братья очень универсальны, но их использование является несколько неуклюжим и приводящим к ошибкам. Особенно прискорбным является использование пронумерованных заполнителей, таких как “{0}”, в строке формата, которые должны совпадать с отдельно указанными аргументами:

var s = String.Format("{0} is {1} year{{s}} old", p.Name, p.Age);

String interpolation позволяет поместить выражения прямо на их место:

var s = "\{p.Name} is \{p.Age} year{s} old";

Так же, как и со String.Format, есть возможность добавить дополнительные выравнивания и форматы:

var s = "\{p.Name,20} is \{p.Age:D3} year{s} old";

Содержание таких “вставок” могут быть различные, например, тернарное выражение:

var s = "\{p.Name} is \{p.Age} year\{(p.Age == 1 ? "" : "s")} old";

Обратите внимание, что условное выражение в скобках.

Примечание: здесь описывается синтаксис, который работает в VS Preview. Тем не менее, синтаксис скорее всего будет изменен и в более поздних версиях вы увидите интерполированные строки, написанные таким образом:

var s = $"{p.Name,20} is {p.Age:D3} year{{s}} old";

 

Null-conditional operators решает многие ситуации, когда нам приходилось проверять переменные null перед обращением к ним. Такие операции позволяют получить доступ к элементу только тогда, когда он не является нулевым, иначе обеспечивает нулевой результат:

int? length = customers?.Length; // null если customers null
Customer first = customers?[0];  // null если customers null

Данный оператор очень  удобно использовать вместе с со следующим оператором “??”:

int length = customers?.Length ?? 0; // 0 если customers null

Также можно смело прописывать функционал после данного оператора — если изначальное условие не будет выполняться — он прекратит работу и вернет null:

int? first = customers?[0].Orders.Count(); // если customers null, то результат будет null

По сути следующий пример — то же самое:

int? first = (customers != null) ? customers[0].Orders.Count() : null;

Также таким образом мы можем проверять на null дальнейшие обращения:

int? first = customers?[0].Orders?.Count();

Обратите внимание, что вызов (передача параметров в скобках) не может следовать сразу после оператора ‘?’ — т.к. это приведет к слишком многим синтаксических двусмысленностям:

if (predicate?(e) ?? false) { … } // Error!!

Тем не менее, вы можете запустить это сценарий с помощью метода Invoke:

if (predicate?.Invoke(e) ?? false) { … }

Это простой и потокобезопасный способ проверить на null, прежде чем вызвать событие.

 

Index initializers  призвано расширить инициализацию объектов во вновь созданном объектом:

var numbers = new Dictionary<int, string> {
   [7] = "seven",
   [9] = "nine",
   [13] = "thirteen"
};

 

Exception filters — способность CLR, которая уже давно реализована для  Visual Basic и F #,  но не было для C#. Выглядит это следующим образом:

try { … }
catch (MyException e) if (myfilter(e))
{
…
}

Если выражение в скобках после «if» истинно, catch сработает.

 

Await в catch и в finally который был невозможен до сих пор. На самом деле это было существенным ограничением и люди приходилось использовать обходные пути (костыли). Но теперь Вы можете обойтись без них:

Resource res = null;
try
{
   res = await Resource.OpenAsync(…); // You could always do this.
   …
}
catch(ResourceException e)
{
   await Resource.LogAsync(res, e); // Now you can do this …
}
finally
{
   if (res != null) await res.CloseAsync(); // … and this.
}

 

Auto-property initializers появилась в новой версии языка; подобна инициализации полей:

public class Customer
{
   public string First { get; set; } = "Jane";
   public string Last { get; set; } = "Doe";
}

 

Getter-only auto-properties позволяют опускать функционал сеттер в авто-свойствах:

public class Customer
{
   public string First { get; } = "Jane";
   public string Last { get; } = "Doe";
}

Поле по сути будет являться ReadOnly. Оно может быть инициализировано через инициализацию при определении (как в примере выше) или же оно может быть установлено в какое-либо значение в теле конструктора объявляющего типа, которое присваивается непосредственно к основной области:

public class Customer
{
      public string Name { get; };
      public Customer(string first, string last)
      {
          Name = first + " " + last;
      }
}

 

Expression-bodied function members позволяют определять методы, свойства и другие виды функций-членов в виде выражений, а не блоков, так же, как с лямбда-выражениями.

Тело методы могут быть представлено выражением с использованием лямбда-выражения:

public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public static implicit operator string(Person p) => "\{p.First} \{p.Last}";

Эффект в точности такой же, как если бы у методов были блоковые тела.

public void Print() => Console.WriteLine(First + " " + Last);

Свойства и индексаторы могут иметь  только get или get/set методы и у индексаторов могут быть только тела выражении:

public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);

 

Parameterless constructors in structs допускаются в данный момент:

struct Person
{
   public string Name { get; }
   public int Age { get; }
   public Person(string name, int age) { Name = name; Age = age; }
   public Person() : this("Jane Doe", 37) { }
}

Выражение “new Person()” будет выполнять заявленный конструктор вместо стандартного поведения предоставления значение по умолчанию. Необходимо подметить, что «default(Person)» будет по-прежнему возвращать значение по-умолчанию.

 

using static — новый вид using, которое позволяет импортировать статические члены типов непосредственно.

В Preview выглядит следующим образом:

using System.Console; 
using System.Math;
class Program
{
   static void Main()
   {
       WriteLine(Sqrt(3*3 + 4*4));
   }
}

Это очень удобно, если у вас есть набор функций, относящихся к определенному пространству имен, например, System.Math.


Ссылка на источник: New Features in C# 6

Реклама
Tagged with: , , , , , , ,
Опубликовано в .Net, Development

Play To. Часть 2

Сегодня мы продолжим рассмотрение контракта Play To: узнаем как его реализовать программно, посмотрим на существующие примеры и узнаем, что делать хорошо, а что нет. Первую часть Вы можете найти здесь.

Для чего нужен PlayTo контракт?

Реализация  Play To очень проста и состоит из нескольких следующих пунктов:

  1. Получение объекта PlayToManager для текущего отображения.
  2. Подписка на событие SourceRequested.
  3. Установите PlayToSource из медиа-элемента. На основе типа распространяемого элемента (видео, аудио и т.п.) система показывает только те устройства, которые могут принимать этот тип медиа. Если пользователь выбирает устройство, PlayToSource отправляется ему.

Давайте рассмотрим простой пример, как включить воспроизведение для одного медиа-элемента.

JavaScript
<video id="videoplayer" src="http://www.contoso.com/clip.mp4" controls autoplay />
<script type="text/javascript">
  // Step 1: Obtain PlayToManager object for app’s current view.
  var ptm = Windows.Media.PlayTo.PlayToManager.getForCurrentView();
  // Step 2: Register for the sourcerequested event (user selects Devices button).
  ptm.addEventListener("sourcerequested", function(e) {
      var request = e.sourceRequest;

      // Step 3: Specify the media to be streamed (to filter devices)
      request.setSource(document.getElementById("videoplayer").msPlayToSource);
      // The media will then be streamed to the device chosen by the user in the UI.
  });
</script>
XAML
<MediaElement x:Name="videoplayer" 
                 Source="http://www.contoso.com/clip.mp4"
                 AutoPlay="true" />
// Step 1: Obtain PlayToManager object for app’s current view.
PlayToManager ptm = Windows.Media.PlayTo.PlayToManager.GetForCurrentView();

// Step 2: Register for the SourceRequested event (user selects Devices button).
ptm.SourceRequested += (PlayToManager sender, PlayToSourceRequestedEventArgs e) => {
  request = e.SourceRequest;

  // Step 3: Specify the media to be streamed.
  PlayToSourceDeferral deferral = request.GetDeferral();
  request.SetSource(videoplayer.PlayToSource);
  deferral.Complete();
}
// The media will then be streamed to the device chosen by the user in the UI.

Как и что необходимо делать?

Если ваше приложение содержит в себе интересные фотографии, музыку или видео, которые могут выглядеть лучше на большом экране и/или высококачественном стерео — у Вас есть смысл использовать Play To контракт.

Play To автоматически включается для приложений, которые воспроизводят музыку и/или видео (но не организовывают просмотр изображений). Этот функционал по-умолчанию отлично работает для отдельных медиа-элементов; однако, возможности Play To позволяют нам намного больше, например, слайд-шоу и плейлисты.

Потоковая передача Play To поддерживает все музыкальные файлы, видео и изображения, которые не находятся под защитой DRM.

Для этого поста я использую стандартное приложение “Фотографии”, как пример прекрасной реализации контракта Play To.

 

Пример

Пользователи могут открывать правую панель и выбрать пункт “Устройства”, и увидеть совместимые устройства из любой точки приложения. Всякий раз, когда пользователь просматривает фото, видео и т.п. у него будет возможность открыть правую панель и просмотреть параметры устройства. Так же пользователи могут отправлять весь альбом на другое устройство для просмотра и т.п..

В данном примере пользователь должен выбрать фото и видео, которые будут  отправлены на телевизор. Когда пользователь открывает пункт “Устройства” в правой панели они увидят доступные устройства, которые могут принимать передаваемый тип содержимого.

skate_park

Вот как приложение выглядит, когда оно работает с телевизором:После того как пользователь выбирает устройство он хочет знать, что именно ретранслируется на него из приложения в данный момент. Приложение “Фотографии” подчеркивает такой элемент прямоугольником вокруг него.

skate_park2

Создание слайд-шоу

Сертифицированные Microsoft Play To устройства поддерживают пребуферизацию следующего элемента. Передача устройству в буфер следующего элемента воспроизведения  — отличный способ обеспечить высокую производительность, которая имеет решающее значение для большинства пользователей. Приложения могут воспользоваться этой возможностью, указав источник для «следующего» элемента в Play To. По данной ссылке Вы сможете найти пособие по быстрой реализации данного функционала.

 

Поддержка воспроизведения

Play To сессия продолжает играть до тех пор, пока пользователь не отключит ее. Это обеспечивается благодаря двум сценариям многозадачности:

  1. Пользователи могут перемещаться между разными фотоальбомами в том же приложении. Поддержка данного сценария требует от приложения возможность продолжать работать при уходе со страницы «Now Playing». Например, в приложении “Фотографии” пользователи могут выбрать несколько фотографий и обмениваться ими по электронной почте, а слайд-шоу продолжает играть на телевизоре, или вы можете искать следующий список фотографий для показа, при этом не прерывая слайд-шоу.
  2. Пользователи могут переключиться на другое приложение, например, чтобы проверить электронную почту, в то время как слайд-шоу продолжает транслироваться на телевизор. В этом случае приложение ничего не делает. Windows автоматически поддерживает работу приложения в фоновом режиме во время работы Play To. У приложения есть примерно 10 секунд для того, чтобы отправить новое изображение после предыдущего и приблизительно 10 секунд для того, чтобы послать следующее аудио или видео после окончания текущего иначе сеанс оборвется. Когда активная трансляция обрывается/заканчивается — Windows приостанавливает приложение.

 

Что можно и нельзя делать?

Можно:

  • Сообщать пользователю, что ваше приложение транслирует контент на другое устройство.
  • Положитесь на функционал “Устройства” для подключения к устройствам (и отключения).
  • Поддерживайте сессию Play To активной при переключении пользователя на другие приложения.
  • При воспроизведении музыки, фотографий и видео, обязательно указывайте следующий элемент PlayToSource для повышения производительности.

Нельзя:

  • Добавлять пользовательские кнопки для начала и/или окончания Play To. Пользователь все должен сделать сам с помощью функционала “Устройства” в правой панели.

 

Что на счет веб-страниц?

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

Музыка и видео, длина которых не достигает и 5 секунд (включительно), и изображения, которые меньше 300х300 пикселей (включительно), игнорируются IE и никогда не включаются автоматически.

По данной ссылке Вы сможете найти больше информации по поводу использования Play To для веб-страниц

 

Немного о форматах

Все форматы, с которыми Windows “дружит” Вы сможет найти здесь. Но для лучшей производительности рекомендуется использовать 1080р для изображений,  720p (или 1080) для MP4 видео — с H264 и AAC аудио.

 

На этом наш рассказ о таком прекрасном функционале как Play To заканчивается. Радуйте своих пользователей новым интересным и полезным функционалом!

 

Ссылка на источник: Enabling great Play To experiences from your Windows 8 apps (and websites!)

Tagged with: , , , , ,
Опубликовано в Development, Windows 8.1

Play To. Часть 1

В первой части мы поверхностно познакомимся с контрактом  Play To, о котором мало кто знает и мало кто реализует, но на самом деле он очень и очень интересный и полезный для пользователя.

Обзор Play To

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

Play To предоставляет…:

  • Пользователи: В сетях, в которых пользователи включили общий доступ (например, домашние сети), Windows 8 автоматически обнаруживает Windows Certified Play To устройства. Операционная система предоставляет единый пользовательский интерфейс, благодаря которому пользователи, используя всего два движения (сдвиг (swipe) и нажатия), могут запустить функционал Play To из любого поддерживаемого приложения. У всех медиа-приложений от Miscrosoft есть встроенная поддержка Play To (Музыка, Видео, Фотографии). Даже Internet Explorer 10 реализует Play Tо контракт, так что пользователи могут ретранслировать фотографии и HTML5 музыку/видео с веб-страниц.
  • Разработчики: для разработчиков Play To контракт является абстракцией высокого уровня потоковой ретрансляции (протоколов, форматов согласования, транскодирования и т.д.), и позволяет реализовывать все это очень быстро и легко.
  • Устройства: Play To контракт предназначен для работы с Windows certified Play To устройствами. Сертифицированные устройства обеспечивают последовательную и надежную работу. Есть ряд компаний, работающих над Play To сертификацией для своих устройств, располагающихся в различных категориях: телевизоры, приставки, громкоговорителей, звуковые приемники и т.п.

Разработка привлекательных и интерактивных приложений

Play To позволяет расширить возможность работы Вашего приложения до многоэкранного режима, что, конечно же, заставит его выделиться в Windows Store. При реализации Play To вы получаете:

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

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

 

Как это работает?

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

В Windows 8 установка Play To очень простая — без ущерба для конфиденциальности пользователя и безопасна в сетях общего использования. Обмен автоматически включается на компьютерах, которые находятся в Домашней группе, и на ПК, где пользователь выбрал Экспресс настройки во время первого запуска системы. Кроме того, при любом подключении к новой сети у пользователя спрашивают, хочет ли он разрешить общий доступ и подключение к устройствам. Пользователи также могут вызвать это сообщение снова, путем нажатия и удерживания на имя сети.

start_screen_sharing

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

pcsettings_screen

После того как Play To устройства установлены, Windows запоминает их и автоматически отслеживает их доступность в сети.

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

big_buck

Что бы посмотреть как это приложение реализует Play To контракт, мы можем сдвинуть правую панель и посмотреть доступные устройства.

big_buck2

Как только вы выберете устройство — Play To начнет свою работу. В этом случае мы выбираем Xbox 360, и как вы можете видеть контрол управления видео переходит в режим когда видео сменяется серым фоном. Пауза, воспроизведение, перемотка и кнопки регулирования громкости теперь работают на целевом устройстве.

playto_stream

Пользователь может завершить данную сессию путем разрыва соединения

playto_stream2

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

Ссылка на источник: Enabling great Play To experiences from your Windows 8 apps (and websites!)

Tagged with: , , , ,
Опубликовано в Development, Windows 8.1

Работа с документами: как (не) использовать возможности documentsLibrary в Windows Store приложениях

Эта возможность весьма ограничена и не предназначена для общего использования. Приложения, которые пытались использовать ее — не удавалось пройти сертификацию.

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

 

Но что же делать, если этот функционал необходим?

 

Хорошей новостью является то, что большинству приложений будет лучше без documentsLibrary. Вместо этого позвольте пользователю выбрать, какие папки использовать. В первый раз, когда необходимо сохранить документ, используйте FolderPicker, чтобы позволить пользователю выбрать место сохранения и запомните выбранное расположение с помощью FutureAccessList.

Для получения более подробной информации о FolderPicker смотрите Quickstart: Accessing files with file pickers и File picker sample. Для получения дополнительной информации о кэширования доступа к файлам см How to track recently used files and folders и File access sample.

 

Ссылка на источник: Dealing with Documents: How (not) to use the documentsLibrary capability in Windows Store apps

Tagged with: , , ,
Опубликовано в Development, Windows 8.1

OCR библиотека для WinRT приложений. Часть 2.

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

Нашим следующим этапом было связывание нашей ViewModel’и с представлением:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App253"
      xmlns:img="Windows.UI.Xaml.Media.Imaging"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:Converters="using:App253.Converters"
      x:Class="App253.MainPage"
      mc:Ignorable="d"
     Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Page.BottomAppBar>
    <CommandBar IsOpen="True">
      <AppBarButton Icon="ZoomIn"
                    Label="recognise"
                    Command="{Binding RecogniseCommand}" />
    </CommandBar>
  </Page.BottomAppBar>

  <Grid x:Name="LayoutRoot">
     <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
 
    <StackPanel Grid.Row="0"
                Margin="19,0,0,0">
      <TextBlock Text="OCR TEST APP"
                 Style="{ThemeResource TitleTextBlockStyle}"
                 Margin="0,12,0,0" />
      <TextBlock Text="cards"
                 Margin="0,-6.5,0,26.5"
                 Style="{ThemeResource HeaderTextBlockStyle}"
                 CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}" />
    </StackPanel>

    <Grid Grid.Row="1"
          x:Name="ContentRoot"
          Margin="19,9.5,19,0">
      <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
      </Grid.RowDefinitions>

      <local:IncrementalFlipView ItemsSource="{Binding Items}"
                                 SelectedValue="{Binding SelectedPhoto, Mode=TwoWay}"
                                 IsEnabled="{Binding IsIdle}"
                                 x:Name="flipView">
        <local:IncrementalFlipView.ItemTemplate>
          <DataTemplate>
            <!-- What I want here is for the Image to size itself and for the Canvas
                 to be the same size as the Image no matter what the image does -->
            <Grid VerticalAlignment="Center"
                  HorizontalAlignment="Center"
                  Width="Auto"
                  Height="Auto">
              <Image Source="{Binding ImageUrl}"
                     Stretch="Uniform">
              </Image>
              <Canvas HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      RenderTransformOrigin="0.5,0.5">
                <Canvas.RenderTransform>
                  <RotateTransform Angle="0" />
                </Canvas.RenderTransform>
              </Canvas>
              <TextBlock Text="{Binding Title}" />
            </Grid>
          </DataTemplate>
        </local:IncrementalFlipView.ItemTemplate>
      </local:IncrementalFlipView>

      <StackPanel Orientation="Vertical"
                  Grid.Row="1"
                  VerticalAlignment="Top"
                  Margin="0,0,0,40">
        <TextBlock Text="name"
                   Style="{StaticResource TitleTextBlockStyle}" />
        <TextBlock Text="{Binding Name, FallbackValue=unset, TargetNullValue=unset}"
                   Style="{StaticResource ListViewItemContentTextBlockStyle}"
                   Margin="10,0,0,0" />
        <TextBlock Text="email"
                   Style="{StaticResource TitleTextBlockStyle}" />
        <TextBlock Text="{Binding Email, FallbackValue=unset, TargetNullValue=unset}"
                   Style="{StaticResource ListViewItemContentTextBlockStyle}"
                   Margin="10,0,0,0" />
        <TextBlock Text="phone"
                   Style="{StaticResource TitleTextBlockStyle}" />
        <TextBlock Text="{Binding Phone, FallbackValue=unset, TargetNullValue=unset}"
                   Style="{StaticResource ListViewItemContentTextBlockStyle}"
                   Margin="10,0,0,0" />
      </StackPanel>
    </Grid>
  </Grid>
 </Page>

и так, в итоге у нас получилось: IncrementalFlipView, который отображает товары и показывает изображения, а также выводит название в TextBlock. Ниже расположены три простых текстовых блока и кнопка, по нажатию на которую мы будем запускать распознавание.

Я буду возвращаться к канвасу, но его суть заключается в обеспечении поверхности рисования поверх изображения

Запустим и посмотрим, что же у нас получилось в итоге:

123

Добавим OCR.

Пока все идет хорошо, но этот пост должен был быть о использованием библиотеки OCR, а не о создании кастомных контролов и т.п, поэтому собственно я и добавлял ссылку на «Microsoft OCR» библиотеку.

Затем я добавил небольшой класс для представления изображения:

class CurrentImageInfo
{
  public uint Width { get; set; }
  public uint Height { get; set; }
  public byte[] Pixels { get; set; }
}

Добавим немного переменных:

CurrentImageInfo currentImageInfo;
HttpClient httpClient;
OcrEngine ocrEngine;

Изменим немного конструктор:

   public ViewModel()
   {
      this.Items = new FlickrBusinessCardPhotoResultCollection();
      this.recogniseCommand = new SimpleCommand(this.OnRecognise);
      this.IsIdle = true;
      this.httpClient = new HttpClient();
      this.ocrEngine = new OcrEngine(OcrLanguage.English);
    }

И наконец-то начнем реализовывать метод OnRecognise:

   async void OnRecognise()
   {
      this.IsIdle = false;
      try
      {
        if (this.selectedPhoto != null)
        {
          // I've deliberately avoided downloading any image bits until this point.
          // We (probably) have the image on the screen. However, that's hidden
          // inside an Image control which I'm letting do the URL->Image work
          // for me (as well as any caching it decides to do).
          // But, now, I actually need the bytes of the image itself and I can't
          // just grab them out of the image control so we go back to the web.
          try
          {
            await this.DownloadImageBitsAsync();
            OcrResult ocrResult = await this.RunOcrAsync();
  
            if (ocrResult != null)
            {
            }
          }
          catch
          {
            // TBD...
          }
        }
      }
      finally
      {
        this.IsIdle = true;
      }

и конечно же функция по загрузке изображения:

   async Task DownloadImageBitsAsync()
   {
      this.currentImageInfo = null;
      // TODO: do I really have to do all this to get the pixels, width, height or
      // can I shortcut it somehow?
      using (var inputStream = await this.httpClient.GetInputStreamAsync(new Uri(this.SelectedPhoto.ImageUrl)))
      {
        using (InMemoryRandomAccessStream memoryStream = new InMemoryRandomAccessStream())
        {
          await RandomAccessStream.CopyAsync(inputStream, memoryStream);
          memoryStream.Seek(0);
          BitmapDecoder decoder = await BitmapDecoder.CreateAsync(memoryStream);
          PixelDataProvider provider = await decoder.GetPixelDataAsync();
          this.currentImageInfo = new CurrentImageInfo()
          {
            Width = decoder.PixelWidth,
            Height = decoder.PixelHeight,
            Pixels = provider.DetachPixelData()
          };
        }
      }
    }

и наконец-то использование искомой библиотеки:

   async Task<OcrResult> RunOcrAsync()
   {
      var results = await this.ocrEngine.RecognizeAsync(
        this.currentImageInfo.Height,
        this.currentImageInfo.Width,
        this.currentImageInfo.Pixels);
 
      return (results);
    }

По-моему использование библиотеки ОЧЕНЬ простое. Наш вариант ни чем не отличается по сложности — выкачали изображение — передали на распознавание!

Получение результатов и рисование квадратов вокруг совпадений.

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

namespace App253
{
  using System.Collections.Generic;
  using System.Text.RegularExpressions;
  enum RecognitionType
  {
    Other,
    Email,
    Phone,
    Name
  }
 
  static class CardTextRecogniser
  {
    public static RecognitionType Recognise(string businessCardText)
    {
      RecognitionType type = RecognitionType.Other;
      foreach (var expression in expressions)
      {
        if (Regex.IsMatch(businessCardText, expression.Value))
        {
          type = expression.Key;
          break;
        }
      }

      return(type);
    }

    static Dictionary<RecognitionType, string> expressions = new Dictionary<RecognitionType,string>()
    {
       // regex taken from MSDN: http://msdn.microsoft.com/en-us/library/01escwtf(v=vs.110).aspx  
       {
          RecognitionType.Email,
          @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" + @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$"
       },
       // regex taken from regex lib: http://regexlib.com/REDetails.aspx?regexp_id=296
       {
          RecognitionType.Phone,
          @"^(\+[1-9][0-9]*(\([0-9]*\)|-[0-9]*-))?[0]?[1-9][0-9\- ]*$"
       },
       // regex taken from regex lib: http://regexlib.com/REDetails.aspx?regexp_id=247
       {
          RecognitionType.Name,
          @"^([ \u00c0-\u01ffa-zA-Z'])+$"
       },
     };
  }
}

ничего заумного там не происходит. Далее рассмотрим выделение необходимого текста на изображении. При этом нельзя забывать, что изображение, которое отображается на экране, возможно, имеет немного другие размеры по отношению к тому, которое мы получили из интернета и передали на распознание OCR.

Именно поэтому я написал следующий класс-маппер:

namespace App253
{
  using Windows.Foundation;
 
  class CoordinateMapper
  {
    public CoordinateMapper(Rect sourceSpace, Rect destSpace)
    {
      this.sourceSpace = sourceSpace;
      this.destSpace = destSpace;
    }

    public Point MapPoint(Point source)
    {
      double x = (source.X - sourceSpace.Left) / sourceSpace.Width;
      x = destSpace.Left + (x * (destSpace.Width));
      double y = (source.Y - sourceSpace.Top) / (sourceSpace.Height);
      y = destSpace.Left + (y * (destSpace.Height));
 
      return(new Point() { X = x, Y = y});
    }
 
    public double MapWidth(double width)
    {
      return(width / sourceSpace.Width * destSpace.Width);
    }
 
    public double MapHeight(double height)
    {
      return (height / sourceSpace.Height * destSpace.Height);
    }
 
    Rect sourceSpace;
    Rect destSpace;
  }
}

После я сделал небольшие изменений в коде страницы и передал ItemContainerGenerator логике, и добавил немного переменных во ViewModel.

static SolidColorBrush redBrush = new SolidColorBrush(Colors.Red);
Canvas drawCanvas;
CurrentImageInfo currentImageInfo;

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

public FlickrPhotoResult SelectedPhoto
{
  get
  {
    return (this.selectedPhoto);
  }
  set
  {
    this.selectedPhoto = value;
    // modified
    this.InitialiseDrawCanvas();
  }
}

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

   void InitialiseDrawCanvas()
   {
     this.drawCanvas = null;
     if (this.SelectedPhoto != null)
     {
       FlipViewItem fvi = (FlipViewItem)this.itemContainerGenerator.ContainerFromItem(this.SelectedPhoto);
       this.drawCanvas = (Canvas)fvi.GetDescendantByType(typeof(Canvas));
       this.drawCanvas.Children.Clear();
       this.Phone = this.Email = this.Name = string.Empty;
     }
   }

Отображение результатов

В итоге я немного модифицировал метод распознования:

async void OnRecognise()
{
    this.IsIdle = false;
    try
    {
       if (this.selectedPhoto != null)
       {
         // I've deliberately avoided downloading any image bits until this point.
         // We (probably) have the image on the screen. However, that's hidden
         // inside an Image control which I'm letting do the URL->Image work
         // for me (as well as any caching it decides to do).
         // But, now, I actually need the bytes of the image itself and I can't
         // just grab them out of the image control so we go back to the web.
         try
         {
           await this.DownloadImageBitsAsync();
           OcrResult ocrResult = await this.RunOcrAsync();
           if (ocrResult != null)
           {
             // Modified
             this.DrawOcrResults(ocrResult);
             this.ApplyPatternMatching(ocrResult);
           }
         }
         catch
         {
           // TBD...
         }
       }
     }
     finally
     {
       this.IsIdle = true;
     }
   }

и собственно отрисовка:

   void DrawOcrResults(OcrResult ocrResult)
   {
      this.RepeatForOcrWords(ocrResult, (result, word) => 
      {
        Rectangle rectangle = MakeOcrDrawRectangle(ocrResult, word);
        this.drawCanvas.Children.Add(rectangle);
      });
   }

   void RepeatForOcrWords(OcrResult ocrResult, Action<OcrResult, OcrWord> repeater)
   {
     if (ocrResult.Lines != null)     
     {
        foreach (var line in ocrResult.Lines)
        {
          foreach (var word in line.Words)
          {
            repeater(ocrResult, word);
          }
        }
     }
   }

   Rectangle MakeOcrDrawRectangle(OcrResult ocrResult, OcrWord word)
   {
     // Avoided using CompositeTransform here as I could never quite get my
     // combination of Scale/Rotate/Translate to work right for a given
     // RenderTransformOrigin. Probably my fault but it was easier to
     // just do it myself.
     CoordinateMapper mapper = new CoordinateMapper(new Rect(0, 0, this.currentImageInfo.Width, this.currentImageInfo.Height), new Rect(0, 0, this.drawCanvas.ActualWidth, this.drawCanvas.ActualHeight));
     Rectangle r = new Rectangle()
     {
       Width = mapper.MapWidth(word.Width),
       Height = mapper.MapHeight(word.Height),
       RenderTransformOrigin = new Point(0.5, 0.5)
     };

     r.Stroke = redBrush;
     r.StrokeThickness = 1;
     Point mappedPoint = mapper.MapPoint(new Point(word.Left, word.Top));
     Canvas.SetLeft(r, mappedPoint.X);
     Canvas.SetTop(r, mappedPoint.Y);
     RotateTransform rotate = this.drawCanvas.RenderTransform as RotateTransform;
     rotate.Angle = 0.0d - ocrResult.TextAngle ?? 0.0d;

     return r;
   }

ну и конечно же не забываем выводить данные на экран:

   void ApplyPatternMatching(OcrResult ocrResult)
   {
      this.RepeatForOcrWords(ocrResult,(result, word) => 
      {
         switch (CardTextRecogniser.Recognise(word.Text))
         {
           case RecognitionType.Other:
             break;
           case RecognitionType.Email:
             this.Email = word.Text;
             break;
           case RecognitionType.Phone:
             this.Phone = word.Text;
             break;
           case RecognitionType.Name:
             this.Name = word.Text;
             break;
           default:
             break;
         }
       }
     );
}

Ну вот и все! Не правда ли все очень просто?

Ссылка на источник: OCR Library for WinRT Apps–Recognising Business Cards…

Tagged with: , , ,
Опубликовано в Development, Windows 8.1

OCR библиотека для WinRT приложений. Часть 1.

В данной серии постов мы рассмотрим функционал относительно новой библиотеки по распознаванию текста для WinRT приложений.

Первая попытка — захват и обработка кадров из видео

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

С этой целью, я создал пустой Windows Phone 8.1 проект, нашел «Microsoft OCR» на NuGet’ах, добавил пакет, изменил конфигурацию CPU под x86, а затем занялся исследованием API. Это не заняло много времени, т.к. это аккуратная библиотека с не слишком большими участками кода. В итоге я добавил классы, которые отображены на следующей диаграмме:

image

Я думаю, что первым шагом будет получение некоторых растровых изображений и передача их в OCR.

Для этого я планировал использовать регулярное MediaCapture API вместе с CaptureElement который может отображать предварительный просмотр из MediaCapture но, к сожалению для меня, я не могу работать с Windows Phone 8.1 прямо сейчас.

Поэтому, на данный момент, я отступил от своего плана и загорелся менее амбициозной идеей.

Попытка вторая. Получение визиток с  fickR.

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

Для этого было проделано несколько этапов…

 

Код для доступа на flickR

Набросаем небольшой класс, который поможет нам предоставить url для вызова поиска.

namespace App253.FlickrSearch
{
  using System.Collections.Generic;  
  using System.Text; 

  internal class FlickrSearchUrl
  { 
    static string apiKey = "SORRY, YOU NEED A KEY";
    static string serviceUri = "https://api.flickr.com/services/rest/?method=";
    static string baseUri = serviceUri + "flickr.photos.search&";
    public int ContentType { get; set; }
    public int PerPage { get; set; }
    public int Page { get; set; }
    public List<string> SearchTags { get; private set; }

    private FlickrSearchUrl()
    { }
 
    public FlickrSearchUrl(string searchTag, int pageNo = 0, int perPage = 5, int contentType = 1) : this()
    { 
      this.SearchTags = new List<string>() { searchTag };
      this.Page = pageNo;
      this.PerPage = perPage;
      this.ContentType = contentType;
    }
  
    public override string ToString()
    { 
       // flickR has pages numbered 1..N (I think :-)) whereas this class thinks
       // of 0..N-1.
       StringBuilder tagBuilder = new StringBuilder();
       for (int i = 0; i < this.SearchTags.Count; i++)
       {
         tagBuilder.AppendFormat("{0}{1}", i != 0 ? "," : string.Empty, this.SearchTags[i]);
       }

       return (string.Format(baseUri + "api_key={0}&" + "safe_search=1&" + "tags={1}&" + "page={2}&" + "per_page={3}&" + "content_type={4}", apiKey, tagBuilder.ToString(), this.Page + 1, this.PerPage, this.ContentType));
    }
  }
}

 и после еще один маленький класс для парсинга моделей ответа:

namespace App253.FlickrSearch
{
  using System.Xml.Linq;

  public class FlickrPhotoResult
  {
    public FlickrPhotoResult(XElement photo)
    {
      Id = (long)photo.Attribute("id");
      Secret = photo.Attribute("secret").Value;
      Farm = (int)photo.Attribute("farm");
      Server = (int)photo.Attribute("server");
      Title = photo.Attribute("title").Value;
    }

    public long Id { get; private set; }
    public string Secret { get; private set; }
    public int Farm { get; private set; }
    public string Title { get; private set; }
    public int Server { get; private set; }

    public string ImageUrl
    {
      get
      {
        return (string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}_z.jpg", Farm, Server, Id, Secret));
      }
    }
  }
}

собственно сам класс ответа с сервера:

namespace App253.FlickrSearch
{
  using System.Collections.Generic;

  internal class FlickrSearchResult
  {
    public int Pages { get; set; }
    public List<FlickrPhotoResult> Photos { get; set; }
  }
}

и, наконец, я собрал небольшой класс-провайдер, который фактически принимает FlickrSearchUrl и выполняет HTTP запрос для получения данные и их дальнейшей сериализации:

namespace App253.FlickrSearch
{
  using System.IO;
  using System.Linq;
  using System.Net.Http;
  using System.Threading.Tasks;
  using System.Xml.Linq;

  internal static class FlickrSearcher
  {
    public static async Task<FlickrSearchResult> SearchAsync(FlickrSearchUrl searchUrl)
    {
      HttpClient client = new HttpClient();
      FlickrSearchResult result = new FlickrSearchResult();

      using (HttpResponseMessage response = await client.GetAsync(searchUrl.ToString()))
      {
        if (response.IsSuccessStatusCode)
        {
           using (Stream stream = await response.Content.ReadAsStreamAsync())
           {
             XElement xml = XElement.Load(stream);
             result.Photos = (from p in xml.DescendantsAndSelf("photo") select new FlickrPhotoResult(p)).ToList();
  
             result.Pages = (int)xml.DescendantsAndSelf("photos").First().Attribute("pages");
           }
         }
       }
  
       return (result);
     }
   }
}

Благодаря всем этим классам теперь я могу выполнять поиск по Flickr по определенному набора поисковых тегов и т.д.

Теперь мне необходимо отобразить эти фотографии …

 

Сделаем FlipView с подгрузкой…

Я хотел сделать своего рода бесконечный поиск по Flickr. Так как делал подобное раньше, решил реализовать ISupportIncrementalLoading. Для поддержки данного функционала мне надо было реализовать свой вспомогательный класс:

namespace App253
{
  using App253.FlickrSearch;
  using System;
  using System.Collections.ObjectModel;
  using System.Linq;
  using System.Threading.Tasks;
  using Windows.Foundation;
  using Windows.UI.Xaml.Data;

  internal class FlickrBusinessCardPhotoResultCollection : ObservableCollection<FlickrPhotoResult>, ISupportIncrementalLoading
  {
     public FlickrBusinessCardPhotoResultCollection()
     {
       this.searchUrl = new FlickrSearchUrl("business card", perPage: 5);
     }

     public bool HasMoreItems
     {
       get
       {
         // initially, we don't know because we haven't asked. once we've asked
         // we'll have a better picture...
         return (!pages.HasValue || this.searchUrl.Page <= pages.Value);
       }
     }

     public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
     {
       return (this.LoadMoreItemsInternalAsync(count).AsAsyncOperation());
     }

     async Task<LoadMoreItemsResult> LoadMoreItemsInternalAsync(uint count)
     {
       LoadMoreItemsResult result = new LoadMoreItemsResult();
       uint pagesRequested = (uint)(count / this.searchUrl.PerPage);
       if ((count % this.searchUrl.PerPage) != 0)
       {
         pagesRequested++;
       }

       for (int i = 0;((i < pagesRequested) && (!this.pages.HasValue || this.searchUrl.Page < this.pages.Value));i++)
       {
         try
         {
           var results = await FlickrSearcher.SearchAsync(this.searchUrl);
           this.searchUrl.Page++;
           pages = results.Pages;

           foreach (var photo in results.Photos)
           {
             this.Add(photo);
           }

           result.Count = (uint)results.Photos.Count();
         }
         catch
         {
           // don't really mind why this failed in this case.
         }        
       }

       return (result);
     }

     int? pages;
     FlickrSearchUrl searchUrl;
   }
}

Весь этот код работает отлично, но затем я встретил «незначительное препятствие». Оно состояло в том, что FlipView не поддерживает набор данных, которые реализуют интерфейс ISupportIncrementalLoading.

Это было небольшим ударом для меня, но я решил двигаться вперед и попробовать создать небольшой контрол на основе FlipView, который, по крайней мере, работал бы для моих целей (поддержка инкрементальной загрузки):

namespace App253
{
  using System;
  using System.Collections;
  using System.Threading.Tasks;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;
  using Windows.UI.Xaml.Data;
  /// <summary>
  ///  Trying to make a cheap/cheerful FlipView that does something with a data
  ///  source that implements ISupportIncrementalLoading. Not trying to deal
  ///  with all cases like e.g. where someone explicitly sets the items source
  ///  and so on - just trying to deal with a regular, data-bound flipview.
  /// </summary>
 
  class IncrementalFlipView : FlipView
  {
    public IncrementalFlipView()
    {
      this.DataContextChanged += OnDataContextChanged;
      this.SelectionChanged += OnSelectionChanged;
    }

    async void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      // are we getting near to the end of the items that we have.
      IList list = this.ItemsSource as IList;\

      if (list != null)
      {
        if (list.Count - this.SelectedIndex <= LOAD_THRESHOLD)
        {
          // see if we've got anything more to load.
          await this.LoadNextDataItemsAsync();
        }
      }   
    }
 
    async void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
      if (this.ItemsSource != this.previousItemsSource)
      {
        this.previousItemsSource = this.ItemsSource;
        this.loader = this.ItemsSource as ISupportIncrementalLoading;
        if (this.loader != null)
        {
          await this.LoadNextDataItemsAsync();
        }
    }
 
    async Task LoadNextDataItemsAsync()
    {
      if (this.loader.HasMoreItems)
      {
        await this.loader.LoadMoreItemsAsync(10);
      }
    }

    static readonly int LOAD_THRESHOLD = 3;
    static readonly int LOAD_ITEMS = 10;
    object previousItemsSource;
    ISupportIncrementalLoading loader;
  }
}

Вся логика заключается в том, что необходимо контролировать изменение DataContext (т.е. все будет работать только в том случае, если все делается путем связывания) и всякий раз, когда изменяется свойство мы смотрим на ItemsSource и, если он реализует ISupportIncrementalLoading, пытаемся изначально загрузить первый набор данных, а затем, всякий раз, когда остается только 3 элемента до конца списка — загружать еще 10 элементов.

Далее необходимо создать ViewModel.

namespace App253
{
  using App253.FlickrSearch;
  using System;
  using System.Threading.Tasks;
  using System.Windows.Input;
  using Windows.Foundation;
  using Windows.Graphics.Imaging;
  using Windows.Storage.Streams;
  using Windows.UI;
  using Windows.UI.Xaml.Controls;
  using Windows.UI.Xaml.Media;
  using Windows.UI.Xaml.Shapes;
  using Windows.Web.Http;
  using WindowsPreview.Media.Ocr;
 
  class ViewModel : ViewModelBase
  {
    public ViewModel()
    {
      this.Items = new FlickrBusinessCardPhotoResultCollection();
      this.recogniseCommand = new SimpleCommand(this.OnRecognise);
      this.IsIdle = true;
    }

    public FlickrBusinessCardPhotoResultCollection Items { get; private set; // never called }
    public FlickrPhotoResult SelectedPhoto
    {
      get
      {
        return (this.selectedPhoto);
      }
      set
      {
        this.selectedPhoto = value;
      }
    }

    public string Name
    {
      get
      {
        return (this.name);
      }
      set
      {
        base.SetProperty(ref this.name, value);
      }
    }

    public string Email
    {
      get
      {
        return (this.email);
      }
      set
      {
        base.SetProperty(ref this.email, value);
      }
    }

    public string Phone
    {
      get
      {
        return (this.phone);
      }
      set
      {
        base.SetProperty(ref this.phone, value);
      }
    }

    public ICommand RecogniseCommand
    {
      get
      {
        return (this.recogniseCommand);
      }
    }

    async void OnRecognise()
    {
      this.IsIdle = false;
      try
      {
        if (this.selectedPhoto != null)
        {
        }
      }
      finally
      {
        this.IsIdle = true;
      }
    }

    public bool IsIdle
    {
      get
      {
        return (this.idle);
      }
      private set
      {
        this.recogniseCommand.SetEnabled(value);
        base.SetProperty(ref this.idle, value);
      }
    }
 
    bool idle;
    SimpleCommand recogniseCommand;
    FlickrPhotoResult selectedPhoto;
    string phone;
    string email;
    string name;
  }
}

И затем связать ее с представлением… Но это уже будет в следующей части данной статьи)

Ссылка на источник: OCR Library for WinRT Apps–Recognising Business Cards…

Tagged with: , , , ,
Опубликовано в Development, Windows 8.1

Тренды магазина Windows и Wndows Phone

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

Прежде, чем мы взглянем на категории, страны, языки и источники дохода, давайте взглянем на тенденции Windows и Windows Phone 8.1.

Windows: доля Windows 8.1 продолжает расти; в сентябре большинство загрузок (около 70%) из Windows Store приложений были сделаны клиентами, работающими под управлением Windows 8.1.

Windows Phone: Windows Phone OS 8.1 растет и стал основной ОС для телефонов, с которых скачивают приложения. Загрузки от Windows Phone устройств под управлением OS 7.x составляют менее 5% от всех загрузок.

Windows 8.1 предоставляет множество преимуществ и повышенную функциональность для клиентов, а также предоставляет ключевую выгоду для разработчиков: универсальные приложения Windows.

Windows Phone 8.1 поддерживает улучшения для более легкой разработки, таких как: возможность использования XAML или WinJS.

Графики ниже показывают, с каких операционных систем осуществляются загрузки

stats 1

Загрузки по категориям

В августе категория игр была самой популярной категорией для всех устройств, за ней следовали музыка и видео, социальные, развлечение, инструменты и продуктивность. Следует отметить: загрузки игр на  Windows на много выше, чем Windows Phone.

Приведенные ниже графики показывают процент всех загрузок, сгруппированных по категориям.

stats 2

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

stats 3

Установки с маркета

Windows Store доступен на 242 рынках, в то время как  Windows Phone магазин доступен на 191 рынках. Что бы понять как локализовать ваше приложение для потребителей, рассмотреть, какие рынки проявляют наиболее высокую загрузку, необходимо внимательно посмотреть на следующий график

stats 8

Рынок с наибольшим количеством загрузок — США, хотя распределение загрузок показывает, что некоторые другие рынки также сгенерировали значительные скачивания — Китай, Индия, Франция, Великобритания, Бразилия, Мексика и России. Это не означает, что вы не должны рассматривать другие рынки.

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

 

Язык

Язык является одним из ключевых факторов загрузки и использования.

stats 5

Выкладывая свое приложение только на английском языке вы будете охватывать только около 25% клиентов Windows Phone, хотя на рынке планшетов и компьютеров пользователей будет по-больше. С добавление испанского, французского, китайского, русского и немецкого увеличивается охват более чем на 75%.

 

Монетизация

Еще одним важным решением является то, какая модель монетизации была принята: платное приложение, покупки в приложении или же реклама внутри  приложения.

In-App Purchase быстрее всего выросла в мобильном магазине. Для Windows она становится все более значительным источником доходов. Реклама также продолжает расти, хотя и является более значительным источником доходов для Windows, чем Windows Phone.

stats 6

Для дальнейшего анализа

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

Я также предлагаю Вам начать разработку под Windows 8.1 и Windows Phone 8.1 с использованием универсальных проектов, так как они стали основной операционной системой для пользователей. Универсальные приложения позволяют развивать один исходный код для обеих платформ. Кроме того, настоятельно рекомендую рассмотреть возможность использования покупок в приложение, для того чтобы увеличить свой ​​доход.

 

 

Ссылка на источник: Windows and Windows Phone Store Trends – September 2014 Update

Tagged with: , , ,
Опубликовано в Windows 8.1, Windows Phone

Windows 10

Windows_Product_Family_9-30-Event

Я думаю все слышали про анонс Windows 10 на той неделе и хочу сказать, что это было круто! Давайте же посмотрим, что нового на этот раз нам преподнесли профессионалы из Редмонта.

 

Windows 10 представляет собой первый шаг целого нового поколения Windows. Она открывает новые впечатления для работы, игр и обычного домашнего просмотра интернета. Windows 10 воплощает все то, что от нее ожидают клиенты.

Windows 10 подойдет невероятно широкому набору устройств — от обычных пользовательских планшетов и компьютеров до серверов в корпоративных центрах обработки данных по всему миру. Некоторые из этих устройств имеют 4-дюймовые экраны — некоторые 80 дюймовыми, а у некоторых их  нет вообще. Некоторые из этих устройств вы можете держать в руках, а другие весят десять футов. Некоторые из этих устройств вы, в первую очередь, будете использовать посредством  сенсора/пера, другие — мышки/клавиатуры и т.п.

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

И через все разнообразие устройств нам (разработчикам) представляют единую платформу. Будь то разработка игр или бизнес-приложений, для всего будет один способ написать универсальное приложение. И конечно же в итоге у нас будет один магазин.

 

Инсайдерская программа

Уже сегодня открыта программа Windows Insider Program, где эксперты PC и IT-специалисты могут получить доступ к технической предварительной версии Windows 10 для настольных ПК и ноутбуков. Вскоре, после этого, также будут выпускаться технические превью для Windows Server.

С помощью данной программы, Microsoft приглашает самых восторженных клиентов Windows формировать Windows 10 вместе с ними. Неправда ли круто — поучаствовать в развитии новой операционной системы?

Она предназначен для специалистов PC и IT, которым будет удобно использовать программное обеспечение с переменным качеством. Инсайдеры будут получать непрерывный поток из ранних сборок с новейшим функционалом.

 

Немного картинок

Предлагаю Вам посмотреть на уже известный новый функционал Windows 10.

Tech Preview_Start menu

Конечно же первым из них — это новый “Пуск”, который будет содержать в себе уже знакомый функционал с более ранних версий, который будет дополняться возможностями метро-стиля.

App_Commands

Уже в Windows 10 Вы сможете открывать абсолютно любые приложения в формате обыкновенного окна (даже метро) с знакомым функционалом “Свернуть”, “Развернуть” и “Закрыть”.

Tech Preview_Three program snap and suggestions

Новая возможность комбинации окон на рабочем столе

Tech Preview_Task view

Новый режим переключения между приложениями.

Tech Preview_Virtual desktop

Одна из совершенно новых и ключевых возможностей — создание нескольких рабочих столов!

Ну  конечно же рекомендую к просмотру видео, в котором вы сможете увидеть все это своими глазами.

Все только начинается!

Ссылка на источник: Announcing Windows 10

Tagged with: , , ,
Опубликовано в Windows 10

Использование Task для обертывания синхронного кода

Сегодня мы рассмотрим довольно распространенный вопрос по поводу использования Task’а как обертки некого синхронного кода.

Представьте, что у вас есть интерфейс:

public interface IAsyncCommand
{
  Task ExecuteAsync();
}

Теперь представьте, что вы хотите реализовать этот интерфейс, но код сработает на самом деле не асинхронно. Это задача не ресурсоемкая и не требует своего собственного потока. Это просто обычный кусок синхронного кода. Есть три способа сделать это:

public class AsyncCommand1 : IAsyncCommand
 {
   public Task ExecuteAsync()
   {
       int x = 2 + 2;
       return Task.FromResult(true);
   }
 }

public class AsyncCommand2 : IAsyncCommand
{
  public async Task ExecuteAsync()
  {
    int x = 2 + 2;
  }
}

public class AsyncCommand3 : IAsyncCommand
{
  public Task ExecuteAsync()
  {
    return Task.Run(() => { int x = 2 + 2; })
  }
}

Так в чем же разница между этими тремя реализациями? С точки зрения генерации IL кода ответов довольно много. С точки зрения относительной производительности эти реализации попадают строго в категорию микро-оптимизации.

 

Код

Первая реализация является наиболее оптимальным подходом. Метод возвращает Task  со внутренним конструктором, который принимает результат. Статический метод FromResult() это просто публичная обертка конструктора, который возвращает выполненную задачу с вашим значением.

Второй подход я нахожу попросту более простым для чтения, потому что вам просто надо добавить async и все. НО он генерирует довольно много IL кода. Вы получаете полный созданный конечный автомат, который инициализируется, а затем выполняется.

[AsyncStateMachine(typeof (Class1.<ExecuteAsync>d__0))]
 [DebuggerStepThrough]
 public Task ExecuteAsync()
 {
   Class1.<ExecuteAsync>d__0 stateMachine;
   stateMachine.<>4__this = this;
   stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
   stateMachine.<>1__state = -1;
   stateMachine.<>t__builder.Start<Class1.<ExecuteAsync>d__0>(ref stateMachine);
   return stateMachine.<>t__builder.Task;
 } 

[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
private struct <ExecuteAsync>d__0 : IAsyncStateMachine
{
  public int <>1__state;
  public AsyncTaskMethodBuilder <>t__builder;
  public Class1 <>4__this;
  void IAsyncStateMachine.MoveNext()
  {
    try
    {
      if (this.<>1__state != -3)
      ;
    }
    catch (Exception ex)
    {
      this.<>1__state = -2;
      this.<>t__builder.SetException(ex);
      return;
    }
    this.<>1__state = -2;
    this.<>t__builder.SetResult();
  } 

  [DebuggerHidden]
  void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
  {
    this.<>t__builder.SetStateMachine(param0);
  }
}

Т.е. очень много сгенерированного кода только для того что бы защитить меня от явно возвращаемого Task’a!

Третий вариант генерирует код лямбда-выражения, а затем передает его в Task. Затем Task передается планировщику и то, что происходит дальше, зависит от вашего планировщика. Хотя этот вариант легче по кодогенерации, процесс “hand-off” делает его самым медленным.

 

Производительность

Я запустил несколько контрольных измерений по этими трем реализациям. В каждом случае метод срабатывал 100000 раз. Первая реализация отработала где-то за 2 мс, вторая  — за 15 мс и третья —  за 170 мс. Как я уже и говорил — все это всего лишь микро-оптимизация. Что интересно отметить, если вы повторите тест с присоединенным отладчиком — третий вариант срабатывает более чем за 30000 мс! Я предполагаю, что еще несколько переключений происходит с присоединенным отладчиком, которые влияют на производительность

 

Заключение

В идеале вы захотите не делать ничего из этого. Ваш лучший вариант заключается в поддержке как синхронных и асинхронных реализаций (в случае необходимости). На практике я часто сталкиваюсь, что часто нужно обрабатывать процесс обертывания (использования Task) и так удобней, но необходимо понимать, что именно Вы просите сделать компилятор в таких случаях. Это может быть микро-оптимизацией, но это не мешает Вам сделать это наилучшим образом!

 

Ссылка на источник: Wrapping synchronous code in a Task returning method

Tagged with: , , , , , , ,
Опубликовано в .Net, Development

Использование ISupportIncrementalLoading

Подгрузка элементов в списках — извечная проблема метро-разработчиков да и не только их. Многие просто пропускают эту проблему или же пишут расширения для списков, которые привязываются к ScrollViewer’y и отслуживают его движение и приближение к концу, и только некоторые используют данный интерфейс. Он поможет Вам решить извечную проблему подгрузок без лишних проблем и очень быстро.

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

public interface ISupportIncrementalLoading
{ 
   bool HasMoreItems { get; }
   IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count);
}

У нас есть одно свойство и один метод в интерфейсе. Свойство HasMoreItems (на самом деле только его getter) мы будем использовать для проверки возможности подгрузки, чтобы загружать постепенно, и метод LoadMoreItemsAsync, где мы будет получать новые элементы и добавлять их в GridView или ListView. Также имейте в виду, что при выборке элементов мы вольны делать все, что угодно, так что вы можете добавить фильтры или делать другие операции по вашему усмотрению.

Хорошо, давайте начнем и создадим наше пустое приложение Windows Store в Visual Studio.

223

 

На стороне интерфейса у нас есть GridView для показа элементов и ProgressBar, что бы показывать процесс загрузки элементов:

 <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
   <GridView x:Name="GridViewMain" Margin="0,100,0,0" Padding="100,0,0,10">
       <GridView.Resources>
           <DataTemplate x:Key="DataTemplateGridViewMain">
               <Grid Width="100" Height="100" Background="#FF7C1A9B">
                   <TextBlock Text="{Binding}" VerticalAlignment="Center" FontSize="20" FontFamily="Tempus Sans ITC" TextAlignment="Center"/>
               </Grid>
           </DataTemplate>
       </GridView.Resources>
       <GridView.ItemTemplate>
           <StaticResource ResourceKey="DataTemplateGridViewMain"/>
       </GridView.ItemTemplate>
   </GridView>
   <ProgressBar x:Name="ProgressBar" Height="10" Margin="0,5,0,0" VerticalAlignment="Top" Visibility="Collapsed"/>
</Grid>

Для того чтобы использовать интерфейс ISupportIncrementalLoading мы изменим логику загрузки наших элементов. Вместо заполнения списка или ObservableCollection и установки ее в ItemsSource GridView, мы создадим класс, который реализует ObservableCollection и ISupportIncrementalLoading, и обрабатывает логику, и установим экземпляр этого класса в ItemsSource GridView. Этот класс будет выглядеть следующим образом:

 using System.Collections.ObjectModel;
using Windows.UI.Core;
using System.Threading.Tasks;
public class ItemsToShow : ObservableCollection<string>, ISupportIncrementalLoading
{
   public int lastItem = 1;

   public bool HasMoreItems
   {
       get
       {
           if (lastItem == 10000)
           {
               return false;
           }
           else
           {
               return true;
           }
       }
   }

   public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
   {
       ProgressBar progressBar = ((Window.Current.Content as Frame).Content as MainPage).ProgressBar;

       CoreDispatcher coreDispatcher = Window.Current.Dispatcher;

       return Task.Run<LoadMoreItemsResult>(async () =>
           {
               await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal,
                   () =>
                   {
                       progressBar.IsIndeterminate = true;
                       progressBar.Visibility = Visibility.Visible;
                   });

               List<string> items = new List<string>();
               for (int i = 0; i < count; i++)
               {
                   items.Add(String.Format("Item {0}", lastItem));
                   lastItem++;
                   if (lastItem == 10000)
                   {
                       break;
                   }
               }

               await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal,
                   () =>
                   {
                       foreach (string item in items)
                       {
                           this.Add(item);
                       }
                       progressBar.Visibility = Visibility.Collapsed;
                       progressBar.IsIndeterminate = false;
                   });

               return new LoadMoreItemsResult() { Count = count };
           }).AsAsyncOperation<LoadMoreItemsResult>();
   }
}

Здесь мы определили количество элементов, а именно 9999 пунктов, начинающиеся с 1 и будем возвращать false в случае превышения этого количества. В LoadMoreItemsAsync мы асинхронно заполняем наш ObservableCollection. Имейте в виду, что любое действие, которое требует от нас манипулирование интерфейсом должно запускаться в CoreDispatcher, потому как этот асинхронный код будет работать в другом потоке и мы получим исключение, если мы попытаемся достучаться к UI из любого места помимо потока пользовательского интерфейса.

Последнее, что нам нужно сделать, это установить ItemsSource в GridView к экземпляру нашего класса:

public MainPage()
{
   this.InitializeComponent();
   this.Loaded += MainPage_Loaded;
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
   GridViewMain.ItemsSource = new ItemsToShow();
}

Ну, хорошо, давайте запустим приложение и попробуем прокрутить вправо весь список до появления элемента номер 9999. Посмотрите на размер полосы прокрутки в GridView в нижней части страницы, когда количество элементов возрастет (на последующих скриншотах).

322

422521618

Как вы можете увидеть, мы будем прокручивать список до тех пор пока мы не подгрузим все 9999 элементов.

Не правда ли это значительно упрощает подгрузку элементов в метро приложениях?:)

Ссылка на источник: INFINITE SCROLLING IN WINDOWS STORE APPS USING ISUPPORTINCREMENTALLOADING

Tagged with: , , , , , ,
Опубликовано в Development, Windows 8.1