Спонсор: Microsoft
Опубликован: 21.03.2013 | Доступ: свободный | Студентов: 6315 / 126 | Длительность: 06:49:00
Лабораторная работа 1:

Создание приложения из шаблона

< Лекция 1 || Лабораторная работа 1: 12 || Онлайн-консультация 1 >
Аннотация: Представлены две версии практической работы: одна для разработчиков на XAML + C# и другая - для разработчиков на HTML + JS.
Ключевые слова: приложение, Windows, RSS, atom

Задание: начиная с базового шаблона, создайте свое первое приложение для Windows 8, работающее с внешним источником данных. В результате должно получиться приложение, отображающее данные с внешнего ресурса (например, через RSS или Atom) в стандартном Grid-шаблоне приложения.

Для разработчиков на XAML + C#

Обновление 1 (05.04.2013). Добавлен пример проверки наличия интернет-соединения.

Версия для разработчиков на HTML + JS - здесь "Создание приложения из шаблона"
  • Не забудьте убедиться, что ваше приложение корректно работает при отключенном интернет-соединении, а также в соответствиями с требованиями Windows Store информирует пользователя о том, как оно работает (или не работает) с его персональными данными.
  • В качестве подтверждения выполнения лабораторной работы от вас потребуется предоставить скриншот работающего приложения с подключенным внешним источником данных.

Начнём разработку с шаблона Grid приложения, выбрав разработку на XAML/C#.

В меню File выберите New Project, далее Visual C#, затем — Windows Store, в центральной части окна New Project выберите шаблон под названием Grid App (XAML). Введите в поле Name название приложения, например, MyReader, если нужно, в поле Location папку, в которой будет размещаться проект приложения и нажмите кнопку OK.


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

Давайте посмотрим, как оно работает, нажав F5, зеленую стрелочку или выбрав Debug -> Start Debugging

Можно убедиться, что это уже практически полнофункциональное приложение:

  • нажмите на любую отдельную серую плитку или на заголовок группы;
  • воспользуйтесь кнопкой назад на внутренних страницах приложения;
  • переведите приложение в Snapped-режим.

Вернитесь в Visual Studio и остановите отладку приложения (Shift+F5, красный квадратик или выберите в меню Debug -> Stop Debugging). Разберёмся, с основными файлами нашего решения:

  • *.xaml — разметка страницы приложения на языке XAML.
  • *.xaml.cs — это code-behind файл на языке C# страницы.
  • App.xaml — инициализация объекта Windows Store-приложения на языке XAML, не имеет визуального представления и не является страницей приложения.
  • App.xaml.cs — это code-behind файл на языке C# для App.xaml. В данном файле обрабатываются события уровня приложения, такие как запуск, активация, в том числе активация по поисковому запросу, и деактивация приложения. Кроме того, в App.xaml.cs вы можете перехватывать необработанные исключения и отлавливать ошибки навигации между страницами.
  • папка Common, файл StandardStyles.xaml — файл ресурсов стилей для графических элементов управления, подключается в App.xaml и его стили доступны во всех страницах приложения.
  • Package.appxmanifest — это манифест приложения, XML-файл, содержит разнообразные настройки приложения. Есть графический редактор, который открывается двойным щелчком мышью по имени файла Package.appxmanifest в окне Solution Explorer.
  • AssemblyInfo.cs — конфигурационный файл, в котором определяются некоторые метаданные главной сборки приложения.
  • MyReader_TemporaryKey.pfx — криптографический сертификат с закрытым ключом, которым подписывается приложение.
  • папка Asset, файлы Logo.png, SmallLogo.png, StoreLogo.png — логотипы для большой и малой плиток приложения, а также иконка для списка всех приложений.
  • SplashScreen.png — картинка для экрана заставки, который отображается во время загрузки приложения.

Также в папке Common, находятся файлы с общим кодом, который используется в приложении, в папке DataModel находится файл с моделью данных приложения — SampleDataSource.cs.

Меняем данные для отображения

Итак, приложение уже работает и в нём реализован пример источника данных. Поэтому задача номер один – подменить пример источника данных на реальный источник данных. Начнём с удаления конструктора по умолчанию класса SampleDataSource, в котором происходит инициализация статическими данными.

public SampleDataSource()

Дальше, воспользуемся встроенными в Visual Studio возможностями рефакторинга и переименуем SampleDataSource в RSSDataSource, SampleDataGroup в RSSDataGroup, SampleDataItem в RSSDataItem и SampleDataCommon в RSSDataCommon.

