WPF如何自定义TabControl控件样式示例详解

一、前言

程序中经常会用到TabControl控件,默认的控件样式很普通。而且样式或功能不一定符合我们的要求。比如:我们需要TabControl的标题能够居中、或平均分布;或者我们希望TabControl的标题能够进行关闭。要实现这些功能我们需要对TabControl的样式进行定义。

二、实现TabControl的标题平均分布

默认的TabControl标题是使用TabPanel容器包含的。要想实现TabControl标题头平均分布,需要把TabPanel替换成UniformGrid;

替换后的TabControl样式如下:

<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
  <Setter Property="Padding" Value="2"/>
  <Setter Property="HorizontalContentAlignment" Value="Center"/>
  <Setter Property="VerticalContentAlignment" Value="Center"/>
  <Setter Property="Background" Value="White"/>
  <Setter Property="BorderBrush" Value="#FFACACAC"/>
  <Setter Property="BorderThickness" Value="1"/>
  <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
  <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="{x:Type TabControl}">
   <Grid x:Name="templateRoot" ClipToBounds="True" SnapsToDevicePixels="True" KeyboardNavigation.TabNavigation="Local">
    <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>
    <UniformGrid x:Name="HeaderPanel" Rows="1" Background="Transparent" Grid.Column="0" IsItemsHost="True" Margin="0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
    <Line X1="0" X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" Stroke="White" StrokeThickness="0.1" VerticalAlignment="Bottom" Margin="0 0 0 1" SnapsToDevicePixels="True"/>
    <Border x:Name="ContentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
    <ContentPresenter x:Name="PART_SelectedContentHost" ContentTemplate="{TemplateBinding SelectedContentTemplate}" Content="{TemplateBinding SelectedContent}" ContentStringFormat="{TemplateBinding SelectedContentStringFormat}" ContentSource="SelectedContent" Margin="0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
    </Border>
   </Grid>
   <ControlTemplate.Triggers>
    <Trigger Property="TabStripPlacement" Value="Bottom">
    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
    </Trigger>
    <Trigger Property="TabStripPlacement" Value="Left">
    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
    <Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
    <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
    <Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
    </Trigger>
    <Trigger Property="TabStripPlacement" Value="Right">
    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
    <Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
    <Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
    </Trigger>
    <Trigger Property="IsEnabled" Value="False">
    <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
    </Trigger>
   </ControlTemplate.Triggers>
   </ControlTemplate>
  </Setter.Value>
  </Setter>
 </Style>

即使这样设置了,TabControl的标题还是很丑,这个时候就需要通过设置TabItem来更改标题样式了。

TabItem样式如下:

<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
  <Setter Property="Foreground" Value="White"/>
  <Setter Property="Background" Value="Transparent"/>
  <Setter Property="BorderBrush" Value="#FFACACAC"/>
  <Setter Property="Margin" Value="0"/>
  <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
  <Setter Property="VerticalContentAlignment" Value="Stretch"/>
  <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="{x:Type TabItem}">
   <Grid x:Name="templateRoot" SnapsToDevicePixels="True" Background="Transparent">
    <TextBlock x:Name="txt" Visibility="Visible" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Header}" ToolTip="{TemplateBinding Header}" Foreground="{TemplateBinding Foreground}" TextTrimming="CharacterEllipsis" />
   </Grid>
   <ControlTemplate.Triggers>
    <MultiDataTrigger>
    <MultiDataTrigger.Conditions>
     <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
     <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
    </MultiDataTrigger.Conditions>

    <Setter Property="Foreground" TargetName="txt" Value="#fffea1"/>
    </MultiDataTrigger>
    <MultiDataTrigger>
    <MultiDataTrigger.Conditions>
     <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
     <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Left"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
    </MultiDataTrigger>
    <MultiDataTrigger>
    <MultiDataTrigger.Conditions>
     <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
     <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Bottom"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
    </MultiDataTrigger>
    <MultiDataTrigger>
    <MultiDataTrigger.Conditions>
     <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
     <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Right"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
    </MultiDataTrigger>
    <MultiDataTrigger>
    <MultiDataTrigger.Conditions>
     <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
     <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
    </MultiDataTrigger>

    <MultiDataTrigger>
    <MultiDataTrigger.Conditions>
     <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
     <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="Panel.ZIndex" Value="1"/>
    <Setter Property="Foreground" TargetName="txt" Value="#fffea1"/>
    </MultiDataTrigger>
   </ControlTemplate.Triggers>
   </ControlTemplate>
  </Setter.Value>
  </Setter>
 </Style>

