A Simple Silverlight Tab Menu

by edsilverton

Tab menus are a common design feature for many web sites. I wasn’t able to find a Tab Menu control with source anywhere so I created my own using the built in Silverlight controls. Here’s how I’ve done it:

Add a StackPanel to your Xaml page.

<StackPanel x:Name=”MainMenu” Orientation=”Horizontal” />

In the CodeBehind for that page add the following:

private void BindMenu()
{
    string[] menuItems = “Home, About Us, Products, Services, Contact”.Split(‘,’);   

    foreach (string item in menuItems)
    {
        RadioButton btn = new RadioButton();
        btn.Content = item;
        btn.Checked +=
new RoutedEventHandler(MainMenu_SelectionChanged);
        btn.GroupName =
“Main Menu”;
        btn.DataContext = item;
        btn.Margin =
new System.Windows.Thickness(0, 0, 2, 0);
        btn.Padding =
new System.Windows.Thickness(6, 6, 6, 4);
        btn.MinWidth = 60;
        btn.Style = (
Style)Application.Current.Resources[“MainMenuTab”];
       
       
MainMenu.Children.Add(btn);
    }
}

void MainMenu_SelectionChanged(object sender, RoutedEventArgs e)
{
    string name = (String)((RadioButton)sender).DataContext;
}

Call the BindMenu() function from your Page_Loaded() function.

You can see that all I’m doing is getting an array of page names then iterating over them to create a RadioButton for each then adding that RadioButton to the previously defined StackPanel. The DataContext property is in this case being set to the string item but you could set this to be any complex object.

Just before adding the RadioButton to the StackPanel you can see that I’m setting it’s Style property to

Application.Current.Resources[“MainMenuTab”];

This Style is contained in the App.xaml file and looks like this:

<Application.Resources>
    <Style x:Key=”MainMenuTab” TargetType=”RadioButton”>
        <Setter Property=”IsEnabled” Value=”true” />
        <Setter Property=”IsTabStop” Value=”true” />
        <Setter Property=”Foreground” Value=”Black” />
        <Setter Property=”Margin” Value=”0″ />
        <Setter Property=”MinWidth” Value=”0″ />
        <Setter Property=”HorizontalContentAlignment” Value=”Center” />
        <Setter Property=”VerticalContentAlignment” Value=”Center” />
        <Setter Property=”Cursor” Value=”Hand” />
        <Setter Property=”TextAlignment” Value=”Left” />
        <Setter Property=”TextWrapping” Value=”NoWrap” />
        <Setter Property=”FontSize” Value=”11″ />
        <Setter Property=”FontFamily” Value=”Verdana”/>
        <Setter Property=”Template”>
            <Setter.Value>
                <ControlTemplate TargetType=”RadioButton”>
                    <Grid x:Name=”RootElement” Cursor=”{TemplateBinding Cursor}”>
                        <Grid.Resources>
                            <Storyboard x:Name=”Normal State”/>
                            <Storyboard x:Name=”Checked State”>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName=”Checked” Storyboard.TargetProperty=”(UIElement.Opacity)” BeginTime=”00:00:00″ Duration=”00:00:00.1000000″>
                                    <SplineDoubleKeyFrame KeyTime=”00:00:00.1″ Value=”1″/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                            <Storyboard x:Name=”MouseOver Checked State”>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName=”Checked” Storyboard.TargetProperty=”(UIElement.Opacity)” BeginTime=”00:00:00″ Duration=”00:00:00.0000000″>
                                    <SplineDoubleKeyFrame KeyTime=”00:00:00″ Value=”1″/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                            <Storyboard x:Name=”Pressed Checked State”>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName=”Checked” Storyboard.TargetProperty=”(UIElement.Opacity)” BeginTime=”00:00:00″ Duration=”00:00:00.0000000″>
                                    <SplineDoubleKeyFrame KeyTime=”00:00:00″ Value=”1″/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                            <Storyboard x:Name=”MouseOver Unchecked State”>                                  
                               
