Продолжаем разрабатывать простой RSS Reader. Начало было положено в статье Разбираемся с разработкой Windows 8 приложений на XAML/С#, реализуя простой RSS Reader. Ч.1, где мы создали проект получили данные RSS и начали делать красиво. В этой части мы добавим красоты и функциональности в нашу читалку.
Продолжаем делать красиво
Теперь у нас красивые плитки разных размеров, причём, мы можем, при желании замостить по разному группы. Давайте это сделаем. Блог Стаса Павлова сделаем с плитками разных размеров, а для Блога Сергея Пугачёва – не будем баловать пользователя таким разнообразием.
Как это сделать? Всё очень просто. Логика, которая определяет размеры плиток вся реализована в функции PrepareContainerForItemOverride класса VariableSizeGridView, так что можно добавить туда соответсвующую логику.
Чтобы отличить группы, будем использовать UniqueID, для группы он соответствует URL на RSS. Добавим следующий код в функцию PrepareContainerForItemOverride:
int group = -1;
if (dataItem.Group.UniqueId.Contains("stas"))
{
group = 1;
}
А теперь изменим поведение функции, в зависимости от того, для какой группы она работает. добавив условие.
if (group > 0)
{
if (index == 2)
{
colVal = 2;
rowVal = 4;
}
if (index == 5)
{
colVal = 4;
rowVal = 4;
}
}
Запустим программу, чтобы проверить, как теперь это работает.
Поставим себе ещё более сложную задачу. А что если у нас для отображения необходимо использовать вообще разные шаблоны отображения? Это также решаемая задача. Нам опять придётся реализовать свой класс, который будет содержать логику, но теперь он будет уже наследоваться от DataTemplateSelector.
Итак, добавим в решение новый класс с именем MyDataTemplateSelector, указываем, что он наследутся от DataTemplateSelector. Добавяляем в using директивы:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
И переопределяем функцию SelectTemplateCore. В результате должно получится следующее:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace MyReader
{
class MyDataTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return base.SelectTemplateCore(item, container);
}
}
}
Теперь нужно определить в XAML файле те шаблоны из которых мы будем выбирать. Для этого отркываем GroupedItemsPage.xaml и переходим к ресурсам страницы, где у нас определён шаблон CustomItemTemplate:
<DataTemplate x:Key="CustomItemTemplate">
<Grid HorizontalAlignment="Left">
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding Image}" Stretch="UniformToFill"
AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}"
VerticalAlignment="Bottom">
<TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Height="90" Margin="15,0,15,0" FontSize="30" />
</StackPanel>
</Grid>
</DataTemplate>
Определим ещё один шаблон с минимальными изменениями – изменим выраваниваия:
<DataTemplate x:Key="CustomItemTemplate2">
<Grid HorizontalAlignment="Right">
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding Image}" Stretch="UniformToFill"
AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}" VerticalAlignment="Top">
<TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Height="90" Margin="15,0,15,0" FontSize="30" />
</StackPanel>
</Grid>
</DataTemplate>
Вернёмся теперь к нашему классу MyDataTemplateSelector и добавим код, который определяет, какой шаблон использовать, в зависимости от того, элемент какой группы отображается:
using MyReader.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace MyReader
{
class MyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate Template1 { get; set; }
public DataTemplate Template2 { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
RSSDataItem dataItem = item as RSSDataItem;
if (dataItem.Group.UniqueId.Contains("stas"))
{
return Template1;
}
else
return Template2;
}
}
}
Возвращаемся к GroupedItemsPage.xaml и переходим к ресурсам страницы, где у нас определены шаблоны и инициализируем наш селектор:
<local:MyDataTemplateSelector x:Key="MyDataSelector"
Template1="{StaticResource CustomItemTemplate}"
Template2="{StaticResource CustomItemTemplate2}"/>
И указываем селектор в вместо указания шаблона для Item для local:VariableSizeGridView:
<local:VariableSizeGridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplateSelector="{StaticResource MyDataSelector}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick">
Запускаем приложение, чтобы убедиться, что всё работает, как запланировано.
Отлично, всё работает так, как и запланировано, можно перейти к работе с живыми плитками.
Но, перед тем, как перейти к живым плиткам, давайте изменим стиль текста подписи на плитках группы.
Создадим на основве стиля BasicTextStyle необходимые нам стили и разместим их в ресурсах страницы GroupedItemsPage.xaml:
<Style x:Key="ExtendedTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BasicTextStyle}">
<Setter Property="LineHeight" Value="40"/>
<Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
<!-- Properly align text along its baseline -->
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform X="-1" Y="4"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExtendedTitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource ExtendedTextStyle}">
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
И наложим этот стиль на текст в наших шаблонах::
<DataTemplate x:Key="CustomItemTemplate">
<Grid HorizontalAlignment="Left">
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding Image}" Stretch="UniformToFill"
AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}" VerticalAlignment="Bottom">
<TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Style="{StaticResource ExtendedTitleTextStyle}" Height="90" Margin="15,0,15,0" FontSize="30" />
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="CustomItemTemplate2">
<Grid HorizontalAlignment="Right">
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding Image}" Stretch="UniformToFill"
AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}" VerticalAlignment="Top">
<TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Style="{StaticResource ExtendedTitleTextStyle}" Height="90" Margin="15,0,15,0" FontSize="30" />
</StackPanel>
</Grid>
</DataTemplate>
Запустим приложение и посмотрим, как теперь выглядят подписи на сгруппированых элементах.
Всё выглядит гораздо лучше. Если есть желание, можно поиграться со стилями, добиваясь желаемого эффекта.
Добавляем живые плитки
Пришло время добавить живые плитки. Так как пока у нес нет сервиса, который следит за обновленями, наши плитки будут обновляться в момент получения RSS, чтобы радовать пользователя вплоть до следующего запуска приложения.
Перейдём в файл RSSDataSource.cs и добавим в класс RSSDataSource метод UpdateTile:
public static void UpdateTile()
{
var news = RSSDataSource.AllGroups[0].Items.ToList();
var xml = new XmlDocument();
xml.LoadXml(
string.Format(
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<tile>
<visual branding=""none"">
<binding template=""TileSquarePeekImageAndText03"">
<image id=""1"" src=""ms-appx:///Assets/Logo.png"" alt=""alt text""/>
<text id=""1"">{0}</text>
<text id=""2"">{1}</text>
<text id=""3"">{2}</text>
<text id=""4"">{3}</text>
</binding>
<binding template=""TileWidePeekImageAndText02"">
<image id=""1"" src=""ms-appx:///Assets/Habr_WideLogo.png"" alt=""alt text""/>
<text id=""1"">{0}</text>
<text id=""2"">{1}</text>
<text id=""3"">{2}</text>
<text id=""4"">{3}</text>
</binding>
</visual>
</tile>",
news.Count > 0 ? System.Net.WebUtility.HtmlEncode(news[0].Title) : "",
news.Count > 1 ? System.Net.WebUtility.HtmlEncode(news[1].Title) : "",
news.Count > 2 ? System.Net.WebUtility.HtmlEncode(news[2].Title) : "",
news.Count > 3 ? System.Net.WebUtility.HtmlEncode(news[3].Title) : ""));
TileUpdateManager.CreateTileUpdaterForApplication().Update(new TileNotification(xml));
}
Не забудте добавить в блок using следующие директивы:
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
Здесь мы создаём XML для обновления плиток, используя шаблоны, и собственно обновляем сами плитки. Код полностью прозрачен. Поскольку это пример, то я всегда беру просто первый RSS поток.
Чтобы это заработало, надо добавить вызов этого метода в любое место, где уже достпен RSS. Давайте добавим await к вызываемым методам AddGroupForFeedAsync загрузки RSS и тогда сможем спокойно вызывать нашу функцию, после добавления первого фида.
Переходим к методу LoadState в файле GroupedItemsPage.xaml.cs, добавляем к нему async, к методам AddGroupForFeedAsync и добавляем вызов UpdateTile::
protected async override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
this.DefaultViewModel["Groups"] = RSSDataSource.AllGroups;
await RSSDataSource.AddGroupForFeedAsync("http://blogs.msdn.com/b/stasus/rss.aspx");
RSSDataSource.UpdateTile();
RSSDataSource.AddGroupForFeedAsync("http://www.spugachev.com/feed");
}
Я специально отставил один вызов без await – позже – обратите внимание, как это выглядит, при старте приложения.
Теперь осталось добавить к приложению поддержку WideLogo. Для этого создайте файл Habr_WideLogo.scale-100.png размером 310 на 150, добавьте его в папку Asset проекта. Далее двойным щелчком по файлу Package.appxmanifest откройте его визуальный редактор и добавьте WideLogo в приложение:
Теперь мы полностью готовы к запуску приложения. Запустите его, потом закройте. Перейдите на стартовый экран и убедитесь, что живые плитки работают, причём, как квадратные, так и прямоугольные!
Пришло время добавлят контракты. Начнём с контракта Share. Реализуем источник данных. Шарить данные будем на странице поста RSS, предоставляя системе максимально возможное количество данных.
Перейдём к коду страницы ItemDetailPage.xaml.cs. Для того, чтобы предоставить системе данные, необходимо обработать событие DataRequested для DataTransferManager. Итак, начнём.
Добавляем контракт Share
Зарегистрируем обработчик события DataRequested в NavigateTo страницы (и отпишемся от события в NavigateFrom), и реализуем обработчик в котором предоставим системе название поста и текст:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
DataTransferManager.GetForCurrentView().DataRequested += Share_DataRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
DataTransferManager.GetForCurrentView().DataRequested -= Share_DataRequested;
}
private void Share_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var selectedItem = (RSSDataItem)this.flipView.SelectedItem;
args.Request.Data.Properties.Title = selectedItem.Title;
args.Request.Data.Properties.Description = selectedItem.Content;
args.Request.Data.SetText(selectedItem.Content);
}
Запустите приложение перейдите на страницу поста и проверьте, что теперь приложение может передать данные в другие приложения.
На самом деле мы получаем гораздо больше данных для расшаривания в RSS фиде. Это картинка поста, URI поста и т.д. Предлагаю самостоятельно модифцировать код метода добавляния фида, а также сопуствующие классы, чтобы было больше данных. Больше данных – с большим колчичеством приложений можно поделиться!
Внезапно возникнувшее желание рассказать про DataTemplateSelector сделало статью уже достаточно большой. Поэтому, придётся сделать третью часть. В неё перенесём контракт поиска и настройки, если получится — параллакс бэкграунда и другие полезные вещи, а если не поместится — сделаю ещё несколько частей.
Текущий код приложения можно скачать по ссылке: sdrv.ms/12CeOGk
Автор: stasus