经过上一章的学习,Geometry抽象类表示形状或路径。Drawing抽象类扮演了互补的角色,它表示2D图画(Drawing)——换句话说,它包含了显示矢量图像或位图须要的全部信息。ide
尽管有几类画图类,但只有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对象都不是元素,因此不能直接使用这两个对象中的任何一个为窗口添加本身绘制的内容,而须要使用另外一个支持图画的类。spa
GeometryDrawing类不是WPF总惟一的图画类(尽管当使用2D矢量图形时,该类是最相关的一个类)。实际上,Drawing类用于表示全部类型的2D图形,而且还有一个小组类继承自该类。下表列出了全部这些类。插件
表 图画类设计
1、显示图画3d
由于继承自Drawing的类不是元素,因此不能将它们放置到用户界面中。为了显示图画,须要使用下表列出的三个类中的一个。code
表 用于显示图画的类orm
全部这些类中都存在通用主题。很是简单,它们提供了使用更少系统资源显示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>
2、导出插图
尽管全部这些示例都内联地声明他们的图画,但更经常使用的方法是将该内容的某些部分放到资源字典中,从而可在整个应用程序中重用(并不是一个地方进行修改)。由用户来肯定如何将这些标记分割到资源中,但两种经常使用的方法是,存储一个充满DrawingImage对象的字典,或存储一个保存DrawingBrush对象的字典。此外,也能够分离出Geometry对象,并将它们存储为独立的资源。如上面的示例所示。
固然,不多有开发人员会手动编写大量图形。反而, 他们将使用专门的设计工具导出所需的XAML内容。大多数设计工具目前还不支持XAML导出功能,不过有许多插件和转换工具能够弥补这一缺陷。下面是几个例子:
然而,即便使用其中某个工具,前面学习的有关图形和图画的知识依然十分重要,主要缘由以下几点:
首先,许多程序容许选择是但愿做为Canvas控件中的独立元素的组合导出图画,仍是但愿做为DrawingBrush或DrawingImage资源的集合导出图画。一般,第一种选择是默认选择,由于它保留了许多特性。然而,若是使用大量图画,而且图画很复杂,或者只是但愿为了尽量减小内存需求而使用静态图形,如按钮图形,使用DrawingBrush或DrawingImage资源更好的多。并且,这些格式和用户界面的其余部分是相互独立的,因此在之后很容易更新它们。
之因此说理解2D图形基础知识是很重要的,另外一个缘由是这样能够更容易地控制它们。例如,可经过如下方式替换标准的2D图形:修改用于绘制各类形状的画刷、为单个几何图形应用变化、改变不透明度或者变换整个形状层(经过DrawingGroup对象)。更富有戏剧性的是,可添加、删除和替换单个几何图形。