至此,样式已经设置完毕,引用示例:

<Grid Background="#858586">
  <TabControl Style="{StaticResource TabControlStyle}" Width="300" Height="200" Background="Transparent" BorderBrush="Transparent" BorderThickness="0">
   <TabItem Style="{StaticResource TabItemStyle}" Cursor="Hand" Header="音乐电台" Height="38" >
   <Grid Background="#33ffffff">
    <TextBlock Text="音乐电台" VerticalAlignment="Center" HorizontalAlignment="Center"/>
   </Grid>
   </TabItem>
   <TabItem Style="{StaticResource TabItemStyle}" Cursor="Hand" Header="Mv电台" Height="38" >
   <Grid Background="#33ffffff">
    <TextBlock Text="Mv电台" VerticalAlignment="Center" HorizontalAlignment="Center"/>
   </Grid>
   </TabItem>
  </TabControl>
  </Grid>

效果如下:

三、实现TabControl标题居中显示(不平均分布)

同理需要更改TabControl的样式和TabItem的样式。需要把使用TabPanel作为标题的容器,设置HorizontalAlignment为Center;

TabControl的样式如下:

<Style x:Key="TabControlWithUnderLineStyle" TargetType="{x:Type TabControl}">
 <Setter Property="Padding" Value="2"/>
 <Setter Property="HorizontalContentAlignment" Value="Center"/>
 <Setter Property="VerticalContentAlignment" Value="Center"/>
 <Setter Property="Background" Value="White"/>
 <Setter Property="BorderBrush" Value="#FFACACAC"/>
 <Setter Property="BorderThickness" Value="1"/>
 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
 <Setter Property="Template">
  <Setter.Value>
  <ControlTemplate TargetType="{x:Type TabControl}">
   <Grid x:Name="templateRoot" ClipToBounds="True" SnapsToDevicePixels="True" KeyboardNavigation.TabNavigation="Local">
   <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" HorizontalAlignment="Center" Background="Transparent" Grid.Column="0" IsItemsHost="True" Margin="0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
   <Line X1="0" X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" Stroke="Gray" StrokeThickness="0.1" VerticalAlignment="Bottom" Margin="0 0 0 1" SnapsToDevicePixels="True"/>
   <Border x:Name="ContentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
    <ContentPresenter x:Name="PART_SelectedContentHost" ContentTemplate="{TemplateBinding SelectedContentTemplate}" Content="{TemplateBinding SelectedContent}" ContentStringFormat="{TemplateBinding SelectedContentStringFormat}" ContentSource="SelectedContent" Margin="0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
   </Border>
   </Grid>
   <ControlTemplate.Triggers>
   <Trigger Property="TabStripPlacement" Value="Bottom">
    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
   </Trigger>
   <Trigger Property="TabStripPlacement" Value="Left">
    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
    <Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
    <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
    <Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
   </Trigger>
   <Trigger Property="TabStripPlacement" Value="Right">
    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
    <Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
    <Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
    <Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
    <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
   </Trigger>
   <Trigger Property="IsEnabled" Value="False">
    <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
   </Trigger>
   </ControlTemplate.Triggers>
  </ControlTemplate>
  </Setter.Value>
 </Setter>
 </Style>

TabItem样式如下:

