【WPF学习】第四十四章 图画
通过上一章的学习,Geometry抽象类表示形状或路径。Drawing抽象类扮演了互补的角色,它表示2D图画(Drawing)——换句话说,它包含了显示矢量图像或位图需要的所有信息。
尽管有几类画图类,但只有GeometryDrawing类能使用已经学习过的几何图形。它增加了决定如何绘制图形的画笔和填充细节。可将GeometryDrawing对象视为矢量插图中的形状。例如,可将标准的窗口元文件格式(.wmf)转换成准备插入用户界面的GeometryDrawing对象的集合。
分析一个简单的示例有帮助的。前面已经看到了如何定义表示三角形的PathGeometry对象:
<PathGeometry>
<PathFigure IsClosed="True" StartPoint="0,100">
<LineSegment Point="100,100"/>
<LineSegment Point="100,50"/>
</PathFigure>
</PathGeometry>
可使用PathGeometry对象创建GeometryDrawing对象,如下所示:
<GeometryDrawing Brush="Yellow" >
<GeometryDrawing.Pen>
<Pen Brush="Blue" Thickness="3"></Pen>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry>
<PathFigure IsClosed="True" StartPoint="10,100">
<LineSegment Point="100,100" />
<LineSegment Point="100,50" />
</PathFigure>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
现在,PathGeometry对象定义了形状(三角形)。GeometryDrawing对象定义了形状的外观(具有蓝色边界的黄色三角形)。PathGeometry对象和GeometryDrawing对象都不是元素,所以不能直接使用这两个对象中的任何一个为窗口添加自己绘制的内容,而需要使用另一个支持图画的类。
GeometryDrawing类不是WPF总唯一的图画类(尽管当使用2D矢量图形时,该类是最相关的一个类)。实际上,Drawing类用于表示所有类型的2D图形,并且还有一个小组类继承自该类。下表列出了所有这些类。
表 图画类
一、显示图画
因为继承自Drawing的类不是元素,所以不能将它们放置到用户界面中。为了显示图画,需要使用下表列出的三个类中的一个。
表 用于显示图画的类
所有这些类中都存在通用主题。非常简单,它们提供了使用更少系统资源显示2D内容的方法。
例如,假如希望使用矢量图像为按钮创建图表。最简单的方法(也是占用资源最多的方法)是在按钮中放置Canvas控件,并在Canvas控件中放置一系列继承自Shape类的元素:
<Button ...>
<Canvas ...>
<Polyline ...>
<Polyline ...>
<Rectangle ...>
<Ellipse ...>
<Polygon ...>
</Canvas>
</Button>
现在已经知道,如果是使用这种方法,每个元素都是完全独立的,具有自己的内存区域和事件处理程序等。一个更好的减少元素数量的方法是Path元素。因为每个路径具有单独的笔画和填充,所以仍需要大量Path对象,不过这还是能够在一定程度上减少元素数量:
<Button ...>
<Canvas ...>
<Path ...>
<Path ...>
...
</Canvas>
</Button>
一旦开始使用Path元素,就将独立形状变换为不同的几何图形。可从路径中提取几何图形、笔画以及填充信息并将它们转换成图画,从而再增加一个抽象层。然后可在DrawingGroup对象中将这些图画融合在一起,并将DrawingGroup对象放置到DrawingImage对象中,DrawingImage对象又可被放入到Image元素中。下面是这一过程创建的新标记:
<Button ...>
<Image ...>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing ...>
<GeometryDrawing ...>
<GeometryDrawing ...>
...
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Button>
这是一次意义重大的改变。该例并没有简化标记,只是用GeometryDrawing对象代替了每个Path对象。然而,由于减少了元素的数量,因此降低了所需的开销。在前面的示例中创建了包含在按钮中的Canvas控件,并为每个路径添加了单独的元素。但该例只需要一个嵌套的元素:位于按钮中的Image元素。付出的代价是不能再为每个不同的路径处理事件(例如,不能探测鼠标在图画中独立区域的单击操作)。但在用于按钮的静态图像中,未必需要使用这种功能。
尽管使用DrawingImage对象已经节省了大量资源,但仍可进一步提高效率,借助于Drawing删除另一个元素。
基本思想是在DrawingBrush对象中的封装DrawingImage对象,如下所示:
<Button ...>
<Button.Background>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing ...>
<GeometryDrawing ...>
<GeometryDrawing ...>
<....>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Button.Background>
</Button>
DrawingBrush方法和前面介绍的DrawingImage方法不完全相同。因为Image元素改变其内容的默认方式是不同的。Image.Stretch属性的默认值是Uniform,该设置会为了适应可用空间而放大或缩小图像。DrawingBrush.Stretch属性的默认值是Fill,该设置可能会扭曲图像。
当改变DrawingBrush的Stretch属性时,为明确扭曲填充区域中图画的位置和尺寸,也可能希望调整Viewport设置。例如,下面的标记缩放由图画画刷使用的图画,以占用填充区域的90%:
<DrawinBrush Stretch="Fill" Viewport="0,0 0.9,0.9">
对于按钮示例这是非常有用的。因为可为按钮周围的边框留出一定的空间。因为DrawingBrush并非元素,所以不能使用WPF布局过程。这意味着和Image元素不同,DrawingBrush中的内容放置不会考虑Button.Paddin属性值。
使用DrawingBrush方式的一个古怪问题是,将鼠标移到按钮上时内容会消失,并且会使用一个新画刷绘制按钮表面。但当使用Image方式时,图片就不受影响。为了解决这个问题,需要为按钮创建自定义按钮控件模板,该模板使用不同的方式绘制按钮的背景。
无论是在DrawingImage本身中使用图形,还是使用DrawingBrush封装图形,都应当考虑使用资源分解标记。基本思想是作为不同资源定义每个DrawingImage或DrawingBrush对象,从而当需要时就可以引用定义的对象。如果希望在多个元素或窗口中显示相同的内容,这是特别好的思想,因为只需要重用资源,而不必复制整块标记。如下面的示例所示:
<Window x:Class="Drawing.Drawings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Drawings" Height="300" Width="300">
<Window.Resources>
<GeometryDrawing x:Key="Drawing" Brush="Yellow" >
<GeometryDrawing.Pen>
<Pen Brush="Blue" Thickness="3"></Pen>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry>
<PathFigure IsClosed="True" StartPoint="10,100">
<LineSegment Point="100,100" />
<LineSegment Point="100,50" />
</PathFigure>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</Window.Resources> <StackPanel Orientation="Horizontal" Margin="5">
<Button Width="30" Height="30">
<Image>
<Image.Source>
<DrawingImage Drawing="{StaticResource Drawing}">
</DrawingImage>
</Image.Source>
</Image>
</Button>
<Button Width="30" Height="30">
<Button.Background>
<DrawingBrush Stretch="Uniform" Viewport="0,0 0.9,1" Drawing="{StaticResource Drawing}">
</DrawingBrush>
</Button.Background>
</Button>
</StackPanel>
</Window>
Drawings
二、导出插图
尽管所有这些示例都内联地声明他们的图画,但更常用的方法是将该内容的某些部分放到资源字典中,从而可在整个应用程序中重用(并非一个地方进行修改)。由用户来确定如何将这些标记分割到资源中,但两种常用的方法是,存储一个充满DrawingImage对象的字典,或存储一个保存DrawingBrush对象的字典。此外,也可以分离出Geometry对象,并将它们存储为独立的资源。如上面的示例所示。
当然,很少有开发人员会手动编写大量图形。反而, 他们将使用专门的设计工具导出所需的XAML内容。大多数设计工具目前还不支持XAML导出功能,不过有许多插件和转换工具可以弥补这一缺陷。下面是几个例子:
- http://www.mikeswanson.com/XAMLExport/上有一个用于Adobe Illustrator工具的免费XAML插件
- http://www.mikeswanson.com/swf2xaml/上有一个用于Adobe Flash文件的免费XAML转换工具。
- Expression Design是Microsoft公司的插入和图形设计程序,内置了XAML导出功能。该程序能够读取各种矢量图形文件格式,包括.wmf(Windows元文件格式)文件,还可以导入已经存在的插图并将其导出为XAML格式。
然而,即使使用其中某个工具,前面学习的有关图形和图画的知识依然十分重要,主要原因如下几点:
首先,许多程序允许选择是希望作为Canvas控件中的独立元素的组合导出图画,还是希望作为DrawingBrush或DrawingImage资源的集合导出图画。通常,第一种选择是默认选择,因为它保留了许多特性。然而,如果使用大量图画,并且图画很复杂,或者只是希望为了尽可能减少内存需求而使用静态图形,如按钮图形,使用DrawingBrush或DrawingImage资源更好的多。而且,这些格式和用户界面的其他部分是相互独立的,所以在以后很容易更新它们。
之所以说理解2D图形基础知识是很重要的,另一个原因是这样可以更容易地控制它们。例如,可通过以下方式替换标准的2D图形:修改用于绘制各种形状的画刷、为单个几何图形应用变化、改变不透明度或者变换整个形状层(通过DrawingGroup对象)。更富有戏剧性的是,可添加、删除和替换单个几何图形。
【WPF学习】第四十四章 图画的更多相关文章
- 【WPF学习】第二十六章 Application类——应用程序的生命周期
在WPF中,应用程序会经历简单的生命周期.在应用程序启动后,将立即创建应用程序对象,在应用程序运行时触发各种应用程序事件,你可以选择监视其中的某些事件.最后,当释放应用程序对象时,应用程序将结束. 一 ...
- 【WPF学习】第十四章 事件路由
由上一章可知,WPF中的许多控件都是内容控件,而内容控件可包含任何类型以及大量的嵌套内容.例如,可构建包含图形的按钮,创建混合了文本和图片内容的标签,或者为了实现滚动或折叠的显示效果而在特定容器中放置 ...
- 【WPF学习】第二十四章 基于范围的控件
WPF提供了三个使用范围概念的控件.这些控件使用在特定最小值和最大值之间的数值.这些控件——ScrollBar.ProgressBar以及Slider——都继承自RangeBase类(该类又继承自Co ...
- 【WPF学习】第二十九章 元素绑定——将元素绑定到一起
数据banding的最简单情形是,源对象时WPF元素而且源属性是依赖性属性.前面章节解释过,依赖项属性具有内置的更改通知支持.因此,当在源对象中改变依赖项属性的值时,会立即更新目标对象中的绑定属性.这 ...
- 【WPF学习】第十五章 WPF事件
前两章学习了WPF事件的工作原理,现在分析一下在代码中可以处理的各类事件.尽管每个元素都提供了许多事件,但最重要的事件通常包括以下5类: 生命周期事件:在元素被初始化.加载或卸载时发生这些事件. 鼠标 ...
- 【WPF学习】第二十二章 文本控件
WPF提供了三个用于输入文本的控件:TextBox.RichTextBox和PasswordBox.PasswordBox控件直接继承自Control类.TextBox和RichTextBox控件间接 ...
- 【WPF学习】第十九章 控件类
WPF窗口充满了各种元素,但这些元素中只有一部分是控件.在WPF领域,控件通常被描述为与用户交互的元素——能接收焦点并接受键盘或鼠标输入的元素.明显的例子包括文本框和按钮.然而,这个区别有时有些模糊. ...
- 【WPF学习】第二十五章 日期控件
WPF包含两个日期控件:Calender和DatePicker.这两个控件都被设计为允许用户选择日期. Calendar控件显示日期,在与Windows操作系统中看到的日历(例如,当配置系统日期时看到 ...
- 我的MYSQL学习心得(十四) 备份和恢复
我的MYSQL学习心得(十四) 备份和恢复 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) ...
- “全栈2019”Java第四十九章:重载与重写对比详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
随机推荐
- angular.foreach 循环方法
angular循环给一个 angular监听的变量复值时.最好还是用angular自带的循环方法.“angular.foreach” 尽量避免代码的冲突,最好不要jq angular 混用 var o ...
- 测开大佬告诉你:如何5分钟快速创建restful风格的API接口-使用django restframework框架
一.思考❓❔ 1.创建API接口难吗? 软件测试工程师: 只测过API接口, 从没创建过 应该需要掌握一门后端开发语言和后端开发框架吧!? 脑容量有限,想想就可怕 2.如何创建API接口呢? 使用Dj ...
- ArcGIS Desktop 10.1 下载地址及破解
ArcGIS Desktop 10.1 正式版请到这里下载 http://pan.baidu.com/share/link?shareid=27476&uk=3608003693 正式版破解方 ...
- String类方法的使用
String类的判断功能: boolean equals(Object obj) //比较字符串内容是否相同(区分大小写). boolean equalsIgnoreCase(String str) ...
- 灵魂画师,在线科普多云平台/CMP云管平台/中间件/虚拟化/容器是个啥
原创: 灵魂工作室 速石科技 经常碰到有人问: 你们是云管吗? 你们和CMP多云管理平台有什么区别? 你们这个多云平台到底是个啥? emmmmm,问题还挺不好回答. 为了说清楚这些问题,但又不希望你们 ...
- Python线程-死锁
死锁产生的4个必要条件: 1.互斥:一个资源同一时刻只允许一个线程进行访问. 2.占有未释放:一个线程占有资源,且没有释放资源. 3.不可抢占:一个已经占有资源的线程无法抢占到其他线 ...
- 我的第一个 Kaggle 比赛学习 - Titanic
背景 Titanic: Machine Learning from Disaster - Kaggle 2 年前就被推荐照着这个比赛做一下,结果我打开这个页面便蒙了,完全不知道该如何下手. 两年后,再 ...
- Python中关于__main__变量的问题
在Python代码的编写中,经常会用到这么一句: if __name__ == "__main__": .... 这句代码之前的语句在整个模块被其他文件调用的时候会被运行,而这句代 ...
- Scala 学习(6)之「对象」
目录 object 伴生对象 继承抽象类 apply方法 main方法 用 object 来实现枚举功能 object 相当于 class 的单个实例,通常在里面放一些静态的 field 或者 met ...
- sqli_labs学习笔记(一)Less-54~Less-65
续上,开门见山 暴库: http://43.247.91.228:84/Less-54/?id=-1' union select 1,2,database() --+ challenges 爆表: h ...