Click here to Skip to main content
Click here to Skip to main content

WPF OutlookBar Control

, 11 Oct 2008
Rate this:
Please Sign up or sign in to vote.
A WPF OutlookBar implementation.

Introduction

The Outlookbar is the third control included in Odyssey.dll, a WPF control library for free that is currently under development.

Background

Articles about the other two controls, the BreadcrumbBar and the ExplorerBar, with animated collapsible panels are already available here on CodeProject (see the History section for links).

Using the code

The Outlookbar is the third control included in Odyssey.dll, a WPF control library for free that is currently under development. Articles about the other two controls, the BreadcrumbBar and the ExplorerBar with animated collapsible panels are already available here on CodeProject.

The OutlookBar is - as the name already indicates - a sidebar control that looks like the bar in Outlook. The OutlookBar is a container for OutlookSections to show each section as a button and the content of the selected section. The OutlookBar has a maximized and a minimized mode. While minimized, the content of the selected section is available as a popup; otherwise, it's always visible above the section buttons. The section buttons are either shown as a maximized button with image and content aligned vertically, or as a minimized button with the image only aligned horizontally. A splitter allows to customize the number of visible maximized buttons. It's also possible to use a splitter to resize the width of the sidebar and minimize or maximize it that way. Furthermore, you can specify the docking behaviour of the OutlookBar to be aligned either to the left or to the right; hence, the content popups either to the right or to the left, as well as it specifies the position of the width splitter. It's also possible to hide the section buttons; for instance, when there is only one section on it, like the To-Do Bar in Outlook at the right side.

The content of an Outlook Section can be any arbitrary control, but OdcExpanders (which was previously introduced) are preferred, and it will have a customized skin inside the OutlookBar.

In minimized mode, the OutlookBar can optionally offer additional buttons.

The OutlookBar has three possible skins: blue, silver, and black:

Interaction between template and code

Various dependency properties enable interaction with the template and the code. Usually, the XAML gets the information from the code using TemplateBinding. However, in some cases, the template needs to update the value of a dependency property. Therefore, it uses two-way binding, as in the following snippet:

<ToggleButton Style="{StaticResource buttonInSection}" Width="18" 
      IsChecked="{Binding IsOverflowVisible, 
      RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" >
     <ToggleButton.Content>
        <Path VerticalAlignment="Center" HorizontalAlignment="Center"
           Fill="{DynamicResource ImageBrush}" 
           Stroke="White" Data="M2,4 L5,7 8,4"/>
     </ToggleButton.Content>
     <ToggleButton.ContextMenu>
       <ContextMenu IsOpen="{Binding IsOverflowVisible, 
        RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
          ItemsSource="{TemplateBinding OverflowMenuItems}">
       </ContextMenu>
     </ToggleButton.ContextMenu>
</ToggleButton>

<Style TargetType="{x:Type ButtonBase }" x:Key="buttonStyle">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border Background="{TemplateBinding Background}"
                        Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}"
                        Focusable="False"
                        x:Name="border" 
                        BorderThickness="{TemplateBinding BorderThickness}" 
                        BorderBrush="{DynamicResource BorderBrush}" >
                    <ContentPresenter Focusable="False" 
                                Content="{TemplateBinding Content}"  
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                VerticalAlignment="Center" Horizontal
                                Alignment="Center"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" 
                            Value="{DynamicResource HighlightButtonGradientBrush}" 
                            TargetName="border"/>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" 
                            Value="{DynamicResource SelectedButtonGradientBrush}" 
                            TargetName="border"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Here, the IsChecked property of the ToggleButton gets and also sets the IsOverflowVisible property of the OutlookBar. When the users clicks on the button, the property will be changed. So, there is no need to implement an EventHandler of the Click event.

In other cases, when a button click has to cause an action, for instance, when a splitter button is pressed, it will raise a command:

<Button x:Name="resizeButton" Width="4" DockPanel.Dock="Right" 
     Visibility="{Binding CanResize,RelativeSource={RelativeSource TemplatedParent},
        Converter={StaticResource visibleConverter}}"
     Cursor="SizeWE"
     ClickMode="Press" Command="local:OutlookBar.ResizeCommand">
     <Button.Template>
        <ControlTemplate>
          <Border Background="Transparent"/>
        </ControlTemplate>
     </Button.Template>
</Button>

The command itself is defined as follows:

/// <summary>
/// Start to resize the Width of the OutlookBar 
/// (used for the xaml template to initiate resizing).
/// </summary>
public static RoutedUICommand ResizeCommand
{
    get { return resizeCommand; }
}
private static RoutedUICommand resizeCommand = 
        new RoutedUICommand("Resize", 
        "ResizeCommand", typeof(OutlookBar));

At the initializer, a method is associated to this command:

