Hi Folks,
It’s very common to find ourselves creating a UI that is composed of a “Header” part and “something else”.
In order to better encapsulate that logic and allow more flexability when reusing it, WPF and the Silverlight Toolkit (codeplex.com/Silverlight) introduce two classes: HeaderedContentControl and HeaderedItemsControl.
Let’s deep dive into these controls and see how we can use them to create a more streamlined UI.
Setup
1. Create a new project.
2. Add a reference to the Silverlight Controls assembly (Microsoft.Windows.Controls.dll) which can be downloaded at http://codeplex.com/Silverlight.
3. Look under "Custom Controls" In the Blend Asset Library.
4. Add a HeaderedContentControl to the Page.
And here's the XAML we got:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SilverlightControlsNovember2008.HeaderedPage"
d:DesignWidth="640" d:DesignHeight="480"
xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls">
<Grid Background="#FFFFFFFF">
<controls:HeaderedContentControl HorizontalAlignment="Left" VerticalAlignment="Top" Content="HeaderedContentControl"/>
</Grid>
</UserControl>
Setting Header & Content Properties
HeaderedContentControl is a very straight-forward control. It’s a Control with two Content areas: Header & Content.
If we don't use the Header property of a HeaderedContentControl – we’re better off using a ContentControl.
So let’s set the Header & Content properties.
Select our HeaderedContentControl and set it’s Header property to “Cow”.
Next, Double Click our HeaderedContentControl (or edit the Content property, or even Right click and select “Edit Text”) and put in “Betsy, Age 32”.
Here’s the control display we’ll get:
here’s the XAML Blend created for this HeaderedContentControl:
<controls:HeaderedContentControl HorizontalAlignment="Left" VerticalAlignment="Top" Content="Betsy, Age 32" Header="Cow"/>
As a side-note, we could have also put-in more advanced controls into both Header & Content. Expression blend will only support doing that for the Content property, which we’ll change to a short video.
Let’s go back to editing our Content.
We’ll Add a MediaElement instead of the existing text.
And change it’s MediaElement.Source property to “http://www.silverlight.net/quickstarts/silverlight10/thebutterflyandthebear.wmv” and set MediaElement.AutoPlay to true.
Now if we run our sample we’ll see:
While a bear isn’t a cow, it’s close enough for us.
Here’s the XAML Blend generated for us:
<controls:HeaderedContentControl HorizontalAlignment="Left" VerticalAlignment="Top" Margin="32,32,0,0" Header="Cow">
<MediaElement Height="119" Width="216" AutoPlay="True" Source="http://www.silverlight.net/quickstarts/silverlight10/thebutterflyandthebear.wmv"/>
</controls:HeaderedContentControl>
If we manually edit the XAML, we could have also used the Header property in the same way and it would look like this:
<controls:HeaderedContentControl HorizontalAlignment="Left" VerticalAlignment="Top" Margin="32,32,0,0" Content="Betsy, Age 32">
<controls:HeaderedContentControl.Header>
<MediaElement Height="119" Width="216" AutoPlay="True" Source="http://www.silverlight.net/quickstarts/silverlight10/thebutterflyandthebear.wmv"/>
</controls:HeaderedContentControl.Header>
</controls:HeaderedContentControl>
The main takeaway from this exercise should be – Header & Content can contain any XAML content.
Styling the Content & Header by setting Template
Let’s try to create a basic GroupBox using our HeaderedContentControl. Something that looks like this:
A good first step for us is changing the Header font from Inside the control Template. (we’ll edit the Template directly because we can’t edit the Header property from Expression Blend)
Right click on our HeaderContentControl –> Edit Controls Parts (Template) –> Edit A copy.
We’ll call our new ControlTemplate “CowTemplate”.
And this is what we’ll see:
Now we’d like to set the first ContentPresenter’s (which is the Header) Font Size to 22.
However, ContentPresenter doesn’t have a FozeSize property, so we’ll wrap it a ContentControl which would allow us to set it’s FontSize.
So, first we’ve changed the Control Hierachy to:
And next we’ll set the ContentControl’s Font size to 22 and Bold.
And this is what we got:
Next, we’ll add a Grey Border that has round corners around our StackPanel.
Right click on our StackPanel ––> Group Into –> Broder.
We’ll set it’s BorderBrush to solid grey, BorderThickness to 2 and CornerRadius to 1.
And here’s how our GroupBox looks like:
here’s the XAML blend created for our HeaderedContentControl:
<controls:HeaderedContentControl HorizontalAlignment="Left" VerticalAlignment="Top"Header="Cow" Content="Betsy, Age 32" Template="{StaticResource CowTemplate}"/>
And for our new DataTemplate:
<UserControl.Resources>
<ControlTemplate x:Key="CowTemplate" TargetType="controls:HeaderedContentControl">
<Border BorderBrush="#FFC8C8C8" BorderThickness="2,2,2,2" CornerRadius="1,1,1,1">
<StackPanel>
<ContentControl FontSize="22" FontWeight="Bold" FontFamily="Portable User Interface">
<ContentPresenter Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" Cursor="{TemplateBinding Cursor}" />
</ContentControl>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Cursor="{TemplateBinding Cursor}" Margin="{TemplateBinding Padding}" VerticalAlignment="Bottom" />
</StackPanel>
</Border>
</ControlTemplate>
</UserControl.Resources>
Binding the Content & Header by binding in ContentTemplate & HeaderTemplate
Let’s say we have this Cow class:
public class Cow
{
public Cow(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; set; }
public int Age { get; set; }
}
This class is pretty straight forward – one numeric property for Age and one string property for Name.
And we have this form:

