目录
二、创建带有关闭功能的TabItem继承控件CloseableTabItem
1、选择自定件控件选项建立 CloseableTabItem 派生自TabItem,并注册一个名称为CloseTab 的路由事件
2、为CloseTabItem创建资源字典CloseableTabItemStyle.xaml
4、Shell.xaml.cs中代码指定路由事件,并为CloseTab实现方法:
由于工具有多个Window窗口(或者UserControl),界面的切换统一利用TabControl来实施。由于Vs提供的TabItem缺少关闭功能,所以利用自定义控件来完成,最终效果如图

一、Shell.Xaml前端代码
<TabControl x:Name="mainTab" Grid.Row="2" VerticalContentAlignment="Center" >
<TabControl.Background>
<ImageBrush ImageSource="/学生成绩;component/Resources/ImgBack.jpg" Opacity="0.8"></ImageBrush>
</TabControl.Background>
</TabControl>
二、创建带有关闭功能的TabItem继承控件CloseableTabItem
1、选择自定件控件选项建立 CloseableTabItem 派生自TabItem,并注册一个名称为CloseTab 的路由事件
using System.Windows;
using System.Windows.Controls;
namespace ScoreTools.Views
{
public class CloseableTabItem : TabItem
{
// this.AddHandler(CloseableTabItem.CloseTabEvent, new RoutedEventHandler(this.CloseTab));
static CloseableTabItem()
{
//This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
//This style is defined in themes\generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(typeof(CloseableTabItem), new FrameworkPropertyMetadata(typeof(CloseableTabItem)));
}
public static readonly RoutedEvent CloseTabEvent =EventManager.RegisterRoutedEvent("CloseTab", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(CloseableTabItem));
public event RoutedEventHandler CloseTab
{
add { AddHandler(CloseTabEvent, value); }
remove { RemoveHandler(CloseTabEvent, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Button closeButton = base.GetTemplateChild("PART_Close") as Button;
if (closeButton != null)
closeButton.Click += new System.Windows.RoutedEventHandler(closeButton_Click);
}
void closeButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
this.RaiseEvent(new RoutedEventArgs(CloseTabEvent, this));
}
}
}
2、为CloseTabItem创建资源字典CloseableTabItemStyle.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:ScoreTools.Views"
>
<Style x:Key="TabItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle SnapsToDevicePixels="true" Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1" Margin="3,3,3,1"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="TabControlNormalBorderBrush" Color="#8C8E94"/>
<LinearGradientBrush x:Key="TabItemHotBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#EAF6FD" Offset="0.15"/>
<GradientStop Color="#D9F0FC" Offset=".5"/>
<GradientStop Color="#BEE6FD" Offset=".5"/>
<GradientStop Color="#A7D9F5" Offset="1"/>
</LinearGradientBrush>
<!--<SolidColorBrush x:Key="TabItemSelectedBackground" Color="#F9F9F9"/>-->
<SolidColorBrush x:Key="TabItemSelectedBackground" Color="Yellow"/>
<SolidColorBrush x:Key="TabItemSelectedForeground" Color="White"/>
<SolidColorBrush x:Key="TabItemHotBorderBrush" Color="#3C7FB1"/>
<SolidColorBrush x:Key="TabItemDisabledBackground" Color="#F4F4F4"/>
<SolidColorBrush x:Key="TabItemDisabledBorderBrush" Color="#FFC9C7BA"/>
<Style TargetType="{x:Type c:CloseableTabItem}" >
<Style.Resources>
<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#F3F3F3" Offset="0"/>
<GradientStop Color="#EBEBEB" Offset="0.5"/>
<GradientStop Color="#DDDDDD" Offset="0.5"/>
<GradientStop Color="#CDCDCD" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ButtonOverBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFFAFAFA" Offset="0"/>
<GradientStop Color="#FFE0E0E3" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ButtonPressedBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFE0E0E2" Offset="0"/>
<GradientStop Color="#FFF8F8F8" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF969696"/>
<Style x:Key="CloseableTabItemButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border SnapsToDevicePixels="true" x:Name="Chrome" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" Opacity="0" />
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" TargetName="Chrome" Value="1"/>
<Setter Property="Background" TargetName="Chrome" Value="{DynamicResource ButtonOverBackground}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Opacity" TargetName="Chrome" Value="1"/>
<Setter Property="Background" TargetName="Chrome" Value="{DynamicResource ButtonPressedBackground}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#ADADAD"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="FocusVisualStyle" Value="{StaticResource TabItemFocusVisual}"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="6,1,6,1"/>
<Setter Property="BorderBrush" Value="{StaticResource TabControlNormalBorderBrush}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:CloseableTabItem}">
<Grid SnapsToDevicePixels="true" >
<Border x:Name="Bd" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" >
<DockPanel x:Name="ContentPanel">
<Button x:Name="PART_Close" HorizontalAlignment="Center" Margin="3,0,3,0" VerticalAlignment="Center" Width="16" Height="16" DockPanel.Dock="Right" Style="{DynamicResource CloseableTabItemButtonStyle}" ToolTip="关闭">
<Path x:Name="Path" Stretch="Fill" StrokeThickness="0.5" Stroke="#FF333333" Fill="#FF969696" Data="F1 M 2.28484e-007,1.33331L 1.33333,0L 4.00001,2.66669L 6.66667,6.10352e-005L 8,1.33331L 5.33334,4L 8,6.66669L 6.66667,8L 4,5.33331L 1.33333,8L 1.086e-007,6.66669L 2.66667,4L 2.28484e-007,1.33331 Z " HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Button>
<ContentPresenter x:Name="Content" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}" />
</DockPanel>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" SourceName="PART_Close" Value="True">
<Setter Property="Fill" TargetName="Path" Value="#FFB83C3D"/>
</Trigger>
<Trigger Property="IsPressed" SourceName="PART_Close" Value="True">
<Setter Property="Fill" TargetName="Path" Value="#FF9D3838"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemHotBackground}"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemSelectedBackground}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="false"/>
<Condition Property="IsMouseOver" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemHotBorderBrush}"/>
</MultiTrigger>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="BorderThickness" TargetName="Bd" Value="1,0,1,1"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="BorderThickness" TargetName="Bd" Value="1,1,0,1"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter Property="BorderThickness" TargetName="Bd" Value="0,1,1,1"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Top"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-2,-2,-2,-1"/>
<Setter Property="Margin" TargetName="ContentPanel" Value="0,0,0,1"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Bottom"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-2,-1,-2,-2"/>
<Setter Property="Margin" TargetName="ContentPanel" Value="0,1,0,0"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Left"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-2,-2,-1,-2"/>
<Setter Property="Margin" TargetName="ContentPanel" Value="0,0,1,0"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Right"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-1,-2,-2,-2"/>
<Setter Property="Margin" TargetName="ContentPanel" Value="1,0,0,0"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemDisabledBackground}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemDisabledBorderBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
3、App.xaml中引用该字典
<Application.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Views/CloseableTabItemStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
4、Shell.xaml.cs中代码指定路由事件,并为CloseTab实现方法:
this.AddHandler(CloseableTabItem.CloseTabEvent, new RoutedEventHandler(this.CloseTab));
private void CloseTab(object sender, RoutedEventArgs e)
{
TabItem tabItem = e.Source as TabItem;
if (tabItem != null)
{
TabControl tabControl = tabItem.Parent as TabControl;
if (tabControl != null)
tabControl.Items.Remove(tabItem);
}
}
至此,TabControl就可以使用TabItem项右上角带有X的功能了。
三、综合应用代码
Shell.xaml.cs代码如下:
using ScoreTools.ViewModels;
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace ScoreTools.Views
{
/// <summary>
/// Shell.xaml 的交互逻辑
/// </summary>
public partial class Shell : Window
{
public Shell()
{
InitializeComponent();
this.AddHandler(CloseableTabItem.CloseTabEvent, new RoutedEventHandler(this.CloseTab));
ShellViewModel shellViewModel = new ShellViewModel();
this.DataContext = shellViewModel;
}
private void CloseTab(object sender, RoutedEventArgs e)
{
TabItem tabItem = e.Source as TabItem;
if (tabItem != null)
{
TabControl tabControl = tabItem.Parent as TabControl;
if (tabControl != null)
tabControl.Items.Remove(tabItem);
}
}
private void ToolBar_Click(object sender, RoutedEventArgs e)
{
var btn = e.OriginalSource as FButton;
string viewName = btn.Name;
//通过反射实例化
Type type = this.GetType();
Assembly assembly = type.Assembly;
string ViewName = type.Namespace + "." + viewName;
var billForm = Assembly.GetExecutingAssembly().CreateInstance(ViewName, false);
// Show the window.
if (billForm != null)
{
if (billForm is Window)
{
Window win = (Window)billForm;
win.Owner = this;
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
win.ShowDialog();
}
else if (billForm is UserControl)
{
foreach (TabItem t in mainTab.Items)
{
if (t.Header.ToString() == btn.Content.ToString())
{
t.IsSelected = true;
return;
}
}
TabItem tabItem = new CloseableTabItem();
tabItem.Header = btn.Content.ToString();
tabItem.Content = billForm;
mainTab.Items.Add(tabItem);
tabItem.IsSelected = true;//激活选项卡
}
}
else
{
MessageBox.Show("此功能尚未开发");
}
}
}
}

这篇博客介绍了如何在WPF应用中开发一个学生成绩管理工具,重点在于创建一个带有关闭功能的TabItem控件。通过自定义控件CloseableTabItem,注册CloseTab路由事件,以及在App.xaml和Shell.xaml.cs中进行相关配置,实现了TabItem的关闭功能。最后展示了在实际项目中的综合应用代码。

被折叠的 条评论
为什么被折叠?



