在 WPF 或 UWP 中,我们平时开发所遇到的那些 UI 控件或组件,都直接或间接继承自 Framework。例如:GridStackPanelCanvasBorderImageButtonSlider。我们总会自然而然地认为这些控件都是有大小的,它们会在合适的位置显示自己,通常不会超出去。但是,FrameworkElement 甚至是 Control 用得久了,都开始忘记 VisualUIElement 带给我们的那些自由。

阅读本文将了解我们熟知的那些功能以及限制的由来,让我们站在限制之外再来审视 WPF 的可视化树,再来看清 WPF 各种控件属性的本质。


 

宽度和高度

如果问 Width/Height 属性来自谁,只要在 WPF 和 UWP 里混了一点儿时间都会知道——FrameworkElement。随着 FrameworkElement 的宽高属性一起带来的还有 ActualWidthActualHeightMinWidthMinHeightMaxWidthMaxHeight。正是这些属性的存在,让我们可以直观地给元素指定尺寸——想设置多少就设置多少。

然而……当你把宽或高设置得比父容器允许的最大宽高还要大的时候呢?我们会发现,控件被“切掉”了。


▲ 被切掉的椭圆

然而,因布局被“切掉”这一特性也是来自于 FrameworkElement

UIElement 布局时即便空间不够也不会故意去将超出边界的部分切掉,这一点从其源码就能得到证明:

/// <summary>
/// This method supplies an additional (to the <seealso cref="Clip"/> property) clip geometry
/// that is used to intersect Clip in case if <seealso cref="ClipToBounds"/> property is set to "true".
/// Typcally, this is a size of layout space given to the UIElement.
/// </summary>
/// <returns>Geometry to use as additional clip if ClipToBounds=true</returns>
protected virtual Geometry GetLayoutClip(Size layoutSlotSize)
{
if(ClipToBounds)
{
RectangleGeometry rect = new RectangleGeometry(new Rect(RenderSize));
rect.Freeze();
return rect;
}
else
return null;
}

只会在 ClipToBounds 设置为 true 的时候进行矩形切割。

然而 FrameworkElement 的切掉逻辑就复杂多了,鉴于有上百行,就只贴出链接 FrameworkElement.GetLayoutClip。其处理了各种布局、变换过程中的情况。

由于 FrameworkElement 的出现是为了让我们编程中像对待一个有固定尺寸的物体一样,所以也在切除上模拟了这样的空间有限的效果。

如果希望不被切掉,有两种方法修正:

  1. 确保布局的时候所需尺寸不大于可用尺寸(一点也不能大于,就算是 double 精度问题导致的细微偏大都不行)

    • MeasureOverride 返回的尺寸不大于参数传入的尺寸
    • ArrangeOverride 返回的尺寸不大于参数传入的尺寸
  2. 重写 GetLayoutClip 方法,并返回 null(或者写成 UIElement 那样)

布局系统

提及 MeasureOverrideArrangeOverride,大家都会认为这是 WPF 布局系统给我们提供的两个可供重写的方法。然而,这两个方法其实也是 FrameworkElement 才提供的。

真正布局的方法是 MeasureArrange,而可供重写的方法是 MeasureCoreArrangeCore。这两组方法均来自于 UIElement,而布局系统其实是 UIElement 引入的。

那么 FrameworkElement 做了什么呢?它密封了 MeasureCoreArrangeCore 这两个布局的重写方法,以便能够处理 WidthHeightMinWidthMinHeightMaxWidthMaxHeightMargin 这些属性对布局的影响。

你觉得 WidthHeight 属性是元素的最终宽高吗?我们在 宽度和高度 一节中已经说了不是,前面一段也说了不是——它们真的只是布局属性!然而,这真的很容易形成误解!Width``Height 属性其实和 MinWidth``MinHeightMaxWidth``MaxHeight 是完全一样的用途,只是在布局过程中为计算最终尺寸提供的布局限制而已。只不过 MinWidth``MinHeightMaxWidth``MaxHeight 用大于和小于进行尺寸的限制,而 Width``Height 用等于进行尺寸的限制。最终的尺寸依然是 ActualWidth``ActualHeight,而这个值跟 RenderSize 其实是一个意思,因为内部获取的就是 RenderSize

值得注意的是,ActualWidth``ActualHeightRenderSize 一样,是布局结束后才会更新的,开发中需要如果修改了属性立即获取这些值其实必然是旧的,拿这些值进行计算会造成错误的尺寸数据。

顺便吐槽一下:其实微软是喜欢用 Core 来作为子类重写方法的后缀的,比如 FreezableEasingFunction 都是用 Core 后缀来处理重写。Override 后缀纯属是因为 UIElement 把这个名字用了而已。

屏幕交互

UIElement 中存在着布局计算,FrameworkElement 中存在着带限制的布局计算,这很容易让人以为屏幕相关的坐标计算会存在于 UIElement 或者 FrameworkElement 中。

然而其实 UIElement 或者 FrameworkElement 只涉及到控件之间的坐标计算(TranslatePoint),真正涉及到屏幕坐标的转换是位于 Visual 中的,典型的是这几个:

  • TransformToAncestor
  • TransformToDescendant
  • TransformToVisual
  • PointFromScreen
  • PointToScreen

所以其实如果希望做出非常轻量级的高性能 UI,继承自 Visual 也是一个大胆的选择。当然,真正遇到瓶颈的时候,继承自 Visual 也解决不了多少问题。

样式和模板

FrameworkElement 开始有了样式(Style),Control 开始有了模板(Template)。而模板极大地方便了样式定制的同时,也造成了强大的性能开销,因为本来的一个 Visual 瞬间变成了几个、几十个。一般情况下这根本不会是性能瓶颈,然而当这种控件会一次性产生几十个甚至数百个(例如表格)的时候,这种瓶颈就会非常明显。

