Skip to content

A WPF ListBox which acts like an Apple style navigation bar

December 19, 2011

At Reasult we are currently working on a new UI style for our WPF applications. One of the UI elements we will use is an Apple style navigation bar.

With WPF is relative easy to create such kind of a control. The trick is to find a standard control which has the behaviour we need. In this case we need ´select item´ behaviour and the ListBox is a good candidate. The only thing we have to do is to change the default ListBox appearance.

Because I´am not a graphical designer I search the internet for some information about the styles Apple is using for the navigation bar. In the article The Apple.com navigation menu created using only CSS3 you can find a lot of style information. I tried to use all the style elements but some effects are not easy to create with WPF so I skip a few effects.

We start with creating the NavigationBar class which inherits from the ListBox.

public class NavigationBar : ListBox
    {
        static NavigationBar()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NavigationBar), new FrameworkPropertyMetadata(typeof(NavigationBar)));
        }
    }

The first thing we must change in the appearance is the direction of the ListBox items. Default the items are showed in vertical direction but for the navigation bar we need items displayed in horizontal direction.

<Style TargetType="{x:Type local:NavigationBar}">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <StackPanel IsItemsHost="True" Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter> 
</Style>

After that we have to override the default ListBox ControlTemplate. We need a transparent border with rounded corners which clips the content. In the article WPF – Easy rounded corners for anything you can read how to achieve this. The result is:

<Style TargetType="{x:Type local:NavigationBar}">
    <Setter Property="ItemContainerStyle" Value="{StaticResource NavigationBarItemStyleKey}"/>
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <StackPanel IsItemsHost="True" Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:NavigationBar}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"></RowDefinition>
                    </Grid.RowDefinitions>

                    <Border HorizontalAlignment="Left" BorderThickness="3" BorderBrush="Transparent" CornerRadius="7" Padding="2">
                        <Grid>
                            <!-- Rounded mask (stretches to fill Grid) -->
                            <Border Name="mask" Background="White" CornerRadius="5"/>

                            <!-- Main content container -->
                            <StackPanel>
                                <!-- Use a VisualBrush of 'mask' as the opacity mask -->
                                <StackPanel.OpacityMask>
                                    <VisualBrush Visual="{Binding ElementName=mask}"/>
                                </StackPanel.OpacityMask>

                                <ItemsPresenter></ItemsPresenter>

                            </StackPanel>

                        </Grid>
                        <Border.Effect>
                            <DropShadowEffect Color="#FFcecece" Opacity="0.6" ShadowDepth="3" BlurRadius="5"/>
                        </Border.Effect>

                    </Border>
                </Grid>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The next step is to define the style for the navigation bar items. First we create a NavigationBarItem class which derives from the standard ListBoxItem. The NavigationBarItem has a border and each border side has a different color. With the standard BorderBrush is not possible to assign different colors to every border side. To achieve this a custom BorderBrush is used.

<Style x:Key="NavigationBarItemStyleKey" TargetType="{x:Type local:NavigationBarItem}"  >
    <Setter Property="BorderBrush">
        <Setter.Value>
            <VisualBrush>
                <VisualBrush.Visual>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>

                        <Path x:Name="ColoredBorderLeft" SnapsToDevicePixels="True" 
                                Data="M0,0 L0,0 1,0.5 L1,0.5 0,1" Fill="#FF929292" Stretch="Fill" Grid.RowSpan="2"/>
                        <Path x:Name="ColoredBorderRight" SnapsToDevicePixels="True" 
                                Data="M1,0 L1,0 0,0.5 L0,0.5 1,1" Fill="#FF5d5d5d" Stretch="Fill" Grid.Column="1" Grid.RowSpan="2"/>
                        <Path x:Name="ColoredBorderTop" SnapsToDevicePixels="True" 
                                Data="M0,0 L0,0 0.5,1 L0.5,1 1,0" Fill="#FF797979" Stretch="Fill" Grid.ColumnSpan="2"/>
                        <Path x:Name="ColoredBorderBottom" SnapsToDevicePixels="True" 
                                Data="M0,1 L0,1 0.5,0 L0.5,0 1,1" Fill="#FF575757" Stretch="Fill" Grid.Row="1" Grid.ColumnSpan="2"/>
                    </Grid>
                </VisualBrush.Visual>
            </VisualBrush>
        </Setter.Value>
    </Setter>
</Style>

Now all the style elements which are needed are defined and we can use the Apple Navigation Bar:

<local:NavigationBar>
    <local:NavigationBarItem>
        <local:NavigationBarItem.Content>
            <Image Source="/AppleNavigationBar;component/apple_logo.png" Height="24" Width="24"/>
        </local:NavigationBarItem.Content>
    </local:NavigationBarItem>
    <local:NavigationBarItem Content="Store"></local:NavigationBarItem>
    <local:NavigationBarItem Content="Mac"></local:NavigationBarItem>
    <local:NavigationBarItem Content="iPod"></local:NavigationBarItem>
    <local:NavigationBarItem Content="iPhone"></local:NavigationBarItem>
    <local:NavigationBarItem Content="iPad"></local:NavigationBarItem>
    <local:NavigationBarItem Content="iTunes"></local:NavigationBarItem>
    <local:NavigationBarItem Content="Support"></local:NavigationBarItem>
</local:NavigationBar>

And the final result in a WPF application looks like:

The Visual Studio 2010 solution can be downloaded from here

About these ads

From → .NET, Navigation, WPF

One Comment
  1. Reblogged this on Antonio Campos and commented:
    Add your thoughts here… (optional)

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: