电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

WPF的图形呈现


发布日期:2018/7/3
 

本主题概述 WPF 可视化层本主题重点讲述 WPF 模型中呈现支持的 Visual 类的角色

Visual 对象的角色

Visual 类是每个 FrameworkElement 对象所派生自的基本抽象该类还充当在 WPF 中编写新控件的入口点在 Win 应用程序模型中该类在许多方面可以被视为窗口句柄 (HWND)

Visual 对象是一个核心 WPF 对象它的主要角色是提供呈现支持用户界面控件(如 Button 和 TextBox)派生自 Visual 类并使用该类来保持它们所呈现的数据Visual 对象为下列功能提供支持

输出显示呈现 Visual 对象的持久的序列化绘图内容

转换针对 Visual 对象执行转换

剪辑为 Visual 对象提供剪辑区域支持

命中测试确定 Visual 对象的边界内是否包含坐标或几何形状

边界框计算确定 Visual 对象的边框

但是Visual 对象不包括对非呈现功能的支持

◆事件处理

◆布局

◆样式

◆数据绑定

◆全球化

Visual 作为子类必须派生自的公共抽象类进行公开下图显示了 WPF 中所公开的可视化对象的层次结构

Visual 类的层次结构

DrawingVisual 类

DrawingVisual 是一个用于呈现形状图像或文本的轻量绘图类此类之所以被视为轻量是因为它不提供布局或事件处理功能从而能够改善运行时性能因此绘图最适于背景和剪贴画DrawingVisual 可用于创建自定义可视化对象

ViewportDVisual 类

ViewportDVisual 在二维 Visual 和 VisualD 对象之间起到桥梁作用VisualD 类是所有三维可视化元素的基类ViewportDVisual 要求您定义一个 Camera 值和一个 Viewport 值可以借助照相机来查看场景投影映射到二维图面的区域称作视区

ContainerVisual 类

ContainerVisual 类用作 Visual 对象集的容器DrawingVisual 类派生自 ContainerVisual 类这允许它包含可视化对象的集合

可视化对象中的绘图内容

Visual 对象将它的呈现数据另存为向量图形指令列表指令列表中的每一项都以序列化格式表示一组低级别的图形数据及其相关资源共有四种不同类型的呈现数据可以包含绘图内容

通过 DrawingContext您可用可视化内容填充 Visual当您使用 DrawingContext 对象的绘图命令时实际上是存储一组日后将由图形系统使用的呈现数据而不是实时绘制到屏幕上

当您创建 WPF 控件(如 Button)时该控件会为绘图对象本身隐式生成呈现数据例如设置 Button 的 Content 属性会导致该控件存储标志符号的呈现表示

Visual 将其内容描述为一个或多个包含在 DrawingGroup 中的 Drawing 对象DrawingGroup 还描述不透明蒙板转换位图效果和应用于其内容的其他操作呈现内容时DrawingGroup 操作按如下顺序应用OpacityMaskOpacityBitmapEffectClipGeometryGuidelineSet 和 Transform

下图显示了在呈现过程中 DrawingGroup 操作的应用顺序

DrawingGroup 操作的顺序

在可视化层绘制内容

绝不能直接实例化 DrawingContext但可以通过某些方法

(例如 DrawingGroup::Open 和 DrawingVisual::RenderOpen)获取绘图上下文下面的示例从 DrawingVisual 中检索 DrawingContext 并将其用于绘制矩形

在可视化层枚举绘图内容

此外Drawing 对象还可提供用来枚举 Visual 内容的对象模型

说明

您在枚举可视化层的内容时就是相当于在检索 Drawing 对象而不是以向量图形指令列表形式检索呈现数据的基础表示

下面的示例使用 GetDrawing 方法来检索 Visual 的 DrawingGroup 值并枚举该值

如何使用可视化对象来生成控件

WPF 中的许多对象都由其他可视化对象组成这意味着它们可以包含子代对象的各种层次结构WPF 中的许多用户界面元素(如控件)都由多个表示不同类型呈现元素的可视化对象组成例如Button 控件可以包含许多其他对象其中包括 ClassicBorderDecoratorContentPresenter 和 TextBlock

下面的代码显示的是在标记中定义的 Button 控件

如果您要枚举包含默认 Button 控件的可视化对象则将发现如下所示的可视化对象层次结构

可视化树层次结构的关系图

Button 控件包含一个 ClassicBorderDecorator 元素该元素又包含一个 ContentPresenter 元素ClassicBorderDecorator 元素负责为 Button 绘制边框和背景ContentPresenter 元素负责显示 Button 的内容在本例中由于您要显示文本因此 ContentPresenter 元素中包含一个 TextBlock 元素Button 控件使用 ContentPresenter这意味着该控件的内容可以由其他元素(如 Image)或几何形状(如 EllipseGeometry)来表示

控件模板

将控件扩展为控件层次结构的关键在于 ControlTemplate控件模板为控件指定默认的可视化层次结构当您显式引用某个控件时会隐式引用它的可视化层次结构您可以重写控件模板的默认值以便为控件创建自定义的可视化外观例如您可以修改 Button 控件的背景颜色值以便它使用线性渐变颜色值而不使用纯色值

用户界面元素(如 Button 控件)包含几个向量图形指令列表这些列表描述控件的全部呈现定义下面的代码显示的是在标记中定义的 Button 控件

如果您要枚举包含 Button 控件的可视化对象和向量图形指令列表

则将发现如下所示的可视化对象层次结构

可视化树和呈现数据的关系图

Button 控件包含一个 ClassicBorderDecorator 元素该元素又包含一个 ContentPresenter 元素ClassicBorderDecorator 元素负责绘制所有构成按钮边框和背景的离散图形元素ContentPresenter 元素负责显示 Button 的内容在本例中由于您要显示图像因此 ContentPresenter 元素中包含一个 Image 元素

对于可视化对象和向量图形指令列表的层次结构需要注意多个事项

该层次结构中的排序表示绘图信息的呈现顺序从可视化元素的根按照从左到右从上到下的顺序遍历子元素如果某个元素有可视化子元素则会先遍历该元素的子元素然后再遍历该元素的同级

层次结构中的非叶节点元素(如 ContentPresenter)用于包含子元素它们并不包含指令列表

如果可视化元素既包含向量图形指令列表又包含可视化子级则会先呈现父级可视化元素中的指令列表然后再呈现任何可视化子对象中的绘图

向量图形指令列表中的项按照从左到右的顺序呈现

可视化树

可视化树中包含某个应用程序的用户界面所使用的所有可视化元素由于可视化元素中包含持久的绘图信息因此您可以将可视化树视为场景图其中包含将输出写入显示设备所必需的全部呈现信息该树汇集了由该应用程序在代码或标记中直接创建的所有可视化元素该可视化树还包含由元素(如控件和数据对象)的模板扩展功能创建的所有可视化元素

下面的代码显示的是在标记中定义的 StackPanel 元素

如果您要枚举包含标记示例中 StackPanel 元素的可视化对象将发现如下所示可视化对象的层次结构

可视化树层次结构的关系图

呈现顺序

通过可视化树可以确定 WPF 可视化对象和绘图对象的呈现顺序将从位于可视化树中最顶层节点中的可视化元素根开始遍历然后将按照从左到右的顺序遍历可视化元素根的子级如果某个可视化元素有子级则将先遍历该可视化元素的子级然后再遍历其同级这意味着子可视化元素的内容先于该可视化元素本身的内容而呈现

可视化树呈现顺序的关系图

可视化元素根

可视化元素根是可视化树层次结构中最顶层的元素在大多数应用程序中可视化元素根的基类是 Window 或 NavigationWindow但是如果您在 Win 应用程序中承载可视化对象则可视化元素根将是在 Win 窗口中承载的最顶层的可视化元素

与逻辑树的关系

WPF 中的逻辑树表示应用程序在运行时的元素尽管您不直接操作该树但是该应用程序视图对于了解属性继承和事件路由非常有用与可视化树不同逻辑树可以表示非可视化数据对象(如 ListItem)在许多情况下逻辑树密切映射到应用程序的标记定义下面的代码显示的是在标记中定义的 DockPanel 元素

如果您要枚举包含标记示例中 DockPanel 元素的逻辑对象则将发现如下所示逻辑对象的层次结构

逻辑树的关系图

可视化树和逻辑树与当前的应用程序元素集合同步并反映对元素进行的任何添加删除或修改但是这些树表示不同的应用程序视图与可视化树不同逻辑树不展开控件的 ContentPresenter 元素这意味着同一组对象的逻辑树和可视化树之间没有直接的一对一对应关系实际上在将同一个元素用作参数的情况下调用 LogicalTreeHelper 对象的 GetChildren 方法与调用 VisualTreeHelper 对象的 GetChild 方法会生成不同的结果

使用 XamlPad 查看可视化树

WPF 工具 (XamlPad) 提供了一个用来查看和浏览可视化树的选项该树与当前所定义的 XAML 内容相对应单击菜单栏上的显示可视化树[Show Visual Tree]按钮可显示相应的可视化树

下面将说明如何在 XamlPad 的可视化树资源管理器[Visual Tree Explorer]面板中将 XAML 内容扩展为可视化树节点

XamlPad 中的可视化树资源管理器[Visual Tree Explorer]面板

请注意LabelTextBox 和 Button 控件在 XamlPad 的可视化树资源管理器[Visual Tree Explorer]面板中各自显示一个可视化对象层次结构这是由于 WPF 控件具有一个包含其可视化树的 ControlTemplate当您显式引用某个控件时会隐式引用它的可视化层次结构

分析可视化性能

WPF 提供了一套性能分析工具来帮助您分析应用程序的运行时行为并确定可以应用的性能优化的类型可视化探查器工具通过直接映射到应用程序的可视化树来为性能数据提供一个丰富的图形视图在此屏幕快照中可视化探查器的CPU Usage[CPU 使用率]部分使您可以清楚地了解对象对 WPF 服务(如呈现和布局)的使用情况

可视化探查器显示输出

可视化对象的呈现行为

WPF 引进了几个影响可视化对象呈现行为的功能保留的模式图形矢量图形和与设备无关的图形

保留的模式图形

了解即时模式和保留模式图形系统之间的区别是了解 Visual 对象角色的要点之一基于 GDI 或 GDI+ 的标准 Win 应用程序使用即时模式图形系统这意味着应用程序负责重新绘制工作区中由于某项操作(如调整窗口大小)或者对象的可视化外观发生变化而失效的部分

Win 呈现顺序的关系图

与之相比WPF 使用保留模式系统这意味着具有可视化外观的应用程序对象定义一组序列化绘图数据在定义了绘图数据之后系统会响应所有的重新绘制请求来呈现应用程序对象甚至在运行时您也可以修改或创建应用程序对象并仍旧依赖系统响应绘制请求保留模式图形系统中有一个强大功能那就是绘图信息总是由应用程序保持为序列化状态但是呈现功能仍由系统负责下面的关系图演示应用程序如何依赖 WPF 来响应绘制请求

WPF 呈现顺序的关系图

智能重绘

使用保留模型图形的最大好处之一就是WPF 可以高效率地优化需要在应用程序中重绘的内容即使您有一个具有各种不透明度的复杂场景通常也不必编写特殊用途的代码来优化重绘功能请将智能重绘功能与 Win 编程进行比较在后者中可以通过最小化更新区域中的重绘量来尽力优化应用程序

向量图形

WPF 使用向量图形作为其呈现数据的格式向量图形(包括可缩放的向量图形 (SVG)Windows 元文件 (wmf) 和 TrueType 字体)存储呈现数据并以指令列表的形式传输该呈现数据这些指令描述如何使用图形基元来重新创建图像例如TrueType 字体是描述一组直线曲线和命令(而不是像素数组)的矢量字矢量图形的主要好处之一就是能够伸缩到任何大小和分辨率

与矢量图形不同位图图形以图像的逐像素表示形式来存储呈现数据而且在特定的分辨率下预先呈现位图图形格式和矢量图形格式的主要区别之一就是对原始图像的保真度例如当某个源图像的大小发生变化时位图图形系统会拉伸该图像而向量图形系统会伸缩该图像从而保持图像的保真度

