翼度科技»论坛 编程开发 .net 查看内容

WPF性能优化:形状(Shape)、几何图形(Geometry)和图画(Drawing)的使用

10

主题

10

帖子

30

积分

新手上路

Rank: 1

积分
30
在用户界面技术中,绘图是一个绕不开的话题。WPF提供了多种可根据应用程序要求进行优化的2D图形和图像的处理功能,包括画刷(Brush)、形状(Shape)、几何图形(Geometry)、图画(Drawing)和变换(Transform)等。其中形状(Shape)、几何图形(Geometry)和图画(Drawing)承担了基础的绘图功能,形状(Shape)使用方便简单,但占用资源相对较多,几何图形(Geometry)和图画(Drawing)则更轻量。
什么是形状、几何图形和图画

在WPF中,形状(Shape)是专门用于表示直线、椭圆、矩形以及多边形的绘图图元(primitive),可以绘制到窗口或控件上。几何图形(Geometry)为形状定义了坐标和尺寸等细节(可以理解为只有线条轮廓),不能直接绘制到窗口和控件上。图画(Drawing)在几何图形的基础上增加了绘制图形的笔触、笔触样式和填充细节,也不能直接绘制到窗口和控件上。
形状(Shape)

WPF中的形状(Shape)都是派生自FrameworkElement类,所以也是UI元素,提供了布局和事件处理等实用功能,可以像其他元素一样支持事件,可以响应焦点、键盘以及鼠标事件。Shape类是一个抽象类,其自身不能执行任何工作,但定义了绘制轮廓以及填充背景的画刷相关的属性,包括Fill、Stroke、StrokeThickness、StrokeStartLineCap、StrokeDashArray、StrokeLineJoin等。具体的绘制工作由以下几个子类完成:

  • Line 绘制直线元素,直线是最简单的图形元素,使用X1、Y1两个属性作为起点坐标,X2、Y2两个属性作为终点坐标。Stroke属性设置绘制直线的画刷(Brush),从基类(Shape)继承来的Fill属性不起作用
  1. [/code]
  2. [list]
  3. [*][b]Rectangle[/b] 绘制矩形的元素,通过笔触(Stroke)绘制矩形边框,使用填充(Fill)绘制背景色,这两个属性至少得设置一个,否则不会绘制矩形。从FrameworkElement继承来的Width和Height属性定义宽和高,默认值为Auto,将填充其可用的宽度或高度。Rectangle类增加了两个属性:RadiusX和RadiusY,通过这两个属性可以设置圆角,甚至可以绘制出椭圆效果。由于Rectangle是闭合的形状,所以从基类(Shape)继承来的StrokeStartLineCap和StrokeEndLineCap属性不起作用。
  4. [/list][code]
复制代码

  • Ellipse 绘制椭圆,用法和Rectangle一致,长和宽相等的椭圆即为圆形
  1. [/code]
  2. [list]
  3. [*][b]Polyline[/b] 绘制折线,有多段首尾相连的直线段组成。通过Points属性提供一些列X和Y坐标。尽管Polyline是非闭合的形状,但是设置了Fill属性时,Points属性中最后一个连接点和开始点形成的不可见虚拟线段与Polyline绘制的折线形成的闭合区间也会被填充。
  4. [/list][code]
复制代码

  • Polygon 绘制多边形,与Polyline相似,有多条直线段组成形成闭合区域。与Polyline唯一的区别就是Polygon会把Points属性中最后一个连接点和开始点连接起来。
  1. [/code]
  2. [list]
  3. [*][b]Path[/b] 绘制路径,是最为灵活的图形,可以由一个或者若干个直线、圆弧、贝塞尔曲线组成。Path类通过Data属性定义绘制的形状。Data属性的类型是Geometry类,也就是接下来要介绍的几何图形(Geometry)。
  4. [/list][size=4]几何图形(Geometry)[/size]
  5. 前边提到几何图形(Geometry)为形状定义了坐标和尺寸,但不能直接绘制到窗口和控件上,而Path形状元素的Data属性就是Geometry类,没错,几何图形(Geometry)是与Path形状结合使用的。
  6. 与Shape类一样,Geometry类也是抽象类,具体的形状的定义是通过它的子类实现的。Geometry类的子类包括:
  7. [list]
  8. [*][b]LineGeometry[/b] 直线几何图形,相当于Line形状。
  9. [*][b]RectangleGeometry[/b] 矩形几何图形,与Rectangle形状一样,可以定义圆角。
  10. [*][b]EllipseGeometry[/b] 椭圆几何图形,相当于Ellipse形状。
  11. [*][b]GeometryGroup[/b] 由多个几何图形(Geometry)组合在一起形成几何图形组,实现为单个路径(Path)添加任意多个几何图形(Geometry),可以使用EvenOdd或者NonZero填充规则来确定要填充的区域,默认的填充规则是EvenOdd。
  12. [*][b]CombinedGeometry[/b] 将两个几何图形合并为一个形状。可以使用CombineMode属性选择如何组合两个几何图形。
  13. [*][b]PathGeometry[/b] 表示更为复杂的由弧线、曲线以及直线段构成的图形,并且可以是闭合的,也可以是不闭合的。
  14. [*][b]StreamGeometry[/b] 相当于是PathGeometry的只读轻量级类。StreamGeometry的优点是可以节省内存,因为它不在内存中同事保存路径的所有单个分段。缺点是一旦被创建就不能再修改,并且不支持Binding、动画等功能。
  15. [/list]LineGeometry、RectangleGeometry、EllipseGeometry与前边介绍的Line、Rectangle、Ellipse形状对应,使用起来也很简单。以矩形为例,使用Rectangle的xaml描述:
  16. [code]
复制代码
使用Path结合RectangleGeometry的xaml描述为:
  1. <Path Fill="AliceBlue">
  2.     <Path.Data>
  3. <Grid>
  4.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  5.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  6. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  7.     </Path.Data>
  8. </Path>
复制代码
GeometryGroup

这样看起来使用几何图形(Geometry)来绘图编码更为繁琐,开篇提到的几何图形(Geometry)更轻量,占用资源更少的优点并没有体现出来。接下来要介绍的GeometryGroup则能很好的体现出几何图形(Geometry)更轻量这个优点。
比如绘制一个铜钱这样一个外圆内方的图案,使用形状(Shape)的xaml描述:
  1. <Grid>
  2.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  3.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  4. </Grid>
复制代码
使用GeometryGroup的xaml描述:
  1. <Path Fill="AliceBlue">
  2.     <Path.Data>
  3. <Grid>
  4.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  5.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  6. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  7.     </Path.Data>
  8. </Path><Path Fill="AliceBlue">
  9.     <Path.Data>
  10. <Grid>
  11.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  12.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  13. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  14.     </Path.Data>
  15. </Path><Path Fill="AliceBlue">
  16.     <Path.Data>
  17. <Grid>
  18.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  19.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  20. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  21.     </Path.Data>
  22. </Path>
复制代码
上述两种方法实现了类似的视觉效果。第一种方法使用了Ellipse和Rectangle两个UI元素,而第二种方案只用了一个Path元素,这意味减少了一个UI元素的开销。通常,一个包含N个几何图形(Geometry)的形状(Shape)比N个形状(Shape)直接进行绘制图案的性能要好。因为形状(Shape)派生自FrameworkElement类,需要维护布局、事件等功能的开销,几何图形(Geometry)则不需要。在只有几十个形状的窗口中这个差距并不明显,但对于需要成百上千个形状的窗口中,这个性能差异就值得考虑了。
GeometryGroup在性能上优于多个形状(Shape)的组合,但是不能为组合中的每个几何图形(Geometry)设置笔触、填充和注册事件,灵活性上稍逊一筹。
CombinedGeometry

GeometryGroup可以把多个几何图形(Geometry)组合成复杂的图形,但是多个图形的边界存在交叉重叠时,可能无法得到预期的效果。这个时候可以使用CombinedGeometry来处理了。CombinedGeometry用于把两个重叠在一起的几何图形(Geometry)合并成一个,通过Geometry1和Geometry2属性提供需要合并的几何图形(Geometry),尽管CombinedGeometry只能合并两个几何图形(Geometry),但是可以把合并后得到的几何图形(Geometry)与第三个进行合并,以此类推可以实现多个几何图形的合并。GeometryCombineMode属性定义了合并的方式,GeometryCombineMode枚举有以下四个值:
名称说明Union创建包含两个几何图形所有区域的GeometryIntersect创建包含两个几何图形共有区域的GeometryXor创建包含两个几何图形非共有区域的Geometry。也就是先使用Union合并几何图形,再去掉使用Intersect合并的那部分Exclude创建的Geometry包含第一个几何图形所有区域,但不包含第二个几何图形的区域用数学中集合的概念可以把Union、Intersect、Exclude理解为并集、交集和差集。下图显示了四种合并方式的区别(合并后的图形设置了填充便于表示合并后包含的区域)。

PathGeometry

前边几种方式都是以WPF内置的几何图形(Geometry)绘制或者组合来定义形状,PathGeometry则提供更小粒度的绘制元素PathSegment,PathSegment可以表示几何图形中的一段直线、弧线或者贝塞尔曲线,PathSegment是一个抽象类,具体的绘制由其派生类实现。
派生类名称说明LineSegment在PathFigure中的两个点之间创建一条直线。ArcSegment在PathFigure中的两个点之间创建一条椭圆弧。BezierSegment在PathFigure中的两个点之间创建一条三次贝塞尔曲线QuadraticBezierSegment在PathFigure中的两个点之间创建一条二次贝塞尔曲线PolyLineSegment表示由 PointCollection 定义的线段集合,可用多个LineSegment得到相同效果,但使用单个PolyLineSegment更加简明PolyBezierSegment创建一条或多条三次贝塞尔曲线PolyQuadraticBezierSegment创建一条或多条二次贝塞尔曲线使用PathGeometry创建路径很简单,以LineSegment为例。在PathGeometry的PathFigure属性中设置StartPoint作为起点,并增加一个LineSegment,其Point属性表示该条线段的结束点以及下一条线段的起点。
  1. <Path Fill="AliceBlue">
  2.     <Path.Data>
  3. <Grid>
  4.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  5.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  6. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  7.     </Path.Data>
  8. </Path><Path Fill="AliceBlue">
  9.     <Path.Data>
  10. <Grid>
  11.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  12.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  13. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  14.     </Path.Data>
  15. </Path><Path Fill="AliceBlue">
  16.     <Path.Data>
  17. <Grid>
  18.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  19.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  20. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  21.     </Path.Data>
  22. </Path><Path Fill="AliceBlue">
  23.     <Path.Data>
  24. <Grid>
  25.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  26.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  27. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  28.     </Path.Data>
  29. </Path><Path Fill="AliceBlue">
  30.     <Path.Data>
  31. <Grid>
  32.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  33.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  34. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  35.     </Path.Data>
  36. </Path>
复制代码
如果要绘制多个不连续的线段,则使用PathFigures属性,在其中添加多个PathFigures即可。
  1. <Path Fill="AliceBlue">
  2.     <Path.Data>
  3. <Grid>
  4.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  5.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  6. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  7.     </Path.Data>
  8. </Path><Path Fill="AliceBlue">
  9.     <Path.Data>
  10. <Grid>
  11.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  12.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  13. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  14.     </Path.Data>
  15. </Path><Path Fill="AliceBlue">
  16.     <Path.Data>
  17. <Grid>
  18.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  19.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  20. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  21.     </Path.Data>
  22. </Path><Path Fill="AliceBlue">
  23.     <Path.Data>
  24. <Grid>
  25.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  26.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  27. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  28.     </Path.Data>
  29. </Path><Path Fill="AliceBlue">
  30.     <Path.Data>
  31. <Grid>
  32.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  33.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  34. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  35.     </Path.Data>
  36. </Path><Path Fill="AliceBlue">
  37.     <Path.Data>
  38. <Grid>
  39.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  40.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  41. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  42.     </Path.Data>
  43. </Path><Path Fill="AliceBlue">
  44.     <Path.Data>
  45. <Grid>
  46.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  47.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  48. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  49.     </Path.Data>
  50. </Path><Path Fill="AliceBlue">
  51.     <Path.Data>
  52. <Grid>
  53.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  54.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  55. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  56.     </Path.Data>
  57. </Path><Path Fill="AliceBlue">
  58.     <Path.Data>
  59. <Grid>
  60.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  61.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  62. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  63.     </Path.Data>
  64. </Path><Path Fill="AliceBlue">
  65.     <Path.Data>
  66. <Grid>
  67.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  68.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  69. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  70.     </Path.Data>
  71. </Path><Path Fill="AliceBlue">
  72.     <Path.Data>
  73. <Grid>
  74.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  75.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  76. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  77.     </Path.Data>
  78. </Path>
复制代码
StreamGeometry

