Tutorial: Create your first Windows Store app using C++

73 out of 120 rated this helpful - Rate this topic

Here, we introduce essential code and concepts to help you use C++ to develop a Windows Store app that has a UI that's defined in Extensible Application Markup Language (XAML), and guide you through the steps to create the app step by step. This tutorial assumes that you are already somewhat familiar with C++, as well as basic programming concepts including object oriented design, user interface controls, and event handling. You can also download the finished app from the MSDN samples web site.

A Windows Store app is tailored for the user experience that's introduced in Windows 8. Every great Windows Store app follows certain design principles that make it look more beautiful, feel more responsive, and behave more intuitively than a traditional desktop app. Before you start creating a Windows Store app, we recommend that you read about the design philosophy of the new model. You can find more info at Designing Windows Store apps.

After following this tutorial, consider reading Developing an end-to-end Windows Store app using C++ and XAML: Hilo to learn more about how to use modern C++, the Windows Runtime, asynchronous programming, XAML, and development patterns such as Model-View-ViewModel (MVVM) in your Windows Store app apps using C++.

If you'd rather use another programming language, see:

Objectives

Before we start coding, let's look at some of the features and design principles that you can use to build a Windows Store app with C++. It will also be helpful to look at how Microsoft Visual Studio Express 2012 for Windows 8 supports the design and development work. And it's important to understand how and when to use the Visual C++ component extensions (C++/CX) to simplify the work of coding against the Windows Runtime. Our example app is a blog reader that downloads and displays data from an RSS 2.0 or Atom 1.0 feed.

This article is designed so that you can follow the steps to create the app yourself. By the time you complete this tutorial, you'll be prepared to build your own Windows Store app by using XAML and C++. You can download the finished app from the MSDN samples web site

Comparing C++ desktop apps to Windows Store apps

If you're coming from a background in Windows desktop programming with C++, you'll probably find some aspects of Windows Store app programming to be very familiar, and other aspects that require some learning.

What's the same?

  • You're still coding in C++, and you can access the STL, the CRT, and any other C++ libraries, except that you can't invoke certain functions directly, such as those related to file I/O.

  • If you're used to visual designers, you can still use them. If you're used to coding UI by hand, you can hand-code your XAML.

  • You're still creating apps that use Windows operating system types and your own custom types.

  • You're still using the Visual Studio debugger, profiler, and other development tools.

  • You're still creating apps that are compiled to native machine code by the Visual C++ compiler. Windows Store apps in C++ don't execute in a managed runtime environment.

What's new?

  • The design principles for Windows Store apps are very different from those for desktop apps. Window borders, labels, dialog boxes, and so on, are de-emphasized. Content is foremost. Great Windows Store apps incorporate these principles from the very beginning of the planning stage.

  • You're using XAML to define the entire UI. The separation between UI and core program logic is much clearer in a Windows Store app than in an MFC or Win32 app. Other people can work on the appearance of the UI in the XAML file while you're working on the behavior in the code file.

  • You're primarily programming against a new, easy-to-navigate, object-oriented API, the Windows Runtime, although Win32 is still available for some functionality.

  • When you use Windows Runtime objects, you're (typically) using C++/CX, which provides special syntax to create and access Windows Runtime objects in a way that enables C++ exception handling, delegates, events, and automatic reference counting of dynamically created objects. When you use C++/CX, the details of the underlying COM and Windows architecture are almost completely hidden from your app code. For more information, see C++/CX Language Reference. However, you can also program directly against the COM interfaces by using the Windows Runtime C++ Template Library.

  • Your app can support new concepts such as suspension, charms, and an app bar to create a more cohesive experience for your users.

  • Your app is compiled into a package that also contains metadata about the types that your app contains, the resources that it uses, and the capabilities that it requires (file access, internet access, camera access, and so forth).

  • In the Windows Store, your app is verified as safe by a certification process and made discoverable to millions of potential customers.

Simple blog reader 1.0

Our sample app is a basic blog reader that downloads and displays data from an RSS 2.0 or Atom 1.0 feed.

We'll approach this walkthrough in two parts. First, we'll create a basic, one-page version of the blog reader so that we can focus on some fundamentals of Windows Store app programming in C++. This app won't have all the functionality that is required in a real Windows Store app, but it will serve to introduce some basic concepts. In part 2, we'll create a more full-featured, multi-page version of the app by using some of the pre-defined XAML templates in Visual Studio Express 2012 for Windows 8.

We'll start with the basics:

  • How to create a Windows Store app project in Visual Studio Express 2012 for Windows 8.

  • How to understand the various project files that are created.

  • How to understand the Visual C++ component extensions, and when to use them.

Visual Studio provides source file management; integrated build, deployment and launching support; editing for XAML, Visual Basic, C#, C++, graphics, and manifests; debugging; and more. Visual Studio is available in several editions, but here we use Visual Studio Express 2012 for Windows 8. You can download it for free and get everything you need to build, package, and deploy your Windows Store apps.

To start creating an app, first create a Windows Store app project that uses C++. Here we use the most basic project template, Blank App (XAML).

Hh465045.wedge(en-us,WIN.10).gifTo create a Windows Store app project

  1. Install Visual Studio Express 2012 for Windows 8. Open it and set the development settings for Visual C++ when prompted.

  2. On the menu bar, choose File > New Project. The New Project dialog box opens.

  3. In the Installed pane, expand Visual C++.

  4. Select the template for a Windows Store app project.

  5. In the center pane, select Blank App (XAML).

  6. Enter a name for the project. We'll call it SimpleBlogReader.

  7. Choose the OK button. Your project files are created.

Before we go on, let’s look at the project files. At the top of the Solution Explorer pane, choose the Show All Files icon to show all of the project files that the Blank App (XAML) template created. You should see something like this depending on what you named your project. (Actually, the Debug folder will not appear until after you build your app the first time by pressing F5.)

Solution Explorer showing all files

Files you edit

Let's look first at the project files that you can edit. Basically, this includes any file that's located directly in the project folder.

File nameDescription

App.xaml, MainPage.xaml

The XAML markup files that represent the app object and the default page for the UI. You can modify these files by using the Visual Studio designer, Microsoft Expression Blend, or another XAML designer tool. Most of the modifications will be in MainPage.xaml.

App.xaml.h, App.xaml.cpp

MainPage.xaml.h, MainPage.xaml.cpp

The user-editable header and implementation code-behind files for the Application and MainPage classes. These classes correspond to the XAML trees in App.xaml and MainPage.xaml, respectively. The MainPage.xaml.h and MainPage.xaml.cpp files are where you add event handlers and other custom program logic that's related to this page. Member variables in the App class are in scope throughout your app. Variables in the Page class are in scope only in that page. App.xaml has no visual design surface, but you can still use the document outline and property inspector in the designer.

Package.appxmanifest

Contains metadata that describes your app, for example, display name, description, logos, and capabilities. When you click on this item, it opens in the Manifest Designer.

*.png

(In Assets folder.) The default logo and splash-screen images that you can replace with your own.

pch.h, pch.cpp

Typical C++ precompiled header files. You can add #include directives to the pch.h file as necessary.

 

Files you don’t touch

These files are generated by the XAML designer or editor whenever you navigate from the *.xaml page in Visual Studio. They enable the code-behind that you write to refer to XAML elements. They also keep IntelliSense in the code-behind up to date. Some of these files are in the Common subfolder (not expanded in the illustration). You can look at these files to get some idea about how the partial classes work, where your variables are being declared, and so on. They can also be useful in debugging. But don't modify these files because any changes you make will be overwritten the next time that you build your app or navigate away from the XAML page.

File nameDescription

App.g.h, App.g.hpp

App.g.hpp contains the app's main method and some associated boilerplate code. App.g.h contains code that enables the operating system to load the .xaml files into memory at run time and create the object graph. Don't modify these files.

StandardStyles.xaml

Contains predefined item templates, styles, and other elements that define the look and feel of a Windows Store app. Don't modify the styles and templates in-place. However, you can base custom styles on these (by using the BasedOn attribute), or copy and paste them into other pages, give the copy a different name, and then modify the copy.

MainPage.g.h, MainPage.g.hpp

Contain the automatically-generated partial class definition for the MainPage and App classes, and the member variables that are generated for each XAML element that has an x:Name attribute. Don't modify these files.

XamlTypeInfo.g.h

The C++ file that the XAML editor generates to enable the Windows Runtime to recognize and load any custom types that are defined in your app and are referenced in any XAML files. Don't modify this file.

 

A first look at the code

In Solution Explorer, open MainPage.xaml and look at the markup in the XAML editor pane. Notice a <Page> element that contains a <Grid> element. Now open MainPage.g.h. Notice a class that's named MainPage that derives from Windows::UI::Xaml::Controls::Page and contains a Windows::UI::Xaml::Controls::Grid member variable.

Each element type in the markup has an associated Windows Runtime type. As you add elements to the XAML, Visual Studio generates the C++ source code so that you can write code-behind that references those elements as Windows Runtime types. Not all the elements are represented in your C++ project code; only those elements that you refer to explicitly.

Let’s go back to MainPage.g.h. Notice that MainPage is declared as a partial ref class.


partial ref class MainPage : public Windows::UI::Xaml::Controls::Page… {…}

The partial and ref keywords are obviously not ISO standard C++. They are component extensions that you use specifically for creating instances of Windows Runtime types. The ref keyword indicates that the class is a Windows Runtime reference type; using ref saves you from having to write a lot of next-generation COM code. In the class member declarations, notice a Platform::Object^. The "^" symbol is known as a "hat" and it means "a handle to an object." Use it instead of "*" when you create Windows Runtime types in dynamic memory. You can also use the C++ auto keyword; the compiler will infer the type. The Platform namespace is specific to C++; it contains public and private types that are essential to the C++ "projection" of the Windows Runtime type system.


Grid^ grid = ref new Grid(); // or: auto grid = ref new Grid();
grid->Width = 600;


In the most basic sense, a ref class is a COM object that implements the IInspectable interface and whose lifetime is managed by a smart pointer. The Windows Runtime defines a language-agnostic abstract binary interface (ABI) that runs natively in machine code, not through a virtual machine. C++/CX makes it possible to program against this ABI in an idiom that is much more like modern C++ than like old-school COM programming. Use C++/CX specifically when you create and access Windows Runtime types. Libs, modules, and functions in a ref class that don't interface with the Windows Runtime can be written entirely in ISO standard C++. It's common to mix standard C++ and C++/CX in the same function. It's all compiled to native C++. For more information about C++/CX, see Visual C++ Language Reference (C++/CX).

The partial keyword tells the compiler that the declaration of this class is continued in another code file. That file is MainPage.xaml.h. If you, the programmer, have to add variables or functions to the MainPage class, do so in MainPage.xaml.h and MainPage.xaml.cpp. If the XAML editor has to add variables or other boilerplate code, it does so in the *.g.h and *.g.hpp files. Although the class definition has two parts, it behaves as one class, both while you are coding and at compile time. In general, you can safely ignore the *.g.* files. That's why Solution Explorer hides them by default. Now that we have looked behind the scenes, if Show All Files is still enabled, then choose the icon to disable it so that you can more easily find the files you are going to modify.

Specifying app capabilities

A Windows Store app runs in a security container that has limited access to the file system, network resources, and hardware. When a user installs an app from the Windows Store, Windows looks at the metadata in the Package.appxmanifest file to determine what capabilities the app requires. For example, an app might have to access data from the Internet, documents from the user's Document Library, or the user's webcam and microphone. When the app is installed, it displays to the user the capabilities it needs, and the user must grant permission before it can access those resources. If the app doesn't request and receive permission for a resource it needs, it won't be allowed access to that resource at run time.

Hh465045.wedge(en-us,WIN.10).gifTo add basic Internet capability to the app

  1. In Solution Explorer, open Package.appxmanifest. The file opens in the Application Manifest Designer.

  2. Select the Capabilities tab.

  3. You will notice that the Internet (Client) check box is already selected. If we needed some other capability, we'd have to select it manually in this designer.

  4. Close the manifest designer.

When you specify a capability, it's listed in the Package.appxmanifest file under the Capabilities element. You'd typically use the Application Manifest Designer to set capabilities, but if you open Package.appxmanifest.xml in the XML Text Editor, you can see the Capabilities element in the XML.


<Capabilities>    
    <Capability Name="internetClient" />
</Capabilities>


For more information about app capabilities, see Manifest Designer.

Getting data into an app

In this section, we show:

  • How to create custom classes that will serve as the data model for the app.

  • How to retrieve an RSS or Atom data feed asynchronously.

Now we can write the code to get the blog feed into the app. The "Developing for Windows" blog exposes the full text of its posts in both RSS and Atom form. We want to display the title, author, date, and content from each of the latest blog posts. We can use the classes in the Windows::Web::Syndication namespace to download the feeds. We could also use those classes to display the data in the UI, but instead, we'll create our own data classes. This gives us additional flexibility and lets us treat RSS and Atom feeds in the same way. We create three classes:

  • FeedData holds info about the RSS or Atom feed.

  • FeedItem holds info about individual blog posts in the feed.

  • FeedDataSource contains the methods to download the feeds and initialize our data classes.

We define these classes as public ref classes to enable data-binding with the XAML elements that display the Title, Author, and so on. We use the Bindable attribute to indicate to the XAML compiler that we are binding dynamically to instances of these types. In a ref class, public data members are exposed as properties. Properties that have no special logic don't require a user-specified getter and setter; the compiler will supply them. In the FeedData class, notice how we use IVector to expose a public collection type to other Windows Runtime classes and components. We also use the Platform::Collections::Vector class internally as the concrete type that implements IVector. Later we'll learn how to consume that type.

