Blog reader complete code (Windows Store apps using C#/VB and XAML)
[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]
This topic provides the complete code sample used in the tutorial Part 5: Create a blog reader.
Important This tutorial is intended for use with Microsoft Visual Studio 2012 and Windows 8. Parts of it will not work correctly with Microsoft Visual Studio 2013 and Windows 8.1.
This topic contains these sections:
- Technologies
- Requirements
- View the code (XAML)
Download location
This sample is not available for download.
Technologies
Programming languages | C#, Visual Basic .NET |
Programming models | Windows Runtime |
Requirements
Minimum supported client | Windows 8 |
Minimum supported server | Windows Server 2012 |
Minimum required SDK | Microsoft Visual Studio Express 2012 for Windows 8 |
View the code (XAML)
App.xaml
<Application
x:Class="WindowsBlogReader.App"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WindowsBlogReader">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--
Styles that define common aspects of the platform look and feel
Required by Visual Studio project and item templates
-->
<ResourceDictionary Source="Common/StandardStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<local:FeedDataSource x:Key="feedDataSource"/>
<local:DateConverter x:Key="dateConverter"/>
<SolidColorBrush x:Key="WindowsBlogBackgroundBrush" Color="#FF0A2562"/>
<Style x:Key="WindowsBlogLayoutRootStyle"
TargetType="Panel"
BasedOn="{StaticResource LayoutRootStyle}">
<Setter Property="Background"
Value="{StaticResource WindowsBlogBackgroundBrush}"/>
</Style>
<ControlTemplate x:Key="DateBlockTemplate">
<Canvas Height="86" Width="86" Margin="8,8,0,8"
HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBlock TextTrimming="WordEllipsis" TextWrapping="NoWrap"
Width="Auto" Height="Auto" Margin="8,0,4,0"
FontSize="32" FontWeight="Bold">
<TextBlock.Text>
<Binding Path="PubDate" Converter="{StaticResource dateConverter}"
ConverterParameter="month" />
</TextBlock.Text>
</TextBlock>
<TextBlock TextTrimming="WordEllipsis" TextWrapping="Wrap"
Width="40" Height="Auto" Margin="8,0,0,0"
FontSize="34" FontWeight="Bold" Canvas.Top="36">
<TextBlock.Text>
<Binding Path="PubDate" Converter="{StaticResource dateConverter}"
ConverterParameter="day" />
</TextBlock.Text>
</TextBlock>
<Line Stroke="White" StrokeThickness="2" X1="54" Y1="46" X2="54" Y2="80" />
<TextBlock TextWrapping="Wrap"
Width="20" Height="Auto"
FontSize="{StaticResource ControlContentThemeFontSize}"
Canvas.Top="42" Canvas.Left="60">
<TextBlock.Text>
<Binding Path="PubDate" Converter="{StaticResource dateConverter}"
ConverterParameter="year" />
</TextBlock.Text>
</TextBlock>
</Canvas>
</ControlTemplate>
<SolidColorBrush x:Key="ListViewItemSelectedBackgroundThemeBrush" Color="#FF465985"/>
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBackgroundThemeBrush" Color="#FF384A72"/>
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBorderThemeBrush" Color="#FF384A72" />
</ResourceDictionary>
</Application.Resources>
</Application>
App.xaml.cs/vb
using System;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The Blank Application template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234227
namespace WindowsBlogReader
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used when the application is launched to open a specific file, to display
/// search results, and so forth.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
WindowsBlogReader.Common.SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
var connectionProfile = Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile();
if (connectionProfile != null)
{
FeedDataSource feedDataSource = (FeedDataSource)App.Current.Resources["feedDataSource"];
if (feedDataSource != null)
{
if (feedDataSource.Feeds.Count == 0)
{
await feedDataSource.GetFeedsAsync();
}
}
}
else
{
var messageDialog = new Windows.UI.Popups.MessageDialog("An internet connection is needed to download feeds. Please check your connection and restart the app.");
var result = messageDialog.ShowAsync();
}
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
await WindowsBlogReader.Common.SuspensionManager.RestoreAsync();
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
if (!rootFrame.Navigate(typeof(ItemsPage), args.Arguments))
{
throw new Exception("Failed to create initial page");
}
}
// Ensure the current window is active
Window.Current.Activate();
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
await WindowsBlogReader.Common.SuspensionManager.SaveAsync();
deferral.Complete();
}
}
}
' The Blank Application template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234227
''' <summary>
''' Provides application-specific behavior to supplement the default Application class.
''' </summary>
NotInheritable Class App
Inherits Application
''' <summary>
''' Invoked when the application is launched normally by the end user. Other entry points
''' will be used when the application is launched to open a specific file, to display
''' search results, and so forth.
''' </summary>
''' <param name="args">Details about the launch request and process.</param>
Protected Overrides Async Sub OnLaunched(args As Windows.ApplicationModel.Activation.LaunchActivatedEventArgs)
Dim rootFrame As Frame = Window.Current.Content
' Do not repeat app initialization when the Window already has content,
' just ensure that the window is active
If rootFrame Is Nothing Then
' Create a Frame to act as the navigation context and navigate to the first page
rootFrame = New Frame()
Common.SuspensionManager.RegisterFrame(rootFrame, "AppFrame")
' Add this code after "rootFrame = new Frame()"
Dim connectionProfile = Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile()
If connectionProfile IsNot Nothing Then
Dim feedDataSource = DirectCast(App.Current.Resources("feedDataSource"), FeedDataSource)
If feedDataSource IsNot Nothing Then
If feedDataSource.Feeds.Count = 0 Then
Await feedDataSource.GetFeedsAsync()
End If
End If
Else
Dim messageDialog = New Windows.UI.Popups.MessageDialog("An internet connection is needed to download feeds. Please check your connection and restart the app.")
Dim result = messageDialog.ShowAsync()
End If
If args.PreviousExecutionState = ApplicationExecutionState.Terminated Then
' TODO: Load state from previously suspended application
Await Common.SuspensionManager.RestoreAsync()
End If
' Place the frame in the current Window
Window.Current.Content = rootFrame
End If
If rootFrame.Content Is Nothing Then
' When the navigation stack isn't restored navigate to the first page,
' configuring the new page by passing required information as a navigation
' parameter
If Not rootFrame.Navigate(GetType(ItemsPage), args.Arguments) Then
Throw New Exception("Failed to create initial page")
End If
End If
' Ensure the current window is active
Window.Current.Activate()
End Sub
''' <summary>
''' Invoked when application execution is being suspended. Application state is saved
''' without knowing whether the application will be terminated or resumed with the contents
''' of memory still intact.
''' </summary>
''' <param name="sender">The source of the suspend request.</param>
''' <param name="e">Details about the suspend request.</param>
Private Async Sub OnSuspending(sender As Object, e As SuspendingEventArgs) Handles Me.Suspending
Dim deferral As SuspendingDeferral = e.SuspendingOperation.GetDeferral()
' TODO: Save application state and stop any background activity
Await Common.SuspensionManager.SaveAsync()
deferral.Complete()
End Sub
End Class
ItemsPage.xaml
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="WindowsBlogReader.ItemsPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WindowsBlogReader"
xmlns:common="using:WindowsBlogReader.Common"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- Collection of items displayed by this page -->
<CollectionViewSource
x:Name="itemsViewSource"
Source="{Binding Items}"/>
<!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
<x:String x:Key="AppName">Windows Team Blogs</x:String>
<!-- light blue -->
<SolidColorBrush x:Key="BlockBackgroundBrush" Color="#FF557EB9"/>
<!-- Grid Styles -->
<Style x:Key="GridTitleTextStyle" TargetType="TextBlock"
BasedOn="{StaticResource BasicTextStyle}">
<Setter Property="FontSize" Value="26.667"/>
<Setter Property="Margin" Value="12,0,12,2"/>
</Style>
<Style x:Key="GridDescriptionTextStyle" TargetType="TextBlock"
BasedOn="{StaticResource BasicTextStyle}">
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Margin" Value="12,0,12,60"/>
</Style>
<DataTemplate x:Key="DefaultGridItemTemplate">
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="{StaticResource BlockBackgroundBrush}" />
<TextBlock Text="{Binding Title}"
Style="{StaticResource GridTitleTextStyle}"/>
<TextBlock Text="{Binding Description}"
Style="{StaticResource GridDescriptionTextStyle}" />
<StackPanel VerticalAlignment="Bottom" Orientation="Horizontal"
Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="Last Updated" Margin="12,4,0,8" Height="42"/>
<TextBlock Text="{Binding PubDate, Converter={StaticResource dateConverter}}"
Margin="12,4,12,8" />
</StackPanel>
</Grid>
</DataTemplate>
<!-- Used in Snapped view -->
<DataTemplate x:Key="NarrowListItemTemplate">
<Grid Height="80">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{StaticResource BlockBackgroundBrush}"
Width="80" Height="80" />
<ContentControl Template="{StaticResource DateBlockTemplate}"
Margin="-12,-12,0,0"/>
<StackPanel Grid.Column="1" HorizontalAlignment="Left"
Margin="12,8,0,0">
<TextBlock Text="{Binding Title}" MaxHeight="56" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Style="{StaticResource WindowsBlogLayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Horizontal scrolling grid used in most view states -->
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.RowSpan="2"
Padding="116,136,116,46"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
ItemTemplate="{StaticResource DefaultGridItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false" IsItemClickEnabled="True" ItemClick="ItemView_ItemClick"/>
<!-- Vertical scrolling list only used when snapped -->
<ListView
x:Name="itemListView"
AutomationProperties.AutomationId="ItemsListView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.Row="1"
Visibility="Collapsed"
Margin="0,-10,0,0"
Padding="10,0,0,60"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
ItemTemplate="{StaticResource NarrowListItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false" IsItemClickEnabled="True" ItemClick="ItemView_ItemClick"/>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Click="GoBack"
IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}"/>
<TextBlock x:Name="pageTitle" Grid.Column="1" Text="{StaticResource AppName}"
IsHitTestVisible="false"
Style="{StaticResource PageHeaderTextStyle}"/>
</Grid>
<VisualStateManager.VisualStateGroups>
<!-- Visual states reflect the application's view state -->
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource PortraitBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView"
Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="96,136,86,56"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--
The back button and title have different styles when snapped,
and the list representation is substituted
for the grid displayed in all other view states
-->
<VisualState x:Name="Snapped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource SnappedBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle"
Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource SnappedPageHeaderTextStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</common:LayoutAwarePage>
ItemsPage.xaml.cs/vb
using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Controls;
// The Items Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234233
namespace WindowsBlogReader
{
/// <summary>
/// A page that displays a collection of item previews. In the Split Application this page
/// is used to display and select one of the available groups.
/// </summary>
public sealed partial class ItemsPage : WindowsBlogReader.Common.LayoutAwarePage
{
public ItemsPage()
{
this.InitializeComponent();
}
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="navigationParameter">The parameter value passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
/// </param>
/// <param name="pageState">A dictionary of state preserved by this page during an earlier
/// session. This will be null the first time a page is visited.</param>
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
// TODO: Assign a bindable collection of items to this.DefaultViewModel["Items"]
FeedDataSource feedDataSource = (FeedDataSource)App.Current.Resources["feedDataSource"];
if (feedDataSource != null)
{
this.DefaultViewModel["Items"] = feedDataSource.Feeds;
}
}
private void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
// Navigate to the split page, configuring the new page
// by passing the title of the clicked item as a navigation parameter
if (e.ClickedItem != null)
{
string title = ((FeedData)e.ClickedItem).Title;
this.Frame.Navigate(typeof(SplitPage), title);
}
}
}
}
' The Items Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234233
''' <summary>
''' A page that displays a collection of item previews. In the Split Application this page
''' is used to display and select one of the available groups.
''' </summary>
Public NotInheritable Class ItemsPage
Inherits Common.LayoutAwarePage
''' <summary>
''' Populates the page with content passed during navigation. Any saved state is also
''' provided when recreating a page from a prior session.
''' </summary>
''' <param name="navigationParameter">The parameter value passed to <see cref="Frame.Navigate"/>
''' when this page was initially requested.
''' </param>
''' <param name="pageState">A dictionary of state preserved by this page during an earlier
''' session. This will be null the first time a page is visited.</param>
Protected Overrides Sub LoadState(navigationParameter As Object, pageState As Dictionary(Of String, Object))
' TODO: Assign a bindable collection of items to Me.DefaultViewModel("Items")
Dim feedDataSource = DirectCast(App.Current.Resources("feedDataSource"), FeedDataSource)
If feedDataSource IsNot Nothing Then
Me.DefaultViewModel("Items") = feedDataSource.Feeds
End If
End Sub
Private Sub ItemView_ItemClick(sender As Object, e As ItemClickEventArgs) _
Handles itemGridView.ItemClick, itemListView.ItemClick
' Navigate to the split page, configuring the new page
' by passing the title of the clicked item as a navigation parameter
If e.ClickedItem IsNot Nothing Then
Dim title = (DirectCast(e.ClickedItem, FeedData)).Title
Me.Frame.Navigate(GetType(SplitPage), title)
End If
End Sub
End Class
SplitPage.xaml
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="WindowsBlogReader.SplitPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WindowsBlogReader"
xmlns:common="using:WindowsBlogReader.Common"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<Style x:Key="WebViewAppBarButtonStyle" TargetType="Button"
BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="WebViewAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="View Web Page"/>
<Setter Property="Content" Value=""/>
</Style>
<Storyboard x:Name="PopInStoryboard">
<PopInThemeAnimation Storyboard.TargetName="contentViewBorder"
FromHorizontalOffset="400"/>
</Storyboard>
<!-- Collection of items displayed by this page -->
<CollectionViewSource
x:Name="itemsViewSource"
Source="{Binding Items}"/>
<DataTemplate x:Key="DefaultListItemTemplate">
<Grid Height="110" Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="#FF6BBD46" Width="110" Height="110">
<ContentControl Template="{StaticResource DateBlockTemplate}" />
</Border>
<!--<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
<Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>-->
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Title}" FontSize="26.667" TextWrapping="Wrap"
MaxHeight="72" Foreground="#FFFE5815" />
<TextBlock Text="{Binding Author}" FontSize="18.667" />
<!--<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding Author}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding PubDate}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>-->
</StackPanel>
</Grid>
</DataTemplate>
<!-- green -->
<SolidColorBrush x:Key="BlockBackgroundBrush" Color="#FF6BBD46"/>
<!-- Used in Filled and Snapped views -->
<DataTemplate x:Key="NarrowListItemTemplate">
<Grid Height="80">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{StaticResource BlockBackgroundBrush}"
Width="80" Height="80"/>
<ContentControl Template="{StaticResource DateBlockTemplate}"
Margin="-12,-12,0,0"/>
<StackPanel Grid.Column="1" HorizontalAlignment="Left"
Margin="12,8,0,0">
<TextBlock Text="{Binding Title}" MaxHeight="56"
Foreground="#FFFE5815" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Author}" FontSize="12" />
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<Page.TopAppBar>
<AppBar Padding="10,0,10,0" Background="#F20A2562">
<Grid>
<Button Click="ViewDetail_Click" HorizontalAlignment="Right"
Style="{StaticResource WebViewAppBarButtonStyle}"/>
</Grid>
</AppBar>
</Page.TopAppBar>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Style="{StaticResource WindowsBlogLayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="primaryColumn" Width="610"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Back button and page title -->
<Grid x:Name="titlePanel" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button
x:Name="backButton"
Click="GoBack"
IsEnabled="{Binding DefaultViewModel.CanGoBack, ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}"/>
<TextBlock x:Name="pageTitle" Grid.Column="1" Text="{Binding Feed.Title}"
Style="{StaticResource PageHeaderTextStyle}" />
</Grid>
<!-- Vertical scrolling item list -->
<ListView
x:Name="itemListView"
AutomationProperties.AutomationId="ItemsListView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.Row="1"
Margin="-10,-10,0,0"
Padding="120,0,0,60"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
IsSwipeEnabled="False"
SelectionChanged="ItemListView_SelectionChanged"
ItemTemplate="{StaticResource DefaultListItemTemplate}"/>
<!-- Details for selected item -->
<ScrollViewer
x:Name="itemDetail"
AutomationProperties.AutomationId="ItemDetailScrollViewer"
Grid.Column="1"
Grid.Row="1"
Padding="70,0,120,0"
DataContext="{Binding SelectedItem, ElementName=itemListView}"
Style="{StaticResource VerticalScrollViewerStyle}">
<Grid x:Name="itemDetailGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="itemTitle" Text="{Binding Title}"
Style="{StaticResource SubheaderTextStyle}"/>
<Border x:Name="contentViewBorder" BorderBrush="Gray" BorderThickness="2"
Grid.Row="1" Margin="0,15,0,20">
<Grid>
<WebView x:Name="contentView" NavigationCompleted="contentView_NavigationCompleted" />
<Rectangle x:Name="contentViewRect" />
</Grid>
</Border>
</Grid>
</ScrollViewer>
<VisualStateManager.VisualStateGroups>
<!-- Visual states reflect the application's view state -->
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscapeOrWide"/>
<!-- Filled uses a simpler list format in a narrower column -->
<VisualState x:Name="FilledOrNarrow">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="420"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="ItemTemplate">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NarrowListItemTemplate}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="60,0,66,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--
The page respects the narrower 100-pixel margin convention for portrait, and the page
initially hides details to show only the list of items
-->
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="100,0,90,60"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--
When an item is selected in portrait the details display requires more extensive changes:
* Hide the master list and the column is was in
* Move item details down a row to make room for the title
* Move the title directly above the details
* Adjust margins and padding for details
-->
<VisualState x:Name="FullScreenPortrait_Detail">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.Row)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.RowSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="titlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailGrid" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,60"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="100,0,90,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--
The back button and title have different styles when snapped, and the page
initially hides details to show only the list of items
-->
<VisualState x:Name="Snapped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="320"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="ItemTemplate">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NarrowListItemTemplate}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="20,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--
When snapped and an item is selected the details display requires more extensive changes:
* Hide the master list and the column is was in
* Move item details down a row to make room for the title
* Move the title directly above the details
* Adjust margins and padding for details
* Use a different font for title and subtitle
* Adjust margins below subtitle
-->
<VisualState x:Name="Snapped_Detail">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.Row)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.RowSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="titlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailTitlePanel" Storyboard.TargetProperty="(Grid.Row)">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>-->
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailTitlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="20,0,20,0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailGrid" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,60"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemTitle" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TitleTextStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemTitle" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemSubtitle" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CaptionTextStyle}"/>
</ObjectAnimationUsingKeyFrames>-->
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</common:LayoutAwarePage>
SplitPage.xaml.cs/vb
using System;
using System.Collections.Generic;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
// The Split Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234234
namespace WindowsBlogReader
{
/// <summary>
/// A page that displays a group title, a list of items within the group, and details for
/// the currently selected item.
/// </summary>
public sealed partial class SplitPage : WindowsBlogReader.Common.LayoutAwarePage
{
public SplitPage()
{
this.InitializeComponent();
}
private void ViewDetail_Click(object sender, RoutedEventArgs e)
{
FeedItem selectedItem = this.itemListView.SelectedItem as FeedItem;
if (selectedItem != null && this.Frame != null)
{
string itemTitle = selectedItem.Title;
this.Frame.Navigate(typeof(DetailPage), itemTitle);
}
}
#region Page state management
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="navigationParameter">The parameter value passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
/// </param>
/// <param name="pageState">A dictionary of state preserved by this page during an earlier
/// session. This will be null the first time a page is visited.</param>
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
// Run the PopInThemeAnimation
Windows.UI.Xaml.Media.Animation.Storyboard sb =
this.FindName("PopInStoryboard") as Windows.UI.Xaml.Media.Animation.Storyboard;
if (sb != null) sb.Begin();
// TODO: Assign a bindable group to this.DefaultViewModel["Group"]
// TODO: Assign a collection of bindable items to this.DefaultViewModel["Items"]
string feedTitle = (string)navigationParameter;
FeedData feedData = FeedDataSource.GetFeed(feedTitle);
if (feedData != null)
{
this.DefaultViewModel["Feed"] = feedData;
this.DefaultViewModel["Items"] = feedData.Items;
}
if (pageState == null)
{
// When this is a new page, select the first item automatically unless logical page
// navigation is being used (see the logical page navigation #region below.)
if (!this.UsingLogicalPageNavigation() && this.itemsViewSource.View != null)
{
this.itemsViewSource.View.MoveCurrentToFirst();
}
else
{
this.itemsViewSource.View.MoveCurrentToPosition(-1);
}
}
else
{
// Restore the previously saved state associated with this page
if (pageState.ContainsKey("SelectedItem") && this.itemsViewSource.View != null)
{
// TODO: Invoke this.itemsViewSource.View.MoveCurrentTo() with the selected
// item as specified by the value of pageState["SelectedItem"]
string itemTitle = (string)pageState["SelectedItem"];
FeedItem selectedItem = FeedDataSource.GetItem(itemTitle);
this.itemsViewSource.View.MoveCurrentTo(selectedItem);
}
}
}
/// <summary>
/// Preserves state associated with this page in case the application is suspended or the
/// page is discarded from the navigation cache. Values must conform to the serialization
/// requirements of <see cref="SuspensionManager.SessionState"/>.
/// </summary>
/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
protected override void SaveState(Dictionary<String, Object> pageState)
{
if (this.itemsViewSource.View != null)
{
var selectedItem = this.itemsViewSource.View.CurrentItem;
// TODO: Derive a serializable navigation parameter and assign it to
// pageState["SelectedItem"]
if (selectedItem != null)
{
string itemTitle = ((FeedItem)selectedItem).Title;
pageState["SelectedItem"] = itemTitle;
}
}
}
#endregion
#region Logical page navigation
// Visual state management typically reflects the four application view states directly
// (full screen landscape and portrait plus snapped and filled views.) The split page is
// designed so that the snapped and portrait view states each have two distinct sub-states:
// either the item list or the details are displayed, but not both at the same time.
//
// This is all implemented with a single physical page that can represent two logical
// pages. The code below achieves this goal without making the user aware of the
// distinction.
/// <summary>
/// Invoked to determine whether the page should act as one logical page or two.
/// </summary>
/// <param name="viewState">The view state for which the question is being posed, or null
/// for the current view state. This parameter is optional with null as the default
/// value.</param>
/// <returns>True when the view state in question is portrait or snapped, false
/// otherwise.</returns>
private bool UsingLogicalPageNavigation(ApplicationViewState? viewState = null)
{
if (viewState == null) viewState = ApplicationView.Value;
return viewState == ApplicationViewState.FullScreenPortrait ||
viewState == ApplicationViewState.Snapped;
}
/// <summary>
/// Invoked when an item within the list is selected.
/// </summary>
/// <param name="sender">The GridView (or ListView when the application is Snapped)
/// displaying the selected item.</param>
/// <param name="e">Event data that describes how the selection was changed.</param>
void ItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Invalidate the view state when logical page navigation is in effect, as a change
// in selection may cause a corresponding change in the current logical page. When
// an item is selected this has the effect of changing from displaying the item list
// to showing the selected item's details. When the selection is cleared this has the
// opposite effect.
if (this.UsingLogicalPageNavigation()) this.InvalidateVisualState();
// Add this code to populate the web view
// with the content of the selected blog post.
Selector list = sender as Selector;
FeedItem selectedItem = list.SelectedItem as FeedItem;
if (selectedItem != null)
{
this.contentView.NavigateToString(selectedItem.Content);
}
else
{
this.contentView.NavigateToString("");
}
}
/// <summary>
/// Invoked when the page's back button is pressed.
/// </summary>
/// <param name="sender">The back button instance.</param>
/// <param name="e">Event data that describes how the back button was clicked.</param>
protected override void GoBack(object sender, RoutedEventArgs e)
{
if (this.UsingLogicalPageNavigation() && itemListView.SelectedItem != null)
{
// When logical page navigation is in effect and there's a selected item that
// item's details are currently displayed. Clearing the selection will return to
// the item list. From the user's point of view this is a logical backward
// navigation.
this.itemListView.SelectedItem = null;
}
else
{
// When logical page navigation is not in effect, or when there is no selected
// item, use the default back button behavior.
base.GoBack(sender, e);
}
}
/// <summary>
/// Invoked to determine the name of the visual state that corresponds to an application
/// view state.
/// </summary>
/// <param name="viewState">The view state for which the question is being posed.</param>
/// <returns>The name of the desired visual state. This is the same as the name of the
/// view state except when there is a selected item in portrait and snapped views where
/// this additional logical page is represented by adding a suffix of _Detail.</returns>
protected override string DetermineVisualState(ApplicationViewState viewState)
{
// Update the back button's enabled state when the view state changes
var logicalPageBack = this.UsingLogicalPageNavigation(viewState) && this.itemListView.SelectedItem != null;
var physicalPageBack = this.Frame != null && this.Frame.CanGoBack;
this.DefaultViewModel["CanGoBack"] = logicalPageBack || physicalPageBack;
// Determine visual states for landscape layouts based not on the view state, but
// on the width of the window. This page has one layout that is appropriate for
// 1366 virtual pixels or wider, and another for narrower displays or when a snapped
// application reduces the horizontal space available to less than 1366.
if (viewState == ApplicationViewState.Filled ||
viewState == ApplicationViewState.FullScreenLandscape)
{
var windowWidth = Window.Current.Bounds.Width;
if (windowWidth >= 1366) return "FullScreenLandscapeOrWide";
return "FilledOrNarrow";
}
// When in portrait or snapped start with the default visual state name, then add a
// suffix when viewing details instead of the list
var defaultStateName = base.DetermineVisualState(viewState);
return logicalPageBack ? defaultStateName + "_Detail" : defaultStateName;
}
#endregion
private void contentView_NavigationCompleted(object sender, WebViewNavigationCompletedEventArgs e)
{
if (!e.IsSuccess)
{
string errorString = "<p>Page could not be loaded.</p><p>Error is: " + e.WebErrorStatus.ToString() + "</p>";
this.contentView.NavigateToString(errorString);
}
}
}
}
' The Split Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234234
''' <summary>
''' A page that displays a group title, a list of items within the group, and details for
''' the currently selected item.
''' </summary>
Public NotInheritable Class SplitPage
Inherits Common.LayoutAwarePage
Private Sub ViewDetail_Click(sender As Object, e As RoutedEventArgs)
Dim selectedItem = DirectCast(Me.itemListView.SelectedItem, FeedItem)
If selectedItem IsNot Nothing AndAlso Me.Frame IsNot Nothing Then
Dim itemTitle = selectedItem.Title
Me.Frame.Navigate(GetType(DetailPage), itemTitle)
End If
End Sub
#Region "Page state management"
''' <summary>
''' Populates the page with content passed during navigation. Any saved state is also
''' provided when recreating a page from a prior session.
''' </summary>
''' <param name="navigationParameter">The parameter value passed to <see cref="Frame.Navigate"/>
''' when this page was initially requested.
''' </param>
''' <param name="pageState">A dictionary of state preserved by this page during an earlier
''' session. This will be null the first time a page is visited.</param>
Protected Overrides Sub LoadState(navigationParameter As Object, pageState As Dictionary(Of String, Object))
' Run the PopInThemeAnimation
Dim sb As Windows.UI.Xaml.Media.Animation.Storyboard = Me.FindName("PopInStoryboard")
If sb IsNot Nothing Then
sb.Begin()
End If
' TODO: Assign a bindable group to Me.DefaultViewModel("Group")
' TODO: Assign a collection of bindable items to Me.DefaultViewModel("Items")
Dim feedTitle = DirectCast(navigationParameter, String)
Dim feedData = FeedDataSource.GetFeed(feedTitle)
If feedData IsNot Nothing Then
Me.DefaultViewModel("Feed") = feedData
Me.DefaultViewModel("Items") = feedData.Items
End If
If pageState Is Nothing Then
' When this is a new page, select the first item automatically unless logical page
' navigation is being used (see the logical page navigation #region below.)
If Not Me.UsingLogicalPageNavigation() AndAlso Me.itemsViewSource.View IsNot Nothing Then
Me.itemsViewSource.View.MoveCurrentToFirst()
Else
Me.itemsViewSource.View.MoveCurrentToPosition(-1)
End If
Else
' Restore the previously saved state associated with this page
If pageState.ContainsKey("SelectedItem") AndAlso Me.itemsViewSource.View IsNot Nothing Then
' TODO: Invoke Me.itemsViewSource.View.MoveCurrentTo() with the selected
' item as specified by the value of pageState("SelectedItem")
Dim itemTitle = DirectCast(pageState("SelectedItem"), String)
Dim selectedItem = FeedDataSource.GetItem(itemTitle)
Me.itemsViewSource.View.MoveCurrentTo(selectedItem)
End If
End If
End Sub
''' <summary>
''' Preserves state associated with this page in case the application is suspended or the
''' page is discarded from the navigation cache. Values must conform to the serialization
''' requirements of <see cref="Common.SuspensionManager.SessionState"/>.
''' </summary>
''' <param name="pageState">An empty dictionary to be populated with serializable state.</param>
Protected Overrides Sub SaveState(pageState As Dictionary(Of String, Object))
If Me.itemsViewSource.View IsNot Nothing Then
Dim selectedItem As Object = Me.itemsViewSource.View.CurrentItem
' TODO: Derive a serializable navigation parameter and assign it to
' pageState("SelectedItem")
If selectedItem IsNot Nothing Then
Dim itemTitle = (DirectCast(selectedItem, FeedItem)).Title
pageState("SelectedItem") = itemTitle
End If
End If
End Sub
#End Region
#Region "Logical page navigation"
' Visual state management typically reflects the four application view states directly
' (full screen landscape and portrait plus snapped and filled views.) The split page is
' designed so that the snapped and portrait view states each have two distinct sub-states:
' either the item list or the details are displayed, but not both at the same time.
'
' This is all implemented with a single physical page that can represent two logical
' pages. The code below achieves this goal without making the user aware of the
' distinction.
''' <summary>
''' Invoked to determine whether the page should act as one logical page or two.
''' </summary>
''' <param name="viewState">The view state for which the question is being posed, or null
''' for the current view state. This parameter is optional with null as the default
''' value.</param>
''' <returns>True when the view state in question is portrait or snapped, false
''' otherwise.</returns>
Private Function UsingLogicalPageNavigation(Optional viewState As ApplicationViewState? = Nothing) As Boolean
If Not viewState.HasValue Then viewState = ApplicationView.Value
Return viewState.Value = ApplicationViewState.FullScreenPortrait OrElse
viewState.Value = ApplicationViewState.Snapped
End Function
''' <summary>
''' Invoked when an item within the list is selected.
''' </summary>
''' <param name="sender">The GridView (or ListView when the application is Snapped)
''' displaying the selected item.</param>
''' <param name="e">Event data that describes how the selection was changed.</param>
Private Sub ItemListView_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
' Invalidate the view state when logical page navigation is in effect, as a change in
' selection may cause a corresponding change in the current logical page. When an item
' is selected this has the effect of changing from displaying the item list to showing
' the selected item's details. When the selection is cleared this has the opposite effect.
If Me.UsingLogicalPageNavigation Then Me.InvalidateVisualState()
' Add this code to populate the web view
' with the content of the selected blog post.
Dim list = DirectCast(sender, Selector)
Dim selectedItem = DirectCast(list.SelectedItem, FeedItem)
If selectedItem IsNot Nothing Then
Me.contentView.NavigateToString(selectedItem.Content)
Else
Me.contentView.NavigateToString("")
End If
End Sub
''' <summary>
''' Invoked when the page's back button is pressed.
''' </summary>
''' <param name="sender">The back button instance.</param>
''' <param name="e">Event data that describes how the back button was clicked.</param>
Protected Overrides Sub GoBack(sender As Object, e As RoutedEventArgs)
If Me.UsingLogicalPageNavigation() AndAlso Me.itemListView.SelectedItem IsNot Nothing Then
' When logical page navigation is in effect and there's a selected item that item's
' details are currently displayed. Clearing the selection will return to the item
' list. From the user's point of view this is a logical backward navigation.
Me.itemListView.SelectedItem = Nothing
Else
' When logical page navigation is not in effect, or when there is no selected item,
' use the default back button behavior.
MyBase.GoBack(sender, e)
End If
End Sub
''' <summary>
''' Invoked to determine the name of the visual state that corresponds to an application
''' view state.
''' </summary>
''' <param name="viewState">The view state for which the question is being posed.</param>
''' <returns>The name of the desired visual state. This is the same as the name of the
''' view state except when there is a selected item in portrait and snapped views where
''' this additional logical page is represented by adding a suffix of _Detail.</returns>
Protected Overrides Function DetermineVisualState(viewState As ApplicationViewState) As String
' Update the back button's enabled state when the view state changes
Dim logicalPageBack As Boolean = Me.UsingLogicalPageNavigation(viewState) AndAlso Me.itemListView.SelectedItem IsNot Nothing
Dim physicalPageBack As Boolean = Me.Frame IsNot Nothing AndAlso Me.Frame.CanGoBack
Me.DefaultViewModel("CanGoBack") = logicalPageBack OrElse physicalPageBack
' Determine visual states for landscape layouts based not on the view state, but
' on the width of the window. This page has one layout that is appropriate for
' 1366 virtual pixels or wider, and another for narrower displays or when a snapped
' application reduces the horizontal space available to less than 1366.
If viewState = ApplicationViewState.Filled OrElse
viewState = ApplicationViewState.FullScreenLandscape Then
Dim windowWidth As Double = Window.Current.Bounds.Width
If windowWidth >= 1366 Then Return "FullScreenLandscapeOrWide"
Return "FilledOrNarrow"
End If
' When in portrait or snapped start with the default visual state name, then add a
' suffix when viewing details instead of the list
Dim defaultStateName As String = MyBase.DetermineVisualState(viewState)
If logicalPageBack Then Return defaultStateName + "_Detail"
Return defaultStateName
End Function
#End Region
Private Sub ContentView_NavigationFailed(sender As Object, e As WebViewNavigationFailedEventArgs) _
Handles contentView.NavigationFailed
Dim errorString = "<p>Page could not be loaded.</p><p>Error is: " & e.WebErrorStatus.ToString() & "</p>"
Me.contentView.NavigateToString(errorString)
End Sub
End Class
DetailPage.xaml
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="WindowsBlogReader.DetailPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WindowsBlogReader"
xmlns:common="using:WindowsBlogReader.Common"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
<x:String x:Key="AppName">My Application</x:String>
<Storyboard x:Name="PopInStoryboard">
<PopInThemeAnimation Storyboard.TargetName="contentViewBorder"
FromHorizontalOffset="400"/>
</Storyboard>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Style="{StaticResource WindowsBlogLayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Click="GoBack"
IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}"/>
<TextBlock x:Name="pageTitle" Grid.Column="1" Text="{Binding Title}"
Style="{StaticResource PageHeaderTextStyle}"/>
</Grid>
<Border x:Name="contentViewBorder" BorderBrush="Gray" BorderThickness="2"
Grid.Row="1" Margin="120,15,20,20">
<WebView x:Name="contentView"
NavigationCompleted="contentView_NavigationCompleted"/>
</Border>
<VisualStateManager.VisualStateGroups>
<!-- Visual states reflect the application's view state -->
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource PortraitBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!-- The back button and title have different styles when snapped -->
<VisualState x:Name="Snapped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource SnappedBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle"
Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource SnappedPageHeaderTextStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="contentViewBorder"
Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="20,5,20,20"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</common:LayoutAwarePage>
DetailPage.xaml.cs/vb
using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Controls;
// The Basic Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234237
namespace WindowsBlogReader
{
/// <summary>
/// A basic page that provides characteristics common to most applications.
/// </summary>
public sealed partial class DetailPage : WindowsBlogReader.Common.LayoutAwarePage
{
public DetailPage()
{
this.InitializeComponent();
}
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="navigationParameter">The parameter value passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
/// </param>
/// <param name="pageState">A dictionary of state preserved by this page during an earlier
/// session. This will be null the first time a page is visited.</param>
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
// Run the PopInThemeAnimation
Windows.UI.Xaml.Media.Animation.Storyboard sb =
this.FindName("PopInStoryboard") as Windows.UI.Xaml.Media.Animation.Storyboard;
if (sb != null) sb.Begin();
// Add this code to navigate the web view to the selected blog post.
string itemTitle = (string)navigationParameter;
FeedItem feedItem = FeedDataSource.GetItem(itemTitle);
if (feedItem != null)
{
this.contentView.Navigate(feedItem.Link);
this.DataContext = feedItem;
}
}
/// <summary>
/// Preserves state associated with this page in case the application is suspended or the
/// page is discarded from the navigation cache. Values must conform to the serialization
/// requirements of <see cref="SuspensionManager.SessionState"/>.
/// </summary>
/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
protected override void SaveState(Dictionary<String, Object> pageState)
{
}
private void contentView_NavigationCompleted(object sender, WebViewNavigationCompletedEventArgs e)
{
if (!e.IsSuccess)
{
string errorString = "<p>Page could not be loaded.</p><p>Error is: " + e.WebErrorStatus.ToString() + "</p>";
this.contentView.NavigateToString(errorString);
}
}
}
}
' The Basic Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234237
''' <summary>
''' A basic page that provides characteristics common to most applications.
''' </summary>
Public NotInheritable Class DetailPage
Inherits Common.LayoutAwarePage
''' <summary>
''' Populates the page with content passed during navigation. Any saved state is also
''' provided when recreating a page from a prior session.
''' </summary>
''' <param name="navigationParameter">The parameter value passed to
''' <see cref="Frame.Navigate"/> when this page was initially requested.
''' </param>
''' <param name="pageState">A dictionary of state preserved by this page during an earlier
''' session. This will be null the first time a page is visited.</param>
Protected Overrides Sub LoadState(navigationParameter As Object, pageState As Dictionary(Of String, Object))
' Run the PopInThemeAnimation
Dim sb As Windows.UI.Xaml.Media.Animation.Storyboard = Me.FindName("PopInStoryboard")
If sb IsNot Nothing Then
sb.Begin()
End If
' Add this code to navigate the web view to the selected blog post.
Dim itemTitle = DirectCast(navigationParameter, String)
Dim feedItem = FeedDataSource.GetItem(itemTitle)
If feedItem IsNot Nothing Then
Me.contentView.Navigate(feedItem.Link)
Me.DataContext = feedItem
End If
End Sub
''' <summary>
''' Preserves state associated with this page in case the application is suspended or the
''' page is discarded from the navigation cache. Values must conform to the serialization
''' requirements of <see cref="Common.SuspensionManager.SessionState"/>.
''' </summary>
''' <param name="pageState">An empty dictionary to be populated with serializable state.</param>
Protected Overrides Sub SaveState(pageState As Dictionary(Of String, Object))
End Sub
Private Sub contentView_NavigationFailed(sender As Object, e As WebViewNavigationFailedEventArgs)
Dim errorString = "<p>Page could not be loaded.</p><p>Error is: " & e.WebErrorStatus.ToString() & "</p>"
Me.contentView.NavigateToString(errorString)
End Sub
End Class
FeedData.cs/vb
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.Web.Syndication;
namespace WindowsBlogReader
{
// FeedData
// Holds info for a single blog feed, including a list of blog posts (FeedItem).
public class FeedData
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime PubDate { get; set; }
private List<FeedItem> _Items = new List<FeedItem>();
public List<FeedItem> Items
{
get
{
return this._Items;
}
}
}
// FeedItem
// Holds info for a single blog post.
public class FeedItem
{
public string Title { get; set; }
public string Author { get; set; }
public string Content { get; set; }
public DateTime PubDate { get; set; }
public Uri Link { get; set; }
}
// FeedDataSource
// Holds a collection of blog feeds (FeedData), and contains methods needed to
// retreive the feeds.
public class FeedDataSource
{
private ObservableCollection<FeedData> _Feeds = new ObservableCollection<FeedData>();
public ObservableCollection<FeedData> Feeds
{
get
{
return this._Feeds;
}
}
public async Task GetFeedsAsync()
{
Task<FeedData> feed1 =
GetFeedAsync("https://windowsteamblog.com/windows/b/developers/atom.aspx");
Task<FeedData> feed2 =
GetFeedAsync("https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx");
Task<FeedData> feed3 =
GetFeedAsync("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx");
Task<FeedData> feed4 =
GetFeedAsync("https://windowsteamblog.com/windows/b/business/atom.aspx");
Task<FeedData> feed5 =
GetFeedAsync("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx");
Task<FeedData> feed6 =
GetFeedAsync("https://windowsteamblog.com/windows/b/windowssecurity/atom.aspx");
Task<FeedData> feed7 =
GetFeedAsync("https://windowsteamblog.com/windows/b/springboard/atom.aspx");
Task<FeedData> feed8 =
GetFeedAsync("https://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx");
// There is no Atom feed for this blog, so use the RSS feed.
Task<FeedData> feed9 =
GetFeedAsync("https://windowsteamblog.com/windows_live/b/windowslive/rss.aspx");
Task<FeedData> feed10 =
GetFeedAsync("https://windowsteamblog.com/windows_live/b/developer/atom.aspx");
Task<FeedData> feed11 =
GetFeedAsync("https://windowsteamblog.com/ie/b/ie/atom.aspx");
Task<FeedData> feed12 =
GetFeedAsync("https://windowsteamblog.com/windows_phone/b/wpdev/atom.aspx");
Task<FeedData> feed13 =
GetFeedAsync("https://windowsteamblog.com/windows_phone/b/wmdev/atom.aspx");
Task<FeedData> feed14 =
GetFeedAsync("https://windowsteamblog.com/windows_phone/b/windowsphone/atom.aspx");
this.Feeds.Add(await feed1);
this.Feeds.Add(await feed2);
this.Feeds.Add(await feed3);
this.Feeds.Add(await feed4);
this.Feeds.Add(await feed5);
this.Feeds.Add(await feed6);
this.Feeds.Add(await feed7);
this.Feeds.Add(await feed8);
this.Feeds.Add(await feed9);
this.Feeds.Add(await feed10);
this.Feeds.Add(await feed11);
this.Feeds.Add(await feed12);
this.Feeds.Add(await feed13);
this.Feeds.Add(await feed14);
}
private async Task<FeedData> GetFeedAsync(string feedUriString)
{
Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
Uri feedUri = new Uri(feedUriString);
try
{
SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri);
// This code is executed after RetrieveFeedAsync returns the SyndicationFeed.
// Process the feed and copy the data you want into the FeedData and FeedItem classes.
FeedData feedData = new FeedData();
if (feed.Title != null && feed.Title.Text != null)
{
feedData.Title = feed.Title.Text;
}
if (feed.Subtitle != null && feed.Subtitle.Text != null)
{
feedData.Description = feed.Subtitle.Text;
}
if (feed.Items != null && feed.Items.Count > 0)
{
// Use the date of the latest post as the last updated date.
feedData.PubDate = feed.Items[0].PublishedDate.DateTime;
foreach (SyndicationItem item in feed.Items)
{
FeedItem feedItem = new FeedItem();
if (item.Title != null && item.Title.Text != null)
{
feedItem.Title = item.Title.Text;
}
if (item.PublishedDate != null)
{
feedItem.PubDate = item.PublishedDate.DateTime;
}
if (item.Authors != null && item.Authors.Count > 0)
{
feedItem.Author = item.Authors[0].Name.ToString();
}
// Handle the differences between RSS and Atom feeds.
if (feed.SourceFormat == SyndicationFormat.Atom10)
{
if (item.Content != null && item.Content.Text != null)
{
feedItem.Content = item.Content.Text;
}
if (item.Id != null)
{
feedItem.Link = new Uri("https://windowsteamblog.com" + item.Id);
}
}
else if (feed.SourceFormat == SyndicationFormat.Rss20)
{
if (item.Summary != null && item.Summary.Text != null)
{
feedItem.Content = item.Summary.Text;
}
if (item.Links != null && item.Links.Count > 0)
{
feedItem.Link = item.Links[0].Uri;
}
}
feedData.Items.Add(feedItem);
}
}
return feedData;
}
catch (Exception)
{
return null;
}
}
// Returns the feed that has the specified title.
public static FeedData GetFeed(string title)
{
// Simple linear search is acceptable for small data sets
var _feedDataSource = App.Current.Resources["feedDataSource"] as FeedDataSource;
var matches = _feedDataSource.Feeds.Where((feed) => feed.Title.Equals(title));
if (matches.Count() == 1) return matches.First();
return null;
}
// Returns the post that has the specified title.
public static FeedItem GetItem(string uniqueId)
{
// Simple linear search is acceptable for small data sets
var _feedDataSource = App.Current.Resources["feedDataSource"] as FeedDataSource;
var _feeds = _feedDataSource.Feeds;
var matches = _feedDataSource.Feeds.SelectMany(group => group.Items).Where((item) => item.Title.Equals(uniqueId));
if (matches.Count() == 1) return matches.First();
return null;
}
}
}
Imports Windows.Web.Syndication
' FeedData
' Holds info for a single blog feed, including a list of blog posts (FeedItem).
Public Class FeedData
Public Property Title() As String = String.Empty
Public Property Description() As String = String.Empty
Public Property PubDate() As DateTime
Private _Items As New List(Of FeedItem)()
Public ReadOnly Property Items() As List(Of FeedItem)
Get
Return Me._Items
End Get
End Property
End Class
' FeedItem
' Holds info for a single blog post.
Public Class FeedItem
Public Property Title() As String = String.Empty
Public Property Author() As String = String.Empty
Public Property Content() As String = String.Empty
Public Property PubDate() As DateTime
Public Property Link() As Uri
End Class
' FeedDataSource
' Holds a collection of blog feeds (FeedData), and contains methods needed to
' retreive the feeds.
Public Class FeedDataSource
Private _Feeds As New ObservableCollection(Of FeedData)()
Public ReadOnly Property Feeds() As ObservableCollection(Of FeedData)
Get
Return Me._Feeds
End Get
End Property
Public Async Function GetFeedsAsync() As Task
Dim feed1 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/developers/atom.aspx")
Dim feed2 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx")
Dim feed3 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx")
Dim feed4 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/business/atom.aspx")
Dim feed5 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx")
Dim feed6 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/windowssecurity/atom.aspx")
Dim feed7 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
Dim feed8 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx")
' There is no Atom feed for this blog, so use the RSS feed.
Dim feed9 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows_live/b/windowslive/rss.aspx")
Dim feed10 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows_live/b/developer/atom.aspx")
Dim feed11 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/ie/b/ie/atom.aspx")
Dim feed12 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows_phone/b/wpdev/atom.aspx")
Dim feed13 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows_phone/b/wmdev/atom.aspx")
Dim feed14 As Task(Of FeedData) =
GetFeedAsync("https://windowsteamblog.com/windows_phone/b/windowsphone/atom.aspx")
Me.Feeds.Add(Await feed1)
Me.Feeds.Add(Await feed2)
Me.Feeds.Add(Await feed3)
Me.Feeds.Add(Await feed4)
Me.Feeds.Add(Await feed5)
Me.Feeds.Add(Await feed6)
Me.Feeds.Add(Await feed7)
Me.Feeds.Add(Await feed8)
Me.Feeds.Add(Await feed9)
Me.Feeds.Add(Await feed10)
Me.Feeds.Add(Await feed11)
Me.Feeds.Add(Await feed12)
Me.Feeds.Add(Await feed13)
Me.Feeds.Add(Await feed14)
End Function
Private Async Function GetFeedAsync(feedUriString As String) As Task(Of FeedData)
Dim Client As New SyndicationClient
Dim FeedUri As New Uri(feedUriString)
Try
Dim Feed As SyndicationFeed = Await Client.RetrieveFeedAsync(FeedUri)
' This code is executed after RetrieveFeedAsync returns the SyndicationFeed.
' Process the feed and copy the data you want into the FeedData and FeedItem classes.
Dim FeedData As New FeedData
If Feed.Title IsNot Nothing AndAlso Feed.Title.Text IsNot Nothing Then
FeedData.Title = Feed.Title.Text
End If
If Feed.Subtitle IsNot Nothing AndAlso Feed.Subtitle.Text IsNot Nothing Then
FeedData.Description = Feed.Subtitle.Text
End If
If Feed.Items IsNot Nothing AndAlso Feed.Items.Count > 0 Then
' Use the date of the latest post as the last updated date.
FeedData.PubDate = Feed.Items(0).PublishedDate.DateTime
For Each Item As SyndicationItem In Feed.Items
Dim FeedItem As New FeedItem
If Item.Title IsNot Nothing AndAlso Item.Title.Text IsNot Nothing Then
FeedItem.Title = Item.Title.Text
End If
FeedItem.PubDate = Item.PublishedDate.DateTime
If Item.Authors IsNot Nothing AndAlso Item.Authors.Count > 0 Then
FeedItem.Author = Item.Authors(0).Name.ToString()
End If
' Handle the differences between RSS and Atom feeds.
If Feed.SourceFormat = SyndicationFormat.Atom10 Then
If Item.Content IsNot Nothing AndAlso Item.Content.Text IsNot Nothing Then
FeedItem.Content = Item.Content.Text
End If
If Item.Id IsNot Nothing Then
FeedItem.Link = New Uri("https://windowsteamblog.com" + Item.Id)
End If
ElseIf Feed.SourceFormat = SyndicationFormat.Rss20 Then
If Item.Summary IsNot Nothing AndAlso Item.Summary.Text IsNot Nothing Then
FeedItem.Content = Item.Summary.Text
End If
If Item.Links IsNot Nothing AndAlso Item.Links.Count > 0 Then
FeedItem.Link = Item.Links(0).Uri
End If
End If
FeedData.Items.Add(FeedItem)
Next
End If
Return FeedData
Catch Ex As Exception
Return Nothing
End Try
End Function
Public Shared Function GetFeed(title As String) As FeedData
' Simple linear search is acceptable for small data sets
Dim _feedDataSource = DirectCast(App.Current.Resources("feedDataSource"), FeedDataSource)
Dim matches = _feedDataSource.Feeds.Where(Function(feed) feed.Title.Equals(title))
If matches.Count() = 1 Then
Return matches.First()
End If
Return Nothing
End Function
Public Shared Function GetItem(uniqueId As String) As FeedItem
' Simple linear search is acceptable for small data sets
Dim _feedDataSource = DirectCast(App.Current.Resources("feedDataSource"), FeedDataSource)
Dim _feeds = _feedDataSource.Feeds
Dim matches = _feedDataSource.Feeds.SelectMany(Function(group) group.Items).Where(Function(item) item.Title.Equals(uniqueId))
If matches.Count() = 1 Then
Return matches.First()
End If
Return Nothing
End Function
End Class
DateConverter.cs/vb
using System;
using Windows.Globalization.DateTimeFormatting;
namespace WindowsBlogReader
{
public class DateConverter : Windows.UI.Xaml.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string culture)
{
if (value == null)
throw new ArgumentNullException("value", "Value cannot be null.");
if (!typeof(DateTime).Equals(value.GetType()))
throw new ArgumentException("Value must be of type DateTime.", "value");
DateTime dt = (DateTime)value;
if (parameter == null)
{
// Date "7/27/2011 9:30:59 AM" returns "7/27/2011"
return DateTimeFormatter.ShortDate.Format(dt);
}
else if ((string)parameter == "day")
{
// Date "7/27/2011 9:30:59 AM" returns "27"
DateTimeFormatter dateFormatter = new DateTimeFormatter("{day.integer(2)}");
return dateFormatter.Format(dt);
}
else if ((string)parameter == "month")
{
// Date "7/27/2011 9:30:59 AM" returns "JUL"
DateTimeFormatter dateFormatter = new DateTimeFormatter("{month.abbreviated(3)}");
return dateFormatter.Format(dt).ToUpper();
}
else if ((string)parameter == "year")
{
// Date "7/27/2011 9:30:59 AM" returns "2011"
DateTimeFormatter dateFormatter = new DateTimeFormatter("{year.full}");
return dateFormatter.Format(dt);
}
else
{
// Requested format is unknown. Return in the original format.
return dt.ToString();
}
}
public object ConvertBack(object value, Type targetType, object parameter, string culture)
{
string strValue = value as string;
DateTime resultDateTime;
if (DateTime.TryParse(strValue, out resultDateTime))
{
return resultDateTime;
}
return Windows.UI.Xaml.DependencyProperty.UnsetValue;
}
}
}
Imports Windows.Globalization.DateTimeFormatting
Public Class DateConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As String) _
As Object Implements IValueConverter.Convert
If value Is Nothing Then
Throw New ArgumentNullException("value", "Value cannot be null.")
End If
If Not GetType(DateTime).Equals(value.GetType()) Then
Throw New ArgumentException("Value must be of type DateTime.", "value")
End If
Dim dt As DateTime = DirectCast(value, DateTime)
If parameter Is Nothing Then
' Date "7/27/2011 9:30:59 AM" returns "7/27/2011"
Return DateTimeFormatter.ShortDate.Format(dt)
ElseIf DirectCast(parameter, String) = "day" Then
' Date "7/27/2011 9:30:59 AM" returns "27"
Dim dateFormatter As DateTimeFormatter = New DateTimeFormatter("{day.integer(2)}")
Return dateFormatter.Format(dt)
ElseIf DirectCast(parameter, String) = "month" Then
' Date "7/27/2011 9:30:59 AM" returns "JUL"
Dim dateFormatter As DateTimeFormatter = New DateTimeFormatter("{month.abbreviated(3)}")
Return dateFormatter.Format(dt).ToUpper()
ElseIf DirectCast(parameter, String) = "year" Then
' Date "7/27/2011 9:30:59 AM" returns "2011"
Dim dateFormatter As DateTimeFormatter = New DateTimeFormatter("{year.full}")
Return dateFormatter.Format(dt)
Else
' Requested format is unknown. Return in the original format.
Return dt.ToString()
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As String) _
As Object Implements Windows.UI.Xaml.Data.IValueConverter.ConvertBack
Dim StrValue As String = DirectCast(value, String)
Dim Result As Date
If DateTime.TryParse(StrValue, Result) Then
Return Result
End If
Return DependencyProperty.UnsetValue
End Function
End Class