与PathGeometry类一样,StreamGeometry可以定义包含曲线、弧线和直线的复杂几何图形。与PathGeometry不同的是,StreamGeometry的内容不支持数据绑定、动画或修改。 当需要描述复杂几何图形,但又不希望产生支持数据绑定、动画或修改的开销时,建议使用 StreamGeometry。由于StreamGeometry类的高效性,该类是描述装饰器的不错选择。以下是StreamGeometry实现与上文中PathGeometry绘制多个不连续线段同样效果的代码:
  1. [/code]在给Data属性复制的时候,是使用几何图形微语言(Geometry mini-language)创建了一个StreamGeometry。示例中的几何图形微语言包含了6条指令。第一条指令(M50,100)创建了一个PathFigure,并把起点设置为(50,100),接下来的指令(L100,100 100,50)其实是(L100,100 L100,50)的简写,是创建两个创建直线段并设置每个线段终点的指令。第四条指令(M150,50)创建了一个PathFigure,并把起点设置为(150,50),接下来的指令是两条创建直线段的指令。
  2. 几何图形微语言通常是和StreamGeometry一起使用,但并不是StreamGeometry的专属。WPF中有两个类可以使用几何图形微语言:StreamGeometry和PathFigureCollection。在设置PathGeometry的Figures属性时,可以通过PathFigureCollection使用几何图形微语言。
  3. [code]<Path Fill="AliceBlue">
  4.     <Path.Data>
  5. <Grid>
  6.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  7.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  8. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  9.     </Path.Data>
  10. </Path>
复制代码
图画(Drawing)

几何图形(Geometry)为可以描述形状或者路径,图画(Drawing)在几何图形的基础上增加了绘制图形的笔触、笔触样式和填充细节,包含了显示矢量图或者位图的信息。
图画(Drawing)也是抽象类,描述矢量图或者位图的具体工作由其派生类完成。这些类包括
类名说明主要属性GeometryDrawing使用指定的画刷(Brush)和画笔(Pen)绘制几何图形。Geometry、Brush、PenImageDrawing使用指定图像(通常是基于文件的位图)和矩形边界绘制图像ImageSource、RectVideoDrawing结合播放视频文件的媒体播放器,使用指定矩形边界绘制(复制)播放器中当前画面Player、RectGlyphRunDrawing表示渲染GlyphRun的绘图对象GlyphRun、ForegroundBrushDrawingGroup组合各种类型的图画(Drawing)创建混合图画,并可以使用它的一个属性一次性为整个组合应用效果BitmapEffect、BitmapEffectInput、Children、ClipGeometry、GuidelineSet、OpacityMask、Opacity、Transform与几何图形(Geometry)类似,图画(Drawing)也不能把自身绘制在窗口或者控件上。为了显示图画,WPF提供了以下三个类。
类基类说明DrawingImageImageSource使用ImageSource封装图画(Drawing),从而在Image元素中显示或者作为ImageBrush绘制UI元素DrawingBrushBrush使用画刷封装图画(Drawing),从而作为画刷绘制UI元素DrawingVisualVisual允许在低级的可视化对象化中放置图画。DrawingImage和DrawingBrush

DrawingImage和DrawingBrush都包含了Drawing属性,从而可以使用更少的资源绘制矢量图或者位图。例如绘制一个关闭按钮,可以先用PathGeometry定义一个X的几何图形,然后用这个几何图形为GeometryDrawing的Geometry属性赋值,紧接着用DrawingBrush把GeometryDrawing封装为画刷,为按钮的Background赋值。
  1. <Path Fill="AliceBlue">
  2.     <Path.Data>
  3. <Grid>
  4.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  5.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  6. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  7.     </Path.Data>
  8. </Path><Path Fill="AliceBlue">
  9.     <Path.Data>
  10. <Grid>
  11.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  12.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  13. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  14.     </Path.Data>
  15. </Path><Path Fill="AliceBlue">
  16.     <Path.Data>
  17. <Grid>
  18.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  19.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  20. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  21.     </Path.Data>
  22. </Path><Path Fill="AliceBlue">
  23.     <Path.Data>
  24. <Grid>
  25.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  26.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  27. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  28.     </Path.Data>
  29. </Path><Path Fill="AliceBlue">
  30.     <Path.Data>
  31. <Grid>
  32.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  33.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  34. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  35.     </Path.Data>
  36. </Path><Path Fill="AliceBlue">
  37.     <Path.Data>
  38. <Grid>
  39.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  40.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  41. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  42.     </Path.Data>
  43. </Path><Path Fill="AliceBlue">
  44.     <Path.Data>
  45. <Grid>
  46.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  47.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  48. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  49.     </Path.Data>
  50. </Path><Path Fill="AliceBlue">
  51.     <Path.Data>
  52. <Grid>
  53.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  54.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  55. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  56.     </Path.Data>
  57. </Path><Path Fill="AliceBlue">
  58.     <Path.Data>
  59. <Grid>
  60.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  61.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  62. </Grid><RectangleGeometry Rect="0,0 80,60"/>
  63.     </Path.Data>
  64. </Path>
