自定义TabControl与TabItem
很长时间没有写博客了,今天来和大家一起分享一下TabControl自定义。
在开发系统中,常常需要用到TabControl控件,而且往往需要我们在运行过程中,动态添加与动态删除标签页面,现在就让我们一起来打造这样的TabControl吧。
主要是通过在TabControl中的标签页中增加一个关闭按钮来实现,是否可关闭可以在TabControl中设置,也可以在TabItem中进行单独设置,关闭按钮只有在鼠标移到标签上或者当前标签才显示。同时和关闭窗口一样,支持即将关闭和关闭后的事件。
效果图:
代码:
1、模板文件(ZbTabControl.xaml):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:Zbsoft.WpfControls"
mc:Ignorable="d">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Zbsoft.WpfControls;component/Resources/ZbShared.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<!--关闭按钮相关开始-->
<sys:Double x:Key="CloseButtonWidthAndHeight">14</sys:Double>
<Geometry x:Key="X_CloseButton">M0,0 L10,10 M0,10 L10,0</Geometry>
<Color x:Key="TabItem_ForegroundColor_Base">#4971A3</Color>
<SolidColorBrush x:Key="TabItemCloseButtonNormalForegroundBrush"
Color="{StaticResource TabItem_ForegroundColor_Base}"/>
<SolidColorBrush x:Key="TabItemCloseButtonHoverForegroundBrush"
Color="White"/>
<SolidColorBrush x:Key="TabItemCloseButtonPressedForegroundBrush"
Color="#EEEE"/>
<LinearGradientBrush x:Key="TabItemCloseButtonNormalBackgroundBrush"
StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#5FFF" Offset="0.0"/>
<GradientStop Color="#5CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="TabItemCloseButtonNormalBorderBrush"
StartPoint="0,0" EndPoint="1,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#5CCC" Offset="0.0"/>
<GradientStop Color="#5444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="TabItemCloseButtonHoverBackgroundBrush"
StartPoint="0,1" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFDF2B1E" Offset="0.0"/>
<GradientStop Color="#FFA41F00" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="TabItemCloseButtonPressedBackgroundBrush"
StartPoint="0,0" EndPoint="1,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFA41F00" Offset="0.0"/>
<GradientStop Color="#FFDF2B1E" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="TabItemCloseButtonPressedBorderBrush"
StartPoint="0,0" EndPoint="1,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#5444" Offset="0.0"/>
<GradientStop Color="#5CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<Style x:Key="TabItemCloseButtonStyle" TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="false"/>
<Setter Property="Height" Value="{StaticResource CloseButtonWidthAndHeight}"/>
<Setter Property="Width" Value="{StaticResource CloseButtonWidthAndHeight}"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="ButtonBorder"
CornerRadius="2"
BorderThickness="1"
Background="{StaticResource TabItemCloseButtonNormalBackgroundBrush}"
BorderBrush="{StaticResource TabItemCloseButtonNormalBorderBrush}">
<Grid>
<Path x:Name="ButtonPath"
Margin="2"
Data="{StaticResource X_CloseButton}"
Stroke="{StaticResource TabItemCloseButtonNormalForegroundBrush}"
StrokeThickness="2"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Stretch="Uniform"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonBorder" Property="Background"
Value="{StaticResource TabItemCloseButtonHoverBackgroundBrush}" />
<Setter TargetName="ButtonPath" Property="Stroke"
Value="{StaticResource TabItemCloseButtonHoverForegroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="ButtonBorder"
Property="Background"
Value="{StaticResource TabItemCloseButtonPressedBackgroundBrush}" />
<Setter TargetName="ButtonBorder"
Property="BorderBrush"
Value="{StaticResource TabItemCloseButtonPressedBorderBrush}" />
<Setter TargetName="ButtonPath" Property="Stroke"
Value="{StaticResource TabItemCloseButtonPressedForegroundBrush}"/>
<Setter TargetName="ButtonPath" Property="Margin" Value="2.5,2.5,1.5,1.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--关闭按钮相关结束-->
<!--<SnippetTabControl>-->
<!--<Snippet13>-->
<Style TargetType="{x:Type local:ZbTabControl}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="IsShowCloseTabItemButton" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ZbTabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.BorderBrush).
(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="#FFAAAAAA" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TabPanel x:Name="HeaderPanel"
Grid.Row="0"
Panel.ZIndex="1"
Margin="1,0,4,-1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent">
</TabPanel>
<Border x:Name="Border"
Grid.Row="1"
BorderThickness="0,2"
CornerRadius="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ContentAreaColorLight}"
Offset="0" />
<GradientStop Color="{DynamicResource ContentAreaColorDark}"
Offset="1" />
</LinearGradientBrush>
</Border.Background>
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource TabItemBorderColor}" />
</Border.BorderBrush>
<ContentPresenter x:Name="PART_SelectedContentHost"
Margin="4"
ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--</Snippet13>-->
<Style TargetType="{x:Type local:ZbTabItem}">
<Setter Property="IsShowCloseTabItemButton"
Value="{Binding Path=IsShowCloseTabItemButton, RelativeSource={RelativeSource AncestorType=local:ZbTabControl}}">
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ZbTabItem}">
<Grid x:Name="Root">
<Border x:Name="Border"
Margin="0,0,-4,0"
BorderThickness="1,1,1,1"
CornerRadius="4,4,0,0">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Offset="0.0" />
<GradientStop Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Offset="0.0" />
<GradientStop Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="16"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="4,2"
RecognizesAccessKey="True" />
<Button Visibility="Hidden" Grid.Column="1" Name="CloseButton"
Tag="{TemplateBinding Header}"
Command="local:ZbTabControl.CloseTabItemCommand"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ZbTabItem}}}"
VerticalAlignment="Center" HorizontalAlignment="Left"
Style="{StaticResource TabItemCloseButtonStyle}">
</Button>
</Grid>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected">
<!--<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.Background).
(GradientBrush.GradientStops)[1].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource ControlPressedColor}" />
</ColorAnimationUsingKeyFrames>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderThickness)"
Storyboard.TargetName="Border">
<EasingThicknessKeyFrame KeyTime="0"
Value="2,2,2,0" />
</ThicknessAnimationUsingKeyFrames>
</Storyboard>-->
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops)[1].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource DisabledControlDarkColor}" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.BorderBrush).
(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource DisabledBorderLightColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
TargetName="Border"
Value="{StaticResource TabItemFocuseBackground}">
</Setter>
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Panel.ZIndex"
Value="100" />
<Setter Property="Background"
TargetName="Border"
Value="{StaticResource TabItemSelectedBackground}">
</Setter>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True"></Condition>
<Condition Property="IsShowCloseTabItemButton"
Value="True"></Condition>
</MultiTrigger.Conditions>
<Setter TargetName="CloseButton" Property="Visibility"
Value="Visible"></Setter>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"
Value="True"></Condition>
<Condition Property="IsShowCloseTabItemButton"
Value="True"></Condition>
</MultiTrigger.Conditions>
<Setter TargetName="CloseButton" Property="Visibility"
Value="Visible"></Setter>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--</SnippetTabControl>-->
</ResourceDictionary>
2、ZbTabControl.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Zbsoft.WpfControls
{
/// <summary>
/// 自定义标签控件,支持关闭单个标签页
/// </summary>
public class ZbTabControl : TabControl
{
/// <summary>
/// 标签关闭后执行的操作
/// </summary>
public event Action<object, ExecutedRoutedEventArgs> Closed;
/// <summary>
/// 标签即将关闭时执行的操作
/// </summary>
public event System.ComponentModel.CancelEventHandler Closing;
/// <summary>
/// 关闭标签命令
/// </summary>
private static RoutedUICommand closeTabItemCommand;
/// <summary>
/// 是否显示关闭标签按钮
/// </summary>
public static DependencyProperty IsShowCloseTabItemButtonProperty;
/// <summary>
/// 获取或设置是否显示关闭标签页面的按钮
/// </summary>
public bool IsShowCloseTabItemButton
{
get { return (bool)GetValue(IsShowCloseTabItemButtonProperty); }
set { SetValue(IsShowCloseTabItemButtonProperty, value); }
}
static ZbTabControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ZbTabControl), new FrameworkPropertyMetadata(typeof(ZbTabControl)));
closeTabItemCommand = new RoutedUICommand("", "CloseTabItemCommand",
typeof(ZbTabControl));
IsShowCloseTabItemButtonProperty = DependencyProperty.Register("IsShowCloseTabItemButton",
typeof(bool), typeof(ZbTabControl));
}
/// <summary>
/// 应用模板时,添加命令到窗口中
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
CommandBinding binding = new CommandBinding(closeTabItemCommand);
binding.Executed += new ExecutedRoutedEventHandler(binding_Executed);
binding.CanExecute += new CanExecuteRoutedEventHandler(binding_CanExecute);
Window w = Window.GetWindow(this);
if (w != null && !w.CommandBindings.Contains(binding))
w.CommandBindings.Add(binding);
}
/// <summary>
/// 判断是否允许执行关闭标签操作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void binding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
TabItem item = e.Parameter as TabItem;
if (item == null)
return;
if (item.IsEnabled)
e.CanExecute = true;
else
e.CanExecute = false;
}
/// <summary>
/// 关闭标签页实际执行函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void binding_Executed(object sender, ExecutedRoutedEventArgs e)
{
ZbTabItem item = e.Parameter as ZbTabItem;
if (item == null)
return;
if (Closing != null)
{
System.ComponentModel.CancelEventArgs arg = new System.ComponentModel.CancelEventArgs();
Closing(item, arg);
if (arg.Cancel)
return;
}
this.Items.Remove(item);
if (Closed != null)
{
Closed(item, e);
}
}
/// <summary>
/// 关闭标签命令
/// </summary>
public static RoutedUICommand CloseTabItemCommand
{
get { return closeTabItemCommand; }
}
}
}
3、ZbTabItem.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Zbsoft.WpfControls
{
/// <summary>
///
/// <MyNamespace:ZbTabItem/>
///
/// </summary>
public class ZbTabItem : TabItem
{
public static DependencyProperty IsShowCloseTabItemButtonProperty;
/// <summary>
/// 是否显示关闭标签页面的按钮
/// </summary>
public bool IsShowCloseTabItemButton
{
get { return (bool)GetValue(IsShowCloseTabItemButtonProperty); }
set { SetValue(IsShowCloseTabItemButtonProperty, value); }
}
static ZbTabItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ZbTabItem), new FrameworkPropertyMetadata(typeof(ZbTabItem)));
IsShowCloseTabItemButtonProperty = DependencyProperty.Register("IsShowCloseTabItemButton",
typeof(bool), typeof(ZbTabItem));
}
}
}
4、例子片断:
<my:ZbTabControl Grid.Row="1" IsShowCloseTabItemButton="True">
<my:ZbTabItem Header="我的同事"
Name="Test"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MinHeight="20" MaxHeight="30"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<my:ZbTextBox Name="searchTextBox" Grid.Row="0" BackPromptString="模糊搜索,多个条件以逗号(半角)间隔为并且,空格间隔为或" Margin="5,2" TextChanged="ZbTextBox_TextChanged"></my:ZbTextBox>
<ScrollViewer Name="MyFriendScroll" Grid.Row="1" VirtualizingStackPanel.IsVirtualizing="False">
<ListBox BorderThickness="0"
Name="MyFriendList"
HorizontalContentAlignment="Stretch"
AlternationCount="2"
ItemTemplate="{StaticResource FriendGroup}"
GotFocus="MyFriendList_Loaded"
PreviewMouseWheel="MyFriendList_PreviewMouseWheel"
MouseDoubleClick="MyFriendList_MouseDoubleClick">
</ListBox>
</ScrollViewer>
<ScrollViewer Name="MyFriendScrollSearch" Grid.Row="1" Visibility="Collapsed">
<ListBox BorderThickness="0"
Name="MyFriendListSearch"
HorizontalContentAlignment="Stretch"
AlternationCount="2"
ItemTemplate="{StaticResource LogonTemplate}"
PreviewMouseWheel="MyFriendList_PreviewMouseWheel"
MouseDoubleClick="MyFriendList_MouseDoubleClick">
</ListBox>
</ScrollViewer>
</Grid>
</my:ZbTabItem>
......
</my:ZbTabControl>