Create a WPF Outlook Bar

 

Introduction

One of the great things about WPF is that it separates a control’s behaviour from its presentation. You can take any control, and by changing a template and some styles, you can make it look completely different. In this tutorial, we will go through the principles of this, and make ourselves a control template that turns a TabControl into an Outlook bar straight from Office 2007. This is all pure XAML, no code required!

Background

A basic understanding of XAML and WPF is assumed, including knowledge of the different layout panels, Resources and Binding.

Starting Out

By creating our tab control:

<Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;

      xmlns:sys=”clr-namespace:System;assembly=mscorlib”

      xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221; >

  <TabControl Name=”outlook”>

    <TabItem Header=”Mail” IsSelected=”True”>

      <ListBox BorderThickness=”0″>

        <ListBoxItem>Your mail here.</ListBoxItem>

      </ListBox>

    </TabItem>

    <TabItem Header=”Calendar” />

    <TabItem Header=”Tasks” />

  </TabControl>

</Window>

We have is a basic TabControl.

 

A Basic Control Template

Control Templates are what make WPF so powerful. You can think of the control template as the entire “look” of a control. By replacing the default control template, we can completely change how it looks, while leaving the behaviour unchanged. Add one to our TabControl:

<Window.Resources>

  <ControlTemplate x:Key=”OutlookBar” TargetType=”{x:Type TabControl}”>

    <ControlTemplate.Resources>

      <SolidColorBrush x:Key=”BorderBrush” Color=”#6593CF” />

    </ControlTemplate.Resources>

    <Border BorderBrush=”{StaticResource BorderBrush}” BorderThickness=”1″

            SnapsToDevicePixels=”True” >

      <DockPanel>

        <StackPanel IsItemsHost=”True” DockPanel.Dock=”Bottom” />

        <ContentPresenter Content=”{TemplateBinding SelectedContent}” />

      </DockPanel>

    </Border>

  </ControlTemplate>

</Window.Resources>

Now we can wire it up to the TabControl by setting the Template property to our new Control Template:

<TabControl Name=”outlook” Template=”{StaticResource OutlookBar}”>

Now our TabControl has a new control template. If you look at it in Visual Studio design view, you will see the changes. It has already taken the shape of an Outlook Bar. Run it and try clicking on the buttons/tabs. The content pane will flick between the content on each of the tabs.

Two key aspects of this template:

<StackPanel IsItemsHost=”True” DockPanel.Dock=”Bottom” />

TabControl descends from ItemsControl. What this means is that, like a ListBox, Menu or TreeView, it displays a list of items in some way, in this case, TabItems. In order to get the ControlTemplate to render the list, we put a container panel in the template and set the IsItemsHost property to True. (I’ve used StackPanel, but it could just as easily be any other container).

<ContentPresenter Content=”{TemplateBinding SelectedContent}” />

We need to display the content of our selected tab in the content pane at the top of our Outlook Bar. Fortunately, there is a dependency property on the TabControl which allows us to do this – SelectedContent. To wire this up we using Template Binding. Template Binding wires the template to the properties of the object(s) the template is applied to. The syntax is just {TemplateBinding PropertyName}.

Adding Some Style

We now have the layout, however our Outlook bar looks like a TabControl. We need to make the tabs look like Outlook bar buttons with the appropriate fonts, backgrounds and highlighting. To do this, we need to use styles.

All a style does is set a group of properties on a control. They are designed so you can make a group of controls look or behave the same way, by just changing one property.

Now add some more resources that we can use for styling.

<ControlTemplate.Resources>

  <SolidColorBrush x:Key=”CaptionBrush” Color= “#15428B” />

  <SolidColorBrush x:Key=”BorderBrush” Color=”#6593CF” />

  <LinearGradientBrush x:Key=”LabelBrush” StartPoint=”0, 0″ EndPoint=”0,1″>

    <GradientStop Color=”#E3EFFF” Offset=”0″ />

    <GradientStop Color=”#AFD2FF” Offset=”1″ />

  </LinearGradientBrush>

</ControlTemplate.Resources>

 

Restyle our TabItems:

