I ran into an issue where I had more tab items than could fit within the desired width of my tab control. Instead of all the tab items appearing on a single line, they would wrap and create a multiline affect which I did not want. I wanted the user to be able to scroll through the tab items if needed. I did some checking to see if someone had built or customized the silverlight toolkit to support this but I did not find anything, so I decided to create a custom tabcontrol with scrollable items. Below is a picture of the default tab control and my custom scrollable tabcontrol. Note: I have only modified the "Top" layout for the tabcontrol so if someone wants to use one of the other layouts, they will have to update accordingly.

Here is the code for the custom scrollable tab control.
file: ScrollableTab.xaml.cs
[StyleTypedProperty(Property = "TabLeftButtonTopStyle", StyleTargetType = typeof(RepeatButton)),
StyleTypedProperty(Property = "TabRightButtonTopStyle", StyleTargetType = typeof(RepeatButton)),
TemplatePart(Name = "TabLeftButtonTop", Type = typeof(RepeatButton)),
TemplatePart(Name = "TabRightButtonTop", Type = typeof(RepeatButton)),
TemplatePart(Name = "TabScrollViewerTop", Type = typeof(ScrollViewer)),
TemplatePart(Name = "TabPanelTop", Type = typeof(TabPanel))]
public class SlidingTabControl : System.Windows.Controls.TabControl
{
private RepeatButton tabLeftButton;
private RepeatButton tabRightButton;
private ScrollViewer tabScrollViewer;
private TabPanel tabPanelTop;
private double currentHorizontalOffset = 0;
private double maxHorizontalOffset = 0;
///
/// Tab Top Left Button style
///
public static readonly DependencyProperty TabLeftButtonTopStyleProperty = DependencyProperty.Register(
"TabLeftButtonTopStyle",
typeof(Style),
typeof(SlidingTabControl),
new PropertyMetadata(null));
///
/// Tab Top Left Button style
///
public static readonly DependencyProperty TabRightButtonTopStyleProperty = DependencyProperty.Register(
"TabRightButtonTopStyle",
typeof(Style),
typeof(SlidingTabControl),
new PropertyMetadata(null));
///
/// Initializes a new instance of the class.
///
public SlidingTabControl()
{
this.DefaultStyleKey = typeof(SlidingTabControl);
this.SelectionChanged += new SelectionChangedEventHandler(SlidingTabControl_SelectionChanged);
}
///
/// Gets or sets the Tab Top Left Button style.
///
/// The left button style style.
[Description("Gets or sets the tab top left button style")]
[Category("ScrollButton")]
public Style TabLeftButtonTopStyle
{
get { return (Style)GetValue(TabLeftButtonTopStyleProperty); }
set { SetValue(TabLeftButtonTopStyleProperty, value); }
}
///
/// Gets or sets the Tab Top Right Button style.
///
/// The left button style style.
[Description("Gets or sets the tab top right button style")]
[Category("ScrollButton")]
public Style TabRightButtonTopStyle
{
get { return (Style)GetValue(TabRightButtonTopStyleProperty); }
set { SetValue(TabRightButtonTopStyleProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
//remove old handlers
if (null != this.tabLeftButton)
{
this.tabLeftButton.Click -= this.tabLeftButton_Click;
}
if (null != this.tabRightButton)
{
this.tabRightButton.Click -= this.tabRightButton_Click;
}
//add new handlers
this.tabLeftButton = GetTemplateChild("TabLeftButtonTop") as RepeatButton;
this.tabRightButton = GetTemplateChild("TabRightButtonTop") as RepeatButton;
this.tabScrollViewer = GetTemplateChild("TabScrollViewerTop") as ScrollViewer;
this.tabPanelTop = GetTemplateChild("TabPanelTop") as TabPanel;
if (null != this.tabLeftButton)
{
this.tabLeftButton.Click += new RoutedEventHandler(tabLeftButton_Click);
}
if (null != this.tabRightButton)
{
this.tabRightButton.Click += new RoutedEventHandler(tabRightButton_Click);
}
}
protected override Size ArrangeOverride(Size finalSize)
{
var size = base.ArrangeOverride(finalSize);
if (this.tabPanelTop != null && this.tabScrollViewer != null)
{
if (this.tabPanelTop.ActualWidth > this.tabScrollViewer.ActualWidth)
{
maxHorizontalOffset = this.tabPanelTop.ActualWidth - this.tabScrollViewer.ActualWidth + 30;
}
SetScrollButtonVisibility();
}
return size;
}
private void SlidingTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SlidingTabControl tabControl = sender as SlidingTabControl;
tabControl.Dispatcher.BeginInvoke(new Action(() => UpdateZIndex(sender as SlidingTabControl)));
}
private void tabRightButton_Click(object sender, RoutedEventArgs e)
{
if (null != this.tabScrollViewer && null != this.tabPanelTop)
{
currentHorizontalOffset = this.tabScrollViewer.HorizontalOffset + 25;
if (currentHorizontalOffset > maxHorizontalOffset)
currentHorizontalOffset = maxHorizontalOffset;
this.tabScrollViewer.ScrollToHorizontalOffset(currentHorizontalOffset);
SetScrollButtonVisibility();
}
}
private void tabLeftButton_Click(object sender, RoutedEventArgs e)
{
if (null != this.tabScrollViewer)
{
currentHorizontalOffset = this.tabScrollViewer.HorizontalOffset - 25;
if (currentHorizontalOffset < 0)
currentHorizontalOffset = 0;
this.tabScrollViewer.ScrollToHorizontalOffset(currentHorizontalOffset);
SetScrollButtonVisibility();
}
}
private void SetScrollButtonVisibility()
{
if (null != this.tabScrollViewer)
{
if (null != this.tabLeftButton)
{
if (this.currentHorizontalOffset > 0)
this.tabLeftButton.Visibility = Visibility.Visible;
else
this.tabLeftButton.Visibility = Visibility.Collapsed;
}
if (null != this.tabRightButton)
{
if (currentHorizontalOffset >= maxHorizontalOffset)
{
this.tabRightButton.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
this.tabRightButton.Visibility = System.Windows.Visibility.Visible;
}
}
}
}
private void UpdateZIndex(SlidingTabControl tc)
{
if (tc != null)
{
foreach (TabItem tabItem in tc.Items)
{
tabItem.SetValue(Canvas.ZIndexProperty, (tabItem == tc.SelectedItem ? tc.Items.Count : (tc.Items.Count - 1) - tc.Items.IndexOf(tabItem)));
}
}
}
}
file generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControl"
xmlns:System_Windows_Controls_Primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls"
>
<Style TargetType="local:SlidingTabControl">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFFFFFFF" Offset="0"/>
<GradientStop Color="#FFFEFEFE" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA3AEB9" Offset="0"/>
<GradientStop Color="#FF8399A9" Offset="0.375"/>
<GradientStop Color="#FF718597" Offset="0.375"/>
<GradientStop Color="#FF617584" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SlidingTabControl">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualTop">
<SplineDoubleKeyFrame KeyTime="0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="DisabledVisualBottom">
<SplineDoubleKeyFrame KeyTime="0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="DisabledVisualLeft">
<SplineDoubleKeyFrame KeyTime="0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="DisabledVisualRight">
<SplineDoubleKeyFrame KeyTime="0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="TemplateTop" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<RepeatButton Content="<" x:Name="TabLeftButtonTop" Style="{TemplateBinding TabLeftButtonTopStyle}"/>
<ScrollViewer x:Name="TabScrollViewerTop" Grid.Column="1" IsTabStop="False" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Hidden" >
<System_Windows_Controls_Primitives:TabPanel x:Name="TabPanelTop" Margin="20,2,2,-1" Canvas.ZIndex="1"/>
</ScrollViewer>
<RepeatButton Content=">" x:Name="TabRightButtonTop" Style="{TemplateBinding TabRightButtonTopStyle}" Grid.Column="2"/>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0,0,3,3" MinWidth="10" MinHeight="10" Grid.Row="1" Grid.ColumnSpan="3">
<ContentPresenter x:Name="ContentTop" Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
</Border>
<Border x:Name="DisabledVisualTop" Background="#8CFFFFFF" CornerRadius="0,0,3,3" IsHitTestVisible="False" Opacity="0" Grid.Row="1" Grid.RowSpan="2" Canvas.ZIndex="1"/>
</Grid>
<Grid x:Name="TemplateBottom" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<System_Windows_Controls_Primitives:TabPanel x:Name="TabPanelBottom" Margin="2,-1,2,2" Grid.Row="1" Canvas.ZIndex="1"/>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="3,3,0,0" MinWidth="10" MinHeight="10">
<ContentPresenter x:Name="ContentBottom" Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
</Border>
<Border x:Name="DisabledVisualBottom" Background="#8CFFFFFF" CornerRadius="3,3,0,0" IsHitTestVisible="False" Opacity="0" Canvas.ZIndex="1"/>
</Grid>
<Grid x:Name="TemplateLeft" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<System_Windows_Controls_Primitives:TabPanel x:Name="TabPanelLeft" Margin="2,2,-1,2" Canvas.ZIndex="1"/>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" CornerRadius="0,3,3,0" MinWidth="10" MinHeight="10">
<ContentPresenter x:Name="ContentLeft" Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
</Border>
<Border x:Name="DisabledVisualLeft" Background="#8CFFFFFF" Grid.Column="1" CornerRadius="0,3,3,0" IsHitTestVisible="False" Opacity="0" Canvas.ZIndex="1"/>
</Grid>
<Grid x:Name="TemplateRight" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<System_Windows_Controls_Primitives:TabPanel x:Name="TabPanelRight" Grid.Column="1" Margin="-1,2,2,2" Canvas.ZIndex="1"/>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="3,0,0,3" MinWidth="10" MinHeight="10">
<ContentPresenter x:Name="ContentRight" Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
</Border>
<Border x:Name="DisabledVisualRight" Background="#8CFFFFFF" CornerRadius="3,0,0,3" IsHitTestVisible="False" Margin="0" Opacity="0" Canvas.ZIndex="1"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Download code
SlidingTabControlDemo.zip (2.10 mb)