スクロール可能なタブコントロール

Last Updated 2011/10/13


タブ数が多くてコントロールの幅を超える場合、タブコントロールの標準の動作はタブを複数行で表示します。ここではタブを 1 行表示のままスクロールする手順を説明します


.NET Framework の System.Windows.Forms.TabControl コントロールのほうには Multiline プロパティがあって、タブ数が多い場合、このプロパティに false を設定するとタブは 1 行のままスクロール可能になります。true の場合は複数行で表示されます。

一方、System.Windows.Controls.TabControl コントロールのほうにはそのような機能はありません。そこで、作ってみました。実行結果は以下のとおりです。

ScrollableTabControl

リソースディクショナリファイルでタブコントロールのスタイルを再定義します。ポイントは TabPanel コントロールの代わりに ScrollViewer コントロール内に TabItem オブジェクトを配置するようにし、スクロールするための RepeatButton コントロールを配置する点にあります。

(ScrollableTabControl.xaml)

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Path x:Key="arrowLeft" Stroke="Black" Fill="Black" Stretch="None"
	VerticalAlignment="Center" HorizontalAlignment="Center">
    <Path.Data>
      <Geometry>M0,3 L6,6 6,0Z</Geometry>
    </Path.Data>
  </Path>
  <Path x:Key="arrowRight" Stroke="Black" Fill="Black" Stretch="None"
	VerticalAlignment="Center" HorizontalAlignment="Center">
    <Path.Data>
      <Geometry>M0,0 L6,3 L0,6Z</Geometry>
    </Path.Data>
  </Path>

  <Style x:Key="{x:Type TabControl}" TargetType="TabControl">
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="BorderThickness" Value="1,0,1,1" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="TabControl">
          <Grid ClipToBounds="True" SnapsToDevicePixels="True" KeyboardNavigation.TabNavigation="Local">
            <Grid.RowDefinitions>
              <RowDefinition Height="24" />
              <RowDefinition />
            </Grid.RowDefinitions>

            <DockPanel Grid.Row="0" Height="{TemplateBinding TabPanel.Height}">
              <RepeatButton Name="rightButton" Width="24" Height="24" VerticalAlignment="Bottom"
                          Content="{StaticResource arrowRight}" DockPanel.Dock="Right"
                          CommandTarget="{Binding ElementName=scrollViewer}"
                          Command="ScrollBar.LineRightCommand" />
              <RepeatButton Name="leftButton" Width="24" Height="24" VerticalAlignment="Bottom"
                          Content="{StaticResource arrowLeft}" DockPanel.Dock="Right"
                          CommandTarget="{Binding ElementName=scrollViewer}"
                          Command="ScrollBar.LineLeftCommand" />
              <ScrollViewer Name="scrollViewer" CanContentScroll="True"
                          IsDeferredScrollingEnabled="False" SnapsToDevicePixels="True"
                          HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled">
                <StackPanel Orientation="Horizontal" IsItemsHost="True" />
              </ScrollViewer>
            </DockPanel>

            <Border Name="ContentPanel" Grid.Row="1"
                      BorderThickness="{TemplateBinding Border.BorderThickness}"
                      BorderBrush="{TemplateBinding Border.BorderBrush}"
                      Background="{TemplateBinding Panel.Background}"
                      KeyboardNavigation.TabIndex="2"
                      KeyboardNavigation.TabNavigation="Local"
                      KeyboardNavigation.DirectionalNavigation="Contained">
              <ContentPresenter ContentSource="SelectedContent"
                            Content="{TemplateBinding TabControl.SelectedContent}"
                            ContentTemplate="{TemplateBinding TabControl.SelectedContentTemplate}"
                            ContentStringFormat="{TemplateBinding TabControl.SelectedContentStringFormat}"
                            Margin="{TemplateBinding Control.Padding}"
                            SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
            </Border>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

  <Style TargetType="TabItem">
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="BorderBrush" Value="Black"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="Margin" Value="0" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type TabItem}">
          <Border CornerRadius="3,3,0,0" Background="{TemplateBinding Background}" 
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Padding="{TemplateBinding Padding}">
            <ContentPresenter ContentSource="Header" 
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
          </Border>
          <ControlTemplate.Triggers>
            <Trigger Property="IsSelected" Value="True">
              <Setter Property="Background" Value="{Binding Background}" />
              <Setter Property="BorderThickness" Value="1,1,1,0"/>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

</ResourceDictionary>

(MainWindow.xaml)

Window x:Class="TabControlTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="タブ行をスクロール可能にする" Height="300" Width="400">

  <!-- ScrollableTabControl.xaml のリソースを読み込む -->
  <Window.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="ScrollableTabControl.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Window.Resources>

  <TabControl Margin="10">
    <TabItem Header="タブ1">
      <TextBlock Text="タブ1を選択しました" />
    </TabItem>
    <TabItem Header="タブ2">
      <TextBlock Text="タブ2を選択しました"/>
    </TabItem>
    <TabItem Header="タブ3(長いタブ)">
      <TextBlock Text="タブ3を選択しました"/>
    </TabItem>
    <TabItem Header="タブ4">
      <TextBlock Text="タブ4を選択しました"/>
    </TabItem>
    <TabItem Header="タブ5(長いタブ)">
      <TextBlock Text="タブ5を選択しました"/>
    </TabItem>
    <TabItem Header="タブ6">
      <TextBlock Text="タブ6を選択しました"/>
    </TabItem>
    <TabItem Header="タブ7">
      <TextBlock Text="タブ7を選択しました"/>
    </TabItem>
    <TabItem Header="タブ8">
      <TextBlock Text="タブ8を選択しました"/>
    </TabItem>
  </TabControl>
</Window>

−以上−