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,定义方法我就不重复赘述了。大家可以通过这个链接跳转查看: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>
效果如下:
所有代码已经上传到github:https://github.com/cmfgit/wpfdemo.git
上一篇: MySQL中表名大小写问题
推荐阅读
-
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
-
[WPF自定义控件库] 模仿UWP的ProgressRing
-
如何在双向绑定的Image控件上绘制自定义标记(wpf)
-
[WPF自定义控件库] 给WPF一个HyperlinkButton
-
[WPF自定义控件库]使用TextBlockHighlightSource强化高亮的功能,以及使用TypeConverter简化调用
-
android开发教程之自定义控件checkbox的样式示例
-
WPF如何自定义ProgressBar滚动条样式
-
WPF自定义MenuItem样式的实现方法
-
WPF自定义实现IP地址输入控件
-
wpf 两个自定义控件