Hh465045.wedge(en-us,WIN.10).gifTo create custom data classes

  1. In Solution Explorer, on the shortcut menu for the SimpleBlogReader project node, choose Add > New Item.

  2. Select Header File (.h) from the list of options and name it FeedData.h. (For convenience, we won't use a separate .cpp file in this example.)

  3. Copy and paste the following code into the file. Take a moment to look at the code and familiarize yourself with the C++/CX constructs. Note the #include directive for "pch.h"; that header is the place to put all your system headers such as <string> and <vector>. By default pch.h includes <collection.h>, which is required for the Platform::Collections::Vector type. For more information about what you are seeing here, see Classes and structures (C++/CX).

    
    
    #include "pch.h"
    
    namespace SimpleBlogReader
    {
        // To be bindable, a class must be defined within a namespace
        // and a bindable attribute needs to be applied.
        // A FeedItem represents a single blog post.
        [Windows::UI::Xaml::Data::Bindable]
        public ref class FeedItem sealed
        {
        public:
            FeedItem(void){}
    
            property Platform::String^ Title;
            property Platform::String^ Author;
            property Platform::String^ Content;      
            property Windows::Foundation::DateTime PubDate;      
            property Windows::Foundation::Uri^ Link;
    
        private:
            ~FeedItem(void){}
        };
    
        // A FeedData object represents a feed that contains 
        // one or more FeedItems. 
        [Windows::UI::Xaml::Data::Bindable]
        public ref class FeedData sealed
        {
        public:
            FeedData(void)
            {
                m_items = ref new Platform::Collections::Vector<FeedItem^>();
            }
    
            // The public members must be Windows Runtime types so that
            // the XAML controls can bind to them from a separate .winmd.
            property Platform::String^ Title;            
            property Windows::Foundation::Collections::IVector<FeedItem^>^ Items
            {
                Windows::Foundation::Collections::IVector<FeedItem^>^ get() {return m_items; }
            }
    
            property Platform::String^ Description;
            property Windows::Foundation::DateTime PubDate;
    
        private:
            ~FeedData(void){}
    
            Platform::Collections::Vector<FeedItem^>^ m_items;
        };   
    
    }
    
    
    

Asynchronous operations in C++: retrieving the feed data

With data classes in place, we can implement the GetFeedData function to download the blog feed. The Windows.Web.Syndication.SyndicationClient class retrieves and parses RSS and Atom feeds. Because this operation involves network I/O, the method executes asynchronously. The asynchronous programming model is found throughout the Windows Runtime class libraries. An asynchronous method call returns control immediately to the UI thread so that the UI remains responsive while the operation is being performed on a background thread.

The Windows Runtime provides a way to invoke asynchronous operations and get their results when they complete; you can program directly against that API. However, the preferred approach is to use the task class class that is defined in ppltasks.h. The task class uses these same asynchronous Windows Runtime APIs, but you can use it to write more concise code, and it's easier to chain asynchronous operations and handle in one spot any exceptions that arise. When you use the task class, the basic steps are always the same:

  1. Create an asynchronous operation by calling a Windows Runtime *Async method, such as Windows::Web::Syndication::ISyndicationClient::RetrieveFeedAsync.

  2. Create a task object by using the operation as input parameter.

  3. Call task::then and specify a lambda that takes the return value of the operation as input.

  4. Optionally, call then again one or more times. These clauses can accept the return value from the previous clause.

  5. Optionally, provide a final then clause that handles any exceptions that were thrown anywhere in the chain of operations.

Hh465045.wedge(en-us,WIN.10).gifTo add the asynchronous download functionality

  1. Add these lines to MainPage.xaml.h:
    
    #include "FeedData.h"
    ...
    // In the MainPage class...
    private:
     void GetFeedData(Platform::String^ feedUriString);
     FeedData^ feedData;
    
    
  2. Add this line to pch.h, to bring in task and related objects:
    
    #include <ppltasks.h>
    
    
  3. Add these lines to MainPage.xaml.cpp:
    
    
    using namespace Windows::Web::Syndication;
    using namespace concurrency;
    
    
  4. Add this line to the MainPage constructor after the call to InitializeComponent:
    
    feedData = ref new FeedData();
    
    
  5. Add the method implementation to MainPage.xaml.cpp. For more information about this code, see Asynchronous programming in C++.
    
    
    
    void MainPage::GetFeedData(Platform::String^ feedUriString)
    {
        // Create the SyndicationClient and the target uri
        SyndicationClient^ client = ref new SyndicationClient();
        Uri^ feedUri = ref new Uri(feedUriString);
    
        // Create the async operation. feedOp is an 
        // IAsyncOperationWithProgress<SyndicationFeed^, RetrievalProgress>^
        auto feedOp = client->RetrieveFeedAsync(feedUri);
        feedOp = client->RetrieveFeedAsync(feedUri);
    
        // Create the task object and pass it the async operation.
        // SyndicationFeed^ is the type of the return value
        // that the feedOp operation will eventually produce.    
    
        // Create a "continuation" that will run when the first task completes.
        // The continuation takes the return value of the first operation,
        // and then defines its own asynchronous operation by using a lambda.
        create_task(feedOp).then([this] (SyndicationFeed^ feed) -> SyndicationFeed^
        {
            // Get the title of the feed (not the individual posts).
            feedData->Title = feed ->Title->Text;
    
            // Retrieve the individual posts from the feed.
            auto feedItems = feed->Items;
    
            // Iterate over the posts. 
            for(auto item : feedItems)
            {  
                FeedItem^ feedItem = ref new FeedItem();
                feedItem->Title = item->Title->Text; 
                feedItem->PubDate = item->PublishedDate;
    
                feedItem->Author = item->Authors->GetAt(0)->Name; 
    
                if (feed->SourceFormat == SyndicationFormat::Atom10)
                {
                    feedItem->Content = item->Content->Text;
                }
                else if (feed->SourceFormat == SyndicationFormat::Rss20)
                {
                    feedItem->Content = item->Summary->Text;
                }
                feedData->Items->Append(feedItem);
            }
    
            this->DataContext = feedData;
            return feed;         
        })
            // The last continuation serves as an error handler. The
            // call to get() will surface any exceptions that were raised
            // at any point in the task chain.
            .then( [this] (concurrency::task<SyndicationFeed^> t)
        {
            try
            {
                t.get();
            }
            // SyndicationClient throws Platform::InvalidArgumentException 
            // if a URL contains illegal characters.
            // We catch this exception for demonstration purposes only.
            // In the current design of this app, an illegal
            // character can only be introduced by a coding error
            // and should not be caught. If we modify the app to allow
            // the user to manually add a new url, then we need to catch
            // the exception.
            catch(Platform::InvalidArgumentException^ e)
            {
                // For example purposes we just output error to console.
                // In a real world app you could alert the user
                // and prompt them to try again.
                OutputDebugString(e->Message->Data());
            }
    
            catch (Platform::COMException^ e)
            {
                // Thrown if the URL does not exist or the content contains invalid XML.
                // Here we just catch the exception. In a real world app that
                // allowed the user to enter a url manually, you could inform 
                // them of the failure and/or prompt them to try again.
                
                OutputDebugString(e->Message->Data());
            }
        }); 
    }
    
    
    

    Notice that we called this->DataContext = feedData after we finished populating the feedData object. We have to make our feedData instance the DataContext of the page so that we can bind our UI to it. With FeedData as the data context, we can write {Binding Path="Title"} in the XAML markup and when the XAML page is loaded at start time and the object graph is constructed, the loader will know that "Title" is the Title property on our FeedData instance.

    In part 2 of this walkthrough, we'll show a more sophisticated chain of asynchronous operations that will create multiple FeedData objects. For more information about C++ asynchronous programming in Windows Store apps, see Asynchronous programming in C++.

When our app starts, we want it to load the "SkyDrive" blog automatically. The best place to do this is in response to the Loaded event that signals that page loading is complete. In the method call, we pass in the URL for the Atom feed because that feed includes author data and the RSS feed doesn't.

Hh465045.wedge(en-us,WIN.10).gifTo handle the Loaded event

  1. In MainPage.xaml, add to the opening Page tag the statement Loaded="PageLoadedHandler", directly after mc:Ignorable="d" but before the closing angle bracket, so that the entire tag looks like this:

    
    <Page
        x:Class="SimpleBlogReader.MainPage"
        IsTabStop="false"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:SimpleBlogReader"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"    
        Loaded="PageLoadedHandler">
    
    
  2. Add the C++ method signature to MainPage.xaml.h:

    
    //In the MainPage class declaration...
    private: 
    void PageLoadedHandler(Platform::Object^ sender,
              Windows::UI::Xaml::RoutedEventArgs^ e);
    
    
  3. Add the stub implementation for the event handler method in your and MainPage.xaml.cpp files:

    
    
    void MainPage::PageLoadedHandler(Platform::Object^ sender,
              Windows::UI::Xaml::RoutedEventArgs^ e)
    {
       GetFeedData("http://blogs.windows.com/skydrive/b/skydrive/atom.aspx");
    }
    
    
    
  4. Now press Ctrl + Shift + B to build the application. It should build with no errors or warnings.

Defining the UI in XAML

Now let's look at how to:

  • Define a layout directly in XAML, without using a designer tool.

  • Define rows and columns in a Grid.

  • Create member variables for XAML elements.

The easiest and most powerful way to create a XAML UI is to start with one of the templates that are provided by Visual Studio, and then use a tool such as Expression Blend or the Visual Studio XAML designer to customize it. However, because we are focusing on the structure of XAML itself, we’ll work directly in the XAML code editor. For more background on XAML, see C#, VB, and C++ programming concepts for Windows Store apps.

A typical Windows Store app has multiple pages, and each has a distinct layout. For example, a blog reader might have a page for browsing through multiple blog posts and selecting one, and another page for reading the selected post. Each page is a separate XAML tree in its own code file. The typical root element for a page, logically enough, is <Page>. Its corresponding Windows Runtime type is Windows::UI::Xaml::Controls::Page. The Page element/class supports basic navigation between pages in an app. A Page has one layout control (or panel) as a direct child. Inside that layout control you can put content controls such as TextBlock and ListView to hold the individual items of graphics, text, and so on.

The XAML layout system supports both absolute layout and dynamic layout. In an absolute layout, you use explicit x-y coordinates to position controls; in a dynamic layout, you enable the layout container and the controls to be automatically sized and positioned as the app resizes. Use the Canvas layout control for absolute positioning, and use Grid, StackPanel, and other controls for dynamic positioning. In practice, you'd typically define the layout of your app by combining absolute and dynamic methods, and by embedding panels in other panels.

A typical layout for a blog reader app has the title at the top, a list of posts on the left, and the content of the selected post on the right. The following illustration shows what our layout will look like:

Layout sketch for the simple blog reader

When we created our project, a file named MainPage.xaml was created for us. This file has a Page element that has one Grid element as a child. The XAML should look something like this:


<Page
    x:Class="SimpleBlogReader.MainPage"
    IsTabStop="false"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SimpleBlogReader"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" 
    Loaded="PageLoadedHandler">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"></Grid>
</Page>


The x:Class attribute associates this Page element with the MainPage class that is declared in MainPage.g.h, and MainPage.xaml.h. Remember that we added the feedData member variable. In the XAML tree, the xmlns attributes are XML namespaces; the only interesting one here is the xmlns:local namespace, which brings our FeedData and FeedItem classes into scope on the XAML page so that we can bind data to them later.

Our layout will consist of three nested Grid elements. The top level Grid, Grid1, has two rows. The top row holds the feed title. The second row embeds Grid2, which has two columns. The left column contains a ListView control that displays the title, author, and date of all the available posts. The right column contains Grid3, which has two rows. The top row contains a TextBlock and the second row has a WebView. The TextBlock will show the title of the blog post, and the WebView will show the content.

Here is a simplified view that shows the basic structure of the layout in the earlier picture. (Don't paste this code because it's not complete.)



<!-- Pseudo-XAML Simplified View -->
<Page>
   ...
   <Grid Name="Grid1">
      <Grid.RowDefinitions...
   ...
      <!--In first row of Grid1.-->
      <TextBlock Grid.Row="0"></TextBlock>
     ...
      <!--In second row of Grid1.-->
      <Grid Name="Grid2" Grid.Row="1">
      <Grid.ColumnDefinitions...
         <!-- In left column of Grid2. -->
         <ListView  Grid.Column="0"></ListView>

         <!-- In right column of Grid2. -->
         <Grid Name="Grid3" Grid.Column="1">
            <Grid.RowDefinitions...
            <TextBlock Grid.Row="0"></TextBlock>
            <WebView  Grid.Row="1"/> 
         </Grid> 
      </Grid>
    </Grid>
</Page>


Now let's create the actual XAML by pasting in the parts one at a time. This exercise helps us understand how XAML user interfaces are constructed.

Hh465045.wedge(en-us,WIN.10).gifTo create the basic layout for our blog reader

  1. In MainPage.xaml, add a Name attribute for the default Grid element so that the entire element looks like this:
    
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" Name="Grid1">
    
    
  2. In MainPage.xaml, define two rows for the Grid1 element by making the following XAML snippet the first child node of the Grid, directly after the opening tag. The Height="140" property setting on the first row definition (row 0) sets the top row to an absolute height of 140 pixels. This height doesn't change regardless of the size of the row contents or the app. The Height="*" setting on the second row definition (row 1) tells the bottom row to take up whatever space is left after row 0 has sized itself. This is known as star sizing.

    Note  Whether you type the code or paste it in, Visual Studio should automatically provide the correct indentation. If it doesn't, click on Edit > Advanced > Format Document.

    
    <Grid.RowDefinitions>
       <RowDefinition Height="140" />
       <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    
    
    
  3. Directly after the row definitions, but still inside Grid1, add the following TextBlock content control to the first row. This holds the main title of the feed, so we give it a large font. We give it an x:Name attribute so that we can refer to it in our C++ code, and also a temporary string to display until we data-bind to it later.

    
    
    <TextBlock x:Name="TitleText" Text="Main Title of Blog Feed"
        VerticalAlignment="Center" FontSize="48" Margin="56,0,0,0"/>
    
    
    
  4. Directly after the TextBlock, add a second Grid element, and give it a Name attribute Grid2. Add a child Grid.ColumnDefinitions element that defines two columns. The Grid.Row attribute here refers to the Grid1 row, so Grid.Row="1" means "put this element in the second row of Grid1." In other words, we are embedding a Grid within a Grid. The column width settings of Width="2*" and Width="3*" tell Grid2 to divide itself into five equal parts. Two parts are used for the first column, and three parts are used for the second column.

    
    <Grid Name="Grid2" Grid.Row="1">
       <Grid.ColumnDefinitions>
          <ColumnDefinition Width="2*" />
          <ColumnDefinition Width="3*" />
       </Grid.ColumnDefinitions>
    </Grid>
    
    
    
  5. Directly after the column definitions of Grid2, but before its closing tag, add the following ListView control. Because no Grid.Column attribute is specified, the control is placed into column 0 of Grid2. We'll leave its contents empty for now. Later we'll hook up some content and an event handler for it

    
    <ListView x:Name="ItemListView"></ListView>
    
    
  6. Directly after the closing ListView tag, still inside the Grid2 element, add a third Grid that has two rows. Give it a Name attribute Grid3 and put it in the right column of Grid2. The setting Height="Auto" tells the top row to make itself as tall as its content. The bottom row uses whatever space is left.

    
    <Grid Name="Grid3" Grid.Column="1" >
       <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="*" />
       </Grid.RowDefinitions>
    </Grid>
    
    
    
  7. Immediately after the previous RowDefinitions, but before the Grid3 closing tag, add a TextBlock and give it some temporary text. Later we will make this TextBlock display the title of the blog post that's shown in the WebView. No x:Name attribute is required for this control because we don’t have to refer to it in the XAML or in the code-behind. But don’t worry; even though no variable is created for this control in our MainPage class, it will be instantiated and fully functional at run time.

    
    <TextBlock Text="Blog Post Title" FontSize="24"/>
    
    
  8. Immediately after the previous TextBlock, add a WebView and put it in the bottom row of the Grid. This control displays the content of the post, including graphics. We use a WebView and not a TextBlock or RichTextBlock because the feed content is formatted as HTML.

    
    
    <WebView x:Name="ContentView" Grid.Row="1" Margin="0,5,20,20"/>
    
    
  9. The XAML tree should now look like this:
    
    <Page
        x:Class="SimpleBlogReader.MainPage"
        IsTabStop="false"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:SimpleBlogReader"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Loaded="PageLoadedHandler">
    
        <Grid Name="Grid1" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Grid.RowDefinitions>
                <RowDefinition Height="140" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
    
            <TextBlock x:Name="TitleText" Text="Main Title of Blog Feed"
                VerticalAlignment="Center" FontSize="48" Margin="56,0,0,0"/>
    
            <Grid Name="Grid2" Grid.Row="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*" />
                    <ColumnDefinition Width="3*" />
                </Grid.ColumnDefinitions>
    
                <ListView x:Name="ItemListView"></ListView>
                <Grid Name="Grid3" Grid.Column="1" >
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
    
                    <TextBlock Text="Blog Post Title" FontSize="24"/>
                    <WebView x:Name="ContentView" Grid.Row="1" Margin="0,5,20,20"/>
                </Grid>
            </Grid>
        </Grid>
    </Page>
    
    
    
    At this point you should be able to see the user interface reflected in the designer surface. You can also press F5 now to see what it looks like so far. You won't see any data yet, just the basic outlines of the user interface. Press F12, click through the breakpoint dialog box, and then press Shift-F5 to return to the code editor.

Formatting the FeedItem data

Now that the basic layout is defined, let’s add the formatting for the ListView items, which as you may recall are FeedItem objects that we defined in FeedData.h, initialized in the GetFeedData method, and inserted into the FeedData::Items collection. We want the control to show the title, author, and publication date of each blog post in the feed. The idea is that a user can scroll through these items, and select one that looks interesting. When an item is selected, the TextBlock to the right displays the title of the post in a larger font, and the WebView displays the content. We want to format the display of the ListView items so that it looks like this:

Layout for list items

To combine the three FeedItem property values into one unit for display purposes, we can use a data template. A data template defines the "look" of one or more pieces of data and is implemented as—what else?—a XAML node. You can use data templates to create powerful and original representations of information that combine text, graphics, animations, and other XAML features. Ours, however, is quite basic. As with the titles that we added earlier, we can put each property value in a TextBlock. We can use each TextBlock to specify a font size and other formatting properties, and also a temporary Text value that we will replace later. To line up the TextBlock elements, we use a StackPanel. StackPanel is a lightweight layout panel that's used extensively in XAML for small layout scenarios such as this.

Hh465045.wedge(en-us,WIN.10).gifTo create the layout for the feed items

  1. In MainPage.xaml, inside the ListView node, add an ItemTemplate that has a DataTemplate node as the immediate child. In controls like ListView, a DataTemplate always nests inside an ItemTemplate. This tells the control to apply the template to each item in its Items collection.

    
    
    <ListView.ItemTemplate>
       <DataTemplate>
       </DataTemplate>
    </ListView.ItemTemplate>
    
    
    
  2. Inside the DataTemplate, add a StackPanel that contains three TextBlock elements, one for each of the three FeedItem properties that we want to display. Because no orientation is specified for the StackPanel, the TextBlock elements will line up vertically. For now, we just specify some temporary strings for the Text property, just to remind ourselves what they represent. They won’t be displayed if you press F5 because this is not an actual item, it's just a template for how items should be displayed.

    
    
    <StackPanel>
       <TextBlock Text="Post title" FontSize="24" Margin="5,0,0,0"
           TextWrapping="Wrap" />
       <TextBlock Text="Post author" FontSize="16" Margin="15,0,0,0"/>
       <TextBlock Text="Publication Date" FontSize="16" Margin="15,0,0,0"/>
    </StackPanel>
    
    
    
  3. The XAML for the ListView should now look like this:
    
    <ListView x:Name="ItemListView">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="Post title" FontSize="24" Margin="5,0,0,0"
                         TextWrapping="Wrap" />
                    <TextBlock Text="Post author" FontSize="16" Margin="15,0,0,0"/>
                    <TextBlock Text="Publication Date" FontSize="16" Margin="15,0,0,0"/>
                </StackPanel>
             </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    
    

So far, we have written a method that downloads actual data from a feed, and we have designed a UI that displays some temporary values. The next step is to add the XAML attributes that connect the actual feed data to the UI. This is known as data-binding.

Displaying data

Now we'll see how to bind data to a UI and use a value converter to convert a DateTime value to a String.

Adding content to controls programmatically

You can insert content into a control programmatically in your code-behind. For example, to populate the feed title TextBlock, we could write this code in an event handler—TitleText->Text = feedData->Title; and it would cause the text to be immediately updated in the control. In case you are wondering how this works, remember that we specified the x:Name attribute on the XAML element like this: <TextBlock x:Name="TitleText" Text="Main Title of Blog Feed" …/>. Adding that XAML element caused a variable to be declared in MainPage.g.h:



// MainPage.g.h -- Do Not Paste
partial ref class MainPage : public Windows::UI::Xaml::Controls::Page, 
                              public Windows::UI::Xaml::Markup::IComponentConnector
{
 	...
	 Windows::UI::Xaml::Controls::TextBlock^ TitleText;
)

...and initialized in MainPage.g.hpp:



// MainPage.g.hpp -- Do Not Paste
// Get the TextBlock named 'TitleText'
TitleText = safe_cast<Windows::UI::Xaml::Controls::TextBlock^>
    (static_cast<Windows::UI::Xaml::IFrameworkElement^>(this)->FindName("TitleText"));


With that code in place, we can refer to the initialized variable in our part of the MainPage partial class in MainPage.xaml.h and MainPage.xaml.cpp.

Adding content to controls by using data-binding

Sometimes setting a Text property dynamically in code works well. But to display data, you typically use data binding to connect a data source to the UI. When you establish a binding and the data source changes, the UI elements that are bound to the data source can reflect the changes automatically. With data-binding, all or almost all of the code is written in the XAML file rather than in the code-behind. Data-binding enables a cleaner separation between the View (or ViewModel) and the other modules (such as a Model or Controller) and is usually the recommended way to populate XAML controls with content.

Binding expressions

To bind a content control to a data source, we assign a {Binding } expression to the content property on the control. For a TextBlock, the content property is Text. We use binding expressions that have a Path value to indicate what the control is binding to. Here’s the binding expression that we use for the TitleText TextBlock: Text="{Binding Path=Title}". The meaning of the Path value (Title in this case) depends on the data context.

We set the default data context for the entire XAML tree dynamically in our code-behind in the MainPage constructor: this->DataContext = feedData;. Because of that statement, the TextBlock knows that "Title" means the Title property on the feedData instance. In that line of code, "this" is the MainPage instance that was constructed at run time from the XAML tree. Setting the data context to the feedData object makes it the default data context for the entire object tree for this page. We can override the context for individual elements if necessary.

Hh465045.wedge(en-us,WIN.10).gifTo bind the feed title to the TitleText TextBlock

  • Modify the Text attribute to bind to the feed title.

    
    <TextBlock x:Name="TitleText" Text="{Binding Path=Title}"
        VerticalAlignment="Center" FontSize="48" Margin="56,0,0,0"/>
    
    

Hh465045.wedge(en-us,WIN.10).gifTo bind the ListView items to the Items property of dataFeed

  • To connect the list view to our data source, add this binding expression to ItemListView: ItemsSource="{Binding Path=Items}". Then add a Margin value so that the opening tag now looks like this:

    
    <ListView x:Name="ItemListView" ItemsSource="{Binding Path=Items}" Margin="10,0,0,10">
    
    
    

Now that we have bound the ListView control to the FeedData Items collection, we can bind to the individual items.

Hh465045.wedge(en-us,WIN.10).gifTo bind the DataTemplate items to the FeedItem properties

  1. In the first TextBlock, use "{Binding Path=Title}" to replace the temporary text so that the element now looks like this:

    
    <TextBlock Text="{Binding Path=Title}"  FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
    
    
    
  2. In the second TextBlock, use "{Binding Path=Author}" to replace the temporary text so that the element now looks like this:

    
    <TextBlock Text="{Binding Path=Author}" FontSize="16" Margin="15,0,0,0"/>
    
    
    

    We will skip the third text box for now because we have to provide a custom converter to display the DateTime value.

The ListView control stores its items as type Platform::Object^ but as long as you use a Platform::Collections::Vector as the data source, the conversion of the elements to and from Object^ is handled automatically by the private Bindable* methods on the class. How then does the data-binding know that the objects in the ListView are FeedItem objects that have properties named "Title," "Author," and "PubDate"? The answer is that it doesn’t know and it doesn’t care. It just uses the metadata to look for a property that has the specified name on the object in its collection. If you specify a property name that doesn’t exist, or make a typo, then the result will be a blank TextBlock at run time. Weak-typing at run time requires careful typing at coding time!

The one remaining TextBlock to data-bind is the one above the WebView control. We want this TextBlock to display the title of the currently selected item in the ListView. If we just use the same binding as on the TitleText control, we'll display the same string, because both the data context and the property name are identical. To make things right, we can override the default DataContext property on the page by setting a new data context in the Grid element that is the immediate parent of the TextBlock. Notice that here we are binding to a property name on a XAML element, and therefore, we use the ElementName attribute to specify which element we are binding to.

Hh465045.wedge(en-us,WIN.10).gifTo data-bind the title of a post to the currently selected item

  1. Set the DataContext attribute in Grid3 so that the opening tag now looks like this:

    
    <Grid Name ="Grid3" Grid.Column="1" DataContext="{Binding ElementName=ItemListView, Path=SelectedItem}">
    
    
  2. Use this binding expression "{Binding Path=Title}" to replace the temporary text in the TextBlock above the WebView so that the element now looks like this:

    
    <TextBlock Text="{Binding Path=Title}" FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
    
    

Because we set a new data context on the enclosing grid element, at run time, the data binding mechanism looks for the Title property on the currently selected FeedItem, not on the DataFeed object.

Formatting data with a value converter

In the ItemListView DataTemplate, we bind the PubDate property, which is a DateTime, to a TextBlock.Text property. By default, the binding engine converts PubDate from a DateTime to a string for us. But the automatic conversion just produces the name of the type Windows::Foundation::DateTime, which is not very informative. To produce an actual date, we have two choices: we can change the type of FeedItem::PubDate to Platform::String^ and do a conversion when we initialize the variable, or we can create a custom value converter and data-bind to that to convert the value at run time. Let's choose the latter approach.

To create a value converter, we create a class that implements the IValueConverter interface and then implement the Convert and (optionally) the ConvertBack methods. Converters can change data from one type to another, translate data based on cultural info, or modify other aspects of the presentation. Here, we create a very basic date converter that converts the date value passed in and formats it so that it shows the day, the month, and the year. (In part 2 of this walkthrough, we'll create a more versatile date converter.)

Hh465045.wedge(en-us,WIN.10).gifTo create a value converter class that implements IValueConverter

  1. On the menu bar, choose Project > Add New Item and select Header File. Name the file DateConverter.h and add this class definition to the file:

    
    
    //DateConverter.h
    
    #pragma once
    
    namespace SimpleBlogReader
    {
    public ref class DateConverter sealed : public Windows::UI::Xaml::Data::IValueConverter  
    {
    public:
        virtual Platform::Object^ Convert(Platform::Object^ value,
            Windows::UI::Xaml::Interop::TypeName targetType,
            Platform::Object^ parameter,
            Platform::String^ language)
            {
                auto dt = (Windows::Foundation::DateTime) value; 
                auto dtf =
                    Windows::Globalization::DateTimeFormatting::DateTimeFormatter::LongDate::get();
                return dtf->Format(dt); 
            }
    
        virtual Platform::Object^ ConvertBack(Platform::Object^ value,
            Windows::UI::Xaml::Interop::TypeName targetType,
            Platform::Object^ parameter,
            Platform::String^ language)
            {   
                //Not used. Left as exercise for the reader!
                throw ref new Platform::NotImplementedException();
            }
        };
    }
    
    
    
  2. Add this #include directive to MainPage.xaml.h:
    
    #include "DateConverter.h"
    
    

    We have to include the file even though it isn't referenced in our own code-behind because the Visual Studio build process requires it to generate the data binding code.

  3. In MainPage.xaml, declare an instance of the class as a resource. Paste the following Page.Resources node between the opening Page tag and the Grid1 element.

    
      <Page.Resources>
        <local:DateConverter x:Key="dateConverter" />
      </Page.Resources>
    
    
    

    The Page tag already has an XML namespace mapping that gives us access to classes in our project that are declared in the SimpleBlogReader namespace: xmlns:local="using:SimpleBlogReader". Without this mapping, our DateConverter class would not be shown here.

  4. Now we can bind the PubDate TextBlock to our DateConverter:
    
    <TextBlock Text="{Binding Path=PubDate, Converter={StaticResource dateConverter}}" FontSize="16" Margin="15,0,0,0"/>
    
    

    With this XAML, the binding engine uses our custom DateConverter to change the DateTime to a string. The string that it returns is formatted the way we want, with only the day, month, and year.

Displaying HTML in a WebView

To show a blog post in our app, we have to get the post content to show in the WebView control. The WebView control gives us a way to host HTML data in our app.

We added a WebView to the right column of the nested Grid, and gave it an x:Name of ContentView because we needed a variable to refer to in our MainPage class.

When we look at the Source property of the WebView, we notice that it requires a URI before the web page can be displayed. Our HTML data is just a string of HTML. It doesn't have a URI that we can bind to the Source property. Luckily, there is a NavigateToString method to which we can pass our FeedItem::Content property. To make this work, we handle the SelectionChanged event of the ListView.

Hh465045.wedge(en-us,WIN.10).gifTo connect the WebView to the FeedItem::Content property of the selected item

  1. Specify a SelectionChanged event handler for the ListView in the XAML file. Set the SelectionChanged attribute and give it the name to call the event handler method, so that the opening tag now looks like this:

    
    <ListView x:Name="ItemListView"
              ItemsSource="{Binding Path=Items}"
              SelectionChanged="ItemListView_SelectionChanged"
              Margin="10,0,0,10">
    
    
    
  2. As with the previous event handler we created, we now have to create the event handler in the code-behind. First, add this line of code in MainPage.xaml.h:

    
    // Declaration in MainPage.xaml.h
    
    public:
        void ItemListView_SelectionChanged(Platform::Object^ sender,
                                       Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);
    
    
    
  3. In MainPage.xaml.cpp, add the method implementation:

    
    void MainPage::ItemListView_SelectionChanged ( 
        Platform::Object^ sender,
        Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
    {
        auto feedItem = safe_cast<FeedItem^>(ItemListView->SelectedItem);
        // Navigate the WebView to the blog post content HTML string.
        ContentView->NavigateToString(feedItem->Content);
    }
    
    
    

Now we have a basic one-page app. If you press F5, something like the following illustration should be displayed. To break out of your app and go back to the Visual Studio IDE, press F12.

Finshed view of simple blog reader.

Tip  For a better debugging experience, download the debugging symbols from the public Microsoft symbol servers. On the main menu, choose Tools and then select Options. In the Options window, expand Debugging, select Symbols, then check the box next to Microsoft Symbol Servers. It might take some time to download them the first time. For faster performance the next time you press F5, specify a local directory in which to cache the symbols.

Here's the complete XAML tree for MainPage.xaml:


<Page
    x:Class="SimpleBlogReader.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SimpleBlogReader"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Loaded="PageLoadedHandler">
    
    <Page.Resources>
        <local:DateConverter x:Key="dateConverter"/>        
    </Page.Resources>

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="140"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="TitleText" Text="{Binding Path=Title}"
                   VerticalAlignment="Center" FontSize="48" Margin="56,0,0,0"/>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="2*"/>
                <ColumnDefinition Width="3*"/>
            </Grid.ColumnDefinitions>
            <ListView x:Name="ItemListView"
                      ItemsSource="{Binding Path=Items}" Margin="10,0,0,10"
                      SelectionChanged="ItemListView_SelectionChanged">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Path=Title}" FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Path=Author}" FontSize="16" Margin="15,0,0,0"/>                            
                            <TextBlock Text="{Binding Path=PubDate, Converter={StaticResource dateConverter}}" FontSize="16" Margin="15,0,0,0" />                            
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
                
            </ListView>
            <Grid Grid.Column="1" DataContext="{Binding ElementName=ItemListView, Path=SelectedItem}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <TextBlock Text="{Binding Path=Title}" FontSize="24" Margin="5,0,0,0"/>
                <WebView x:Name="ContentView" Grid.Row="1" Margin="0,5,20,20"/>
            </Grid>
        </Grid>
        
    </Grid>
</Page>


This is the end of part 1. You have now created a basic app that works well for most situations, and learned the basics of XAML and its related code-behind in the process.

Simple blog reader 2.0

Now that we understand the basics of XAML and C++/CX, let's create a version in a way that is much closer to a real-world app. Our SimpleBlogReader 2.0 will have the following features:

  • It will support multiple blog feeds.
  • It will support Process Lifetime Management (PLM) and correctly save and reload its state if the system shuts it down while another task was in the foreground.
  • It will adapt to different view states, which includes screen resolutions and device orientations (landscape or portrait) and view states (snapped or filled).
  • It will use simple animations and transitions to add life to the user interface.
  • It will use styles to enhance the visual appeal of the app.

Adding pages and navigation

If we're going to support multiple blogs, we have to add some pages to the app, and handle the navigation between these pages. Fortunately, Visual Studio gives us several page templates that implement most of the page navigation and orientation functionality for us. To upgrade our app, we're going to abandon the blank page that we've worked so hard to create, but we'll keep the basic idea, and reuse some of the code. More importantly, the knowledge we gained in making that page the hard way will help us better understand how to create Windows Store apps the easy way, by using the page templates that are provided in Visual Studio.

First, we want a page that lists all of the Windows Team Blogs; for this we can use an Items Page template. When a reader picks a blog from this page, we load the list of posts for that blog into another page. The MainPage.xaml page that we've already created still works for this, but we'll use the SplitPage template in order to get the page navigation and view state support. We'll also add a detail page so that the user can choose to read individual blog posts without the list view taking up space. Each of these templates has extensive navigation support built in. Our main task will be to write custom logic for the LoadState and SaveState methods that are stubbed out in each code-behind class, and add an app bar with a button to enable forward navigation from the split page to the details page.

Page templates

Visual Studio Express 2012 for Windows 8 includes a collection of page templates that are useful for a variety of situations. Here are the available page templates.

Page TypeDescription

Group Detail Page

Displays details for one group and a preview of each item in the group.

Grouped Items Page

Displays grouped collections.

Item Detail Page

Displays one item in detail, and enables navigation to adjacent items.

Items Page

Displays a collection of items.

Split Page

Displays a list of items and details for the selected item.

Basic Page

An empty page that has layout awareness, a title, and a back button.

Blank Page

A blank page for a Windows Store app.

 

Hh465045.wedge(en-us,WIN.10).gifTo add new pages to the app

  1. On the menu bar, choose Project > Add New Item. The Add New Item dialog box opens.

  2. In the Installed pane, expand Visual C++.

  3. Select the template for a Windows Store app project.

  4. In the center pane, select ItemsPage and accept the default name.

  5. Choose Yes when the dialog asks if you want to add necessary files automatically. These files are added to the Common folder and they contain the base classes and other code to support different view states as well as serialization and deserialization of simple types when your app is terminated and resumed.
  6. Choose the Add button. The XAML and code-behind files for your page are added to the project.

  7. Repeat steps 1 through 5, but select Split Page.

  8. Repeat steps 1 through 5, but select Basic Page. Give this page the name "DetailPage".

The Items Page will show the list of Windows Team Blogs. The Split page will show the posts for each blog on the left side and the content of the selected post on the right side, like the MainPage we created previously. The Basic Page will show nothing but the content of a selected post, a back button, and the page title. On this page, instead of loading the post content into the WebView from a string of HTML as we do in the SplitView page, we navigate to the URL of the post and show the actual web page. After this work, the pages of the app will look something like this:

Three-page navigation example

When we add the page templates to our project and look at the XAML and code-behind, it's apparent that these page templates do a lot of work for us. In fact, it's easy to get lost at first, but it helps to understand that each page template has three main sections:

Resources

Styles and data templates for the page are defined in the Resources section. We talk more about this in the Creating a consistent look with styles section.

Visual State Manager

Animations and transitions that adapt the app to different layouts and orientations are defined in the Visual State Manager (VSM). We talk more about this in the Adapting to different layouts section.

App Content

The controls and content that make up the app UI are defined in the root layout panel.

 

Better data classes

The first task in upgrading to v 2.0 is to modify the data classes so that we obtain a FeedData^ object for each blog. We want to be able to download each blog feed independently of the others, and we want to be able to click on a downloaded feed even if all the feeds have not finished downloading. This functionality deserves its own class, and since we're adding a new class we might as well separate out our data classes into the proper .h and .cpp files. We will create a new class called FeedDataSource to handle the downloading logic. This will give us a cleaner separation between our data model and our user interface. We expose the data through a property on FeedDataSource of type Platform::Collections::Vector<FeedData^>^

Hh465045.wedge(en-us,WIN.10).gifTo modify the data classes

  1. Overwrite the contents of FeedData.h with the following code. Note that we add a new class called FeedDataSource that represents a collection of feeds and provides the functions to download them. This code is more complex, so we'll split it out into a .h and .cpp file.
    
    
    //feeddata.h
    
    #pragma once
    #include "pch.h"
    
    namespace SimpleBlogReader
    {
        // To be bindable, a class must be defined within a namespace
        // and a bindable attribute needs to be applied.
        // A FeedItem represents a single blog post.
        [Windows::UI::Xaml::Data::Bindable]
        public ref class FeedItem sealed
        {
        public:
            FeedItem(void){}
    
            property Platform::String^ Title;
            property Platform::String^ Author;
            property Platform::String^ Content;      
            property Windows::Foundation::DateTime PubDate;      
            property Windows::Foundation::Uri^ Link;
    
        private:
            ~FeedItem(void){}
        };
    
        // A FeedData object represents a feed that contains 
        // one or more FeedItems. 
        [Windows::UI::Xaml::Data::Bindable]
        public ref class FeedData sealed
        {
        public:
            FeedData(void)
            {
                m_items = ref new Platform::Collections::Vector<FeedItem^>();
            }
    
            // The public members must be Windows Runtime types so that
            // the XAML controls can bind to them from a separate .winmd.
            property Platform::String^ Title;            
            property Windows::Foundation::Collections::IVector<FeedItem^>^ Items
            {
                Windows::Foundation::Collections::IVector<FeedItem^>^ get() {return m_items; }
            }
    
            property Platform::String^ Description;
            property Windows::Foundation::DateTime PubDate;
            property Platform::String^ Uri;
    
        private:
            ~FeedData(void){}
    
            Platform::Collections::Vector<FeedItem^>^ m_items;
        };   
    
        // A FeedDataSource represents a collection of FeedData objects
        // and provides the methods to download the source data from which
        // FeedData and FeedItem objects are constructed. This class is 
        // instantiated at startup by this declaration in the 
        // ResourceDictionary in app.xaml: <local:FeedDataSource x:Key="feedDataSource" />
        [Windows::UI::Xaml::Data::Bindable]
        public ref class FeedDataSource sealed
        {
        private:
            Platform::Collections::Vector<FeedData^>^ m_feeds;
            std::map<Platform::String^, concurrency::task_completion_event<FeedData^>> m_feedCompletionEvents;
            FeedData^ GetFeedData(Platform::String^ feedUri, Windows::Web::Syndication::SyndicationFeed^ feed);       
    
        public:
            FeedDataSource();
            property Windows::Foundation::Collections::IObservableVector<FeedData^>^ Feeds
            {
                Windows::Foundation::Collections::IObservableVector<FeedData^>^ get()
                {
                    return this->m_feeds;
                }
            }
            void InitDataSource();
            static Windows::Foundation::IAsyncOperation<FeedData^>^ GetFeedAsync(Platform::String^ title);
            static FeedItem^ GetFeedItem(FeedData^ fd, Platform::String^ uniqueiD);
        };
    }
    
    

    Create a new .cpp file as before by choosing Project | New Item and call it FeedData.cpp. Then copy and paste the following code into that file:

    
    
    // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
    // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
    // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
    // PARTICULAR PURPOSE.
    //
    // Copyright (c) Microsoft Corporation. All rights reserved
    //
    
    #include "pch.h"
    #include "FeedData.h"
    
    
    using namespace std;
    using namespace concurrency;
    using namespace SimpleBlogReader;
    using namespace Platform;
    using namespace Platform::Collections;
    using namespace Windows::Foundation;
    using namespace Windows::Web::Syndication;
    
    
    
    FeedDataSource::FeedDataSource()
    {
        m_feeds = ref new Vector<FeedData^>();
    }
    
    // Retrieve the data for each atom or rss feed and put it
    // into our custom data structures.
    void FeedDataSource::InitDataSource()
    {
        // Left as an exercise: store the urls separately and let the user configure them.
        // It might be more convenient to use Platform::Strings here, but using wstring 
        // serves to demonstrate how standard C++ types can be used here.
        std::vector<std::wstring> urls; 
    
        urls.push_back(L"http://blogs.windows.com/skydrive/b/skydrive/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows/b/windowsexperience/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows/b/extremewindows/atom.aspx");
    
        urls.push_back(L"http://blogs.windows.com/windows/b/business/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows/b/bloggingwindows/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows/b/windowssecurity/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows/b/springboard/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows/b/windowshomeserver/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows_live/b/windowslive/rss.aspx");
        urls.push_back(L"http://blogs.windows.com/windows_live/b/developer/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/ie/b/ie/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows_phone/b/wpdev/atom.aspx");
        urls.push_back(L"http://blogs.windows.com/windows_phone/b/wmdev/atom.aspx");
    
        // If we are resuming, we need to create a map of completion events which will
        // prevent the UI from attempting to restore the page's state before it's 
        // backing data has been loaded. First we create all the  events in an "unset" state, 
        // mapped to the urls as keys. We set the event after we asynchronously load the feed
        // in the then clause below.
        for( wstring url : urls)
        {
            String^ uri = ref new String(url.c_str());
            task_completion_event<FeedData^> e;
            m_feedCompletionEvents.insert(make_pair(uri, e));
        }
    
        SyndicationClient^ client = ref new SyndicationClient();   
    
        // Range-based for loop.
        for (wstring url : urls)
        {
            // Create the async operation. feedOp is an 
            // IAsyncOperationWithProgress<SyndicationFeed^, RetrievalProgress>^
    
            String^ uri = ref new String(url.c_str());
            auto feedUri = ref new Uri(uri);
            IAsyncOperationWithProgress<SyndicationFeed^, RetrievalProgress>^  feedOp;
            feedOp = client->RetrieveFeedAsync(feedUri);
    
            // Create the task object and pass it the async operation.
            // SyndicationFeed^ is the type of the return value
            // that the feedOp operation will eventually produce.       
    
            // Then, initialize a FeedData object with the feed info. Each
            // operation is independent and does not have to happen on the
            // UI thread. Therefore, we specify use_arbitrary.
            create_task(feedOp)
                .then([this, uri]  (SyndicationFeed^ feed) -> FeedData^
            {     
                return GetFeedData(uri, feed);
            }, concurrency::task_continuation_context::use_arbitrary())
    
            // Append the initialized FeedData object to the list
            // that is the data source for the items collection.
            // This has to happen on the UI thread. By default, a .then
            // continuation runs in the same apartment thread that it was called on.
            // Because the actions will be synchronized for us, we can append 
            // safely to the Vector without taking an explicit lock.
            .then([this] (FeedData^ fd)
            {
                m_feeds->Append(fd);
    
                // Signal that the feed is ready to be loaded into the UI.
                // If we are resuming from termination then the UI thread  
                // is probably already waiting for this event to be set.
                m_feedCompletionEvents[fd->Uri].set(fd);
    
                // Write to VS output window in debug mode only. Requires <windows.h>.
                OutputDebugString(fd->Title->Data());
                OutputDebugString(L"\r\n");
            })
    
            // The last continuation serves as an error handler. The
            // call to get() will surface any exceptions that were raised
            // at any point in the task chain.
            .then( [] (task<void> t)
            {
                try
                {
                    t.get();
                }
                // SyndicationClient throws Platform::InvalidArgumentException 
                // if a URL contains illegal characters.
                // We catch this exception for demonstration purposes only.
                // In the current design of this app, an illegal
                // character can only be introduced by a coding error
                // and should not be caught. If we modify the app to allow
                // the user to manually add a new url, then we need to catch
                // the exception.
                catch (Platform::InvalidArgumentException^ e)
                {
                    // For example purposes we just output error to console.
                    // In a real world app that allowed the user to enter
                    // a url manually, you could prompt them to try again.
                    OutputDebugString(e->Message->Data());
                }
    
                catch (Platform::COMException^ e)
                {
                    // Thrown if the URL does not exist or the content contains invalid XML.
                    // This isn't a fatal error, we just catch the exception and 
                    // continue loading the other feeds. In a real world app that
                    // allowed the user to enter a url manually, you could inform 
                    // them of the failure and/or prompt them to try again.
                    OutputDebugString(e->Message->Data());
                }
            }); // end task chain
        } // end loop
    }
    
    
    FeedData^ FeedDataSource::GetFeedData(String^ feedUri, SyndicationFeed^ feed)
    {
        FeedData^ feedData = ref new FeedData();    
    
        // Knowing this makes it easier to map completion_events 
        // when we resume from termination.
        feedData->Uri = feedUri;
    
        // Get the title of the feed (not the individual posts).
        feedData->Title = feed->Title->Text; 
    
        if (feed->Subtitle->Text != nullptr)
        {
            feedData->Description = feed->Subtitle->Text;
        }	 
        // Use the date of the latest post as the last updated date.
        feedData->PubDate = feed->Items->GetAt(0)->PublishedDate;	
    
        // Construct a FeedItem object for each post in the feed
        // using a range-based for loop. Preferable to a 
        // C-style for loop, or std::for_each.
        for (auto  item : feed->Items)
        {
            FeedItem^ feedItem = ref new FeedItem();
            feedItem->Title = item->Title->Text; 
            feedItem->PubDate = item->PublishedDate;		
    
            // We only get first author in case of multiple entries.
            feedItem->Author = item->Authors->GetAt(0)->Name; 
    
            if (feed->SourceFormat == SyndicationFormat::Atom10)
            {
                feedItem->Content = item->Content->Text;
                feedItem->Link = ref new Uri(item->Id);
            }
    
            else if (feed->SourceFormat == SyndicationFormat::Rss20)
            {
                feedItem->Content = item->Summary->Text;
                feedItem->Link = item->Links->GetAt(0)->Uri;
            }
    
            feedData->Items->Append(feedItem);
        };
    
        return feedData;
    
    } //end GetFeedData
    
    
    // We use this method to get the proper FeedData object when resuming
    // from shutdown. We need to wait for this data to be populated before
    // we attempt to restore page state. Note the use of task_completion_event
    // which doesn't block the UI thread.
    IAsyncOperation<FeedData^>^ FeedDataSource::GetFeedAsync(String^ uri)
    {
        return create_async([uri]()
        {
            auto feedDataSource = safe_cast<FeedDataSource^>( 
                App::Current->Resources->Lookup("feedDataSource"));
    
            // Does not block the UI thread.
            auto f = feedDataSource->m_feedCompletionEvents[uri];
    
            // In the callers we continue from this task after the event is 
            // set in InitDataSource and we know we have a FeedData^.
            task<FeedData^> t = create_task(f);
            return t;
        });
    }
    
    // We stored the stringID when the app was suspended
    // because storing the FeedItem itself would have required
    // more custom serialization code. Here is where we retrieve
    // the FeedItem based on its string ID.
    FeedItem^ FeedDataSource::GetFeedItem(FeedData^ feed, String^ uniqueId)
    {
        auto itEnd = end(feed->Items);
        auto it = std::find_if(begin(feed->Items), itEnd, 
            [uniqueId] (FeedItem^ fi)
        {
            return fi->Title == uniqueId;
        });
    
        if (it != itEnd)
            return safe_cast<FeedItem^>(*it);
    
        return nullptr;
    }
    
    
    
  2. Add the necessary #include directives to pch.h so that it looks like this:

    
    #pragma once
    
    #include <collection.h>
    #include <ppltasks.h>
    #include <string>
    #include <map>
    #include "DateConverter.h"
    #include "App.xaml.h"
    
    
  3. Add this #include directive to app.xaml.h:

    
    #include "FeedData.h"
    
    
  4. Add this #include directives to app.xaml.cpp:

    
    
    #include "ItemsPage.xaml.h"
    
    
  5. Add the following XAML element in the app.xaml <ResourceDictionary> node.. Note that we instantiate the FeedDataSource declaratively in the XAML. It will be constructed when the app class is constructed, without us ever explicitly calling its constructor:

    
    
     <local:FeedDataSource x:Key="feedDataSource" />
    
    
    
  6. Now press Ctrl-Shift-B to build the app. It should compile without errors or warnings.

A better IValueConverter class for dates

Now is a good time to modify our DateConverter class so that it can return the day, month, and year individually instead of as one single string. We will need this feature in the new style that we'll define for grid items and list view items later on.

Hh465045.wedge(en-us,WIN.10).gifTo modify the DateConverter class

  1. In DateConverter.h, replace the existing DateConverter class with this new implementation:

    
    
    //DateConverter.h
    
    #pragma once
    
    namespace SimpleBlogReader
    {
    
    public ref class DateConverter sealed : public Windows::UI::Xaml::Data::IValueConverter  
    {
        public:
        virtual Platform::Object^ Convert(Platform::Object^ value,
                                          Windows::UI::Xaml::Interop::TypeName targetType,
                                          Platform::Object^ parameter,
                                          Platform::String^ language)
        {		
            if(value == nullptr)
            {
                throw ref new Platform::InvalidArgumentException();
            }
            auto dt = safe_cast<Windows::Foundation::DateTime>(value);
            auto param = safe_cast<Platform::String^>(parameter);
            Platform::String^ result;
            if(param == nullptr)
            {
                auto dtf =
                Windows::Globalization::DateTimeFormatting::DateTimeFormatter::ShortDate::get();
                result = dtf->Format(dt);
            }
            else if(wcscmp(param->Data(), L"month") == 0)
            {
                auto month = 
                    ref new Windows::Globalization::DateTimeFormatting::DateTimeFormatter("{month.abbreviated(3)}");
                result = month->Format(dt);
            }
            else if(wcscmp(param->Data(), L"day") == 0)
            {
                auto month = 
                   ref new Windows::Globalization::DateTimeFormatting::DateTimeFormatter("{day.integer(2)}");
                result = month->Format(dt);
            }
            else if(wcscmp(param->Data(), L"year") == 0)
            {
                auto month = 
                    ref new Windows::Globalization::DateTimeFormatting::DateTimeFormatter("{year.full}");
    				        result = month->Format(dt);
            }
            else
            {
                // We don't handle other format types currently.
                throw ref new Platform::InvalidArgumentException();
            }
    
            return result; 
         }
    
        virtual Platform::Object^ ConvertBack(Platform::Object^ value,
                                              Windows::UI::Xaml::Interop::TypeName targetType,
                                              Platform::Object^ parameter,
                                              Platform::String^ language)
        {   
            // Not needed in SimpleBlogReader. Left as an exercise.
            throw ref new Platform::NotImplementedException();
        }
    };
    }
    
    
  2. To use the class, we add a reference to it in the ResourceDictionary node in App.xaml, just after the FeedDataSource tag. This causes DateConverter to be instantiated when the app is created.
    
    <local:DateConverter x:Key="dateConverter" />
    
    

Navigating between pages

One of the main activities in a Windows Store app is navigating between pages. A user might be able to choose a forward or back button to move through the pages, or choose an item that opens another page—for example, to show details about the item. Here's the navigation design for Simple Blog Reader:

  • When the app starts, the items page shows a grid of blog feeds (DataFeed objects). Only the blog titles are displayed, along with the dates of the last posts and descriptions if they exist. When the user selects a feed, the app navigates to the SplitPage.

  • In landscape mode, the SplitPage shows the list of posts (FeedItem objects) on the left, and a preview of the currently selected post on the right. In portrait or snapped view, the SplitPage shows either the list or the preview, but not both at the same time. When the user selects another item in the list, the preview changes.

  • The SplitPage has an app bar that the user can invoke by swiping from the top of the screen or by right-clicking the mouse. The app bar has a button; when the user chooses it, the app navigates to the DetailPage that shows the blog post as a complete web page.

  • The user can choose the back button on the DetailPage to go back to the SplitPage, and can choose the back button on the SplitPage to go back to the ItemsPage.

To complicate things, every time we load a page we must consider both the view state—portrait mode, snapped view, filled view, or landscape—and whether the navigation is invoked by a user action (forward or back) or by the system resuming the app after terminating it at some point in the past.

Fortunately, most of the code to handle view state is provided for us by the pages themselves and by their base class, LayoutAwarePage. The SuspensionManager class provides much of the code to save and restore page state when the app is suspended and terminated. The system can terminate our app if it is in a suspended state and the system needs to reclaim some memory resources. It is up to us to implement the specific logic that's required to load or re-load the pages.

Hh465045.wedge(en-us,WIN.10).gifTo implement PLM and page navigation in app.xaml.cpp

  • Replace the OnLaunched and OnSuspending methods in App.xaml.cpp with the following code:

    
    
    /// <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>
    void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ pArgs)
    {
        // Do not repeat app initialization when already running, just ensure that
        // the window is active
        if (pArgs->PreviousExecutionState == ApplicationExecutionState::Running)
        {
            Window::Current->Activate();
            return;
        }
    
        FeedDataSource^ feedDataSource = safe_cast<FeedDataSource^>(App::Current->Resources->Lookup("feedDataSource"));
        if (feedDataSource->Feeds->Size == 0)
        {
            feedDataSource->InitDataSource();
        }
    
        auto prerequisite = task<void>([](){});
        auto rootFrame = ref new Frame();
        SuspensionManager::RegisterFrame(rootFrame, "AppFrame");
        if (pArgs->PreviousExecutionState == ApplicationExecutionState::Terminated)
        {
            // Restore the saved session state only when appropriate, scheduling the
            // final launch steps after the restore is complete
            prerequisite = SuspensionManager::RestoreAsync();
        }
        prerequisite.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 (rootFrame->Content == nullptr)
            {
                if (!rootFrame->Navigate(TypeName(ItemsPage::typeid)))
                {
                    throw ref new FailureException("Failed to create initial page");
                }
            }
    
            // Place the frame in the current Window and ensure that it is active
            Window::Current->Content = rootFrame;
            Window::Current->Activate();
        }, task_continuation_context::use_current());
    }
    
    /// <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>
    void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e)
    {
        (void) sender;	// Unused parameter
        (void) e;	// Unused parameter
    
        //Save application state and stop any background activity
        auto deferral = e->SuspendingOperation->GetDeferral();
    
        auto saveOp = SuspensionManager::SaveAsync();
    
        saveOp.then ([deferral]()
        {
            deferral->Complete();
        });
    }
    
    
    

    The XAMLUI framework provides a built-in navigation model that uses Frames and Pages and works much like the navigation in a web browser. The Frame control hosts Pages, and has a navigation history that you can use to go back and forward through pages that you've visited. You can pass data between pages as you navigate.

    Navigation starts when the app is started. The infrastructure that supports navigation is in the App class. In the Visual Studio project templates, a Frame that's named rootFrame is set as the content of the app window. Notice that after the rootFrame is set, the app checks its current state because it might starting from a closed state or resuming from suspension and already have its content in memory. If it's starting from a terminated state, it has to load the state that was saved when it was terminated. After handling these various cases, the app navigates to its first window.

    To enable navigation between pages, use the Frame control's Navigate, GoForward, and GoBack methods. By using the Navigate(TypeName, Object) method, we can navigate to a new page and pass data to it at the same time. The first parameter is the TypeName of the page that we are navigating to. We obtain the TypeName of a type by using the static typeid operator. In this case, we want to navigate to the ItemsPage as the first page that users encounter when they start or resume the app. We will no longer be using the MainPage.xaml or its related .cpp and .h files. You can exclude those from the project if you like.

    The second parameter is the data object that we pass to the page that we're navigating to. In that page, it's passed to the LoadState method in the navigationParameter parameter. In the earlier one-page version of the app in which we showed only one blog feed, we passed the first feed in our blog feeds collection. In this new version of the app, we are more constrained because we have to support Process Lifetime Management (PLM) and we don't want to write any custom serialization code. The PLM functionality in the generated code automatically tries to save the object that's passed in the navigationParameter, but without additional help from us, it can succeed only if the type is a String, Guid, or primitive type. Therefore, we won't pass an entire FeedData object, but we can pass a string and use it as a key to look up the specific FeedData or FeedItem object. That's the purpose of the GetFeedAsync and GetFeedItem methods in the FeedDataSource class. We'll look at those methods in more detail later.

Hh465045.wedge(en-us,WIN.10).gifTo navigate from the App class to the items page

  • In the previous snippet, in the App::OnLaunched method, notice that the two calls to Navigate refer to ItemsPage::typeid. When the Navigate method is invoked in App::OnLaunched, it eventually causes the ItemsPage::LoadState event handler to be called. As discussed earlier, we can't pass the feedDataSource object or even the Feeds property by using navigationParameter because we don't want to have to serialize it ourselves. Instead, we store a reference to the feedDataSource as a resource in App.xaml, and we just access it here. We still have to initialize the DefaultViewModel for ItemsPage by using the Feeds property. Here, the Feeds Vector is associated with a key that's named "Items" and is inserted into the DefaultViewModel member for ItemsPage. DefaultViewModel is a Windows::Foundation::Collections::IObservableMap. Each page has its own DefaultViewModel. After you intialize the DefaultViewModel with some data, the ItemsViewSource::View property will point to your data. If the elements in the collection are bindable, then the items will show up in the UI.

    In ItemsPage.xaml.cpp, replace the LoadState method implemention with the following code:

    
    
    void ItemsPage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState)
    {
        // This is the first page to load on startup. The feedDataSource was constructed when the app loaded
        // in response to this declaration in app.xaml: <local:FeedDataSource x:Key="feedDataSource" />
        // and was initialized aynchronously in the OnLaunched event handler in app.xaml.cpp. 
        // Initialization might still be happening, but that's ok. 
        auto feedDataSource = safe_cast<FeedDataSource^>(App::Current->Resources->Lookup("feedDataSource"));
    
        // In ItemsPage.xaml (and every other page), the DefaultViewModel is set as DataContext:
        // DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
        // Because ItemsPage displays a collection of feeds, we set the Items element
        // to the FeedDataSource::Feeds collection. By comparison, the SplitPage sets this element to 
        // the Items collection of one FeedData object.
        this->DefaultViewModel->Insert("Items", feedDataSource->Feeds);
    
    }
    
    

