吵吵   2015-11-04  阅读:929

自从WPF引入page之后,整个windows程序的开发就再也不像是传统的窗口式的开发了。这种类似网页一样的跳转布局,其实更加符合日益复杂的工作需求,毕竟使用tab切换和使用窗口切换这之间的差别还是挺大的。

使用WPF自然很容易做出那种类似网页的效果来,当然,如果你开始参考网页布局的时候,你就开始要考虑如何建立一个可以自由切换的Tab页面,更加重要的是这个Tab页面是可以关掉的。

好在WPF实在是灵活的很,我们既可以使用类似在winform年代集成控件之后自绘,也可以用xaml语言来定义控件的界面和部分事件。并且,当依赖属性引入之后,当MVVM模式成为主流,当真正的数据驱动UI理念付诸实施之后,一切看起来真的都是相当不错的。

好了,开始进入主题,看我们如何一步步的实现一个可以关闭的TabControl。

1、先来定义界面,实际上我们点击tab标签页的时候,并不是点击的TabControl,而是其下的TabItem,因此我们先添加一个界面xaml文件TabItemExStyle.xaml。

2、在TabItemExStyle.xaml中我们需要先定义个关闭的按钮,当我们点击到关闭按钮的时候,它的圆圈背景变成红色。定义完关闭按钮后,我们才定义TabItemEx,当然在控件模板里面我们是一定要加入这个关闭按钮的,并命名为TabItemCloseButton。

整个文件如下:

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

    <SolidColorBrush x:Key="TabItemSelectedBrush" Color="#FFE8F1FD"/>
    <!--关闭按钮-->
    <Style x:Key="TabItemCloseButtonStyle" 
TargetType="{x:Type Button}">

        <Setter Property="Foreground" 
Value="Black"/>
        <!--修改模板属性-->
        <Setter Property="Template">
            <Setter.Value>
                <!--控件模板-->
                <ControlTemplate TargetType="Button">
                    <!--背景色-->
                    <Grid>
                    <Ellipse x:Name="back" Fill="#00FFFFFF" />
                  
                    <!--按钮内容-->
                        <Path x:Name="cp"
 Width="10" Height="10"  
                                 Stroke="#FF666666"
                                  StrokeThickness="2"  >

                            <Path.Data>
                                <
PathGeometry Figures="M 1,1 L 8,8 M 1,8 L 8,1" />
                            </Path.Data>
                        </Path>
                    </Grid>
                        <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">

                            <Setter Property="Fill" TargetName="back" Value="#FFFF3333"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Fill" TargetName="back" Value="#5BBC1717"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>


    <!--BOF TabItemStyle-->
    <ControlTemplate x:Key="TabItemExControlTemplate" TargetType="{x:Type TabItem}">
        <Border Margin="0,0,10,0"   
BorderThickness="{TemplateBinding BorderThickness}" 
BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}"    
SnapsToDevicePixels="True"  CornerRadius="4,4,0,0" >
            <StackPanel Orientation="Horizontal" >
                <ContentPresenter Grid.Column="0"  ContentTemplate="{TemplateBinding HeaderTemplate}"
 Content="{TemplateBinding Header}" ContentStringFormat="{TemplateBinding HeaderStringFormat}" 
ContentSource="Header" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}" 
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" 
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
                <!--关闭按钮-->
                <Button x:Name="TabItemCloseButton"   Width="15" Height="15"  
Style="{DynamicResource TabItemCloseButtonStyle}"/>
            </StackPanel>
        </Border>
    </ControlTemplate>
    
    
    
    <Style x:Key="TabItemExStyle" TargetType="{x:Type TabItem}">
        <Setter Property="FocusVisualStyle">
            <Setter.Value>
                <Style>
                    <Setter Property="Control.Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <Rectangle Margin="4,4,4,2" 
SnapsToDevicePixels="True" Stroke="{DynamicResource BorderBrush}" 
StrokeThickness="1" StrokeDashArray="1 2"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Setter.Value>
        </Setter>

        <Setter Property="Foreground" Value="black"/>
        <Setter Property="FontSize" Value="12"/>
        <Setter Property="Background" Value="#4CDCFFD2"/>

      

        <Setter Property="Template" Value="{StaticResource TabItemExControlTemplate}"/>
        <Style.Triggers>

            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Panel.ZIndex" Value="1"/>
                <Setter Property="Background" Value="#FFFFFFFF"/>
                <Setter Property="BorderThickness" Value="1,1,1,0"/>
                <Setter Property="BorderBrush" Value="#FF999999"/>
                <Setter Property="Margin" Value="0,0,0,-1"/>
               
            </Trigger>

            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="#FFFFFFFF"/>
            </Trigger>


         
        </Style.Triggers>
    </Style>

    <!--EOF TabItemExStyle-->

</ResourceDictionary>

3、定义完资源字典之后,我们并不能直接使用它,还需要在App中引用它。因此在App.xaml文件的Resource中要加入文件的引用:

<Application.Resources>

        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>
           
                <ResourceDictionary Source="Controls/TabItemExStyle.xaml" />
              


            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>


    </Application.Resources>

4、好了这会儿我们一个独立的控件算是成型了,如果你直接把一个tabItem的style设置为如上我们定义的样式的话,其实样子已经出来了,问题是,点击关闭按钮没法关闭啊。

于是乎我们还要解决关闭的问题:

定义一个TabItemEx的类,继承自TabItem,这是不是有点winform自绘控件的味道?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;

namespace LQMS
{
    public class TabItemEx : TabItem
    {
        public TabItemEx()
        {
            InitializeStyle();
            this.Loaded += new RoutedEventHandler(InitializeEvent);
        }
        private void InitializeEvent(object sender, RoutedEventArgs e)
        {



            Button btnClose = (Button)this.Template.FindName("TabItemCloseButton", this);


            if (btnClose == null) return;
          
                btnClose.Click += delegate
                {

                    DependencyObject parent = VisualTreeHelper.GetParent(this);
                    parent = VisualTreeHelper.GetParent(parent);
                    parent = VisualTreeHelper.GetParent(parent);
                    TabControlEx tabParent = (TabControlEx)parent;

                    tabParent.Items.Remove(this);

                };


        }
        private void InitializeStyle()
        {
            this.Style = (Style)App.Current.Resources["TabItemExStyle"];
        }
    }
}

看得懂以上的代码么?首先是在构造函数中把控件的Style设置为TabItemExStyle,那么该控件的风格就是如上我们定义的样子啦。订阅时间Loaded的控件,在控件加载的时候,用Template.FindName()函数找到我们原来定义的关闭按钮,然后订阅关闭按钮的事件。当关闭按钮点击的时候,我们通过VisualTreeHelper.GetParent()找到父元素,直到找到的父元素为TabControl,这个时候,我们就直接把自己(tabItem)给移除了!

5、做到这里,我们实际上完成了一个WPF自定义控件的设计。在你的文件里面如果需要引用这个控件的话,你需要带上命名空间:

xmlns:src="clr-namespace:LQMS"

<src:TabItemEx Header="首  页" x:Name="tiHeader"   Height="30"   >
                </src:TabItemEx>

好了,还是来看看我们做出来的控件究竟是啥样子吧?
TabControlEx

当然至于边框什么的,你还得定义一下TabControl的样式了。

数据驱动UI,再说一次,本质上来说,所有的软件,都是数据啊,只是呈现的方式不同罢了。

你同样也可以说,这世上的一切东西不过都是数据,罢了。

吵吵微信朋友圈,请付款实名加入:

吵吵 吵吵

发表评论

电子邮件地址不会被公开。 必填项已用*标注