CommandBindings.Add(new CommandBinding(ResizeCommand, ResizeCommandExecuted));

Therefore, when the Resize button is clicked, it sends the ResizeCommand to the Outlookbar, which then will call the ResizeCommandExecuted method.

A third way to interact is to use a specific control that is defined in the XAML in the OnApplyTemplate method:

public override void OnApplyTemplate()
{
    minimizedButtonContainer = 
        this.GetTemplateChild(partMinimizedButtonContainer) 
        as FrameworkElement;
    base.OnApplyTemplate();
}

Although it works with any name, it is recommended that you use a name that begins with "PART_", and the name should be registered with the TemplatePart attribute as follows:

[TemplatePart(Name = partMinimizedButtonContainer)]
public class OutlookBar : HeaderedItemsControl
{
    const string partMinimizedButtonContainer = "PART_MinimizedContainer";

Note that although TemplatePart is not recognized in WPF, it is useful for external editors such as Blend.

Skins instead of themes, and how it works

Unlike the ExplorerBar that uses themes, OutlookBar uses skins that do not depend on the selected theme of XP or Vista. Therefore, the templates of the controls use DynamicResources instead of StaticResources to specify properties that depend on the skin, such as color brushes:

<Style TargetType="{x:Type ButtonBase }" x:Key="buttonStyle">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border Background="{TemplateBinding Background}"
                        Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}"
                        Focusable="False"
                        x:Name="border"  
                        BorderThickness="{TemplateBinding BorderThickness}" 
                        BorderBrush="{DynamicResource BorderBrush}" >
                    <ContentPresenter Focusable="False" 
                               Content="{TemplateBinding Content}"  
                               ContentTemplate="{TemplateBinding ContentTemplate}"
                               VerticalAlignment="Center" 
                               HorizontalAlignment="Center"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" 
                            Value="{DynamicResource HighlightButtonGradientBrush}" 
                            TargetName="border"/>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" 
                            Value="{DynamicResource SelectedButtonGradientBrush}" 
                            TargetName="border"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The DynamicResources is applied to the ResourceDictionary of the OutlookBar at runtime, because other than a StaticResource, DynamicResources is not detected when it is specified as a resource in the same XAML with the template, so ApplySkin is always necessary when the OutlookBar is initialized:

protected void ApplySkin()
{
    string skinName;
    switch (Skin)
    {
       case OdysseySkin.OutllookBlue: skinName = "OutlookBlueSkin"; break;
       case OdysseySkin.OutlookSilver: skinName = "OutlookSilverSkin"; break;
       case OdysseySkin.OutlookBlack: skinName = "OutlookBlackSkin"; break;
       default: skinName = "OutlookBlueSkin"; break;
    }
    if (!string.IsNullOrEmpty(skinName))
    {
       skinName = string.Format
        ("pack://application:,,,/Odyssey;Component/Skins/OutlookBar/{0}.xaml",
                                skinName);
       Uri uri = new Uri(skinName, UriKind.Absolute);
       ResourceDictionary skin = new ResourceDictionary();
       skin.Source = uri;
       this.Resources = skin;
    }
}

A skin is a ResourceDictionary applied to the binary as resource, that looks like this:

<ResourceDictionary xmlns=
    "xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush Color="#FFC7CBD1" x:Key="BorderBrush"/>
    <SolidColorBrush Color="#FF313431" x:Key="ImageBrush"/>
    <SolidColorBrush Color="#FFD8DBDF" x:Key="LightBackgroundBrush"/>
    <SolidColorBrush Color="#FF000000" x:Key="ForegroundBrush"/>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0" 
              x:Key="HighlightedExpanderHeaderBrush">
        <LinearGradientBrush.GradientStops>
            <GradientStop Color="#FFDADFE6" Offset="0"/>
            <GradientStop Color="White" Offset="0.5"/>
            <GradientStop Color="#FFDADFE6" Offset="1"/>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" 
                x:Key="OutlookbarHeaderBrush">
        <LinearGradientBrush.GradientStops>
            <GradientStop Color="#FFF0F1F2" Offset="0"/>
            <GradientStop Color="#FFBDC1C8" Offset="1"/>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" 
                x:Key="SectionButtonGradientBrush">
        <LinearGradientBrush.GradientStops>
            <GradientStop Color="#FFF8F8F9" Offset="0"/>
            <GradientStop Color="#FFDFE2E4" Offset="0.4"/>
            <GradientStop Color="#FFC7CBD1" Offset="0.4"/>
            <GradientStop Color="#FFDBDEE2" Offset="1"/>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
    <SolidColorBrush Color="#FFFFAB3F" x:Key="SelectedButtonSolidBrush"/>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" 
                x:Key="SelectedButtonGradientBrush">
        <LinearGradientBrush.GradientStops>
            <GradientStop Color="#FFFFFEE4" Offset="0"/>
            <GradientStop Color="#FFFFBB6E" Offset="0.4"/>
            <GradientStop Color="#FFFFAB3F" Offset="0.4"/>
            <GradientStop Color="#FFFEE17A"  Offset="1"/>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
    <SolidColorBrush Color="#FFFFD76A" x:Key="HighlightButtonSolidBrush"/>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" 
                x:Key="HighlightButtonGradientBrush">
        <LinearGradientBrush.GradientStops>
            <GradientStop Color="#FFFFFCDE" Offset="0"/>
            <GradientStop Color="#FFFFEAAD" Offset="0.4"/>
            <GradientStop Color="#FFFFD76A" Offset="0.4"/>
            <GradientStop Color="#FFFFE69E"  Offset="1"/>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