Now press F5 to run the app with this modification. Notice that without any changes yet to the template code, some of the data that we passed to the ItemsPage is already showing up in the grid squares. It looks something like this (the items might be arranged differently depending on your screen resolution):

Items page

The only thing left to do on ItemsPage is to tell it what to do when the user chooses one of its items.

Hh465045.wedge(en-us,WIN.10).gifTo navigate from the items page to the split page

  1. When the user picks a blog from the ItemsPage collection, we navigate from the Items page to the Split page. To enable this navigation, we want GridView items to behave like buttons instead of selected items. To make the GridView items respond like buttons, we set the SelectionMode and IsItemClickEnabled properties as shown in the next example. We then add a handler for the ItemClicked event of the GridView. In ItemsPage.xaml find the itemGridView element and use the following markup to replace it:

    
    
     <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 Standard250x250ItemTemplate}"
                SelectionMode="None"
                IsItemClickEnabled="true"
                IsSwipeEnabled="false"
                ItemClick="ItemView_ItemClick"/>
    
    
    
  2. The items page also has a list view—itemListView—that's shown in place of the grid when the app is snapped. We talk more about this in the section Adapting to different layouts. For now, we just have to make the same changes to the ListView that we made to the GridView so that they have the same behavior. Find the itemListView in ItemsPage.xaml and add the required attributes so that it looks like this:

    
    
    <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 Standard80ItemTemplate}"
                SelectionMode="None"
                IsSwipeEnabled="false"
                IsItemClickEnabled="true"
                ItemClick="ItemView_ItemClick"/>
    
    
    
  3. Add these directives to ItemsPage.xaml.cpp:

    
    #include "SplitPage.xaml.h"
    //...
    using namespace Windows::UI::Xaml::Interop;
    
    
    
  4. Add the event handler prototype to ItemsPage.xaml.h and the implementation to ItemsPage.xaml.cpp:

    
    
    //ItemsPage.xaml.h:
    protected: 
    virtual void ItemView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e);
    
    
    //ItemsPage.xaml.cpp:
    void ItemsPage::ItemView_ItemClick(Object^ sender, ItemClickEventArgs^ e)
    {
        // We must manually cast from Object^ to FeedData^.
        auto feedData = safe_cast<FeedData^>(e->ClickedItem);
    
        // Store the current URI so that we can look up the
        // correct feedData object on resume.
        App::Current->Resources->Insert("CurrentFeed", feedData);
          
        // Navigate to SplitPage and pass the title of the selected feed.
        // SplitPage will receive this in its LoadState method in the navigationParamter.
        this->Frame->Navigate(TypeName(SplitPage::typeid), feedData->Title);
    }
    
    
  5. Add this using directive to SplitPage.xaml.cpp:

    
    using namespace concurrency;
    
    

    The header file for the concurrency namespace is <ppltasks.h> and it is already included in our local pch.h file.

  6. When ItemsPage navigates to SplitView, it causes the SplitView::LoadState method to be called. As in ItemsPage, LoadState has to determine how it has been navigated to, and what the app's previous state was. The code comments in the next example provide more detail; you'll probably recognize much of it from looking at App::OnLaunched and ItemsPage::LoadState. Now open SplitPage.xaml.cpp and use this code to replace the entire method:

    
    /// <summary>
    /// Populates the page with content that's passed during navigation.  Any saved state is also
    /// provided when a page is recreated from a prior session.
    /// </summary>
    /// <param name="navigationParameter">The parameter value that was passed to
    /// <see cref="Frame::Navigate(Type, Object)"/> when this page was initially requested.
    /// </param>
    /// <param name="pageState">A map of state that was preserved by this page during an earlier
    /// session.  This is null the first time that a page is visited.</param>
    void SplitPage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState)
    {
        // If we are navigating forward from ItemsPage, there is no existing page state.
        if (pageState == nullptr)
        {
            // Current feed was set in the click event in ItemsPage. We don't pass it in
            // through navigationParameter because on suspension, the default serialization 
            // mechanism will try to store that value but fail because it can only handle 
            // primitives, strings, and Guids.
            auto fd = safe_cast<FeedData^>(App::Current->Resources->Lookup("CurrentFeed"));
    
            // Insert into the ViewModel for this page to initialize itemsViewSource->View
            this->DefaultViewModel->Insert("Feed", fd);
            this->DefaultViewModel->Insert("Items", fd->Items);
    
            // 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 (!UsingLogicalPageNavigation() && itemsViewSource->View != nullptr)
            {
                this->itemsViewSource->View->MoveCurrentToFirst();
            }
            else
            {
                this->itemsViewSource->View->MoveCurrentToPosition(-1);
            }
        }
    
        // pageState != null means either (1) we are returning from DetailPage
        // or (2) we are resuming from termination. If (1), then we still have our
        // state and don't have to do anything. If (2), then we have to restore the page.
        else if (!this->DefaultViewModel->HasKey("Feed"))
        {   
            // All we stored is the Uri string for the feed, not the object. 
            auto uri = safe_cast<String^>(pageState->Lookup("Uri"));
    
            // FeedDataSource::InitDataSource has already been called. 
            // It's an asynchronous operation, so our FeedData might not
            // be available yet. GetFeedAsync uses a task_completion_event to  
            // wait (on its own thread) until the specified FeedData is available.
            // The next three methods follow the basic async pattern in C++:
            // 1. Call the async method.
            auto feedDataOp = FeedDataSource::GetFeedAsync(uri);
    
            // 2. Create a task from it.
            auto feedDataTask = create_task(feedDataOp);
    
            // 3. Define the work to be performed after the task completes.
            feedDataTask.then([this, pageState](FeedData^ feedData)
            {
                // Now we have the feedData, so it's safe to get the FeedItem
                // synchronously. Inserting into DefaultViewModel
                // initializes the itemsViewSource-View object.
                this->DefaultViewModel->Insert("Feed", feedData);
                this->DefaultViewModel->Insert("Items", feedData->Items);
    
                // DetailsPage has to get the new Uri from this value.
                App::Current->Resources->Insert("CurrentFeed", feedData);
    
                // Now that we have a FeedData^, we can call GetFeedItem safely and
                // pass in the title that we stored before the app was terminated.
                auto itemTitle = safe_cast<String^>(pageState->Lookup("SelectedItem"));
                auto selectedItem = FeedDataSource::GetFeedItem(feedData, itemTitle);
    
                if (selectedItem != nullptr)
                {
                    this->itemsViewSource->View->MoveCurrentTo(selectedItem);
                }
            });
        }
    }
    
    
    

    When we resume from termination, we don't have a FeedItem object, we only have a string. Therefore, we have to look up the FeedItem based on the string. But this part is tricky—we have to wait until the FeedItem has been downloaded by FeedDataSource. And we don't want to wait for all the feeds to download; we want to wait only until the one we need is ready for use. All of this synchronization happens here and in the FeedDataSource::GetFeedAsync method. After we have the FeedData object, we can call the GetFeedItem method synchronously to get the FeedItem for the selected post so that we can populate the preview pane.

    Note  We insert the Items property separately into the DefaultViewModel for the SplitPage to make the items accessible for XAML data-binding.

    No extra work is required to navigate back to the Items page. The template includes code to handle the BackButton::Click event and call the Frame::GoBack method.

  7. When you run the app at this point, notice that the blog text in the detail pane is displaying raw HTML. To fix that, we have to change the layout that's used for the title and content of the selected blog post. If the app is running, press F12 to break out of it, move through the dialog box, and then press Shift-F5 to stop debugging and return to the Visual Studio code editor.

