效果如图:

虽然说是自适应可关闭的TabControl,但TabControl并不需要改动,不如叫自适应可关闭的TabItem.

大体思路:建一个用户控件,继承自TabItem,里面放个按钮,点击的时候在TabControl中移除自身.在添加,移除TabItem和TabControl尺寸变化时,通过Items的个数计算合适的Width.

新建用户控件

新建用户控件,并继承自TabItem,这样它就拥有TabItem所有的属性和事件.而这个功能不需要自定义依赖属性和事件.它的用法就和TabItem完全一样.

建完后把UserControl换成TabItem,去掉多余部分

后台继承自UserControl改成继承自TabItem

更改样式添加关闭按钮

在Xmal里添加一个自己喜欢的样式,最主要的是在Template里添加一个按钮,注册一个Click事件,用于关闭.

 1 <Style TargetType="{x:Type TabItem}">
2 <Setter Property="BorderBrush" Value="Black"></Setter>
3 <Setter Property="Background" Value="White"></Setter>
4 <Setter Property="Foreground" Value="Black"></Setter>
5 <Setter Property="Padding" Value="5,0,0,0"></Setter>
6 <Setter Property="HorizontalAlignment" Value="Left"></Setter>
7 <Setter Property="VerticalAlignment" Value="Center"></Setter>
8 <Setter Property="HorizontalContentAlignment" Value="Left"></Setter>
9 <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
10 <Setter Property="Template">
11 <Setter.Value>
12 <ControlTemplate TargetType="{x:Type TabItem}">
13 <Border CornerRadius="5,0,0,0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
14 <Grid>
15 <Grid.ColumnDefinitions>
16 <ColumnDefinition Width="*"></ColumnDefinition>
17 <ColumnDefinition Width="20"></ColumnDefinition>
18 </Grid.ColumnDefinitions>
19 <ContentPresenter Grid.Column="0" ContentSource="Header" Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"></ContentPresenter>
20 <Button Grid.Column="1" Name="btn_Close" Click="btn_Close_Click"></Button>
21 </Grid>
22 </Border>
23 <ControlTemplate.Triggers>
24 <Trigger Property="IsSelected" Value="true">
25 <Setter Property="Background" Value="#FFFF923E"></Setter>
26 <Setter Property="Foreground" Value="White"></Setter>
27 </Trigger>
28 </ControlTemplate.Triggers>
29 </ControlTemplate>
30 </Setter.Value>
31 </Setter>
32 </Style>

后台的逻辑

查找父级TabControl

注意TabItem并不能关闭自身,这里所说的关闭其实是在他父级TabControl的Items集合里移除.而且父级TabControl的尺寸改变时还要注册事件去改变每个Item的Width.所以我决定找到它的父级TabControl,声明一个私有变量添加对父级的引用.

可以通过可视化树的帮助类VisualTreeHelper来找到它的父级TabControl.当然并不是它的父级直接就是TabControl了,需要递归去查找

 1 /// <summary>
2 /// 递归找父级TabControl
3 /// </summary>
4 /// <param name="reference">依赖对象</param>
5 /// <returns>TabControl</returns>
6 private TabControl FindParentTabControl(DependencyObject reference)
7 {
8 DependencyObject dObj = VisualTreeHelper.GetParent(reference);
9 if (dObj == null)
10 return null;
11 if (dObj.GetType() == typeof(TabControl))
12 return dObj as TabControl;
13 else
14 return FindParentTabControl(dObj);
15 }

计算尺寸

既然是自适应,总得有一个正常的尺寸,只有空间不足的时候才去缩小每个Item.我想到的最简单的办法就是做个约定,把这个尺寸放到父级TabControl的Tag里,这样可以通过对父级TabControl的引用,轻松拿到这个尺寸.

计算方法就是取父级TabControl运行时的宽度ActualWidth除以约定的尺寸,取整形int,这个就是保持约定宽度item个数的临界值了.

小于等于这个值就用约定宽度,大于这个值就用父级运行宽度除以Items的个数求出平均宽度,然后遍历父级TabControl的Items,都赋上这个平均值.

需要注意的是,如果所有Items的尺寸加起来大于等于父级的尺寸,Items会换行,感觉有点丑啊.所以我取的是父级运行宽度-5做的运算,这样就永远也抵达不到边界,不会换行.

不过也可以改写TabControl的控件模版,把放Hrader的容器换成Stackpanel就不会换行了,我只是觉得上面的方法比较简单.

父级尺寸改变

可以通过TabControl的SizeChanged事件监测到.需要干的事就是重新计算尺寸.

关闭按钮

在父级TabControl的Items集合里移除自身后,注意重新计算下尺寸和移除注册SizeChanged事件的方法.

最后附上代码 自适应可关闭的Tab

这个效果比较常见,可能您已经做过了,有更好的想法希望您能分享出来,大家共同进步