<Style x:Key="TabItemExWithUnderLineStyle" TargetType="{x:Type TabItem}">
   <Setter Property="Foreground" Value="White"/>
   <Setter Property="Background" Value="Transparent"/>
   <Setter Property="BorderBrush" Value="#FFACACAC"/>
   <Setter Property="Margin" Value="0"/>
   <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
   <Setter Property="VerticalContentAlignment" Value="Stretch"/>
   <Setter Property="Template">
    <Setter.Value>
     <ControlTemplate TargetType="{x:Type TabItem}">
      <Grid x:Name="templateRoot" SnapsToDevicePixels="True" Background="Transparent">
       <Border x:Name="_underline" BorderBrush="#37aefe" BorderThickness="0" Margin="{TemplateBinding Margin}"/>
       <Grid>
        <TextBlock x:Name="txt" Visibility="Visible" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Header}" ToolTip="{TemplateBinding Header}" Foreground="{TemplateBinding Foreground}" TextTrimming="CharacterEllipsis" />
       </Grid>
      </Grid>
      <ControlTemplate.Triggers>
       <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
         <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
         <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
        </MultiDataTrigger.Conditions>

        <Setter Property="Foreground" TargetName="txt" Value="#37aefe"/>
       </MultiDataTrigger>
       <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
         <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
         <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Left"/>
        </MultiDataTrigger.Conditions>
        <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
       </MultiDataTrigger>
       <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
         <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
         <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Bottom"/>
        </MultiDataTrigger.Conditions>
        <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
       </MultiDataTrigger>
       <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
         <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
         <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Right"/>
        </MultiDataTrigger.Conditions>
        <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
       </MultiDataTrigger>
       <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
         <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
         <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>
        </MultiDataTrigger.Conditions>
        <Setter Property="Opacity" TargetName="templateRoot" Value="0.56"/>
       </MultiDataTrigger>

       <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
         <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
         <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top"/>        </MultiDataTrigger.Conditions>
        <Setter Property="Panel.ZIndex" Value="1"/>
        <Setter Property="Foreground" TargetName="txt" Value="#37aefe"/>
        <Setter Property="BorderThickness" TargetName="_underline" Value="0 0 0 2"/>
       </MultiDataTrigger>
      </ControlTemplate.Triggers>
     </ControlTemplate>
    </Setter.Value>
   </Setter>
  </Style>

引用示例:

<Grid Background="#858586">
    <TabControl Style="{StaticResource TabControlWithUnderLineStyle}" Foreground="Black" Width="300" Height="200" Background="Transparent" BorderBrush="Transparent" BorderThickness="0">
     <TabItem Style="{StaticResource TabItemExWithUnderLineStyle}" Cursor="Hand" Header="音乐电台" Height="38" Width="70" Margin="5 0">
      <Grid Background="#33ffffff">
       <TextBlock Text="音乐电台" VerticalAlignment="Center" HorizontalAlignment="Center"/>
      </Grid>
     </TabItem>
     <TabItem Style="{StaticResource TabItemExWithUnderLineStyle}" Cursor="Hand" Header="Mv电台" Height="38" Width="70" Margin="5 0">
      <Grid Background="#33ffffff">
       <TextBlock Text="Mv电台" VerticalAlignment="Center" HorizontalAlignment="Center"/>
      </Grid>
     </TabItem>
    </TabControl>
   </Grid>

效果如下:

四、带关闭按钮的TabControl

带关闭按钮的TabControl其实就是就是扩展TabItem,需要新建WPF自定义控件,命名为TabItemClose吧;

C#代码如下:

public class TabItemClose : TabItem
 {
  static TabItemClose()
  {
   DefaultStyleKeyProperty.OverrideMetadata(typeof(TabItemClose), new FrameworkPropertyMetadata(typeof(TabItemClose)));
  }

  private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
   d.SetValue(e.Property, e.NewValue);
  }

  /// <summary>
  /// 是否可以关闭
  /// </summary>
  public bool IsCanClose
  {
   get { return (bool)GetValue(IsCanCloseProperty); }
   set { SetValue(IsCanCloseProperty, value); }
  }

  public static readonly DependencyProperty IsCanCloseProperty =
   DependencyProperty.Register("IsCanClose", typeof(bool), typeof(TabItemClose), new PropertyMetadata(true, OnPropertyChanged));

  /// <summary>
  /// 关闭的图标
  /// </summary>
  public ImageSource CloseIcon
  {
   get { return (ImageSource)GetValue(CloseIconProperty); }
   set { SetValue(CloseIconProperty, value); }
  }

  public static readonly DependencyProperty CloseIconProperty =
   DependencyProperty.Register("CloseIcon", typeof(ImageSource), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));

  /// <summary>
  /// 正常背景色
  /// </summary>
  public SolidColorBrush NormalBackground
  {
   get { return (SolidColorBrush)GetValue(NormalBackgroundProperty); }
   set { SetValue(NormalBackgroundProperty, value); }
  }

  public static readonly DependencyProperty NormalBackgroundProperty =
   DependencyProperty.Register("NormalBackground", typeof(SolidColorBrush), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));

  /// <summary>
  /// 悬浮背景色
  /// </summary>
  public SolidColorBrush OverBackgound
  {
   get { return (SolidColorBrush)GetValue(OverBackgoundProperty); }
   set { SetValue(OverBackgoundProperty, value); }
  }

  public static readonly DependencyProperty OverBackgoundProperty =
   DependencyProperty.Register("OverBackgound", typeof(SolidColorBrush), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));

  /// <summary>
  /// 选中背景色
  /// </summary>
  public SolidColorBrush SelectedBackgound
  {
   get { return (SolidColorBrush)GetValue(SelectedBackgoundProperty); }
   set { SetValue(SelectedBackgoundProperty, value); }
  }

  public static readonly DependencyProperty SelectedBackgoundProperty =
   DependencyProperty.Register("SelectedBackgound", typeof(SolidColorBrush), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));

  /// <summary>
  /// 默认前景色
  /// </summary>
  public SolidColorBrush NormalForeground
  {
   get { return (SolidColorBrush)GetValue(NormalForegroundProperty); }
   set { SetValue(NormalForegroundProperty, value); }
  }

  public static readonly DependencyProperty NormalForegroundProperty =
   DependencyProperty.Register("NormalForeground", typeof(SolidColorBrush), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));

  /// <summary>
  /// 悬浮前景色
  /// </summary>
  public SolidColorBrush OverForeground
  {
   get { return (SolidColorBrush)GetValue(OverForegroundProperty); }
   set { SetValue(OverForegroundProperty, value); }
  }

  public static readonly DependencyProperty OverForegroundProperty =
   DependencyProperty.Register("OverForeground", typeof(SolidColorBrush), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));

  /// <summary>
  /// 选中前景色
  /// </summary>
  public SolidColorBrush SelectedForeground
  {
   get { return (SolidColorBrush)GetValue(SelectedForegroundProperty); }
   set { SetValue(SelectedForegroundProperty, value); }
  }

  public static readonly DependencyProperty SelectedForegroundProperty =
   DependencyProperty.Register("SelectedForeground", typeof(SolidColorBrush), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));
  /// <summary>
  /// 控件圆角
  /// </summary>
  public CornerRadius CornerRadius
  {
   get { return (CornerRadius)GetValue(CornerRadiusProperty); }
   set { SetValue(CornerRadiusProperty, value); }
  }
  public static readonly DependencyProperty CornerRadiusProperty =
   DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(TabItemClose), new PropertyMetadata(new CornerRadius(0), OnPropertyChanged));
  /// <summary>
  /// 前置Logo
  /// </summary>
  public ImageSource LogoIcon
  {
   get { return (ImageSource)GetValue(LogoIconProperty); }
   set { SetValue(LogoIconProperty, value); }
  }
  public static readonly DependencyProperty LogoIconProperty =
   DependencyProperty.Register("LogoIcon", typeof(ImageSource), typeof(TabItemClose), new PropertyMetadata(null, OnPropertyChanged));
  /// <summary>
  /// 前置Logo宽度
  /// </summary>
  public double LogoIconWidth
  {
   get { return (double)GetValue(LogoIconWidthProperty); }
   set { SetValue(LogoIconWidthProperty, value); }
  }
  public static readonly DependencyProperty LogoIconWidthProperty =
   DependencyProperty.Register("LogoIconWidth", typeof(double), typeof(TabItemClose), new PropertyMetadata(double.Parse("0"), OnPropertyChanged));
  /// <summary>
  /// 前置Logo高度
  /// </summary>
  public double LogoIconHeigth
  {
   get { return (double)GetValue(LogoIconHeigthProperty); }
   set { SetValue(LogoIconHeigthProperty, value); }
  }
  public static readonly DependencyProperty LogoIconHeigthProperty =
   DependencyProperty.Register("LogoIconHeigth", typeof(double), typeof(TabItemClose), new PropertyMetadata(double.Parse("0"), OnPropertyChanged));
  /// <summary>
  /// LogoPadding
  /// </summary>
  public Thickness LogoPadding
  {
   get { return (Thickness)GetValue(LogoPaddingProperty); }
   set { SetValue(LogoPaddingProperty, value); }
  }

  public static readonly DependencyProperty LogoPaddingProperty =
   DependencyProperty.Register("LogoPadding", typeof(Thickness), typeof(TabItemClose), new PropertyMetadata(new Thickness(0), OnPropertyChanged));
  /// <summary>
  /// 关闭item事件
  /// </summary>
  public event RoutedEventHandler CloseItem
  {
   add { AddHandler(CloseItemEvent, value); }
   remove { RemoveHandler(CloseItemEvent, value); }
  }
  public static readonly RoutedEvent CloseItemEvent =
   EventManager.RegisterRoutedEvent("CloseItem", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TabItemClose));
  /// <summary>
  /// 关闭项的右键菜单
  /// </summary>
  public ContextMenu ItemContextMenu { get; set; }

  Border ItemBorder;
  public override void OnApplyTemplate()
  {
   base.OnApplyTemplate();
   ItemBorder = Template.FindName("_bordertop", this) as Border;
   if (ItemContextMenu != null)
   {
    ItemBorder.ContextMenu = ItemContextMenu;
   }
  }
 }