<Style TargetType=”{x:Type TabItem}”>

  <Setter Property=”Background” Value=”{StaticResource ButtonNormalBrush}” />

  <Setter Property=”Template”>

  <Setter.Value>

    <ControlTemplate TargetType=”{x:Type TabItem}”>

        <Grid Background=”{TemplateBinding Background}” MinHeight=”32″>

          <Line Stroke=”{StaticResource BorderBrush}” VerticalAlignment=”Top”

                Stretch=”Fill” X2=”1″ SnapsToDevicePixels=”True” />

          <ContentPresenter Margin=”5,0,5,0″ TextBlock.FontFamily=”Tahoma”

                TextBlock.FontSize=”8pt” TextBlock.FontWeight=”Bold”

                TextBlock.Foreground=”{StaticResource CaptionBrush}”

                Content=”{TemplateBinding Header}” VerticalAlignment=”Center”/>

        </Grid>

      </ControlTemplate>

    </Setter.Value>

  </Setter>

</Style>

For our style, we specify a TargetType of TabItem – this means it will affect all TabItems within the current scope.

 

 

 

Now the tab control will look something like this:

Our TabControl is starting to look like an Outlook bar. However, when we hover over a button in Outlook, the button will change colour to indicate it. We also need a way of highlighting which button is selected.

Pulling the Trigger

You can think of triggers as conditional styles.

<Trigger Property=”IsSelected” Value=”False”>

  <Setter Property=”TextElement.Foreground” Value=”{StaticResource CaptionBrush}” />

</Trigger>

As you can see, this trigger sets the text colour whenever the IsSelected property is set to false.

You can set triggers to depend on more than one condition, using a MultiTrigger:

<MultiTrigger>

  <MultiTrigger.Conditions>

    <Condition Property=”IsSelected” Value=”False” />

    <Condition Property=”IsMouseOver” Value=”False” />

  </MultiTrigger.Conditions>

  <MultiTrigger.Setters>

    <Setter Property=”Background” Value=”{StaticResource ButtonNormalBrush}” />

  </MultiTrigger.Setters>

</MultiTrigger>

The conditions all need to be satisfied for a trigger to take effect, so for the one above, if the button is not selected and the mouse is not hovering over it, then the background brush is set.

 

For our TabItem, we put our triggers inside the ControlTemplate.Triggers element:

<ControlTemplate TargetType=”{x:Type TabItem}”>

  <ControlTemplate.Triggers>

    <MultiTrigger>

      <MultiTrigger.Conditions>

        <Condition Property=”IsSelected” Value=”False” />

        <Condition Property=”IsMouseOver” Value=”False” />

      </MultiTrigger.Conditions>

      <MultiTrigger.Setters>

        <Setter Property=”Background” Value=”{StaticResource ButtonNormalBrush}” />

      </MultiTrigger.Setters>

    </MultiTrigger>

    <!– More Triggers here… –>

  </ControlTemplate.Triggers>

  <Grid Background=”{TemplateBinding Background}”

        MinHeight=”32″ SnapsToDevicePixels=”True”>

    <Line Stroke=”{StaticResource BorderBrush}”

        VerticalAlignment=”Top” Stretch=”Fill” X2=”1″ SnapsToDevicePixels=”True” />

    <ContentPresenter Margin=”5,0,5,0″ TextBlock.FontFamily=”Tahoma”

        TextBlock.FontSize=”8pt” TextBlock.FontWeight=”Bold”

        TextBlock.Foreground=”{StaticResource CaptionBrush}”

        Content=”{TemplateBinding Header}” VerticalAlignment=”Center”/>

  </Grid>

</ControlTemplate>

For clarity, I’ve not included all the triggers above.

All that’s left is to add in the label that sits at the top, which turns out to be very straightforward:

<Border BorderBrush=”{StaticResource BorderBrush}”

    BorderThickness=”1″ SnapsToDevicePixels=”True” >

  <DockPanel>

  <StackPanel DockPanel.Dock=”Bottom” IsItemsHost=”True” />

  <!– Top label –>

  <Grid DockPanel.Dock=”Top” MinHeight=”28″

        Background=”{StaticResource ButtonNormalBrush}” SnapsToDevicePixels=”True”>

    <TextBlock FontFamily=”Tahoma” Foreground=”{StaticResource CaptionBrush}”

        VerticalAlignment=”Center” Margin=”5,0″ FontSize=”18″ FontWeight=”Bold” />

    <Line Stroke=”{StaticResource BorderBrush}”

        VerticalAlignment=”Bottom” X2=”1″ Stretch=”Fill”/>

  </Grid>

  <ContentPresenter Content=”{TemplateBinding SelectedContent}” />

  </DockPanel>

</Border>

 

done!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: