Introduction
One of the really great enhancements in ASP.NET was the introduction of master pages. They help developers to create a consistent layout for the pages in an application. Unfortunately there is no such concept in WPF and XAML. In the following sample, I would like to show a simple way to build a control in WPF similar to an ASP.NET master page.
Building Master Pages in WPF
My application consists of a Window, which contains a menu and a frame, and three Pages. These pages will be displayed in the frame when a user clicks on the menu.
When I add a new page to my project I can add controls to this page and arrange them the way I like. In my sample I use a Grid to arrange a title and a description on my new page.
<Page x:Class="MasterPages.Page.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page1"
>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource Title}" Grid.Column="0"
Grid.Row="0" Text="Page 1" />
<TextBlock Grid.Column="0" Grid.Row="1"
Text="This is the content of Page 1." />
</Grid>
</Page>
If I want to add a second page with a similar layout to my project without master pages, I would have to repeat the whole code for the grid to arrange title and description on my new page.
An easy way to encapsulate the grid layout is to build a user control for the general layout of a page. Therefore I add a new UserControl
to my project. In the codebehind file of my control, I add two dependency properties, one for the title and one for the page content:
public partial class Master : System.Windows.Controls.UserControl
{
public Master()
{
InitializeComponent();
}
public object Title
{
get
{
return (object)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
"Title",
typeof(object),
typeof(Master),
new UIPropertyMetadata());
public object PageContent
{
get
{
return (object)GetValue(PageContentProperty);
}
set
{
SetValue(PageContentProperty, value);
}
}
public static readonly DependencyProperty PageContentProperty =
DependencyProperty.Register(
"PageContent",
typeof(object),
typeof(Master),
new UIPropertyMetadata());
}
Title
and PageContent
act as a placeholder. In the XAML file of my user control, I can arrange these placeholders:
<UserControl x:Class="MasterPages.App_Master.Master"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:MasterPages.App_Master">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl
Grid.Column="0"
Grid.Row="0"
Style="{StaticResource Title}"
Margin="0 0 0 10"
Content="{Binding RelativeSource=
{RelativeSource Mode=FindAncestor,
AncestorType={x:Type m:Master}}, Path=Title}" />
<ContentControl
Grid.Column="0"
Grid.Row="1"
Content="{Binding RelativeSource=
{RelativeSource Mode=FindAncestor,
AncestorType={x:Type m:Master}}, Path=PageContent}" />
</Grid>
</UserControl>
Now I can use my user control to build pages with a title and a content area. Therefore I have to include the namespace of the user control in the page. Then I can add the user control to the page as the root element. Within the user control I can specify a Title
and a PageContent
element in XAML. Title
and PageContent
can contain any other WPF elements:
<Page x:Class="MasterPages.Page.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:MasterPages.App_Master"
Title="Page1">
<m:Master>
<m:Master.Title>
Page 1
</m:Master.Title>
<m:Master.PageContent>
This is the content of Page 1.
</m:Master.PageContent>
</m:Master>
</Page>
Page1 is now a lot easier to build. I do not have to remember for every page that I have to use a Grid with a margin of ten and other details. I simply can set the content for the properties Title
and PageContent
.
But it is not only possible to add text to the placeholder properties but also to add other WPF controls. In Page2 I add a StackPanel
with further controls to the PageContent
property:
<Page x:Class="MasterPages.Page.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:MasterPages.App_Master"
Title="Page2">
<m:Master>
<m:Master.Title>
Page 2
</m:Master.Title>
<m:Master.PageContent>
<StackPanel>
<TextBlock Text="Page 2 contains additional elements." />
<ListBox>
<ListBoxItem>Item 1</ListBoxItem>
<ListBoxItem>Item 2</ListBoxItem>
<ListBoxItem>Item 3</ListBoxItem>
</ListBox>
</StackPanel>
</m:Master.PageContent>
</m:Master>
</Page>