Для создания целостной картины мира можно файл SampleDataSource.cs переименовать в RSSDataSource.cs.

Для упрощения работы с классом данных в этом примере — упростим его. Для этого заменим следующий кусок класса RSSDataSource

private static RSSDataSource _sampleDataSource = new RSSDataSource();

private ObservableCollection<RSSDataGroup> _allGroups = new ObservableCollection<RSSDataGroup>();
public ObservableCollection<RSSDataGroup> AllGroups
{
     get { return this._allGroups; }
}

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

public static readonly ObservableCollection<RSSDataGroup> AllGroups = new ObservableCollection<RSSDataGroup>();

В завершение убираем ненужную локальную переменную _sampleDataSource из кода класса.

Итак, мы подготовились к добавлению реальных данных.

Допишем простой метод, который будет брать поток данных RSS или ATOM и представлять его для дальнейшего отображения в интерфейсе приложения.

 public static async Task<bool> AddGroupForFeedAsync(string feedUrl)
{
    string clearedContent = String.Empty;

    if (RSSDataSource.GetGroup(feedUrl) != null) return false;

    var feed = await new SyndicationClient().RetrieveFeedAsync(new Uri(feedUrl));

    var feedGroup = new RSSDataGroup(
        uniqueId: feedUrl,
        title: feed.Title != null ? feed.Title.Text : null,
        subtitle: feed.Subtitle != null ? feed.Subtitle.Text : null,
        imagePath: feed.ImageUri != null ? feed.ImageUri.ToString() : null,
        description: null);

    foreach (var i in feed.Items)
    {
        string imagePath = null;

        if (i.Summary != null)
            clearedContent = Windows.Data.Html.HtmlUtilities.ConvertToText(i.Summary.Text);
        else
            if (i.Content != null)
                clearedContent = Windows.Data.Html.HtmlUtilities.ConvertToText(i.Content.Text);

        if (imagePath != null && feedGroup.Image == null)
            feedGroup.SetImage(imagePath);

        if (imagePath == null) imagePath = "ms-appx:///Assets/DarkGray.png";

        feedGroup.Items.Add(new RSSDataItem(
            uniqueId: i.Id, title: i.Title.Text, subtitle: null, imagePath: imagePath,
            description: null, content: clearedContent, @group: feedGroup));
    }

    AllGroups.Add(feedGroup);
    return true;
}

Код достаточно простой. Используя SyndicationClient, получаем и разбираем данные из RSS/ATOM, после чего создаём группу и добавляем в неё записи. Попутно, очищаем содержимое от HTML тегов, просто, чтобы выглядело лучше, а также предусматриваем добавление картинки, чем займёмся позже.

Не забудьте добавить в блок using следующую директивы:

using Windows.Web.Syndication;
using System.Threading.Tasks;

Чтобы сразу проверить, как это работает, двойным щелчком откроем файл GroupedItemsPage.xaml.cs и перейдём к методу LoadState, который инициализирует данные и заменим источник данных sampleDataGroups на RSSDataSource.AllGroups, а также удалим ненужную теперь инициализацию sampleDataGroups. Теперь, код LoadState состоит из одной строчки:

protected override async void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
    this.DefaultViewModel["Groups"] = RSSDataSource.AllGroups;
}

Теперь, чтобы всё заработало, нужно добавить какой-нибудь RSS. Воспользуемся свежесозданной функцией AddGroupForFeedAsync и добавим две строчки в функцию LoadState:

RSSDataSource.AddGroupForFeedAsync("http://blogs.msdn.com/b/stasus/rss.aspx");
RSSDataSource.AddGroupForFeedAsync("http://www.spugachev.com/feed");

Собираем проект и запускаем.


Ура — работает!

Интернет, контракт настроек и политики конфиденциальности

Приложение мы пишем не просто так — мы хотим опубликовать его в Windows Store. Если прибавить к этому, что наше приложение ходит в интернет, и прочитать требования к сертификации приложений, окажется, что нам необходимо реализовать контракт настроек, который бы тем или иным образом указывал на Privacy Settings (Политику конфиденциальности).

Обратите внимание, что для большинства шаблонов по умолчанию устанавливается Capabilities (Возможность) – интернет-клиент, которая требует от нас наличия политики конфиденциальности. Нам эта возможность безусловно нужна, потому что мы ходим в интернет за новостями:

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