WPF自适应可关闭的TabControl 类似浏览器的标签页(转)的更多相关文章

  1. WPF自适应可关闭的TabControl 类似浏览器的标签页

    效果如图: 虽然说是自适应可关闭的TabControl,但TabControl并不需要改动,不如叫自适应可关闭的TabItem. 大体思路:建一个用户控件,继承自TabItem,里面放个按钮,点击的时 ...

  2. vue单页面条件下添加类似浏览器的标签页切换功能

    在用vue开发的时候,单页面应用程序,而又有标签页这种需求,各种方式实现不了, 从这个 到这个,然后再返回上面那个 因为每个标签页的route不一样,导致组件重新渲染的问题,怎么都不知道如何实现... ...

  3. 阻止iOS Web APP中点击链接跳转到Safari 浏览器新标签页

    问题:ios封装完之后,点击里边的按钮会跳转到网页上 ——小卡遇到这个问题就是这样解决的↓↓↓ 解决方法:建议将代码放到</head>标签前,当然,另外存为一个js 文件引用也是可以的呦~ ...

  4. 监听浏览器tab选项卡选中事件,点击浏览器tab标签页回调事件,浏览器tab切换监听事件

    js事件注册代码: <script> document.addEventListener('visibilitychange',function(){ //浏览器tab切换监听事件 if( ...

  5. 类似Jquery ui 标签页(Tabs)

    <div class="indexnew_tit"> <a href="javascript:;" class="on"& ...

  6. selenium 对浏览器标签页进行关闭和切换

    关闭标签页 # 1.关闭浏览器全部标签页 driver.quit() # 2.关闭当前标签页(从标签页A打开新的标签页B,关闭标签页A) driver.close() 切换标签页 from selen ...

  7. python selenium 对浏览器标签页进行关闭和切换

    1.关闭浏览器全部标签页 driver.quit() 2.关闭当前标签页(从标签页A打开新的标签页B,关闭标签页A) driver.close() 3.关闭当前标签页(从标签页A打开新的标签页B,关闭 ...

  8. selenium WebDriver 对浏览器标签页的切换

    关于selenium WebDriver 对浏览器标签页的切换,现在的市面上最新的浏览器,当点击一个链接打开一个新的页面都是在浏览器中打开一个标签页,而selenium只能对窗口进行切换的方法,只能操 ...

  9. Winform 自定义TabControl实现浏览器标签

    作者:Gavin(daisong.michelangelo@gmail.com) 时间: Nov, 2015 封面图片为Gavin原创,请勿未经允许私自引 最近因为工作需要,要做一个桌面浏览器,和大多 ...

随机推荐

  1. Codeforces Round #378 (Div. 2)F - Drivers Dissatisfaction GNU

    http://codeforces.com/contest/733/problem/F 题意:给你一些城市和一些路,每条路有不满意程度和每减少一点不满意程度的花费,给出最大花费,要求找出花费小于s的最 ...

  2. hdu4081次小生成树

    先求一遍最小生成树,然后遍历所有边,如果这条边在最小生成树中就直接减去这条边的距离,如果不在最小生成树中,那么就构成了一个环,此时需要减去最小生成树中最大的边,即求次小生成树时的maxx, 有一点要注 ...

  3. 闲话__stdcall, __cdecl, __fastcall出现的历史背景以及各自解决的问题

    可以认为最先由微软搞出来了__stdcall, 其实就是和WINAPI的声明是一样的,入栈顺序是从右到左,函数返回时,会进行出栈操作. PASCAL语言是非常古老的编程语言,在C语言之前,因此在当时的 ...

  4. 关系型数据库事务遵循ACID原则

    事务在英文中是transaction,和现实世界中的交易很类似,它有如下四个特性: 1.A (Atomicity) 原子性 原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功 ...

  5. js获取显示器、页面等高度 (转)

    网页可见区域宽:document.body.clientWidth网页可见区域高:document.body.clientHeight网页可见区域宽:document.body.offsetWidth ...

  6. SpringMVC启动和执行流程

    Spring框架大家用得很多,相当熟悉,但是我对里面的运作比较好奇,例如bean的加载和使用,和我们定义的配置文件有什么联系;又例如aop在什么时候起作用,原理又是怎样.经过一个了解后,整理了启动和执 ...

  7. RxJava 详解

    给 Android 开发者的 RxJava 详解: http://gank.io/post/560e15be2dca930e00da1083#toc_1

  8. poj 1182 食物链(高级的带权并查集)

    食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 76486   Accepted: 22742 Description ...

  9. ElasticSearch6.0 高级应用之 多字段聚合Aggregation(二)

    ElasticSearch6.0 多字段聚合网上完整的资料很少 ,所以作者经过查阅资料,编写了聚合高级使用例子 例子是根据电商搜索实际场景模拟出来的 希望给大家带来帮助! 下面我们开始吧! 1. 创建 ...

  10. 判断两个控件在同一个Window上是否有重叠

    判断两个控件在同一个Window上是否有重叠 //对UIView写分类 - (BOOL)intersectWithView:(UIView *)view; - (BOOL)intersectWithV ...