Page view counter

Silverlight Toolkit: HeaderedContentControl & HeaderedItemsControl

 

 

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.

image2_thumb

 

2. Add a reference to the Silverlight Controls assembly (Microsoft.Windows.Controls.dll) which can be downloaded at http://codeplex.com/Silverlight.

image10_thumb   image_thumb33[1]

 

3. Look under "Custom Controls" In the Blend Asset Library.

image13_thumb

 

4. Add a HeaderedContentControl to the Page.

 

 image

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.

image 

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

image

Next, Double Click our HeaderedContentControl (or edit the Content property, or even Right click and select “Edit Text”) and put in “Betsy, Age 32”.

image

Here’s the control display we’ll get:

image

 

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.

image

We’ll Add a MediaElement instead of the existing text.

image

And change it’s MediaElement.Source property to “http://www.silverlight.net/quickstarts/silverlight10/thebutterflyandthebear.wmv” and set MediaElement.AutoPlay to true.

image

Now if we run our sample we’ll see:

image

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:

image

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.

image

We’ll call our new ControlTemplate “CowTemplate”.

image

And this is what we’ll see:

image

 

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:

image

And next we’ll set the ContentControl’s Font size to 22 and Bold.

image

And this is what we got:

image

 

Next, we’ll add a Grey Border that has round corners around our StackPanel.

Right click on our StackPanel ––> Group Into  –> Broder.

image

We’ll set it’s BorderBrush to solid grey, BorderThickness to 2 and CornerRadius to 1.

image

 

And here’s how our GroupBox looks like:

image

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:

image

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:

     image 

 

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.

image

And we’ll call this new Template “CowHeaderTemplate”.

image

And here’s what we can see:

image

Our HeaderTemplate is empty (since we chose we want to create an empty template).

We’ll add a ContentControl.

image

And than Bind this ContentControl.Content to Name. We’ll do that by setting it’s Custom Expression to “{Binding Name"}”. 

image

 

 

Next, we’d like to edit our ContentTemplate to show “Cow, Age <Age>”.

Right click on HeaderedContentControl –> Edit Generated Content (ContentTemplate) –> Create Empty.

image

And we’ll call it CowContentTemplate.

image

Again, this DataTemplate is going to be empty as well.

image

We’ll add a TextBlock that says “Cow, Age “ and a ContentPresenter.

image

We’ll Bind the content with a Custom Expression to “{Binding Age}”.

image

 

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}”.

image  image

image  image 

 

Now, we’ll run our Application:

image

 

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

image

Right click on each HeaderedContentControl –> Edit HeaderTemplate –> Apply Resource –> CowHeaderTemplateimage

Right click on each HeaderedContentControl –> Edit Generated Content (ContentTemplate)–> Apply Resource –> CowContentTemplate

image

Next we’ll need to set their Header and Content properties to “{Binding}”.

image

image

 

Now we we run we’ll see that all HeaderedContentControls have the same templates:

image 

 

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)

image

Let’s add a HeaderedItemsControl to our page.

image

 

Let’s name it “Cows” and Set it’s Header property to “Cows”.

image

image

 

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:

image

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

image

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:

image

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.

image

We’ll name it “CowItemTemplate”.

image

And here’s what we’ll see:

image

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}”.

image

We’ll add another TextBlock with the Text “, Age ”.

image

And another TextBlock with TextBlock.Text bound to Age.

image

 

 

If we run our example now we can see:

image

 

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:

image

 

First step is to edit the Template of our HeaderedItemsControl.

image

We’ll call this new ControlTemplate “CowItemsTemplate”.

image

And here’s what we would see:

image

 

First, we’d like to make give the HeaderContent a font size of 22 and set it to bold.

image

Next, we’d like to have a Left Margin of 10 for our Items.

image

We’ll “group into“ our Grid to a Border.

image

Set the Border’s BorderBrush to light silver, BorderThinckness to 2 and CornerRadius to 2.

image

One last thing I’d like us to do here is set Background with a light silver gradient.

First, we’ll select Background.

image

Next, we’ll select Gradient Brush.

image

 

We’ll move our last Gradient stop from the End, to about 10% near the beginning.

 

image

We’ll select the last Gradient stop and change it’s Color to “#FFB5B5B5” (light gray).

image

 

Now, let’s run our sample.

image

 

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

 

(Acknowledgment: Shawn Oster Rules! He has the best Midwestern cow names ever!)

Published Tuesday, November 11, 2008 6:08 PM by JustinAngel
Filed under:

Comments

# Silverlight Cream for November 12, 2008 -- #426

In this issue: Silverlight Girl, Mehdi Slaoui Andaloussi, David Anson, Cheryl, Justin Angel, Kathy Kam

Thursday, November 13, 2008 1:16 AM by Community Blogs

# 2008 November 13 - Links for today &laquo; My (almost) Daily Links

Pingback from  2008 November 13 - Links for today &laquo; My (almost) Daily Links

Thursday, November 13, 2008 4:25 AM by 2008 November 13 - Links for today « My (almost) Daily Links

# re: Silverlight Toolkit: HeaderedContentControl & HeaderedItemsControl

Hi Justin,

You have put together a number of very good Silverlight Toolkit tutorials - your efforts are very helpful and very appreciated.

Thank you,

David Roh

JK@SilverlightAzure.com

Monday, November 17, 2008 7:01 AM by davidjjon77