Windows 8 XAML Tips - Master Pages

By Fons Sonnemans, posted on
3896 Views 5 Comments

Besides Windows Phone and Windows 8 apps I develop Web sites using ASP.NET. In ASP.NET Web Forms you can define a MasterPage. This technique is called Layout pages in ASP.NET MVC. A simular technique can also be used in your Windows Store apps using XAML. In this blog I will explain how.

Master Page and Content Pages

In the example above all pages have a red header and a blue footer.

Master pages allow you to create a consistent layout for the pages in your application. A single master page defines the look and feel and standard behavior that you want for all of the pages (or a group of pages) in your application. You can then create individual content pages that contain the content you want to display. When users request the content pages, they merge with the master page to produce output that combines the layout of the master page with the content from the content page. 

XAML MasterPage

I have created a Windows Store app using the 'Grid App (XAML)' template in Visual Studio. With this template you get a project in which 3 pages are predefined. These pages will be used as the content pages in my demo project.

Next I have added a Blank Page to my project named MasterPage.xaml. In this page I have added my header, footer and a Frame control.

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

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

        <!-- Header -->
        <Rectangle Fill="#FF0000"
                   Height="130"
                   VerticalAlignment="Top" />

        <!-- Footer -->
        <Rectangle Fill="#00B0F0"
                   Height="30"
                   VerticalAlignment="Bottom" />

        <TextBlock Text="© 2013 Reflection IT"
                   VerticalAlignment="Bottom"
                   HorizontalAlignment="Left"
                   Foreground="White"
                   Margin="120,5"
                   FontSize="16" />

        <!-- Frame -->
        <Frame x:Name="frameBody" />
    </Grid>
</Page>

In the codebehind (MasterPage.xaml.cs) I have added a public property named ContentFrame which returns the Frame control.

namespace MasterPageDemo {
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MasterPage : Page {
        public MasterPage() {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e) {
        }

        public Frame ContentFrame {
            get { return this.frameBody; }
        }
    }
}

Next I have modified the OnLaunched() method of the App.xaml.cs. In this method I have initialized the rootFrame with a MasterPage object instead of a Frame (line 9 and 18). Instead of using the Frame I now use the ContentFrame of the MasterPage for navigation (line 23, 39 and 45).

/// <summary>
/// Invoked when the application is launched normally by the end user.  Other entry points
/// will be used when the application is launched to open a specific file, to display
/// search results, and so forth.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override async void OnLaunched(LaunchActivatedEventArgs args) {
    //Frame rootFrame = Window.Current.Content as Frame;
    var rootFrame = Window.Current.Content as MasterPage;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active

    if (rootFrame == null) {
        // Create a Frame to act as the navigation context and navigate to the first page
                
        //rootFrame = new Frame();
        rootFrame = new MasterPage();
                
        //Associate the frame with a SuspensionManager key                                
                
        //SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
        SuspensionManager.RegisterFrame(rootFrame.ContentFrame, "AppFrame");

        if (args.PreviousExecutionState == ApplicationExecutionState.Terminated) {
            // Restore the saved session state only when appropriate
            try {
                await SuspensionManager.RestoreAsync();
            } catch (SuspensionManagerException) {
                //Something went wrong restoring state.
                //Assume there is no state and continue
            }
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }
    //if (rootFrame.Content == null)
    if (rootFrame.ContentFrame.Content == null) {
        // When the navigation stack isn't restored navigate to the first page,
        // configuring the new page by passing required information as a navigation
        // parameter
                
        //if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups")) {
        if (!rootFrame.ContentFrame.Navigate(typeof(GroupedItemsPage), "AllGroups")) {
            throw new Exception("Failed to create initial page");
        }
    }
    // Ensure the current window is active
    Window.Current.Activate();
}

Finally I have removed the Background property from the LayoutRootStyle in the StandardStyles.xaml. This will make it transparent which allows you to view the content of the MasterPage.

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

I did some minor appearance changes like setting the RequestedTheme property of the Application (App.xaml) to "Light". I also changed the Foreground property of the PageHeaderTextStyle (StandardStyles.xaml) to "White".

Result

The result is an application in which all 3 content pages have the same header and footer.

Closure and download

I hope you like my solution. You can download my code below.

Cheers,

Fons

Download

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.

Leave a comment

Blog comments

Mark Moeykens

15-Apr-2013 3:01
Thank you for this, Fons. I was looking for a solution to not have to duplicate my Settings Charm logic on every single page. I used your solution to have the master page contain all the settings charm logic. Great stuff!

Ryan Rife

26-Dec-2013 9:44
How do you get the "standard" back button to work in the master page?

Vasu Mahesh

07-Sep-2014 4:27
Hey, Great article! But I need help building a Base Page for Windows Phone 8.1 ... I'm Having difficulties when I tried using a similar approach as above. Could you please send me a link or certain article which does the same above but now for wp8.1 ?

Ammar

20-Sep-2014 10:14
It worked like a charm in my applicaiton. But now how do i change the Title of the master page from content pages.

Xlcnt

26-Nov-2014 3:03
Here my solution for the title : //Master.xaml.cs public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(MasterPage), new PropertyMetadata("")); // Page1.xaml.cs or BasePage.cs void BasePage_Loaded(object sender, RoutedEventArgs e) { var rootFrame = Window.Current.Content as MasterPage; rootFrame.Title = "My Title"; }