</ResourceDictionary> 

More details

If you're interested in more details, e.g., how the manual splitting works, or how the section buttons are separated between the maximized and minimized, you are free to look at the code to figure out how it works. I won't go into details, as it is hard to explain but easy to understand when you just see the code available here.

Where can I get updates?

Odyssey is an open source project available on CodePlex as source code.

What comes next?

The next control that I will possibly add to Odyssey is a RibbonBar.

History

See also:

for the other articles about the BreadcrumbBar and the ExplorerBar that are also included in the odyssey.dll (and which have been improved since the article was written, by the way...)

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Thomas Gerber
Software Developer (Senior)
Germany Germany
MCPD
Enterprise Application Developer 3.5
Windows Developer 3.5
.ASP.NET Developer 3.5
.NET 2.0 Windows Developer
.NET 2.0 Web Developer
.NET 2.0 Enterprise Application Developer
 

MCTS
.NET 3.5 Windows Forms Applications
.NET 3.5 ASP.NET Applications
.NET 3.5, ADO.NET Application Development
.NET 3.5 WCF
.NET 3.5 WPF
.NET 3.5 WF
Microsoft SQL Server 2008, Database Development
.NET 2.0 Windows Applications
.NET 2.0 Web Applications
.NET 2.0 Distributed Applications
SQL Server 2005
Sharepoint Services 3.0 Application Development
Windows Vista Client Configuration

Comments and Discussions

 
QuestionHow can I change the height of the selected outlookbar.section's header and the height of odcExpander's header PinmemberShally Liu17-Dec-13 16:15 
QuestionYou coped this demo some where PinmemberMember 141681629-Jun-13 22:44 
AnswerRe: You coped this demo some where PinmemberThomas Gerber30-Jun-13 3:15 
QuestionPrism PinmemberMico Perez20-Mar-13 0:53 
Questionyour source code Pinmembersoheaila21-Nov-11 4:06 
AnswerRe: your source code PinmemberJaredThirsk7-Jan-13 20:45 
QuestionPLEASE source code Pinmembersoheaila20-Nov-11 0:42 
GeneralMy vote of 5 Pinmemberdipal_bhavsar19-Dec-10 23:02 
GeneralCreate my own skin PinmemberHenrique C6-Jul-10 1:02 
GeneralRe: Create my own skin PinmemberMarrowmaw13-Apr-11 20:13 
GeneralMessage Automatically Removed PinmemberMember 31108198-Mar-10 16:09 
GeneralGreat!!! Pinmembericetea945-Feb-10 4:45 
QuestionBinding? PinmemberKen Hadden21-Dec-09 13:24 
GeneralMissing Separator Pinmemberpoornimaraju6-Dec-09 20:37 
QuestionCould you please upload you demo source code here? Pinmemberjianecho25-Aug-09 8:44 
QuestionCan I integrate outlookBar into ms-outlook 2007 ? Pinmembergorfi20-Jul-09 8:56 
AnswerRe: Can I integrate outlookBar into ms-outlook 2007 ? PinmemberIOIOOIIIOOI3-Sep-09 2:48 
GeneralOutlookBar Expander issue Pinmemberrobertofrassa25-Mar-09 22:05 
GeneralRe: OutlookBar Expander issue PinmemberThomas Gerber8-May-09 12:52 
GeneralRe: OutlookBar Expander issue Pinmemberrobertofrassa3-Aug-09 21:56 
GeneralPrism(CAL) adapter Pinmembersamael_16-Mar-09 5:07 
GeneralRe: Prism(CAL) adapter PinmemberKumarManoj3-Aug-09 3:38 
GeneralOutlookbar content sizing PinmemberMember 123153911-Feb-09 10:23 
GeneralRe: Outlookbar content sizing Pinmemberdarwin6411-Feb-09 10:27 
AnswerRe: Outlookbar content sizing PinmemberThomas Gerber24-Feb-09 13:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 11 Oct 2008
Article Copyright 2008 by Thomas Gerber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid