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

Контекстное масштабирование и альтернативные шаблоны

< Онлайн-консультация 1 || Лабораторная работа 4: 12

XAML+C#. Практическое занятие №4

Задание: продолжая проект, полученный в результате выполнения третьего практического задания, добавьте поддержку контекстного масштабирования на главной странице (GroupDetailPage.xaml) и определите альтернативное представления для одной из групп контента на странице группы (GroupDetailPage.xaml).

  • В качестве подтверждения выполнения лабораторной работы от вас потребуется предоставить скриншот страницы группы (GroupDetailPage.xaml) из приложения, отображающей применение альтернативного шаблона представления данных.

ЗАМЕЧАНИЕ: напоминаем, что ваше приложение должно быть уникальным:

  • Использовать в качестве источников данных источники, отличные от тех, которые приводятся в инструкциям к практическим занятиям
  • Внешний вид приложения должен соответствовать выбранной вами тематике приложения (для всех страниц приложения).
  • Политика конфиденциальности (Privacy Policy) должна соответствовать вашему приложению и быть доступна внутри приложения.

Контекстное масштабирование

Начнем с достаточно простой задачи – добавления контекстного масштабирования. В XAML для этого существует специальный компонент SemanticZoom, который позволяет определить 2 состояния просмотра: детальное и выскоуровневое.

Итак, детальное представление на нашей главной странице GroupedItemsPage.xaml – это собственно сам уже существующий модернизированный GridVew: VariableSizeGridView. Определим у SemanticZoom его свойство ZoomedInView элемента управления SimanticZoom, поместив в ZoomedOutView в качестве заготовки пустой GridView:


Если теперь запустить приложение, можно будет выполнить переход в состояние ZoomedOut путем нажатия специальной копки внизу экрана мышкой или пальцами или Ctrl с управляющими клавишами. Обратно, правда, перейти не удастся, так как у нас пока не определено представление для ZoomedOut.

Давайте определим, как будут выглядеть наши RSS потоки при уменьшении. Классический вариант – некие прямоугольники с названиями. Понятно, что нам нужно где-то взять эти названия. Брать мы их будем из того же самого источника данных, который мы используем и при отображении полного представления.

Итак, для начала дополним GridView в ZoomedOutView шаблонами для отображения нашего представления групп:

<SemanticZoom.ZoomedOutView>
    <GridView 
        x:Name="zommedOutView"
        Grid.RowSpan="2" 
        Padding="116,137,40,46"
        SelectionMode="None">
        <GridView.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapGrid Margin="0,0,80,0" ItemWidth="400" ItemHeight="400" 
                 MaximumRowsOrColumns="1" VerticalAlignment="Center"/>
            </ItemsPanelTemplate>
        </GridView.ItemsPanel>
        <GridView.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Group.Title}" Style="{StaticResource HeaderTextStyle}" />                            
            </DataTemplate>
        </GridView.ItemTemplate>
    </GridView>
</SemanticZoom.ZoomedOutView>

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

Так как для связывания мы будем использовать группы из уже используемого groupedItemsViewSource, мы указываем, что нужно взять title-группы для отображения.

Теперь осталось в коде в файле GroupedItemsPage.xaml.cs добавить источник данных для отображения нашего представления. Для этого, в функции LoadState, после загрузки данных для RSS в цикле добавьте строчку:

zommedOutView.ItemsSource = groupedItemsViewSource.View.CollectionGroups;

В результате код будет выглядеть аналогично:

protected async override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
    if (NetworkInformation.GetInternetConnectionProfile() != null && NetworkInformation.GetInternetConnectionProfile()
    .GetNetworkConnectivityLevel() != NetworkConnectivityLevel.InternetAccess)
    {
        //получаем папку с именем Data в локальной папке приложения
        var localFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync
          ("Data", CreationCollisionOption.OpenIfExists);
 
        //получаем список файлов в папке Data
        var cachedFeeds = await localFolder.GetFilesAsync();
 
        //получаем список всех файлов, имя которых config.xml
        var feedsToLoad = from feeds in cachedFeeds
                            where feeds.Name.EndsWith(".rss")
                            select feeds;
 
        //нам возращается IEnumrable - а он гарантирует тольок один проход
        //копируем в массив                
        var feedsEntries = feedsToLoad as StorageFile[] ?? feedsToLoad.ToArray();
 
        if (feedsEntries.Any())
        {
            this.DefaultViewModel["Groups"] = RSSDataSource.AllGroups;
                    
                    
            foreach (var feed in feedsEntries)
            {
                await RSSDataSource.AddGroupForFeedAsync(feed);
            }
 
            zommedOutView.ItemsSource = groupedItemsViewSource.View.CollectionGroups;
 
            OfflineMode.Visibility = Visibility.Visible;
        }
        else
        {
            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;
        OfflineMode.Visibility = Visibility.Collapsed;
 
        var feeds = await App.ReadSettings();
 
        foreach (var feed in feeds)
        {
            await RSSDataSource.AddGroupForFeedAsync(feed.url, feed.id);
        }
 
        zommedOutView.ItemsSource = groupedItemsViewSource.View.CollectionGroups;
 
    }
            
}

Если теперь запустить приложение и использовать SemanticZoom, приложение будет выглядеть приблизительно так:

Выполняем контекстное масштабирование:


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

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

Альтернативные шаблоны

Зачем могут потребоваться альтернативные шаблоны? Ответ очень прост. Если вы делаете тематическое приложение, например "все о том, как прокачать мышцу", вы, скорее всего, захотите включить разные представления как группового представления на главной странице, так и для представления самой группы, не говоря уже о представлении элемента.

Для того, чтобы было понятнее, я добавил в приложение RSS поток твиттера bash.im: https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=b_o_r

Понятно, что нам абсолютно бессмысленно стандартное представление из шаблона Grid приложения:

Нам нужен другой шаблон! При XAML/C# разработке проще всего создать дополнительную страницу для этого представления.

Уже знакомым нам способом – правой копкой по проекту, Add, New Item …., и выбираем Items Page, которую назовем TwitterPage.xaml


Сразу же перейдем в TwitterPage.xaml и удалим строчку:

<!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
<x:String x:Key="AppName">My Application</x:String>

Эта переменная у нас уже определена на глобальном уровне.

Собственно, в первом приближении – все что нужно сделать со страницей мы уже сделали. Теперь нужно добавить дополнительную логику на основную страницу GroupedItemsPage.xaml в метод, который обрабатывает нажатие на заголовок группы:

void Header_Click(object sender, RoutedEventArgs e)

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

void Header_Click(object sender, RoutedEventArgs e)
{
    // Determine what group the Button instance represents
    var group = (sender as FrameworkElement).DataContext;
 
    //проверяем, что группа отображает сообщения из twitter-а
    //мы точно значем, что из 3-х групп, только у одной есть это слово в названии
    //у той, которая относится к твиттеру
    if (((RSSDataGroup)group).Title.Contains("Twitter"))
    {
        //переходим на странцу для отображения списка твитов и передаем ID группы
        this.Frame.Navigate(typeof(TwitterPage), ((RSSDataGroup)group).UniqueId);
    }
    else
    { 
    // Navigate to the appropriate destination page, configuring the new page
    // by passing required information as a navigation parameter
    this.Frame.Navigate(typeof(GroupDetailPage), ((RSSDataGroup)group).UniqueId);
    }
 
}

Теперь нам надо вернуться в код страницы TwitterPage.xaml - TwitterPage.xaml.cs в метод LoadPage и добавить обработку входящего параметра и указание странице, откуда брать данные для отображения:

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
    
    var group = RSSDataSource.GetGroup((String)navigationParameter);
 
    this.DefaultViewModel["Group"] = group;
    this.DefaultViewModel["Items"] = group.Items;
}

Чтобы проект собирался, не забудьте додавить в using:

using MyReader.Data;

Соберите и запустите проект. Убедитесь, что теперь при выборе группы, которая отображает Twitter у вас используется специальная страница:

Как видно, странице еще не до конца готова к отображению Twitt-ов. Самостоятельно, используя свои знания о том, где и как можно переопределить шаблон отображения, сделайте эту страницу более привлекательно.

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

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

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

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