WPF绘制图形有三种方式:
- 使用FrameworkElement的派生类
- 使用图元转换器绘制几何图形
- 使用DrawingContext绘制
1. 使用FrameworkElement派生类
FrameworkElement类继承自UIElement类,意味它的派生类,都是UI元素,可以直接显示在界面上中。例如Shape的子类,Control的子类等。这是最简单的方式,就不再赘述。
2. 使用图元转换器绘制图形
上一篇文章已经介绍过Geometry了。但Geometry创建的仅仅是“理论上”的几何图形,不能直接显示出来。如果还要显示出来,还需要借助以下两个类:
- XXDrawing类(Drawing在这里不是动词,而是名词“图画”的意思,XX代表不同的内容)。
- DrawingXX类(“转换器”,XX代表不同的内容)
2.1 图元包装器:Drawing
Drawing类用于包装2D图元(图元可以是Geometry,也可以是GlyphRun、Image、Video),并为其添加一些额外的信息,比如Pen,Brush等属性。
它是一个抽象类,可以使用它的派生类来包装不同的图形:
类名 | 功能 |
GeometryDrawing | 包装几何图形 |
GlyphRunDrawing | 包装字符 |
ImageDrawing | 包装图片 |
VideoDrawing | 包装视频 |
DrawingGroup | 将多个Drawing对象组合到一个绘图器中,进行统一包装 |
2.2 图元转换器:DrawingXX
使用Drawing类的子类包装之后,它依旧不能直接显示出来。因为它还只是“理论上”的图元,还需要借助“图元转换器”来显示。图元转换器有两种:
类名 | 功能 |
DrawingBrush | 将Drawing对象当成画刷。类似与Photoshop中的画刷功能 |
DrawingImage | 将Drawing对象当成ImageSource。 |
2.2.1 DrawingBrush
DrawingBrush的作用是将Drawing对象转换为Brush。在任何需要用到Brush的地方,就可以使用它。
// 创建几何图形 EllipseGeometry cicle = new(new Point(5, 5), 5, 5); EllipseGeometry cicle2 = new(new Rect(new Size(20, 16))); GeometryGroup geoGroup = new() { Children = { cicle, cicle2 }, FillRule= FillRule.EvenOdd }; // 创建图画 GeometryDrawing drawing = new(Brushes.Red, new Pen(Brushes.Blue, 2), geoGroup); // 创建DrawingBrush DrawingBrush brushes = new DrawingBrush(drawing); border.BorderThickness = new Thickness(50); border.Background = brushes;
2.2.2. DrawingImage
DrawingImage的作用是把Drawing对象转换为ImageSource。在任何需要ImageSource的地方,就可以使用它。
// 创建几何图形 EllipseGeometry cicle = new(new Point(5, 5), 5, 5); EllipseGeometry cicle2 = new(new Rect(new Size(20, 16))); GeometryGroup geoGroup = new() { Children = { cicle, cicle2 }, FillRule= FillRule.EvenOdd }; // 创建图画 GeometryDrawing drawing = new(Brushes.Red, new Pen(Brushes.Blue, 2), geoGroup); // 创建DrawingBrush DrawingBrush brushes = new DrawingBrush(drawing); DrawingImage image = new(drawing); img.Source = image;
3. 使用DrawingContext绘制图形
DrawingContext是真正的“绘图”的类。它提供了一系列的方法来绘制图元。但是它是一个抽象类,不能直接创建类的实例。而且也不能创建它的子类(因为它没有公开的构造函数)。
3.1 创建DrawingContext对象
要创建DrawingContext对象,有两种方式:
-
- 使用类的方法
- 继承自UIElement类
3.1.1 使用类的方法:
某些类提供一个方法以创建DrawingContext对象,例如DrawingVisual类的RendOpen()方法、DrawingGroup类Open()方法。
// DrawingVisual.RendOpen()方法 DrawingVisual dv = new DrawingVisual(); using DrawingContext context = dv.RenderOpen(); context.DrawXX(...); // 绘制图形 // DrawingGroup.Open()方法 DrawingGroup dg = new DrawingGroup(); using DrawingContext dc = dg.Open(); dc.DrawXX(...); // 绘制图形
3.1.2 继承自UIElement类
public class visualElement:UIElement { protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); } }
注释:
-
- UIElement类提供了OnRender()方法,但在工作中,不一定要直接继承UIElement,可以从Framework,或者Control类继承。
- OnRender()方法中的DrawingContext对象,是系统传递过来的,而不是自己的代码创建的。因此可以直接使用。
3.2 显示DrawingContext对象
创建完DrawingContext对象,并用其绘制了图元后的,发现它依旧无法显示出来。为什么呢?这是因为DrawingContext不是一个Visual对象,而仅仅只是一个绘图环境,绘制的图形仅保存在系统中。如果要显示出来,就必须将其转换为Visual对象。
3.2.1 使用DrawingVisual对象
在3.1.1的代码示例中,使用DrawingVisual类的RendOpen()方法创建了DrawinContext对象。而DrawingVisual就是一个Visual的对象,那么要如何才能显示它呢?
根据微软官方的解释,需要为DrawingVisual对象提供一个容器,它的约束条件如下:
-
-
- 容器必须继承自FrameworkElement;
- 必须重写GetVisualChild()方法;
- 必须重写VisualChildrenCount属性。
- 在界面元素中添加该容器的实例。
-
示例代码如下:
(1)创建可视化对象的内容
public class VisualElement : FrameworkElement { readonly DrawingVisual dv = new(); // 重写VisualChildrenCount属性 protected override int VisualChildrenCount => dv.Children.Count; // 重写GetVisualChild()方法 protected override Visual GetVisualChild(int index) { return dv.Children[index]; } public VisualElement() { // 确定dv在VisualTree的父级。否则无法支持鼠标事件 this.AddVisualChild(dv); // 确定dv在逻辑树上的父级。 this.AddLogicalChild(dv); } // 添加视觉对象 public virtual void AddVisual(Visual visual) { if (visual == null) return; dv.Children.Add(visual); } public void DrawLine(Pen pen, Point start, Point end) { DrawingVisual visual = new(); using DrawingContext dc = visual.RenderOpen(); dc.DrawLine(pen, start, end); AddVisual(visual); } }
(2)显示容器中的内容
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 创建容器对象,并绘制图像 VisualElement element = new(); element.DrawingLine(new Pen(Brushes.Yellow,2),new Point(0,0),new Point(200,300)); // 因为容器是一个FrameworkElement,所有可以加入到Grid的子集中。 grid.Children.Add(element); } }
注释:
-
-
- Visual类有一个AddVisualChildren()的方法。该方法不是为了显示“可视化”对象的,而是为了确定可视化对象的父子关系。WPF不是一个实时绘图程序,只有在需要的时候才绘制界面。所以该方法不能用来“显示”。
- DrawingVisual类,表示一个可视化的对象。它继承自ContainerVisual,进而继承自Visual,因此它是一个可以显示在界面上的东西。
- DrawingContext类,表示一个绘图环境,在这个环境中绘图完毕之后,需要将其关闭。一旦绘图环境关闭,就不能在打开并修改了。
-
3.2.2 使用DrawingXX对象
在3.1.1示例代码中,使用DrawingGroup类的Open()方法创建了DrawinContext对象。
但DrawingGroup是一个Drawing对象,而不是Visual对象,无法直接显示。那要怎么显示呢?
只能利用第2节提到的图元转换器:DrawingBrush、DrawingImage类。详细参见第2节,这里不再一一赘述。
3.2.3 继承自UIElement类
在3.1.2的示例代码中,创建了一个继承自的UIElement的类,并重写了OnRender()方法。
因为子类是继承自UIElement的,因此子类本身就是一个可以现在在界面上的元素。那么在OnRender()方法中绘制的图元,可以直接显示出来。这里就不再赘述。
标签:对象,创建,DrawingVisual,绘图,dv,new,WPF,绘制,DrawingContext From: https://www.cnblogs.com/raynado/p/17775357.html