Hh465045.wedge(en-us,WIN.10).gifTo implement SplitPage::SaveState

  • In ItemsPage, there was no need to save page state because we always show all the items. In SplitPage, we have to save the currently selected item so that the app can start from exactly this state if it gets terminated and resumed. As we mentioned earlier, because we are relying on the SuspensionManager to do the serialization work for us, we can only save strings and numbers, not FeedItem objects. Therefore, we save the name of the currently selected item. We have to save both the title of the FeedItem and the Uri of the FeedData object so that we can find this FeedItem again after termination and resume.

    
    /// <summary>
    /// Preserves state that's associated with this page in case the application is suspended or the
    /// 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 map to be populated with serializable state.</param>
    void SplitPage::SaveState(IMap<String^, Object^>^ pageState)
    {
        if (itemsViewSource->View != nullptr)
        {
            auto selectedItem = itemsViewSource->View->CurrentItem;
            // Derive a serializable navigation parameter and pass it to
            // pageState->Insert("SelectedItem", <value>).
            if (selectedItem != nullptr)
            {
                auto feedItem = safe_cast<FeedItem^>(selectedItem);
                auto itemTitle = feedItem->Title;
                pageState->Insert("SelectedItem", itemTitle);
            }
    
            // Save the feed title also.
            auto feedData = safe_cast<FeedData^>(this->DefaultViewModel->Lookup("Feed"));
            pageState->Insert("Uri", feedData->Uri);
        }
    }
    
    