这里面我们添加了很多扩展功能,包括右键菜单,图标显示和控件圆角,以及各种背景色属性。

然后为TabItemClose设置样式

<Style TargetType="{x:Type local:TabItemClose}">
  <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
  <Setter Property="VerticalContentAlignment" Value="Stretch"/>
  <Setter Property="Foreground" Value="#666666"/>
  <Setter Property="Margin" Value="0 0 0 0"/>
  <Setter Property="Padding" Value="0"/>
  <Setter Property="BorderThickness" Value="0"/>
  <Setter Property="CloseIcon" Value="/Images/close2.png"/>
  <Setter Property="NormalBackground" Value="White"/>
  <Setter Property="OverBackgound" Value="#33ca5100"/>
  <Setter Property="SelectedBackgound" Value="#ca5100"/>
  <Setter Property="NormalForeground" Value="#555558"/>
  <Setter Property="OverForeground" Value="White"/>
  <Setter Property="SelectedForeground" Value="White"/>
  <Setter Property="Template">
   <Setter.Value>
    <ControlTemplate TargetType="{x:Type local:TabItemClose}">
     <Border x:Name="_bordertop" Width="{TemplateBinding Width}" MaxWidth="{TemplateBinding MaxWidth}" Height="{TemplateBinding Height}" CornerRadius="{TemplateBinding CornerRadius}" Background="{TemplateBinding NormalBackground}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" ToolTip="{TemplateBinding Header}" >
      <DockPanel>
       <Image x:Name="_logo" DockPanel.Dock="Left" Visibility="Visible" Margin="{TemplateBinding LogoPadding}" Source="{TemplateBinding LogoIcon}" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform" Width="{TemplateBinding LogoIconWidth}" Height="{TemplateBinding LogoIconHeigth}" />
       <Grid Name="_grid" SnapsToDevicePixels="True">
        <Grid.ColumnDefinitions>
         <ColumnDefinition Width="*" />
         <ColumnDefinition x:Name="_col_close" Width="20" />
        </Grid.ColumnDefinitions>
        <Border Grid.ColumnSpan="2" Background="White" Opacity="0"/>
        <TextBlock x:Name="_txt" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Margin="3 0 3 0" Foreground="{TemplateBinding NormalForeground}" TextAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Header}" />
        <Grid x:Name="_gridclose" Grid.Column="1" >
         <Border x:Name="_borderbg" Background="Black" Opacity="0" />
         <local:ButtonEx x:Name="PART_Close_TabItem" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Transparent" Visibility="Visible" Icon="{TemplateBinding CloseIcon}" ButtonType="Icon" />
        </Grid>
       </Grid>
      </DockPanel>

     </Border>
     <ControlTemplate.Triggers>
      <Trigger Property="LogoIcon" Value="{x:Null}">
       <Setter TargetName="_logo" Property="Visibility" Value="Collapsed" />
      </Trigger>
      <Trigger Property="IsCanClose" Value="false">
       <Setter TargetName="_gridclose" Property="Visibility" Value="Collapsed"/>
       <Setter TargetName="_col_close" Property="Width" Value="0"/>
      </Trigger>
      <Trigger Property="IsSelected" Value="true">
       <Setter TargetName="_bordertop" Property="Background" Value="{Binding SelectedBackgound,RelativeSource={RelativeSource TemplatedParent}}" />
       <Setter TargetName="_txt" Property="Foreground" Value="{Binding SelectedForeground,RelativeSource={RelativeSource TemplatedParent}}"/>
      </Trigger>
      <MultiTrigger>
       <MultiTrigger.Conditions>
        <Condition Property="IsMouseOver" Value="true"/>
        <Condition Property="IsSelected" Value="false"/>
       </MultiTrigger.Conditions>
       <Setter TargetName="_txt" Property="Foreground" Value="{Binding OverForeground,RelativeSource={RelativeSource TemplatedParent}}"/>
       <Setter TargetName="_bordertop" Property="Background" Value="{Binding OverBackgound,RelativeSource={RelativeSource TemplatedParent}}"/>
      </MultiTrigger>
     </ControlTemplate.Triggers>
    </ControlTemplate>
   </Setter.Value>
  </Setter>
 </Style>

