This topic provides the complete code sample used in the tutorial Part 5: Create a blog reader.
This topic contains these sections:
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 ()
App.xaml
<Application x:Class="WindowsBlogReader.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://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 http://go.microsoft.com/fwlink/?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(); } } }
ItemsPage.xaml
<common:LayoutAwarePage x:Name="pageRoot" x:Class="WindowsBlogReader.ItemsPage" DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WindowsBlogReader" xmlns:common="using:WindowsBlogReader.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://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 http://go.microsoft.com/fwlink/?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); } } } }
SplitPage.xaml
<common:LayoutAwarePage x:Name="pageRoot" x:Class="WindowsBlogReader.SplitPage" DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WindowsBlogReader" xmlns:common="using:WindowsBlogReader.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://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" NavigationFailed="ContentView_NavigationFailed" /> <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 http://go.microsoft.com/fwlink/?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_NavigationFailed(object sender, WebViewNavigationFailedEventArgs e) { string errorString = "<p>Page could not be loaded.</p><p>Error is: " + e.WebErrorStatus.ToString() + "</p>"; this.contentView.NavigateToString(errorString); } } }
DetailPage.xaml
<common:LayoutAwarePage x:Name="pageRoot" x:Class="WindowsBlogReader.DetailPage" DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WindowsBlogReader" xmlns:common="using:WindowsBlogReader.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://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" NavigationFailed="contentView_NavigationFailed"/> </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 http://go.microsoft.com/fwlink/?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_NavigationFailed(object sender, WebViewNavigationFailedEventArgs e) { string errorString = "<p>Page could not be loaded.</p><p>Error is: " + e.WebErrorStatus.ToString() + "</p>"; this.contentView.NavigateToString(errorString); } } }
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("http://blogs.windows.com/skydrive/b/skydrive/atom.aspx"); Task<FeedData> feed2 = GetFeedAsync("http://blogs.windows.com/windows/b/windowsexperience/atom.aspx"); Task<FeedData> feed3 = GetFeedAsync("http://blogs.windows.com/windows/b/extremewindows/atom.aspx"); Task<FeedData> feed4 = GetFeedAsync("http://blogs.windows.com/windows/b/business/atom.aspx"); Task<FeedData> feed5 = GetFeedAsync("http://blogs.windows.com/windows/b/bloggingwindows/atom.aspx"); Task<FeedData> feed6 = GetFeedAsync("http://blogs.windows.com/windows/b/windowssecurity/atom.aspx"); Task<FeedData> feed7 = GetFeedAsync("http://blogs.windows.com/windows/b/springboard/atom.aspx"); Task<FeedData> feed8 = GetFeedAsync("http://blogs.windows.com/windows/b/windowshomeserver/atom.aspx"); // There is no Atom feed for this blog, so use the RSS feed. Task<FeedData> feed9 = GetFeedAsync("http://blogs.windows.com/windows_live/b/windowslive/rss.aspx"); Task<FeedData> feed10 = GetFeedAsync("http://blogs.windows.com/windows_live/b/developer/atom.aspx"); Task<FeedData> feed11 = GetFeedAsync("http://blogs.windows.com/ie/b/ie/atom.aspx"); Task<FeedData> feed12 = GetFeedAsync("http://blogs.windows.com/windows_phone/b/wpdev/atom.aspx"); Task<FeedData> feed13 = GetFeedAsync("http://blogs.windows.com/windows_phone/b/wmdev/atom.aspx"); Task<FeedData> feed14 = GetFeedAsync("http://blogs.windows.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(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 != null && 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 matches = _feedDataSource.Feeds .Where(group => group != null) .SelectMany(group => group.Items).Where((item) => item.Title.Equals(uniqueId)); if (matches.Count() == 1) return matches.First(); return null; } } }
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; } } }
Build date: 3/11/2013