Hh465045.wedge(en-us,WIN.10).gifTo modify the bindings and layout in SplitPage and ItemsPage

  1. We have to make a few more changes to finish adding functionality to the new pages we added to our app. After we add this code, we can move on to styling and animating.

    Because we used a key that's named "Feed" when we added our data to the DefaultViewModel, we have to change the binding in the page title to bind to the Feed property instead of "Group", which is the default. In SplitPage.xaml, change the Text binding of the TextBlock that's named pageTitle to bind to Feed.Title, like this:

    
    <TextBlock x:Name="pageTitle" Grid.Column="1" Text="{Binding Feed.Title}" 
        Style="{StaticResource PageHeaderTextStyle}"/>
    
    
  2. Also in SplitPage.xaml, change the Grid named titlePanel to span 2 columns.

    
    <!-- Back button and page title -->
    <Grid x:Name="titlePanel" Grid.ColumnSpan="2">
    
    
  3. In ItemsPage.xaml, the page title is bound to a static resource that has the key AppName. Update the text in this resource to Windows Team Blogs, like this:

    
    <x:String x:Key="AppName">Windows Team Blogs</x:String>
    
    

Hh465045.wedge(en-us,WIN.10).gifTo add a WebView control to SplitPage.xaml

  1. In SplitPage.xaml, we have to change the layout that's used to show the title and content of the selected blog post. To do that, replace the ScrollViewer named itemDetail with the following ScrollViewer layout. You should recognize much of this XAML from our previous work in MainPage.xaml. The purpose of the Rectangle element will be shown later in this article.

    
    <!-- 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" />
                    <Rectangle x:Name="contentViewRect" />
                </Grid>    
            </Border>
        </Grid>
    </ScrollViewer>
    
    
    
  2. In SplitPage.xaml.cpp, modify the event handling code that causes the WebView to update when the ListView selection changes. The ItemListView_SelectionChanged function signature and implementation are already there. We just have to add these lines at the end of the method:

    
    
        // Sometimes there is no selected item, e.g. when navigating back
        // from detail in logical page navigation.
        auto fi = dynamic_cast<FeedItem^>(itemListView->SelectedItem);
        if(fi != nullptr)
        {
            contentView->NavigateToString(fi->Content);
        }    
    
    
    

