首页 > 其他分享 >WPF绘图(二):绘制图形

WPF绘图(二):绘制图形

时间:2023-10-19 18:56:36浏览次数:34  
标签:对象 创建 DrawingVisual 绘图 dv new WPF 绘制 DrawingContext

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

相关文章

  • WPF绘图(一):几何(Geometry)与形状(Shape)
    1.Geometry在数学中,我们可以用一个方程描述圆:x2+y2=25。这个方程描述的是,一个半径为5,中心点在(0,0)的圆。这种纯数学的描述就是Geometry(几何)。但此时,这个“圆”我们是看不见,摸不着的。如果想要看到这个几何图形,就必须用画笔,颜色等信息,去“绘制”它。.Net中,Geometry类就是用于描述......
  • wpf TextBox复制文字卡顿 和迅雷的冲突
    迅雷会在后台一直监控读取用户剪切板,如果发现是下载格式,将会弹出下载框在wpf的TextBox中,快速按CtrlC会导致获取剪切板和设置剪切板的命令争抢引发System.Runtime.InteropServices.COMException这个异常通过查看源代码,发现在复制文字时,他们都不同情况的重试,暂停这可能就导致了......
  • 解决winform调用wpf窗体时原窗体缩小的问题
    在使用winform调用wpf窗体时,原来的winform窗体会缩小,同时分辨率会发生变化,用如下方法来解决这个问题。方法一、首先找到winform项目中的Properties ==>AssemblyInfo.cs,打开该文件,在末尾加入如下代码,之后重新运行即可。[assembly:System.Windows.Media.DisableDpiAwareness]/......
  • 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在Dat
    有时候,一些数据的录入可能需要使用表格直接录入会显得更加方便快捷,这种情况有时候也是由于客户使用习惯而提出,本篇随笔介绍在WPF应用端上使用DataGrid来直接新增、编辑、保存数据的处理。录入数据的时候,我们都采用在一个窗体界面中,根据不同内容进行录入,但是有时候涉及主从表的数......
  • 错误 NETSDK1136 如果使用 Windows 窗体或 WPF,或者引用使用 Windows 窗体或 WPF 的项
    背景:当同一解决方案的项目A引用项目B的时候出现引用异常 大概意思是项目A的框架类型是.net7.0,项目B的框架类型是net7.0-windows两者不兼容查看了连着的项目类型发现项目B是指定为Windows操作系统(注:建立类库项目B时没有指定操作系统,不知为啥显示指定了) 类库项目A是没指定......
  • WPF 编译出现大量系统文件
    .net版本号:.netframework4.6.1WPF编译后为什么会出现如下的系统文件,本身自带的框架应该是包含这些文件。查看这些DLL的版本都是隶属于4.6.2版本的 详细排查插件,发现某个插件最小支持4.6.2版本,本身是4.6.1,这才出现了许多DLL的原因。解决方法很简单:1.降插件的版本2.......
  • 初学Bokeh:使用自适应绘图大小【16】跬步
    学习Bokeh:使用自适应绘图大小【16】跬步在绘图的过程中,如果要使绘图自动适应浏览器或屏幕大小,可以使用属性:sizing_modefrombokeh.plottingimportfigure,show#preparesomedata#定义显示数据x=[1,2,3,4,5]y=[4,5,5,7,2]#createanewplotwithrespo......
  • 服务部署图绘制--Mermaid
    官网:https://mermaid.js.org/syntax/flowchart.html 例子:https://blog.csdn.net/weixin_55508765/article/details/125735923 npminstallmermaid <template><divclass="test-container"><h1>输入编辑流程图</h1><divcla......
  • 初学Bokeh:调整绘图大小【15】跬步
    初学Bokeh:调整绘图大小【15】跬步Bokeh中的绘图对象具有多种属性,这些属性会影响绘图的外观。调用figure()函数时使用width和height属性就可以设置绘图的大小:frombokeh.plottingimportfigure,show#preparesomedata#定义绘图数据x=[1,2,3,4,5]y=[4,......
  • WPF性能优化:Freezable 对象
    Freezable是WPF中一个特殊的基类,用于创建可以冻结(Freeze)的可变对象。冻结一个对象意味着将其状态设置为只读,从而提高性能并允许在多线程环境中共享对象。Freezable的应用我们定义画刷资源的时候常常会这样写:<SolidColorBrushx:Key="RedBrush"Color="Red"o:Freeze="True"/>......