这里面使用了一个close的图标

TabControl的图标可设置可不设置,看自己需要。

这里面还用到了前面讲的控件ButtonEx,定义方法我就不重复赘述了。大家可以通过这个链接跳转查看://www.jb51.net/article/138475.htm。ButtonEx.cs里面还要添加几个方法用来支持关闭TabItem:

protected override void OnClick()
  {
   base.OnClick();
   if (!string.IsNullOrEmpty(Name) && Name == "PART_Close_TabItem")
   {
    TabItemClose itemclose = FindVisualParent<TabItemClose>(this);
    (itemclose.Parent as TabControl).Items.Remove(itemclose);
    RoutedEventArgs args = new RoutedEventArgs(TabItemClose.CloseItemEvent, itemclose);
    itemclose.RaiseEvent(args);
   }
  }

  public static T FindVisualParent<T>(DependencyObject obj) where T : class
  {
   while (obj != null)
   {
    if (obj is T)
     return obj as T;

    obj = VisualTreeHelper.GetParent(obj);
   }
   return null;
  }

引用示例:

<Grid Background="#858586">
    <TabControl Foreground="Black" Width="300" Height="200" Background="Transparent" BorderBrush="Transparent" BorderThickness="0">
     <local:TabItemClose Cursor="Hand" Header="音乐电台" Height="20" Width="100">
      <Grid Background="#aaffffff">
       <TextBlock Text="音乐电台" VerticalAlignment="Center" HorizontalAlignment="Center"/>
      </Grid>
     </local:TabItemClose>
     <local:TabItemClose Cursor="Hand" Header="Mv电台" Height="20" Width="100">
      <Grid Background="#aaffffff">
       <TextBlock Text="Mv电台" VerticalAlignment="Center" HorizontalAlignment="Center"/>
      </Grid>
     </local:TabItemClose>
    </TabControl>
   </Grid>