<DoubleAnimationUsingKeyFrames Storyboard.TargetName=”Checked” Storyboard.TargetProperty=”(UIElement.Opacity)” BeginTime=”00:00:00″ Duration=”00:00:00.5000000″>
                                    <SplineDoubleKeyFrame KeyTime=”00:00:00.1″ Value=”0.5″/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                            <Storyboard x:Name=”Pressed Unchecked State”>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName=”Checked” Storyboard.TargetProperty=”(UIElement.Opacity)” BeginTime=”00:00:00″ Duration=”00:00:00.5000000″>
                                    <SplineDoubleKeyFrame KeyTime=”00:00:00.1″ Value=”1″/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                            <Storyboard x:Name=”Disabled Unchecked State”>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName=”Unchecked” Storyboard.TargetProperty=”(Shape.Background).(SolidColorBrush.Color)” BeginTime=”00:00:00″ Duration=”00:00:00.0000000″>
                                    <SplineColorKeyFrame KeyTime=”00:00:00″ Value=”Gray”/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>
                            <Storyboard x:Name=”Disabled Checked State”>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName=”Unchecked” Storyboard.TargetProperty=”(Shape.Background).(SolidColorBrush.Color)” BeginTime=”00:00:00″ Duration=”00:00:00.0000000″>
                                    <SplineColorKeyFrame KeyTime=”00:00:00″ Value=”Gray”/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>
                        </Grid.Resources>
                        <Border x:Name=”Unchecked”
                           Opacity=”1″
                           BorderBrush=”BurlyWood”
                           BorderThickness=”1,1,1,0″
                           Background=”Beige”
                           CornerRadius=”4,4,0,0″>
                        </Border>
                        <Border x:Name=”Checked”
                           Opacity=”0″
                           BorderBrush=”BurlyWood”
                           BorderThickness=”1,1,1,0″
                           Background=”White”
                           CornerRadius=”4,4,0,0″>
                        </Border>
                        <ContentPresenter
                         Content=”{TemplateBinding Content}”
                         ContentTemplate=”{TemplateBinding ContentTemplate}”
                         Background=”Transparent”
                         FontFamily=”{TemplateBinding FontFamily}”
                         FontSize=”{TemplateBinding FontSize}”
                         FontStretch=”{TemplateBinding FontStretch}”
                         FontStyle=”{TemplateBinding FontStyle}”
                         FontWeight=”{TemplateBinding FontWeight}”
                         Foreground=”{TemplateBinding Foreground}”
                         HorizontalContentAlignment=”{TemplateBinding HorizontalContentAlignment}”
                         Padding=”{TemplateBinding Padding}”
                         TextAlignment=”{TemplateBinding TextAlignment}”
                         TextDecorations=”{TemplateBinding TextDecorations}”
                         TextWrapping=”{TemplateBinding TextWrapping}”
                         VerticalContentAlignment=”{TemplateBinding VerticalContentAlignment}”
                         Margin=”{TemplateBinding Margin}”
                         IsHitTestVisible=”true”
                         MinWidth=”{TemplateBinding MinWidth}”/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Application.Resources>

All I’ve done here is taken the default RadioButton style downloaded from here, removed the requisite ellipses and replaced them with Border controls. I’ve also adjusted the various state StoryBoards to target these Borders. The Borders are the familiar rounded tab shape surrounding the text and the StoryBoards define their mouseover, pressed and disabled states. RadioButtons are ideal for this purpose because they contain all the toggling functionality that a simple tab menu requires.

This is what it should look like:

tabstrip

Clicking on each tab will animate it to white and toggle the previously selected tab. Mousing over a tab turns it 50% white. I personally prefer tabs to be all the same width regardless of their text content. You can make each tab’s width match it’s text by setting each RadioButton’s MinWidth to 0 or just omitting MinWidth. It’s also worth noting that you don’t have to have tab backgrounds at all and can just use this as a handy way of creating horizontal lists of text-only buttons.

From here you could use the MainMenu_SelectionChanged event handler to show/hide corresponding Canvas controls when the tabs are toggled.

I may turn this into a more generic reusable TabMenu control in future but for now this is a pretty good way to get quick results.

Download the source code here

Advertisements