Разработка Windows 8.1 приложений на XAML/С#. Часть 1. Делаем основной экран приложения на основе Hub

  • Tutorial


Этой статьей мы открываем серию материалов, посвященных новым возможностям разработки приложений для Windows 8.1 на XAML/C#. Мы планируем последовательно пройти путь от пустого шаблона до работающего приложения «Каталог товаров».

Первая статья посвящена созданию простой версии приложения на базе стандартного шаблона проекта Hub App в Visual Studio 2013. В ней мы научимся реализовывать удобные и разнообразные стартовые экраны для ваших приложений.


Для работы вам понадобятся:


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


Откройте Visual Studio 2013, выберите создание нового проекта (File -> New -> Project…). Далее в шаблонах выберите проект на Visual C# -> Windows Store. Укажите, что будете использовать шаблон Hub App.



Укажите любое название проекта, например, eShop.

Рассмотрим структуру создавшегося проекта:
  • Assets\ — папка с изображениями и иконками для проекта.
  • Common\ — файлы с вспомогательными классами для реализации навигации и обеспечения жизненного цикла приложения.
  • DataModel\ — файлы, описывающие работу с данными.
  • Strings\ — папка с файлами ресурсов для реализации мультиязычности и локализации приложения.
  • Package.appxmanifest — манифест приложения, описывающий ключевые настройки, используемые возможности, название приложения, плитки и другие параметры.
  • App.xaml — инициализация приложения.
  • HubPage.xaml — главный экран приложения. Будет содержать список категорий товаров нашего магазина.
  • SectionPage.xaml — категории товаров.
  • ItemPage.xaml — экран просмотра детальной информации по товару.

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



Изучите работу приложения:
  • Попробуйте прокрутить экран приложения вправо.
  • Попробуйте нажать на отдельную серую плитку.
  • Попробуйте нажать на заголовок группы.
  • Попробуйте нажать на заголовок элемента группы.
  • Попробуйте нажать на кнопку назад во внутренних страницах.

Вернитесь в Visual Studio и остановите отладку (Shift+F5, красный квадратик или выберите в меню Debug -> Stop Debugging).

Рассмотрите как устроены:
  • HubPage.xaml
  • DataModel\SampleData.json
  • DataModel\SampleDataSource.cs

Создание витрины каталога товаров на основе элемента управления Hub


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

Hub – это новый элемент управления, реализующий дизайн-паттерн для иерархической системы навигации в приложении.

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

Теперь поработаем над приложением.

1. Подготовим данные в формате JSON.

Откройте файл SampleData.json и скопируйте следующий текст:

{"Groups":[
  {
    "UniqueId": "Group-1",
    "Title": "Гарнитуры для Nokia Lumia",
    "Subtitle": "Гарнитуры для Nokia Lumia",
    "ImagePath": "Assets/DarkGray.png",
    "Description" : "Наушники и гарнитуры Nokia. Великолепный звук. Комфорт без остановки.",
    "Items":
    [
      {
        "UniqueId": "Group-1-Item-1",
        "Title": "НАУШНИКИ COLOUD KNOCK",
        "Subtitle": "Великолепный звук. Комфорт без остановки.",
        "ImagePath": "Assets/2-Misc-Product-Page-Gear-593x500.png",
	 "Price": "999 руб.",
        "Description" : "Великолепный звук. Комфорт без остановки.",
        "Content" : "Великолепный звук. Комфорт без остановки."
      },
      {
        "UniqueId": "Group-1-Item-2",
        "Title": "НАУШНИКИ COLOUD POP",
        "Subtitle": "НАУШНИКИ COLOUD POP",
        "ImagePath": "Assets/3-Misc-Product-Page-Gear-594x500.png",
	 "Price": "999 руб.",
        "Description" : "Великолепный звук. Куда бы вы ни направлялись.",
        "Content" : "Великолепный звук. Куда бы вы ни направлялись."
      },
      {
        "UniqueId": "Group-1-Item-3",
        "Title": "НАУШНИКИ COLOUD BOOM ",
        "Subtitle": "Item Subtitle: 3",
        "ImagePath": "Assets/1-Misc-Product-Page-Gear-636x500.png",
	 "Price": "999 руб.",
        "Description" : "Великолепный звук. Великолепный стиль.",
        "Content" : "Великолепный звук. Великолепный стиль."
      },
      {
        "UniqueId": "Group-1-Item-4",
        "Title": "БЕСПРОВОДНАЯ СТЕРЕОГАРНИТУРА NOKIA PURITY PRO ОТ MONSTER",
        "Subtitle": "БЕСПРОВОДНАЯ СТЕРЕОГАРНИТУРА NOKIA PURITY PRO ОТ MONSTER",
        "ImagePath": "Assets/BH-940-Front.png",
	 "Price": "999 руб.",
        "Description" : "Меньше шума, только музыка",
        "Content" : "Меньше шума, только музыка"
      },
      {
        "UniqueId": "Group-1-Item-5",
        "Title": "СТЕРЕОГАРНИТУРА NOKIA PURITY",
        "Subtitle": "СТЕРЕОГАРНИТУРА NOKIA PURITY",
        "ImagePath": "Assets/WH-920-cyan-png.png",
	 "Price": "999 руб.",
        "Description" : "Невероятный звук в наушниках-вкладышах",
        "Content" : "Невероятный звук в наушниках-вкладышах"
      }
    ]
  },
  {
    "UniqueId": "Group-2",
    "Title": "Зарядка и передача данных",
    "Subtitle": "Зарядка и передача данных",
    "ImagePath": "Assets/LightGray.png",
    "Description" : "Зарядные устройства. Беспроводная зарядка в пути",
    "Items":
    [
      {
        "UniqueId": "Group-2-Item-1",
        "Title": "ПОРТАТИВНАЯ БЕСПРОВОДНАЯ ЗАРЯДНАЯ ПАНЕЛЬ NOKIA DC-50",
        "Subtitle": "ПОРТАТИВНАЯ БЕСПРОВОДНАЯ ЗАРЯДНАЯ ПАНЕЛЬ NOKIA DC-50",
        "ImagePath": "Assets/Nokia-Portable-Wireless-Charging-Plate-DC-50.png",
	 "Price": "999 руб.",
        "Description" : "Беспроводная зарядка в пути",
        "Content" : "Беспроводная зарядка в пути"
      },
      {
        "UniqueId": "Group-2-Item-2",
        "Title": "УНИВЕРСАЛЬНОЕ ПОРТАТИВНОЕ ЗАРЯДНОЕ УСТРОЙСТВО NOKIA DC-19",
        "Subtitle": "УНИВЕРСАЛЬНОЕ ПОРТАТИВНОЕ ЗАРЯДНОЕ УСТРОЙСТВО NOKIA DC-19",
        "ImagePath": "Assets/Nokia-universal-portable-USB-charger-DC-19.png",
	 "Price": "999 руб.",
        "Description" : "Полный карман энергии",
        "Content" : "Полный карман энергии"
      },
      {
        "UniqueId": "Group-2-Item-3",
        "Title": "БЕСПРОВОДНАЯ ЗАРЯДНАЯ ПАНЕЛЬ",
        "Subtitle": "БЕСПРОВОДНАЯ ЗАРЯДНАЯ ПАНЕЛЬ",
        "ImagePath": "Assets/DT-900-Front.png",
	 "Price": "999 руб.",
        "Description" : "Непринужденная зарядка",
        "Content" : "Непринужденная зарядка"
      }
    ]
  }
]
}


Картинки и описания товаров для тестового наполнения были взяты с сайта Nokia: www.nokia.com/ru-ru/accessories/all/headsets

2. Подготовим интерфейс приложения и обеспечим отображение данных.

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

Откройте файл HubPage.xaml и вставьте туда следующий код:

<Page  
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    x:Name="pageRoot"
    x:Class="eShop.HubPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:eShop"
    xmlns:data="using:eShop.Data"
    xmlns:common="using:eShop.Common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <DataTemplate x:Key="CategoryItemTemplate">
            <Grid Height="280" Width="310" Margin="5,10,5,10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Height="150">
                    <Image Source="{Binding ImagePath}" Stretch="None" AutomationProperties.Name="{Binding Title}"/>
                </Border>
                <StackPanel Grid.Row="1" Margin="0,10,0,0">
                    <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
                    <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
                </StackPanel>
                <Button Grid.Row="2" Content="купить" Margin="0,10,0,0" HorizontalAlignment="Right" />
            </Grid>
        </DataTemplate>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition/>
            </TransitionCollection>
        </Grid.ChildrenTransitions>
        <Hub SectionHeaderClick="Hub_SectionHeaderClick" >
            <Hub.Header>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="80"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Button  x:Name="backButton" Style="{StaticResource NavigationBackButtonNormalStyle}"
                        Margin="-1,-1,39,0" 
                        VerticalAlignment="Top"
                        Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                        AutomationProperties.Name="Back"
                        AutomationProperties.AutomationId="BackButton"
                        AutomationProperties.ItemType="Navigation Button"/>
                    <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                        VerticalAlignment="Top" IsHitTestVisible="false" TextWrapping="NoWrap" />
                </Grid>
            </Hub.Header>
            <HubSection Width="780" Margin="0,0,80,0">
                <HubSection.Background>
                    <ImageBrush ImageSource="Assets/Background.jpg" Stretch="UniformToFill" />
                </HubSection.Background>
            </HubSection>
            <HubSection Width="500" Header="Акции и предложения" >
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Image Source="Assets/DT-900-Front.png" Stretch="None" Width="420" Height="280"/>
                        <TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Grid.Row="1" Margin="0,10,0,0" TextWrapping="Wrap"  
                                   Text="Забудьте о проводах" />                        
                        <TextBlock Style="{StaticResource BodyTextBlockStyle}" Grid.Row="2"
                                   Text="Просто положите телефон на панель беспроводной зарядки - удобнее не бывает!" />
                        <Button Grid.Row="3" Content="купить" Margin="0,10,0,0" HorizontalAlignment="Right" />
                    </Grid>
                </DataTemplate> 
            </HubSection>
            <HubSection IsHeaderInteractive="True" DataContext="{Binding Group1Items}" Header="{Binding Title}" Padding="40,40,40,32">
                <DataTemplate>
                    <GridView
                        x:Name="itemGridView"
                        ItemsSource="{Binding Items}"
                        Margin="-9,-14,0,0"
                        AutomationProperties.AutomationId="ItemGridView"
                        AutomationProperties.Name="Items In Group"
                        ItemTemplate="{StaticResource CategoryItemTemplate}"
                        SelectionMode="None"
                        IsSwipeEnabled="false"
                        IsItemClickEnabled="True"
                        ItemClick="ItemView_ItemClick">
                    </GridView>
                </DataTemplate>
            </HubSection>
            <HubSection IsHeaderInteractive="True" DataContext="{Binding Group2Items}" Header="{Binding Title}" Padding="40,40,40,32">
                <DataTemplate>
                    <GridView
                        x:Name="itemGridView"
                        ItemsSource="{Binding Items}"
                        Margin="-9,-14,0,0"
                        AutomationProperties.AutomationId="ItemGridView"
                        AutomationProperties.Name="Items In Group"
                        ItemTemplate="{StaticResource CategoryItemTemplate}"
                        SelectionMode="None"
                        IsSwipeEnabled="false"
                        IsItemClickEnabled="True"
                        ItemClick="ItemView_ItemClick">
                    </GridView>
                </DataTemplate>
            </HubSection>
        </Hub>
    </Grid>
</Page>


Обратите внимание на следующие строчки:

<Page DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"> 

{Binding DefaultViewModel} – определяет коллекцию объектов, элементы который могут быть использованы для привязки данных в рамках этой страницы.

<HubSection DataContext="{Binding Group1Items}" Header="{Binding Title}">

{Binding Group1Items} – определяет из какого объекта будут взяты данные для отображения в элементе управления. Объект Group1Items содержит информацию о категории товаров и присутствующие в ней товары.
{Binding Title} – указывает на свойство, содержащее название категории товаров в объекте Group1Items.

Подробнее о привязке данных к элементам управления вы можете найти тут.

3. Займемся получением данных и осуществим их привязку.

Откройте файл HubPage.cs, найдите в нем метод navigationHelper_LoadState и замените его на следующий код:

private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
            var sampleDataGroup1 = await SampleDataSource.GetGroupAsync("Group-1");
            this.DefaultViewModel["Group1Items"] = sampleDataGroup1;

            var sampleDataGroup2 = await SampleDataSource.GetGroupAsync("Group-2");
            this.DefaultViewModel["Group2Items"] = sampleDataGroup2;
}