效果如下:

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • WPF自定义选择年月控件详解

    本文实例为大家分享了WPF自定义选择年月控件的具体代码,供大家参考,具体内容如下 封装了一个选择年月的控件,XAML代码: <UserControl x:Class="SunCreate.CombatPlatform.Client.DateMonthPicker" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.micr

  • WPF的ListView控件自定义布局用法实例

    本文实例讲述了WPF的ListView控件自定义布局用法.分享给大家供大家参考,具体如下: 概要: 以源码的形式贴出,免得忘记后,再到网上查资料.在VS2008+SP1环境下调试通过 引用的GrayscaleEffect模块,可根据参考资料<Grayscale Effect...>中的位置下载. 正文: 如何布局是在App.xaml中定义源码如下 <Application x:Class="CWebsSynAssistant.App" xmlns="http

  • WPF自定义TreeView控件样式实现QQ联系人列表效果

    一.前言 TreeView这个控件对于我来说是用得比较多的,以前做的小聊天软件(好友列表).音乐播放器(播放列表).类库展示器(树形类结构)等都用的是TreeView,普通的TreeView并不能满足我们的需求.因此我们需要滴对TreeView进行改造.下面的内容将介绍仿QQ联系人TreeView样式及TreeView数据绑定方法. 二.TreeView仿QQ联系人列表 准确的说不是仿QQ联系人列表,这个TreeView样式作为组织架构来使用更好.废话不多说,先看效果:  2.1.基本思路 像这

  • WPF自定义控件和样式之自定义按钮(Button)

    一.前言 程序界面上的按钮多种多样,常用的就这几种:普通按钮.图标按钮.文字按钮.图片文字混合按钮.本文章记录了不同样式类型的按钮实现方法.下面话不多说了,来一起看看详细的介绍吧. 二.固定样式的按钮 固定样式的按钮一般在临时使用时或程序的样式比较固定时才会使用,按钮整体样式不需要做大的改动. 2.1 普通按钮-扁平化风格 先看效果: 定义Button的样式,详见代码: <Style x:Key="BtnInfoStyle" TargetType="Button&quo

  • WPF自定义实现IP地址输入控件

    一.前言 WPF没有内置IP地址输入控件,因此我们需要通过自己定义实现. 我们先看一下IP地址输入控件有什么特性: 输满三个数字焦点会往右移 键盘←→可以空光标移动 任意位置可复制整段IP地址,且支持x.x.x.x格式的粘贴赋值 删除字符会自动向左移动焦点 知道以上特性,我们就可以开始动手了. 二.构成 Grid+TextBox*4+TextBlock*3 通过这几个控件的组合,我们完成IP地址输入控件的功能. 界面代码如下: <UserControl x:Class="IpAddress

  • WPF如何自定义TabControl控件样式示例详解

    一.前言 程序中经常会用到TabControl控件,默认的控件样式很普通.而且样式或功能不一定符合我们的要求.比如:我们需要TabControl的标题能够居中.或平均分布:或者我们希望TabControl的标题能够进行关闭.要实现这些功能我们需要对TabControl的样式进行定义. 二.实现TabControl的标题平均分布 默认的TabControl标题是使用TabPanel容器包含的.要想实现TabControl标题头平均分布,需要把TabPanel替换成UniformGrid: 替换后的

  • WPF自定义Expander控件样式实现酷炫Style

    首先, 看一下效果图. 点我看视频教程 实现思路 1.PS处理两张选中得特效背景, 一张为主选择得效果图, 另外一张为次选择项得效果图. ![](//files.jb51.net/file_images/article/202201/2022128103603692.jpg) ![](//files.jb51.net/file_images/article/202201/2022128103615294.jpg) 图片资源定义 <!--静态资源--> <ImageBrush x:Key=

  • C# WPF ListView控件的实例详解

    C# WPF ListView控件的实例详解 C#的WPF作为现在微软主流的桌面程序开发平台,相比过去的MFC时代,有了非常多的不同.本人刚从MFC平台转过来,以为可以轻松上手,哪知碰到了很多问题,十分不解.不得不乖乖回去看了本书,再继续回到边左边边学的路上.在这边也推荐<深入浅出WPF>这本书,拿来上手还是极好的. 由于WPF以数据驱动UI的设计理念,很多控件用起来都与之前平台的相差很多,ListView控件算是有代表性的,这是进化的成果.关于该控件的应该,很多参考了这篇博文,如觉本人记述不

  • Android中Spinner(下拉框)控件的使用详解

    android给我们提供了一个spinner控件,这个控件主要就是一个列表,那么我们就来说说这个控件吧,这个控件在以前的也看见过,但今天还是从新介绍一遍吧. Spinner位于 android.widget包下,每次只显示用户选中的元素,当用户再次点击时,会弹出选择列表供用户选择,而选择列表中的元素同样来自适配器.Spinner是View类得一个子类. 1.效果图 2.创建页面文件(main.xml) <Spinner android:id="@+id/spinner1" and

  • C# Chart控件标记问题详解

    前言 在做项目的时候,遇到一个需求,需要我对Chart图标标记数据正在运行,实现数据可视化,因为我们的表格是隐藏Y轴的刻度是看不到数据值的,于是采用数据标记的形式来动态展示值,那么我们应该怎么去处理这个问题呢,阅读这篇文章吧,我们一起学习学习一下,创作不易,大家点赞关注评论收藏,你的点赞和关注是我创作的动力,也是我持续不断学习的动力.谢谢大家啦!!! 效果展示 先来展示一下我们的效果,看看是怎么回事,在看看有没有欲望往下面看看文章,主要是对Chart图的标记问题做了处理,我们使用了Chart控件

  • Python中Qslider控件实操详解

    在学习一些pyqt5的内容后,我们对于其中的组件也有所接触.本篇所要带来的是Qslider控件,也可以说是python中比较常见的控件了.在一些具体的使用和方向等相关的操作上,很多人是没有全面的进行过系统学习的.下面我们就这些操作逐个为大家带来介绍,一起来看下Qslider控件的使用吧. 1.控件介绍和使用 qslider解释为滑块控件,用于方便左右滑动. 往往这类滑动更多用于屏幕可以触碰的设备. 我们想要使用滑块控件,本质上实则就是调用Qslider类. 使用的时候,首先对qslider这个类

  • Go json自定义Unmarshal避免判断nil示例详解

    目录 前言 使用默认的 Unmarshal 方法 自定义的 Unmarshal 方法 前言 腾讯<Go安全指南>中提到[必须]nil指针判断:进行指针操作时,必须判断该指针是否为nil,防止程序panic,尤其在进行结构体Unmarshal时.但如果每次使用都要判断一下是否 nil 防止 panic的话,那么这样的代码就会比较麻烦,这里我们可以使用一个自定义的方法,来避免这种情况. 使用默认的 Unmarshal 方法 package main import ( "encoding/

  • OpenLayers3加载常用控件使用方法详解

    本文实例为大家分享了OpenLayers3加载常用控件使用的具体代码,供大家参考,具体内容如下 1. 前言 地图控件就是对地图的缩放.全屏.坐标显示控件等,方便我们对地图进行操作.OpenLayers 3 封装了很多常用的地图控件,例如地图导航.比例尺.鹰眼.测量工具等,这些控件都是基于ol.control.Control虚基类进行封装,ol.control.Control的子类为各类常用的地图控件,可以通过Map对象的Control参数进行设置或者通过addControl方法将控件添加到地图窗

  • Flutter折叠控件使用方法详解

    本文实例为大家分享了Flutter折叠控件使用的具体代码,供大家参考,具体内容如下 1.官方折叠控件ExpansionTiles 官方默认提供了一个折叠控件 ExpansionTiles 主要用于listView做折叠和展开操作的,先来看看一般的用法 Widget _buildTiles(Entry root) {     return new ExpansionTile(       title: new Text(root.title),       children: root.child

随机推荐