复制代码
DrawingVisual

DrawingVisual是一个轻量级绘图类,用于呈现形状、图像或文本,由于不支持布局、输入、焦点和事件处理,所以绘图性能较好。可用于绘制背景,或者脉冲图。
使用DrawingVisual绘图时,需要一个派生自FrameworkElement类的对象作为宿主容器来呈现图画。这个宿主容器类负责管理其DrawingVisual对象的集合,并通过重写FrameworkElement的以下两个属性为WPF提供需要绘制的内容。

  • GetVisualChild:从Visual对象集合中返回指定索引处的Visual对象。
  • VisualChildrenCount:获取此元素内可视子元素的数目。
  1. public class MyVisualHost : FrameworkElement{    // 创建`Visual`对象的集合    private VisualCollection _children;    public MyVisualHost()    {<Grid>
  2.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  3.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  4. </Grid>_children = new VisualCollection(this);<Grid>
  5.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  6.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  7. </Grid>_children.Add(CreateDrawingVisualRectangle());<Grid>
  8.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  9.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  10. </Grid>_children.Add(CreateDrawingVisualText());<Grid>
  11.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  12.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  13. </Grid>_children.Add(CreateDrawingVisualEllipses());<Grid>
  14.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  15.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  16. </Grid>// 注册MouseLeftButtonUp事件处理<Grid>
  17.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  18.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  19. </Grid>this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);    }    // 重写VisualChildrenCount成员提供此UI元素(宿主容器)内可视子元素的数目.    protected override int VisualChildrenCount    {<Grid>
  20.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  21.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  22. </Grid>get { return _children.Count; }    }    // 重写GetVisualChild方法返回指定索引处的`Visual`对象    protected override Visual GetVisualChild(int index)    {<Grid>
  23.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  24.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  25. </Grid>if (index < 0 || index >= _children.Count)<Grid>
  26.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  27.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  28. </Grid>{<Grid>
  29.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  30.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  31. </Grid>    throw new ArgumentOutOfRangeException();<Grid>
  32.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  33.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  34. </Grid>}<Grid>
  35.     <Ellipse Width="50" Height="50" Fill="AliceBlue" Stroke="Blue"/>
  36.     <Rectangle Width="20" Height="20" Stroke="Blue" Fill="Transparent"/>
  37. </Grid>return _children[index];    }}
复制代码
上面代码中在宿主容器类的构造方法里给Visual对象的集合添加了三个DrawingVisual 对象。接下来以CreateDrawingVisualRectangle为例介绍DrawingVisual对象的创建。DrawingVisual类没有绘图内容,需要通过RenderOpen方法获取DrawingContext对象,并在其中进行绘制来添加文本、图形或图像内容,DrawingContext提供了绘制直线、矩形、椭圆、文本以及几何图形等一系列方法。用法上和Winform中GDI+绘图比较相似。
  1. private DrawingVisual CreateDrawingVisualRectangle()
  2. {
  3.     DrawingVisual drawingVisual = new DrawingVisual();
  4.     DrawingContext drawingContext = drawingVisual.RenderOpen();
  5.     Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
  6.     drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
  7.     drawingContext.Close();
  8.     return drawingVisual;
  9. }
复制代码
小结


  • 形状(Shape)作为WPF中的UI元素,提供了便捷的绘图功能,以及布局、焦点和事件处理等实用功能,但绘制复杂图形相对繁琐,性能也相对较差。
  • 几何图形(Geometry)是与Path形状结合使用,为绘制形状提供了轻量的实现,并通过减少UI元素获得更好的性能,其中使用几何图形微语言创建StreamGeometry的方式可以像PathGeometry一样实现复杂的图形,并且具有更好的性能。除了绘制形状外,还可以用于设置Clip属性,对任何UI元素进行裁剪。但几何图形(Geometry)只定义了形状(线条轮廓),不能直接作为绘制UI元素的画刷。
  • 图画(Drawing)包含了显示矢量图或者位图需要的所有信息,并且可以封装几何图形(Geometry)或者位图作为画刷,为UI元素设置Background、BorderBrush等属性。DrawingVisual作为一个轻量级的图画类,具有较好的性能,在需要大量绘制工作的场景中是一个不错的选择。

来源:https://www.cnblogs.com/czwy/p/18000108
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具