下图显示了源图像在放大到 倍时的情况请注意当源图像作为位图图形拉伸时会发生失真而当源图像作为矢量图形伸缩时则不会发生失真

光栅图形和矢量图形之间的区别

下面的标记显示所定义的两个 Path 元素第二个元素使用 ScaleTransform 将第一个元素的绘图指令放大到 请注意 Path 元素中的绘图指令保持不变

关于分辨率和与设备无关的图形

可通过以下两个系统因子来确定屏幕上的文本大小和图形大小分辨率和 DPI分辨率描述出现在屏幕上的像素数量由于分辨率变大因此像素会变小从而导致所显示的图形和文本会变小在将显示器的分辨率从 x 更改为 x 显示器上所显示的图形会小得多

另一个系统设置 (DPI) 以像素数来描述屏幕英寸的大小大多数 Windows 系统的 DPI 都为 这意味着一屏幕英寸等于 个像素增加 DPI 设置会使屏幕英寸变大减小 DPI 会使屏幕英寸变小这意味着屏幕英寸与实际的英寸不相等在多数系统上二者很有可能不相等当您增加 DPI 时屏幕英寸会变大因此支持 DPI 的图形和文本也会变大增加 DPI 可能会增强文本的可读性在高分辨率下尤其如此

并非所有的应用程序都支持 DPI一些应用程序将硬件像素作为其主要计量单位更改系统 DPI 不会对这些应用程序产生任何影响许多其他应用程序都使用支持 DPI 的单位来描述字号使用像素来描述任何其他内容DPI 太小或太大都可能会导致这些应用程序出现布局问题因为应用程序的文本会随着系统的 DPI 设置而伸缩而应用程序的 UI 却不会出现此类问题对于使用 WPF 开发的应用程序此问题已经消除

WPF 支持通过将与设备无关的像素(而不是硬件像素)用作其主要计量单位来自动伸缩图像和文本会适当伸缩而无需应用程序开发人员执行任何额外的工作下图显示了 WPF 文本和图形在不同 DPI 设置下的显示方式的示例

不同 DPI 设置下的图形和文本

VisualTreeHelper 类

VisualTreeHelper 类是一个静态帮助器类它提供了一个要在可视化对象级别编程的低级功能该类在非常特殊的方案(如开发高性能自定义控件)中非常有用

在大多数情况下更高级的 WPF 框架对象(如 Canvas 和 TextBlock)提供更大的灵活性且更易于使用

命中测试

VisualTreeHelper 类提供了当默认的命中测试支持无法满足您的需要时针对可视化对象的命中测试方法可以在 VisualTreeHelper 类中使用 HitTest 方法来确定几何形状或点坐标值是否位于给定对象(如控件或图形元素)的边界内例如您可以使用命中测试来确定鼠标在对象边框中的单击点是否落在圆形几何形状内部您还可以选择重写对命中测试的默认实现来执行自己的自定义命中测试计算

枚举可视化树

VisualTreeHelper 类提供了用来枚举可视化树成员的功能若要检索父级请调用 GetParent 方法若要检索可视化对象的子级或直接子代请调用 GetChild 方法此方法返回父级在指定索引处的子 Visual

下面的示例演示如何枚举一个可视化对象的所有子代如果您对序列化可视化对象层次结构的所有呈现信息感兴趣则可能希望使用该技术

在大多数情况下逻辑树更能表示 WPF 应用程序中的元素尽管您不直接修改逻辑树但是该应用程序视图对于了解属性继承和事件路由非常有用与可视化树不同逻辑树可以表示非可视化数据对象(如 ListItem)

VisualTreeHelper 类提供用来返回可视化对象边框的方法可以通过调用 GetContentBounds 来返回可视化对象的边框可以通过调用 GetDescendantBounds 来返回可视化对象及其所有子代的边框下面的代码演示如何计算可视化对象及其所有子代的边框

上一篇:用NextResult方法取得多个Result Set

下一篇:VB Shell调用后 等待程序运行结束