Метод SampleDataSource.GetGroupAsync – получает данные по товарам и категории из файла SampleData.json, который мы в начале наполняли контентом.

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



DefaultViewModel – это публичное свойство, которое представляет собой справочник объектов, использующихся на странице для осуществления привязки интерфейса к данным. Использование на странице определяется в тэге <Page>.

4. Настало время запустить приложение.

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

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



image

Как видите, приложение визуально изменилось и научилось использовать локальные данные. Однако, до полноценного приложения еще многое предстоит сделать.

Этим мы займемся в следующих статьях.

Скачать готовый пример можно по ссылке: http://sdrv.ms/1gKmlvw
Microsoft
138,19
Microsoft — мировой лидер в области ПО и ИТ-услуг
Поделиться публикацией

Комментарии 17

    0
    Боюсь быть заминусованным, но разве только мне одному весь этот Metro кажется нерациональным использованием пространства? Пол-экрана — заставка, а чтобы увидеть весь каталог надо столько телодвижений!
      +1
      А пальцем вы как собираетесь попадать по кнопкам, стоящим вплотную?
        +2
        Вы так говорите, будто этот интерфейс только на экраны планшетов продвигается.
        А на планшете таки да, будет удобно…
          +2
          Давайте не будем планшетами ограничиваться.

          Вот на Xbox тот же Метро-интерфейс и я считаю его вполне уместным. Если всё сделать маленьким, я просто ничего на экране не увижу (как это происходит в GTA5).

          А на компьютере этими Metro-приложениями можно не пользоваться. Никто же не запрещает использовать обычные десктопные программы. Я вообще в магазин залез только один раз, чтобы до 8.1 обновиться.
          • НЛО прилетело и опубликовало эту надпись здесь
              0
              Абсолютно согласен! А еще лучше не то чтобы отключить, а просто выбрать версию windows без Metro интерфейса, потому что для десктопа этот интерфейс полное убожество и издевательство над UX и UI. Посему для игр установлена Семерка до сих пор.
                0
                Так ведь есть версия без Metro — Windows 7!
                • НЛО прилетело и опубликовало эту надпись здесь
                  +1
                  Не соглашусь. Даже для десктопа Metro UI очень удобен.
                  Попадать по элементам управления гораздо проще.

                  +1
                  Я на десктопе без сенсорного экрана использую его всегда, если это возможно.
                  И отдаю предпочтение приложениям магазина, если есть такие для необходимых задач, ибо они в разы удобней.
                  • НЛО прилетело и опубликовало эту надпись здесь
            +3
            Если посмотреть разные дизаны сайтов и приложений, сейчас заметен тренд это самое пространство не сильно экономить. Но это и не означает, что пространство надо не рационально использовать. Заставка в каких-то случаях может быть нужна и содержать информацию, например рекламмного характера, а в каких-то, её может не быть вообще. В данном случае, у этого решения есть логическое обоснование. Оно похоже на печатный каталог товаров, который люди привыкли видеть в обычной жизни.
            P.S. Наличие собственного мнения не должно приводить к минусам :)
            –6
            Кто сказал что мне не хватает кучи кнопок? Но раз уж на то пошло, мышкой легче выбирать кнопки, когда они сгруппированы, а не разнесены в пространстве на 2-3 ширины экрана, что нужно еще и скроллить.
              0
              Такой интерфейс удобен для touch и, может быть, не привычен для мышки. Но это разные сценарии использования. Сидя за ноутбуком — пользователь, скорее всего, пойдет на сайт для покупок, поиска чего-нибудь, или для того, чтобы воспользоваться интернет банком. Но если пользователь сидит в кафе с планшетом — ему удобнее приложение.
                +1
                Можете определять какой экран: сенсорный или нет, и в зависимости от этого делать разный дизайн.
                Все продумано, было бы желание…
                  0
                  Уж лучше тогда ещё и мышь детектить. И оставить кнопочку для переключения между режимами. Такая кнопочка, кстати, есть в онлайновом MS Office. Довольно удачное решение, как по мне. Особенно вспоминая, как у гугла была версия десктопного хрома с конскими пунктами меню =)
                0
                Вот что за народ пошел, вместо того чтоб что-то по статье обсуждать или спрашивать они, все еще, осуждают Metro UI.
                Спасибо за статью, очень познавательно.

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

                Самое читаемое