欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF 自定义TabControl控件样式

程序员文章站 2022-03-04 09:57:08
一、前言程序中经常会用到tabcontrol控件,默认的控件样式很普通。而且样式或功能不一定符合我们的要求。比如:我们需要tabcontrol的标题能够居中、或平均分布;或者我们希望tabcontro...

一、前言

程序中经常会用到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>

效果如下:

WPF 自定义TabControl控件样式

三、实现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>

效果如下:

WPF 自定义TabControl控件样式

四、带关闭按钮的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的图标

WPF 自定义TabControl控件样式

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

这里面还用到了前面讲的控件buttonex,定义方法我就不重复赘述了。大家可以通过这个链接跳转查看:http://www.cnblogs.com/xiaomingg/p/8699125.html。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>

效果如下:

WPF 自定义TabControl控件样式

所有代码已经上传到github:https://github.com/cmfgit/wpfdemo.git