We’d like to Bind 4 Cows to each of these HeaderedIContentControls.
void HeaderedPage_Loaded(object sender, RoutedEventArgs e)
{
hdrCow1.DataContext = new Cow("Betsy", 3);
hdrCow2.DataContext = new Cow("Martha", 1);
hdrCow3.DataContext = new Cow("Flossy", 5);
hdrCow4.DataContext = new Cow("Hoss", 2);
}
Eventually we want the page to look like this:
We’ll start off by editing the HeaderTemplate of each HeaderedContentControls and Binding it to Name.
Right Click on HeaderedContentControl –> Edit Other Templates –> Edit HeaderTemplate –> Create empty.
And we’ll call this new Template “CowHeaderTemplate”.
And here’s what we can see:
Our HeaderTemplate is empty (since we chose we want to create an empty template).
We’ll add a ContentControl.
And than Bind this ContentControl.Content to Name. We’ll do that by setting it’s Custom Expression to “{Binding Name"}”.
Next, we’d like to edit our ContentTemplate to show “Cow, Age <Age>”.
Right click on HeaderedContentControl –> Edit Generated Content (ContentTemplate) –> Create Empty.
And we’ll call it CowContentTemplate.
Again, this DataTemplate is going to be empty as well.
We’ll add a TextBlock that says “Cow, Age “ and a ContentPresenter.
We’ll Bind the content with a Custom Expression to “{Binding Age}”.
Additionally, We’ll need to set the Header and Content to pass the DataContext straight to their respective template.
We’ll do that by setting both Header and Contentb properties to “{Binding}”.
Now, we’ll run our Application:
We need to set all templates on all HeaderedContentControls exactly like the templates on the 1st HeaderedContentControl.
Right click on each HeaderedContentControl –> Edit Control Parts (Templates) –> Apply Resource –> Cow Template
Right click on each HeaderedContentControl –> Edit HeaderTemplate –> Apply Resource –> CowHeaderTemplate
Right click on each HeaderedContentControl –> Edit Generated Content (ContentTemplate)–> Apply Resource –> CowContentTemplate
Next we’ll need to set their Header and Content properties to “{Binding}”.


Now we we run we’ll see that all HeaderedContentControls have the same templates:
Here’s the XAML blend generated for us:
<controls:HeaderedContentControl x:Name="hdrCow1" Template="{StaticResource CowTemplate}" HeaderTemplate="{StaticResource CowHeaderTemplate}" ContentTemplate="{StaticResource CowContentTemplate}" Header="{Binding}" Content="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" />
<controls:HeaderedContentControl x:Name="hdrCow2" Template="{StaticResource CowTemplate}" HeaderTemplate="{StaticResource CowHeaderTemplate}" ContentTemplate="{StaticResource CowContentTemplate}" Header="{Binding}" Content="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" />
<controls:HeaderedContentControl x:Name="hdrCow3" Template="{StaticResource CowTemplate}" HeaderTemplate="{StaticResource CowHeaderTemplate}" ContentTemplate="{StaticResource CowContentTemplate}" Header="{Binding}" Content="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" />
<controls:HeaderedContentControl x:Name="hdrCow4" Template="{StaticResource CowTemplate}" HeaderTemplate="{StaticResource CowHeaderTemplate}" ContentTemplate="{StaticResource CowContentTemplate}" Header="{Binding}" Content="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" />
here’s the content for our templates:
<UserControl.Resources>
<ControlTemplate x:Key="CowTemplate" TargetType="controls:HeaderedContentControl">
<Border BorderBrush="#FFC8C8C8" BorderThickness="2,2,2,2" CornerRadius="1,1,1,1">
<StackPanel>
<ContentControl FontSize="22" FontWeight="Bold" FontFamily="Portable User Interface">
<ContentPresenter Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" Cursor="{TemplateBinding Cursor}" />
</ContentControl>
<ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Cursor="{TemplateBinding Cursor}" Margin="{TemplateBinding Padding}" VerticalAlignment="Bottom" d:LayoutOverrides="Width" />
</StackPanel>
</Border>
</ControlTemplate>
<DataTemplate x:Key="CowHeaderTemplate">
<Grid>
<ContentControl HorizontalAlignment="Left" VerticalAlignment="Top" Content="{Binding Path=Name}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="CowContentTemplate">
<Grid>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text="Cow, Age " TextWrapping="Wrap"/>
<ContentControl HorizontalAlignment="Left" VerticalAlignment="Top" Content="{Binding Path=Age}" Margin="60,0,0,0"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
And our code-behind:
void HeaderedPage_Loaded(object sender, RoutedEventArgs e)
{
hdrCow1.DataContext = new Cow("Betsy", 3);
hdrCow2.DataContext = new Cow("Martha", 1);
hdrCow3.DataContext = new Cow("Flossy", 5);
hdrCow4.DataContext = new Cow("Hoss", 2);
}
Binding a list of strings to a HeaderedItemsControl
There’s a special case of HeaderedContentControl – HeaderedItemsControl.
In concept – it’s basically a a HeaderedContentControl with a list of items inside of it.
(off-beat comment: API-wise though, it’s an ItemsControl that happens to have a Header)
Let’s add a HeaderedItemsControl to our page.
Let’s name it “Cows” and Set it’s Header property to “Cows”.
From our code-behind we’ll set the ItemsSource to a collection of strings.
private void HeaderedItemsPage_Loaded(object sender, RoutedEventArgs e)
{
Cows.ItemsSource = new string[] { "Betsy", "Martha", "Flossy", "Hoss" };
}
And if we’ll run our application we can see this:
“Cows” being the Header for our HeaderedItemsControl.
Here’s the XAML blend generated for us:
<controls:HeaderedItemsControl x:Name="Cows" Header="Cows"/>
Binding custom CLR types to a HeaderedItemsControl
We’d like to Bind our HeaderedItemsControl to a collection of Cows and have it wind up looking like this:
from our code-behind we’ll set the ItemsSource to a collection of cows.
void HeaderedItemsPage_Loaded(object sender, RoutedEventArgs e)
{
Cows.ItemsSource = new Cow[]
{
new Cow("Betsy", 3), new Cow("Martha", 1),
new Cow("Flossy", 5), new Cow("Hoss", 2)
};
}
And if we run our sample we’ll see:
Not very impressive is it?
We’d should edit the template for each of our items.
Right click on our HeaderedItemsControl –> Edit Other Templated –> Edit Generated Items (ItemTemplate) –> Create Empty.
We’ll name it “CowItemTemplate”.
And here’s what we’ll see:
We can see that our DataTemplate is empty. So no we need to Add a TextBlock for the name of the Cow.
We’ll set the TextBlock.Text to be databound to Name. We’ll do that by setting it’s value to a Custom Expression of “{Binding Name}”.
We’ll add another TextBlock with the Text “, Age ”.
And another TextBlock with TextBlock.Text bound to Age.
If we run our example now we can see:
Here’s the XAML Blend generated for our HeaderedItemsControl:
<controls:HeaderedItemsControl x:Name="Cows" Header="Cows" ItemTemplate="{StaticResource CowItemTemplate}"/>
here’s our DataTemplate XAML:
<UserControl.Resources>
<DataTemplate x:Key="CowItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock TextWrapping="Wrap" Text="{Binding Path=Name}"/>
<TextBlock Text=", Age " TextWrapping="Wrap"/>
<TextBlock Text="{Binding Path=Age}" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
And our code-behind:
Cows.ItemsSource = new Cow[]
{
new Cow("Betsy", 3), new Cow("Martha", 1),
new Cow("Flossy", 5), new Cow("Hoss", 2)
};
Styling the HeaderedItemsControl
As you might have noticed our HeaderedItemsControl doesn’t achieve it’s full visual potential right now. (managerial speak for: looks fugly)
Let’s try and make it look a little bit more like this:
First step is to edit the Template of our HeaderedItemsControl.
We’ll call this new ControlTemplate “CowItemsTemplate”.
And here’s what we would see:
First, we’d like to make give the HeaderContent a font size of 22 and set it to bold.
Next, we’d like to have a Left Margin of 10 for our Items.
We’ll “group into“ our Grid to a Border.
Set the Border’s BorderBrush to light silver, BorderThinckness to 2 and CornerRadius to 2.
One last thing I’d like us to do here is set Background with a light silver gradient.
First, we’ll select Background.
Next, we’ll select Gradient Brush.
We’ll move our last Gradient stop from the End, to about 10% near the beginning.
We’ll select the last Gradient stop and change it’s Color to “#FFB5B5B5” (light gray).
Now, let’s run our sample.
Here’s the XAML Blend generated for our HeaderedItemsControl:
<controls:HeaderedItemsControl VerticalAlignment="Top" Height="Auto" x:Name="Cows" Header="Cows" ItemTemplate="{StaticResource CowItemTemplate}" Template="{StaticResource CowItemsTemplate}" />
And the DataTemplate that is our ItemsTemplate:
<ControlTemplate x:Key="CowItemsTemplate" TargetType="controls:HeaderedItemsControl">
<Border Height="Auto" Width="189" BorderBrush="#FFE0E0E0" BorderThickness="2,2,2,2" CornerRadius="2,2,2,2">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFB5B5B5"/>
<GradientStop Color="#FFFFFFFF" Offset="0.253"/>
</LinearGradientBrush>
</Border.Background>
<Grid x:Name="Root" Height="Auto" Width="189">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl x:Name="HeaderContent" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" FontSize="22" FontWeight="Bold" FontFamily="Portable User Interface" />
<ItemsPresenter x:Name="Items" Grid.Row="1" Margin="10,0,0,0" />
</Grid>
</Border>
</ControlTemplate>
And our code-behind:
void HeaderedItemsPage_Loaded(object sender, RoutedEventArgs e)
{
Cows.ItemsSource = new Cow[]
{
new Cow("Betsy", 3), new Cow("Martha", 1),
new Cow("Flossy", 5), new Cow("Hoss", 2)
};
}
public class Cow
{
public Cow(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; set; }
public int Age { get; set; }
}
-- Justin Angel
Microsoft SIlverlight Program Manager