Добавим контракт настроек (Settings) только для начальной страницы. Для этого переопределим методы OnNavigatedTo и OnNavigatedFrom, регистрацию и дерегистрирую обработчика для настроек:

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    SettingsPane.GetForCurrentView().CommandsRequested -= Settings_CommandsRequested;
    base.OnNavigatedFrom(e);
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    SettingsPane.GetForCurrentView().CommandsRequested += Settings_CommandsRequested;
    base.OnNavigatedTo(e);
}

Теперь определим обработчик Settings_CommandsRequested:

private void Settings_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
{
    var viewPrivacyPage = new SettingsCommand("", "Privacy Statement", cmd =>
    {
        Launcher.LaunchUriAsync(new Uri("http://go.microsoft.com/fwlink/?LinkId=248681", UriKind.Absolute));
    });
    args.Request.ApplicationCommands.Add(viewPrivacyPage);
}

Не забудте добавить в блок using следующую директиву:

using Windows.System;
using Windows.UI.ApplicationSettings;

Код очень простой. Мы отображаем пункт настроек с названием Privacy Statement и, по нажатию на него, отправляем на Privacy Policy блогов MSDN.

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

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

if (NetworkInformation.GetInternetConnectionProfile().GetNetworkConnectivityLevel() 
!=  NetworkConnectivityLevel.InternetAccess)
{
    var msg = new MessageDialog("The program need an internet connection to work.
       Please check it and restart the porgram.");
    await msg.ShowAsync();
}

Давайте добавим этот код в метод LoadState страницы GroupedItemsPage страницы следующим образом:

protected override async void LoadState(Object navigationParameter, 
Dictionary<String, Object> pageState)
{
if (NetworkInformation.GetInternetConnectionProfile() == null 
|| NetworkInformation.GetInternetConnectionProfile().GetNetworkConnectivityLevel() != NetworkConnectivityLevel.InternetAccess)
{
    var msg = new MessageDialog("The program need an internet connection to work. 
      Please check it and restart the porgram.");
    await msg.ShowAsync();
 }
 else
 {

     this.DefaultViewModel["Groups"] = RSSDataSource.AllGroups;
     RSSDataSource.AddGroupForFeedAsync("http://blogs.msdn.com/b/stasus/rss.aspx");
     RSSDataSource.AddGroupForFeedAsync("http://www.spugachev.com/feed");

 }	
}

Обратите внимание, что метод помечен ключевым словом async, поскольку теперь внутри него используется await.

Не забудьте добавить в блок using следующую директивы:

using Windows.Networking.Connectivity;
using Windows.UI.Popups;

Теперь, при отсутствии соединения с интернетом мы будем отображать пользователю сообщение и не будем пытаться выйти в интернет.

Бонус: Добавляем картинки

Первое с чего надо начать — это картинки в посты и на плитки в сгруппированном отображении.

Перейдём обратно в RSSDataSource.cs и добавим магический метод, добывающий изображения из RSS постов:

private static string GetImageFromPostContents(SyndicationItem item)
{
    string text2search = "";

    if (item.Content != null) text2search += item.Content.Text;
    if (item.Summary != null) text2search += item.Summary.Text;

    return Regex.Matches(text2search,
            @"(?<=<img\s+[^>]*?src=(?<q>['""]))(?<url>.+?)(?=\k<q>)",
            RegexOptions.IgnoreCase)
        .Cast<Match>()
        .Where(m =>
        {
            Uri url;
            if (Uri.TryCreate(m.Groups[0].Value, UriKind.Absolute, out url))
            {
                string ext = Path.GetExtension(url.AbsolutePath).ToLower();
                if (ext == ".png" || ext == ".jpg" || ext == ".bmp") return true;
            }
            return false;
        })
        .Select(m => m.Groups[0].Value)
        .FirstOrDefault();
}

В методе, мы, используя регулярные выражения пытаемся найти картинки (с расширениями png, jpg и bmp) в тексте поста или его summary.

Не забудте добавить в блок using следующую директивы:

using System.Text.RegularExpressions;
using System.IO;

Теперь добавим вызовы этого метода в заботливо оставленную в методе AddGroupForFeedAsync заглушку. Меняем

string imagePath = null;

на

string imagePath = GetImageFromPostContents(i);

Пересобираем и запускаем приложение.


Вот теперь всё гораздо красивее!

< Лекция 1 || Лабораторная работа 1: 12 || Онлайн-консультация 1 >
Андрей Милютин
Андрей Милютин

Будьте добры сообщите какой срок проверки заданий и каким способом я буду оповещен!

Данила Слупский
Данила Слупский

К сожалению, я не могу выполнить данную практическую работу в VS 2013 на WIndows 8.1. Код описанных файлов отличается от кода в моем проекте. Как мне быть?