在设计用户界面中,经常会使用到TabControl,比如标签式浏览器、选项卡设置等等。在WPF中本身已经提供了一个功能强大的可以自定义样式与系统风格统一的TabControl。对于其他控件的样式/模板自定义我想大家也应该有所了解,我不想多叙述,大家可以去MSDN Library下载整套自定义样式/模板示例:http://msdn.microsoft.com/en-us/library/aa970773.aspx
在这里我首先要感谢 Olaf Rabbachin ,一位活跃的MSDN支持者,他的Blog详细介绍了多种的TabControl自定义样式:http://blogs.intuidev.com/。不过我今天要设计的是一个特殊的样式,不多说,看最终效果图:
设计一个标签样式,能够自定的在左右侧增加标签页。
好,我们一步步来,先从Tab Control的默认样式看起。下面是我从Blend 3中复制出来的默认样式:
TabControl Default Control Template<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Padding" Value="4,4,4,4"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{StaticResource TabControlNormalBorderBrush}"/>
<Setter Property="Background" Value="#F9F9F9"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid ClipToBounds="true" KeyboardNavigation.TabNavigation="Local" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<TabPanel
x:Name="HeaderPanel"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
Panel.ZIndex="1"
IsItemsHost="true"
KeyboardNavigation.TabIndex="1"/>
<Border
x:Name="ContentPanel"
Grid.Column="0"
Grid.Row="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local">
<ContentPresenter
x:Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter TargetName="HeaderPanel" Property="Grid.Row" Value="1"/>
<Setter TargetName="ContentPanel" Property="Grid.Row" Value="0"/>
<Setter TargetName="RowDefinition0" Property="Height" Value="*"/>
<Setter TargetName="RowDefinition1" Property="Height" Value="Auto"/>
<Setter TargetName="HeaderPanel" Property="Margin" Value="2,0,2,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter TargetName="HeaderPanel" Property="Grid.Row" Value="0"/>
<Setter TargetName="ContentPanel" Property="Grid.Row" Value="0"/>
<Setter TargetName="HeaderPanel" Property="Grid.Column" Value="0"/>
<Setter TargetName="ContentPanel" Property="Grid.Column" Value="1"/>
<Setter TargetName="ColumnDefinition0" Property="Width" Value="Auto"/>
<Setter TargetName="ColumnDefinition1" Property="Width" Value="*"/>
<Setter TargetName="RowDefinition0" Property="Height" Value="*"/>
<Setter TargetName="RowDefinition1" Property="Height" Value="0"/>
<Setter TargetName="HeaderPanel" Property="Margin" Value="2,2,0,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter TargetName="HeaderPanel" Property="Grid.Row" Value="0"/>
<Setter TargetName="ContentPanel" Property="Grid.Row" Value="0"/>
<Setter TargetName="HeaderPanel" Property="Grid.Column" Value="1"/>
<Setter TargetName="ContentPanel" Property="Grid.Column" Value="0"/>
<Setter TargetName="ColumnDefinition0" Property="Width" Value="*"/>
<Setter TargetName="ColumnDefinition1" Property="Width" Value="Auto"/>
<Setter TargetName="RowDefinition0" Property="Height" Value="*"/>
<Setter TargetName="RowDefinition1" Property="Height" Value="0"/>
<Setter TargetName="HeaderPanel" Property="Margin" Value="0,2,2,2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
从这里面,我们忽略后面一大堆的触发器(Trigger),只关注前面的模板,我们可以发现模板主要包括两个部分;第一部分定义在Grid的第一行中(Grid.Row=”0”),
<TabPanel x:Name="HeaderPanel" Margin="2,2,2,0" IsItemsHost="true" …… />
这个部分就是TabControl中的Tab标签区域,注意到 IsItemsHost="true" 这个属性在所有的Panel中都有。
第二部分就是在下方的一个Border块,是每个标签页的内容体,使用了一个以命名的模板 PART_SelectedContentHost ,这里不多叙述,详细可以阅读 Programming WPF 这本书。
好了,我们主要是要修改Tab区域,所以只需要关注第一部分。从 IsItemHost 出发,我们就可以想到可以使用多种 Panel 来替换掉 TabPanel。要想达到我们的目标,DockPanel 是一个很不错的选择。DockPanel 停靠面板,有四个方向的区域可以让控件停靠,这里我们就只需要 Left 和 Right 区域。动手写代码…
TabControl DockPanel Template<TabControl Name="tabControl1" Margin="12">
<TabControl.Template>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid ClipToBounds="True" KeyboardNavigation.TabNavigation="Local" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Name="ColumnDefinition0" Width="*"/>
<ColumnDefinition Name="ColumnDefinition2" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Name="RowDefinition0" Height="Auto"/>
<RowDefinition Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<DockPanel
Name="HeaderPanel"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
Panel.ZIndex="1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"/>
<Border
Name="ContentPanel"
Grid.Column="0"
Grid.Row="1"
Background="{TemplateBinding Panel.Background}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local">
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="{TemplateBinding Control.Padding}"
Content="{TemplateBinding TabControl.SelectedContent}"
ContentSource="SelectedContent"
ContentStringFormat="{TemplateBinding TabControl.SelectedContentStringFormat}"
ContentTemplate="{TemplateBinding TabControl.SelectedContentTemplate}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/>
</Border>
</Grid>
</ControlTemplate>
</TabControl.Template>
<TabItem Name="tabItem1" Header="Tab 1">
<Grid>
<TextBlock Text="Page 1" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</TabItem>
<TabItem Name="tabItem2" Header="Tab 2">
<Grid>
<TextBlock Text="Page 2" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</TabItem>
<TabItem Name="tabItem3" Header="Tab 3" DockPanel.Dock="Right" HorizontalAlignment="Right">
<Grid>
<TextBlock Text="Page 3" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</TabItem>
</TabControl>
在每个TabItem中通过设置 DockPanel.Dock="Right" HorizontalAlignment="Right" 这两个属性就可以控制 TabItem 的位置,怎么样不难吧?我们还可以设置上下交错的布局效果,如:
从更改Panel出发,我们就可以简单的设计出各种布局样式的Tab来,当让如果熟练于自定义布局的,还可以更自由的来更改设计。
最后我提供这个示例的代码,欢迎下载:http://cid-51b2fdd068799d15.skydrive.live.com/self.aspx/.Public/CustomTabControl.zip
WPF QQ交流群: 113404016 欢迎加入