Hh465045.wedge(en-us,WIN.10).gifTo add a WebView control to DetailPage.xaml

  • In DetailPage.xaml, we have to bind the title text to the blog post title and add a WebView control to show the blog page. To do that, replace the Grid that contains the back button and page title with this Grid and WebView:

    
    <!-- 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" />
    </Border>
    
    
    

Hh465045.wedge(en-us,WIN.10).gifTo implement LoadState and SaveState in the DetailPage class

  1. Add the following private data members to the DetailPage class in DetailPage.xaml.h:

    
    private:
            Platform::String^ m_itemTitle;
            Platform::String^ m_feedUri;
    
    
    
  2. Add using namespace concurrency; to DetailPage.xaml.cpp.
  3. In DetailPage.xaml.cpp, add code to the LoadState method to navigate to the blog post and set the DataContext of the page. As in SplitPage, we have to determine the previous state of the app. The updated method looks like this:

    
    
    /// <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 map of state preserved by this page during an earlier
    /// session.  This will be null the first time a page is visited.</param>
    void DetailPage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState)
    {
        
        // Lookup the URL for the blog title that was either
        // (a) passed to us in this session or
        // (b) saved in the SaveState method when our app was suspended.
        m_itemTitle = safe_cast<String^>(navigationParameter);
    
        // We are navigating forward from SplitPage
        if (pageState == nullptr)
        {
            auto feedData = safe_cast<FeedData^>(App::Current->Resources->Lookup("CurrentFeed"));
            m_feedUri = feedData->Uri;
    
            auto feedItem = FeedDataSource::GetFeedItem(feedData, m_itemTitle);
            if (feedItem != nullptr)
            {               
                DefaultViewModel->Insert("Title", m_itemTitle);
                // Display the web page.
                contentView->Navigate(feedItem->Link);
            }
        }
       
        // We are resuming from suspension:
        else
        {
            // We are resuming, and might not have our FeedData object yet
            // so must get it asynchronously and wait on the result.
            String^ uri = safe_cast<String^>(pageState->Lookup("FeedUri"));
            auto feedDataOp = FeedDataSource::GetFeedAsync(uri); //URL
            auto feedDataTask = create_task(feedDataOp);
    
            feedDataTask.then([this, pageState](FeedData^ feedData)
            {            
                App::Current->Resources->Insert("CurrentFeed", feedData);
    
                m_feedUri = feedData->Uri;
                m_itemTitle = safe_cast<String^>(pageState->Lookup("Item"));
                auto feedItem = FeedDataSource::GetFeedItem(feedData, m_itemTitle);
    
                if (feedItem != nullptr)
                {               
                    DefaultViewModel->Insert("Title", m_itemTitle);
                    // Display the web page.
                    contentView->Navigate(feedItem->Link);
                }
            });
        }
    }
    
    
  4. In contrast to LoadState, SaveState consists of only two lines of code. As in SplitPage, we save the URI of the feed because we need it in LoadState to look up the FeedItem in the case of resuming from termination.

    
    
    /// <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 map to be populated with serializable state.</param>
    void DetailPage::SaveState(IMap<String^, Object^>^ pageState)
    {
        // Store the itemTitle in case we are suspended or terminated.
        pageState->Insert("Item", m_itemTitle);
        pageState->Insert("FeedUri", m_feedUri);
    }
    
    
    

App bars

Most of the navigation in our blog reader app happens when the user picks an item in the UI. But on the Split page, we must provide a way for the user to go to the detail view of the blog post. We could put a button somewhere on the page, but that would distract from the core app experience, which is reading. Instead, we put the button in an app bar that's hidden until the user needs it. We'll add an app bar with a button to navigate to the detail page.

An app bar is a piece of UI that's hidden by default, and is shown or dismissed when the user swipes from the edge of the screen or interacts with the app, or clicks the right mouse button. It can present navigation, commands, and tools to the user. An app bar can appear at the top of the page, at the bottom of the page, or both. We recommend that you put navigation in the top app bar, and tools and commands in the bottom app bar. To add an app bar in XAML, we assign an AppBar control to the TopAppBar or BottomAppBar property of a Page.

Hh465045.wedge(en-us,WIN.10).gifTo add a button to the Split page app bar

  • The StandardStyles.xaml file contains a variety of app bar button styles for common scenarios. We'll use these styles as a guide to create a style for our button. We put our style in the Page.Resources section of SplitPage.xaml:

    
    
    <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="&#xE12B;"/>
    </Style>
    
    
    
    

    Next, paste this code after the Page.Resources node to create a top app bar that contains the button we just defined:

    
    
    <Page.TopAppBar>
       <AppBar Padding="10,0,10,0">
            <Grid>            
                <Button Click="ViewDetail_Click" HorizontalAlignment="Right" 
                        Style="{StaticResource WebViewAppBarButtonStyle}"/>
            </Grid>
        </AppBar>
    </Page.TopAppBar>
    
    

Hh465045.wedge(en-us,WIN.10).gifTo add navigation to the detail view

  1. Add the following method signature to the SplitPage class in SplitPage.xaml.h:

    
    void ViewDetail_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
    
    
    
  2. Add the following #include directive and using statement in SplitPage.xaml.cpp:

    
    #include "DetailPage.xaml.h"
    ...
    using namespace Windows::UI::Xaml::Interop;
    
    
  3. Add this method body in SplitPage.xaml.cpp:

    
    void SplitPage::ViewDetail_Click(Object^ sender, RoutedEventArgs^ e)
    {
        // Navigate to the appropriate destination page, and configure the new page
        // by passing required information as a navigation parameter.
    
        auto selectedItem = dynamic_cast<FeedItem^>(this->itemListView->SelectedItem);
    
        // selectedItem will be nullptr if the user invokes the app bar
        // and clicks on "view web page" without selecting an item.
        if (this->Frame != nullptr && selectedItem != nullptr)
        {
            auto itemTitle = safe_cast<String^>(selectedItem->Title);
            this->Frame->Navigate(TypeName(DetailPage::typeid), itemTitle);
        }
    }
    
    
    
  4. At this point, the basic app functionality is complete! Try building the sample and running it in the Simulator to test the navigation between pages in both portrait and landscape modes. When you choose an item on ItemsPage, the app should navigate to SplitPage. On SplitPage you can use the back button at the top to go back to ItemsPage, or you can swipe from the top of the screen or right-click to invoke the app bar, and press or click its button to go to the DetailPage. DetailPage has a back button that takes you to SplitPage. As you change the selected item in SplitPage, the preview of the page should appear (note the difference in behavior between landscape mode and portrait mode).

Adding animations and transitions

When we talk about animations, we often think of objects bouncing around on the screen. But in XAML, an animation is essentially just a way to change the value of a property on an object. This makes animations useful for a lot more than just bouncing balls. In our blog reader app, we use some default animations and transitions to adapt our UI to different layouts and orientations. We find these in the Windows.UI.Xaml.Media.Animation namespace.

Adding theme animations

A theme animation is a pre-configured animation that we can put in a Storyboard. The PopInThemeAnimation makes the web view slide in from right to left when the page loads. Increasing the value of the FromHorizontalOffset property makes the effect more extreme. Here, we put the PopInThemeAnimation in a Storyboard and make it a resource in DetailPage.xaml. Set the target of the animation to be the Border that surrounds our web content. This animates the Border and everything in it.

Hh465045.wedge(en-us,WIN.10).gifTo add a theme animation to the Detail page

  1. Paste the following XAML snippet into the Page.Resources node in DetailPage.xaml:

    
    
    <Storyboard x:Name="PopInStoryboard">
        <PopInThemeAnimation  Storyboard.TargetName="contentViewBorder" FromHorizontalOffset="400"/>
    </Storyboard>
    
    
    
  2. Paste the following code into the beginning of the DetailPage::LoadState method in DetailPage.xaml.cpp. Our override of LoadState is called by the OnNavigatedTo method in the base class LayoutAwarePage:

    
    // Run the PopInThemeAnimation. 
    Windows::UI::Xaml::Media::Animation::Storyboard^ sb = dynamic_cast<Windows::UI::Xaml::Media::Animation::Storyboard^>(this->FindName("PopInStoryboard"));
    if (sb != nullptr)
    {
        sb->Begin();
    }    
    //... rest of method as before
    
    
    

Adding theme transitions

A theme transition is a complete set of animations and a Storyboard that's combined into a prepackaged behavior that we can attach to a UI element. A ContentThemeTransition is used with a ContentControl, and is automatically triggered when the content of the control changes.

In our app, let's add a theme transition to the TextBlock that holds the post titles in the Split page list view. When the content of the TextBlock changes, the ContentThemeTransition is triggered and runs automatically. The animation is predefined, and we don't have to do anything to run it. We just attach it to the TextBlock.

Hh465045.wedge(en-us,WIN.10).gifTo add a theme transition to SplitPage.xaml

  • In SplitPage.xaml, the TextBlock that's named pageTitle is an empty-element tag. To add a theme transition, we embed it in the TextBlock and therefore need to change the TextBlock to have an opening and closing tag. Replace the existing tag with the following XAML node:

    
    
    <TextBlock x:Name="pageTitle" Grid.Column="1" Text="{Binding Feed.Title}" Style="{StaticResource PageHeaderTextStyle}">
       <TextBlock.Transitions>
          <TransitionCollection>
              <ContentThemeTransition />
          </TransitionCollection>
       </TextBlock.Transitions>
    </TextBlock>
    
    
    

    When the content of the TextBlock changes, the ContentThemeTransition is triggered and runs automatically. The animation is predefined, and we don't have to do anything to run it. We just attach it to the TextBlock. For more info, and a list of theme animations and transitions, see QuickStart: Animations.

Creating a consistent look with styles

We want to make our blog reader app look and feel like the Windows Team Blogs website. We want our users to have a seamless experience when they move between the website and our app. The default dark theme of our new Windows UI doesn't match very well with the Windows Team Blogs website. This is most clear on the detail page, where we load the actual blog page into a WebView, as shown here:

Detail page with dark theme.

To give our app a consistent appearance that we can update as required, we use brushes and styles. A Brush lets us define a look in one place and use it wherever we need it. By using a Style, we can set values for the properties of a control and reuse those settings across our app.

Before we get into the details, let's look at how we use a brush to set the background color of the pages in our app. Each page in our app has a root Grid that has a Background property that's set to define the background color of the page. We could set each page background individually, like this: <Grid Background="Blue">. However, a better way is to define a Brush as a resource and use it to define the background color of all of our pages. This is how it's done in the Visual Studio templates, as we see here: <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">.

This is fine to set the Background property of the Grid, but you typically need to set more than one property to get the look you want. You can group settings for any number of properties together is a Style, and apply the Style to the control.

You can define resources in an individual page's XAML file, in the App.xaml file, or in a separate resource dictionary XAML file, like StandardStyles.xaml. Where the resource is defined determines the scope in which it can be used. Visual Studio creates the StandardStyles.xaml file as part of the project template and puts it in the Common folder. It's a resource dictionary that contains the values, styles, and data templates used in the Visual Studio page templates. A resource dictionary XAML file can be shared across apps, and more than one resource dictionary can be merged in a single app.

In our blog reader app, we define resources in App.xaml to make them available across our entire app. And we have some resources defined in the XAML file for individual pages. These resources are available only in the page where they are defined. If resources with the same key are defined in both App.xaml and in a page, the resource in the page overrides the resource in App.xaml. In the same way, a resource defined in App.xaml will override a resource defined with the same key in a separate resource dictionary file. For more info, see Quickstart: Styling controls.

Now let's look at an example of using Styles in our app. The look of the template pages is defined by a style with the key LayoutRootStyle. The Style definition is in the StandardStyles.xaml file.



 ...Not for pasting!
   <!-- Page layout roots typically use entrance animations and a theme-appropriate background color -->

    <Style x:Key="LayoutRootStyle" TargetType="Panel">
        <Setter Property="Background" Value="{StaticResource ApplicationPageBackgroundThemeBrush}"/>
        <Setter Property="ChildrenTransitions">
            <Setter.Value>
                <TransitionCollection>
                    <EntranceThemeTransition/>
                </TransitionCollection>
            </Setter.Value>
        </Setter>
    </Style>

In the Style definition, we need a TargetType attribute and a collection of one or more Setters. We set the TargetType to a string that specifies the type that the Style is applied to, in this case, a Panel. If you try to apply a Style to a control that doesn't match the TargetType attribute, an exception occurs. Each Setter element requires a Property and aValue. These property settings indicate what control property the setting applies to, and the value to set for that property.

To change the Background of our pages, we need to replace the ApplicationPageBackgroundThemeBrush with our own custom brush. For our custom brush, we use the Color #FF0A2562, which is a nice blue. To replace the system theme brush, we create a new Style that is based on LayoutRootStyle, and change the Background property there.