总结容易出现理解偏差的几个点

  1. WidthHeight 属性其实只是为布局过程中的计算进行限制而已,跟 MinWidthMinHeightMaxWidthMaxHeight 没有区别,并不直接决定实际尺寸。
  2. 如果发现元素布局中被切掉了,这并不是不可避免的问题;因为切掉是 FrameworkElement 为我们引入的特性,不喜欢可以随时关掉。
  3. 微软对于子类重写核心逻辑的方法喜欢使用 Core 后缀,布局中用了 Override 只是因为名字被占用了。
  4. Visual 就可以计算与屏幕坐标之间的转换。
  5. 模板(Template)会额外产生很多个 Visual,有可能会成为性能瓶颈。

参考资料

Visual->UIElement->FrameworkElement,带来更多功能的同时也带来了更多的限制的更多相关文章

  1. jQuery+php+Ajax文章列表点击加载更多功能

    jQuery+php+Ajax实现的一个简单实用的文章列表点击加载更多功能,点击加载更多按钮,文章列表加载更多数据,加载中有loading动画效果. js部分: <script type=&qu ...

  2. Android 上拉加载更多功能

    前几天看了github上面的例子,参照它的实现,自己又稍微改了一点,往项目里面增加了一个上拉加载更多功能.具体的实现如下: 首先要重写ListView: import android.content. ...

  3. 关闭 Visual Studio 的 Browser Link 功能

    最近公司弄新项目需要用 MVC,就把 IDE 升级到了 Visual Studio 2013,在开发的时候发现有好多请求一个本地49925的端口 . 很奇怪,一开始以为是 Visual Studio ...

  4. React-Native 之 GD (十一)加载更多功能完善 及 跳转详情页

    1.加载更多功能完善 GDHome.js /** * 首页 */ import React, { Component } from 'react'; import { StyleSheet, Text ...

  5. (数据科学学习手札134)pyjanitor:为pandas补充更多功能

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 pandas发展了如此多年,所包含的功能已 ...

  6. Visual Studio 2013 Preview 新功能

    先来看一下Visual Studio的版本历史: 1. Visual Studio.NET 2002 2. Visual Studio.NET 2003 3. Visual Studio.NET 20 ...

  7. Visual Studio Community 2013,功能完整,免费使用

    http://www.infoq.com/cn/news/2014/11/VSC2013 微软刚刚宣布了.NET平台的开源计划,与此同时,它还推出了源自Visual Studio Profession ...

  8. 分页插件思想:pc加载更多功能和移动端下拉刷新加载数据

    感觉一个人玩lol也没意思了,玩会手机,看到这个下拉刷新功能就写了这个demo! 这个demo写的比较随意,咱不能当做插件使用,基本思想是没问题的,要用就自己封装吧! 直接上代码分析下吧! 布局: & ...

  9. 内核调试神器SystemTap — 更多功能与原理(三)

    a linux trace/probe tool. 官网:https://sourceware.org/systemtap/ 用户空间 SystemTap探测用户空间程序需要utrace的支持,3.5 ...

随机推荐

  1. 使用SoupUI进行简单的WebService接口测试

    1.工具介绍 SoapUI是一个开源测试工具,通过soap/http来检查.调用.实现Web Service的功能/负载/符合性测试.该工具既可作为一个单独的测试软件使用,也可利用插件集成到Eclip ...

  2. springboot获取配置文件中的内容

    代码: GrilApplication.java @SpringBootApplication public class GrilApplication { public static void ma ...

  3. 如何高效的使用 Git

    -- 代码昨天还是运行好好的今天就不行了. 代码被删了. 突然出现了一个奇怪的 bug,但是没人知道怎么回事. 如果你出现过上面的任何一种情况,那本篇文章就是为你准备的. 除了知道 git add, ...

  4. ASP.NET 4.5 MVC 4 无法运行在Windows2008的IIS7.0上显示404的解决方案

    需要在web.config下加上这个 <system.webServer> <modules runAllManagedModulesForAllRequests="tru ...

  5. vs 2017 保存文件 utf8

    vs 2017 保存文件 utf8 转自:https://blog.csdn.net/jiegemena/article/details/79369650

  6. Maven 中的dependencies与dependencyManagement的区别

    1.dependencyManagement 在Maven中dependencyManagement的作用其实相当于一个对所依赖jar包进行版本管理的管理器 在pom.xml文件中,jar的版本判断的 ...

  7. js排序算法05——快速排序

    终于到了传说中的快速排序算法了,快速排序的思想和归并排序一样,都是采用分治思想,不同之处在于归并每次将数组一分为二,最后将小的数组进行比较,合并为大数组.快排是每次找一个主元,也就是基准数,按照这个基 ...

  8. Linux安装apue.3e(基于ubuntu16.0.4)

    本菜刚刚学习UNIX高级编程,无奈搭建本书编程环境时遇到不少问题,参考了网上各路大神的解决办法,最终解决了问题. (1)下载源代码,可以去官网下载:http://apuebook.com/code3e ...

  9. hdu2426

    题解: KM模板题 如果n>m,输出-1 如果a[match[i]][i]==-1输出-1 负的边不用考虑 初始都赋值为-1 代码: #include<cstdio> #includ ...

  10. JBPM4入门——1.jbpm简要介绍

    本博文只是简要对JBPM4进行介绍,如需更详细内容请自行google 链接: JBPM入门系列文章: JBPM4入门——1.jbpm简要介绍 JBPM4入门——2.在eclipse中安装绘制jbpm流 ...