Hh465045.wedge(en-us,WIN.10).gifTo set the background color for all pages

  1. To define a new style for the layout root in App.xaml, paste the following ResourceDictionary with its brush and style definitions into the <ResourceDictionary.MergedDictionaries> node, after the ResourceDictionary that contains StandardStyles.xaml:

    
    
    <ResourceDictionary>
        <SolidColorBrush x:Key="WindowsBlogBackgroundBrush" Color="#FF0A2562"/>
        <Style x:Key="WindowsBlogLayoutRootStyle" TargetType="Panel" BasedOn="{StaticResource LayoutRootStyle}">
            <Setter Property="Background" Value="{StaticResource WindowsBlogBackgroundBrush}"/>
        </Style>
    </ResourceDictionary>
    
    

    Important  Because we based a style on a system style in StandardStyles.xaml, the ResourceDictionary that includes StandardStyles.xaml has to be declared before our app ResourceDictionary in the MergedDictionaries. If it's not, the XAML parser can't find the LayoutRootStyle that our style is based on.

  2. Update the root Grid in ItemsPage.xaml, SplitPage.xaml, and DetailPage.xaml to use WindowsBlogLayoutRootStyle.

    
    
     <Grid Style="{StaticResource WindowsBlogLayoutRootStyle}">
    
    

    Our new Style is just like the default style, but with a blue background. The line BasedOn="{StaticResource LayoutRootStyle}" tells our new Style to inherit from LayoutRootStyle any properties that we don't explicitly set.

  3. Press F5 to build and run the app, and see the blue background on the pages.

Control and data templates

To give our app the look and feel of the Windows Team Blogs website, we can use custom control templates and data templates in addition to Brushes and Styles. We talked about data templates in the section Displaying data. A control template customizes the visual appearance of a control. To display the date of the feed posts in SplitView, we'll define a control template and apply it to a ContentControl element.

Hh465045.wedge(en-us,WIN.10).gifTo add a control template for the date

  • In App.xaml, add the following ControlTemplate inside the ResourceDictionary node. This control template defines a square block that shows the date. By defining this in App.xaml, we can consume it in both ItemsPage.xaml and SplitPage.xaml. The markup for those pages will be added later in this tutorial.

    
    
    
            <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>
    
    
    

    Notice that this template defines parameters "day," "month," and "year" that are passed to the new Convert function that we created in the DateConverter earlier in part 2. We format the day, month and year as independent elements, each with a different font size, so we had to write new functionality in the Convert method to return the parts of the date separately.

Hh465045.wedge(en-us,WIN.10).gifTo add a data template for the items page

  1. In ItemsPage.xaml, we add these resources to define the look of the grid items in the default view. Notice that we apply the new styles that we defined earlier.

    
    
    <Page.Resources>
    
    ...
        
       <!-- 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>
    
    </Page.Resources>
    
    
  2. Also in ItemsPage.xaml, update the ItemTemplate property of the itemGridView to use our DefaultGridItemTemplate resource instead of Standard250x250ItemTemplate, which is the default template that's defined in StandardStyles.xaml.

    
    
            <GridView
                x:Name="itemGridView"
                AutomationProperties.AutomationId="ItemsGridView"
                AutomationProperties.Name="Items"
                TabIndex="1"
                Grid.Row="1"
                Margin="0,-4,0,0"
                Padding="116,0,116,46"
                SelectionMode="None"
                IsItemClickEnabled="True"
                ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
                ItemTemplate="{StaticResource DefaultGridItemTemplate}"
                ItemClick="ItemView_ItemClick"/>
    
    
    

Hh465045.wedge(en-us,WIN.10).gifTo add a data template for the Split page

  1. In SplitPage.xaml, add these resources to define the look of the list items in the default view. Note the ContentControl element that uses the DateBlockTemplate we defined earlier in this tutorial:

    
    
    <Page.Resources>
    				...
        <!-- green -->
        <SolidColorBrush x:Key="BlockBackgroundBrush" Color="#FF6BBD46"/>
    
        <DataTemplate x:Key="DefaultListItemTemplate">
            <Grid HorizontalAlignment="Stretch" Width="Auto" Height="110" 
             Margin="10,10,10,0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <!-- Green date block -->
                <Border Background="{StaticResource BlockBackgroundBrush}" 
                 Width="110" Height="110" />
                <ContentControl Template="{StaticResource DateBlockTemplate}" />
                <StackPanel Grid.Column="1"  HorizontalAlignment="Left" 
                 Margin="12,8,0,0">
                    <TextBlock Text="{Binding Title}" FontSize="26.667" 
                     TextWrapping="Wrap" MaxHeight="72" Foreground="#FFFE5815" />
                    <TextBlock Text="{Binding Author}" FontSize="18.667" />
                </StackPanel>
            </Grid>
        </DataTemplate>
        ...
    </Page.Resources>
    
    
  2. In SplitPage.xaml, we also update the ItemTemplate property in itemListView to use our DefaultListItemTemplate resource instead of Standard130ItemTemplate, which is the default template. Here's the updated XAML for itemListView.

    
    
    <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}"/>
    
    
    

With our styles applied, our app fits with the look and feel of the Windows Team Blogs website:

Styled collection page.

Styled split page.

Styled detail page.

By using styles and basing them on other styles, we can quickly define and apply different looks for our app. In the next section, we combine what we know about animations and styles to make our app fluidly adapt to various layouts and orientations while it's running.

Adapting to different layouts

Typically, an app is designed to be viewed full-screen in the landscape orientation. But a new Windows UI must adapt to different orientations and layouts. Specifically, it must support both landscape and portrait orientations. In the landscape orientation, it must support Full Screen, FilledOrNarrow, and Snapped layouts. We saw when we made our blog reader page from a blank template that it didn't look as good in the portrait orientation. In this section, we learn how we can make our app look good in any resolution or orientation.

When developing in Visual Studio, you can use the Simulator debugger to test layouts. You can use the debugger tool bar to debug with the Simulator when you press F5.

The Visual Studio templates include code that handles changes to the view state. This code is in the LayoutAwarePage.cs or LayoutAwarePage.vb file, and it maps our app state to visual states that are defined in our XAML. Because the page layout logic is provided for us, we only have to provide the views to be used for each of the page's visual states.

To use XAML to transition between different views, we use the VisualStateManger to define different VisualStates for our app. Here we have a VisualStateGroup that's defined in ItemsPage.xaml. This group has 4 VisualStates, which are named FullScreenLandscape, FilledOrNarrow, FullScreenPortrait, and Snapped. Different VisualStates from the same VisualStateGroup can't be used at the same time. Each VisualState has animations that tell the app what has to change from the baseline that's specified in the XAML for the UI.



...not for pasting!
<!--App Orientation States-->
  <VisualStateManager.VisualStateGroups>
     <VisualStateGroup>
        <VisualState x:Name="FullScreenLandscape" />
        <VisualState x:Name="FilledOrNarrow"> ... </VisualState>
        <VisualState x:Name="FullScreenPortrait"> ... </VisualState>
        <VisualState x:Name="Snapped"> ... </VisualState>
    </VisualStateGroup>
 </VisualStateManager.VisualStateGroups>

We use the FullScreenLandscape state when the app is full-screen in the landscape orientation. Because we designed our default UI for this view, no changes are required and this is just an empty VisualState.

We use the FilledOrNarrow state when the user has another app that's snapped to one side of the screen. In this case, the Items View page just moves over, and no changes are required. This also is just an empty VisualState.

We use the FullScreenPortrait state when our app is rotated from landscape to portrait orientation. In this visual state, we have two animations. One changes the style that's used for the Back button, and the other one changes the margin of itemGridView so that everything fits better on the screen. In the XAML for the page UI, both a GridView and a ListView are defined and bound to the data collection. By default, the GridView is shown, and the ListView is collapsed. In the Portrait state, we have three animations that collapse the GridView, show the ListView, and change the Style of the Back button to make it smaller.



...Not for pasting!
<!-- 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="Margin">
         <DiscreteObjectKeyFrame KeyTime="0" Value="100,0,90,60"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>


We use the Snapped state when the user has two apps that are showing and our app is the narrower of the two. In this state, our app is only 320 device-independent pixels (DIPs) wide, so more drastic changes are required. In the XAML for the Items page UI, both a GridView and a ListView are defined and bound to the data collection. By default, itemGridViewScroller is shown, and itemListViewScroller is collapsed. In the Snapped state, we have four animations that collapse itemListViewScroller, show itemListViewScroller, and change the Style of the Back button and page title to make them smaller.



...not for pasting!
<!--
    The Back button and title have different styles when they're snapped, and the list representation
    is substituted for the grid that's 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="itemListScrollViewer" 
         Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames 
         Storyboard.TargetName="itemGridScrollViewer"
         Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>

In the Creating a consistent look with styles section of this tutorial, we created styles and templates to give our app a custom look. The default landscape view uses these styles and templates. To keep the custom look in different views, we also have to create custom styles and templates for those views.

Hh465045.wedge(en-us,WIN.10).gifTo add a data template for the Items page Snapped view

  1. In ItemsPage.xaml, we created a data template for the grid items. We also have to provide a new data template for the list items that are shown in Snapped view. We name this template NarrowListItemTemplate and add it to the resources section of ItemsPage.xaml, just after the DefaultGridItemTemplate resource.

    
    <Page.Resources>
    ...
        <!-- 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>
    
    
  2. To make the ListView show our new data template, we update the ItemTemplate property of itemListView to use our NarrowListItemTemplate resource instead of Standard80ItemTemplate, which is the default template that's defined in StandardStyles.xaml. In ItemsPage.xaml, replace the itemListView with the following snippet:

    
    
    <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"
        IsItemClickEnabled="True"
        ItemClick="ItemView_ItemClick"/>
    
    
    

Hh465045.wedge(en-us,WIN.10).gifTo add a data template for Split page Snapped and FilledOrNarrow views

  1. In SplitPage.xaml, we create a ListView template that’s used in the FilledOrNarrow and Snapped views, and in the FullScreenLandscape view if the screen width is less than 1366 DIPs. We also name this template NarrowListItemTemplate and add it to the resources section of SplitPage.xaml, just after the DefaultListItemTemplate resource.

    
    <Page.Resources>
    ...
        <!-- Used in FilledOrNarrow 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>
    
    
  2. To use this data template, we update the visual states where it's used. In the XAML for the Snapped and FilledOrNarrow visual states, we find the animation that targets the ItemTemplate property of itemListView. We then change the value to use the NarrowListItemTemplate resource instead of the default Standard80ItemTemplate resource. Here's the updated XAML for the animation.

    
    
    <VisualState x:Name="FilledOrNarrow">
        <Storyboard>
        ....
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" 
              Storyboard.TargetProperty="ItemTemplate">
                <DiscreteObjectKeyFrame KeyTime="0" 
                 Value="{StaticResource NarrowListItemTemplate}"/>
            </ObjectAnimationUsingKeyFrames>
        ....
        </Storyboard>
    </VisualState>
    ...
    <VisualState x:Name="Snapped">
        <Storyboard>
        ....
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" 
             Storyboard.TargetProperty="ItemTemplate">
                <DiscreteObjectKeyFrame KeyTime="0" 
                 Value="{StaticResource NarrowListItemTemplate}"/>
            </ObjectAnimationUsingKeyFrames>
        ....
        </Storyboard>
    </VisualState>
    
    
  3. We also replaced the item detail section of the Split page with our own detail section that uses a WebView. Because we made this change, the animations in the Snapped_Detail visual state target elements that no longer exist. These will cause errors when we use this visual state, so we have to remove them. In SplitPage.xaml, we remove these animations from the Snapped_Detail visual state.

    
    
    <VisualState x:Name="Snapped_Detail">
        <Storyboard>
        ...
           <!-- REMOVE THESE ELEMENTS: -->
            <!--<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="itemSubtitle" 
                 Storyboard.TargetProperty="Style">
                    <DiscreteObjectKeyFrame KeyTime="0" 
                     Value="{StaticResource CaptionTextStyle}"/>
                </ObjectAnimationUsingKeyFrames>-->
        </Storyboard>
    </VisualState>
    
    

Hh465045.wedge(en-us,WIN.10).gifTo adjust the WebView margin in Snapped view

  • In DetailPage.xaml, we just have to adjust the margin of our WebView in the Snapped view to use all of the available space. In the XAML for the Snapped visual state, we add an animation to change the value of the Margin property on contentViewBorder, as shown here:

    
    
    <VisualState x:Name="Snapped">
        <Storyboard>
        ...
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="contentViewBorder" 
             Storyboard.TargetProperty="Margin">
                <DiscreteObjectKeyFrame KeyTime="0" Value="20,5,20,20"/>
            </ObjectAnimationUsingKeyFrames>
    
        </Storyboard>
    </VisualState>
    
    

Adding a splash screen and logo

The first impression our app makes on a user comes from the splash screen. The splash screen is shown when a user starts the app, and gives immediate feedback to the user while our app initializes its resources. It's dismissed when the first page of the app is ready to show.

The splash screen consists of a background color and an image that's 624 x 300 pixels. We set these values in the Package.appxmanifest file. You can open this file in the manifest editor. On the Application UI tab of the manifest editor, we set the path of our splash screen image and the background color. The project template provides a default blank image that's named SplashScreen.png. We replace this with our own splash screen image, which clearly identifies our app and immediately draws users into it. Here's the splash screen for our blog reader:

Splash screen image.

The basic splash screen works well for our blog reader, but you can also extend the splash by using the properties and methods of the SplashScreen class. You can use the SplashScreen class to get the coordinates of the splash screen and use them to place the first page of the app. And you can find out when the splash screen is dismissed so that you know it's time to start any content-entrance animations for the app.

What's next

In this article, we learned how to use built-in page templates from Visual Studio Express 2012 for Windows 8 to build a multi-page app, and how to navigate and pass data between the pages. We learned how to use styles and templates to make our app fit the personality of the Windows Team Blogs website. We also learned how to use theme animations, an app bar, and a splash screen to make our app fit the personality of Windows 8. Finally, we learned how to adapt our app to various layouts and orientations so that it always looks its best.

Our app is almost ready to submit to the Windows Store. For more info about how to submit an app to the Windows Store, see:

Related topics

Roadmap for Windows Store apps using C++
Develop Windows Store apps using Visual Studio 11

 

 

Build date: 3/11/2013

Did you find this helpful?
(1500 characters remaining)
© 2013 Microsoft. All rights reserved.