在 WPF 里面,可以通过 DrawingVisual 来进行使用底层的绘制方法,此方法需要调用 DrawingVisual 的 RenderOpen 拿到 DrawingContext 类型的对象,接着调用此对象的方法来进行界面绘制。在绘制完成之后,如果依然保存绘制过程的对象,例如 Transform 对象,那当界面再次刷新时,如果更改此对象的属性,将会影响渲染

似乎这不是一个可以做简单描述的问题,其实这个问题也让我前天花了半天的时间才解决的一个界面渲染问题的其中一个。我在编写一个简单的轻量的文本库的时候,发现了文本字排版存在了一点问题。我的文本排版才能的是将文本转换为 Geometry 对象,接着在 DrawingContext 里面绘制出来。我为了实现让文本可以叠加特效的功能,因此不采用 GlyphRun 类型,同时为了减少 Geometry 对象的创建,我不能在 Geometry 对象上叠加变换

因为为了让文本的字能排版对,我就需要设置每个字在界面绘制的坐标。为了简化逻辑,我采用一个 RectangleGeometry 来代替文字的 Geometry 对象。如基础的知识,在 DrawingContext 里面如果想要在指定的地方绘制某个内容,可以采用的方法是调用 PushTransform 方法,设置当前绘制的变换,也就包括了设置当前绘制在哪,如下面代码

            var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
var rectangleGeometry = new RectangleGeometry(new Rect(0, 0, 10, 10)); for (int i = 0; i < 10; i++)
{
var translateTransform = new TranslateTransform(); translateTransform.X = i * 15; drawingContext.PushTransform(translateTransform); drawingContext.DrawGeometry(Brushes.Red, null, rectangleGeometry); drawingContext.Pop();
}
}

此时的界面能工作,大概如下

然而我看到了每次都需要创建一个 TranslateTransform 对象,我觉得也许会影响内存。是否 TranslateTransform 对象可以和 RectangleGeometry 对象一样复用。在调用 Pop 方法之后,是否 TranslateTransform 对象的内容已被拷贝,于是我变更代码如下

            var drawingVisual = new DrawingVisual();

            using (var drawingContext = drawingVisual.RenderOpen())
{
var rectangleGeometry = new RectangleGeometry(new Rect(0, 0, 10, 10)); var translateTransform = new TranslateTransform(); for (int i = 0; i < 10; i++)
{
translateTransform.X = i * 15 + 10; drawingContext.PushTransform(translateTransform); drawingContext.DrawGeometry(Brushes.Red, null, rectangleGeometry); drawingContext.Pop();
}
}

此时的 TranslateTransform 是复用的,然而界面就不能很好工作,所有的矩形都会绘制到最后的地方。看起来 PushTransform 内部没有拷贝 TranslateTransform 的对象,只是记录这条指令而已

从以上的例子可以看到在 DrawingContext 里面绘制的内容,其实调用 PushTransform 方法只是将传入的 TranslateTransform 进行记录,而没有进行更多的拷贝。在后续变更 TranslateTransform 时,将会在渲染的时候,读取到变更之后的 TranslateTransform 对象的属性

在调用 DrawingVisual 的 RenderOpen 之后,在 DrawingContext 里面调用绘制方法时,不是立刻进行绘制,而是收集绘制的指令。实际的绘制渲染是在渲染线程通过 DirectX 等来实现的

在 RenderOpen 关闭之后,对 TranslateTransform 对象的变更也会影响到最终的渲染结果,因为 RenderOpen 关闭时不是立刻进行渲染。如下面代码,将会让所有的绘制的矩形都放在 X 是 500 的地方

            var drawingVisual = new DrawingVisual();
var translateTransform = new TranslateTransform();
using (var drawingContext = drawingVisual.RenderOpen())
{
var rectangleGeometry = new RectangleGeometry(new Rect(0, 0, 10, 10)); for (int i = 0; i < 10; i++)
{
translateTransform.X = i * 15; drawingContext.PushTransform(translateTransform); drawingContext.DrawGeometry(Brushes.Red, null, rectangleGeometry); drawingContext.Pop();
}
} translateTransform.X = 500;

那如果再做一些更有趣的事情呢?我在不断的更改 TranslateTransform 的属性,如下面代码

    class Foo : UIElement
{
public Foo()
{
var drawingVisual = new DrawingVisual();
var translateTransform = new TranslateTransform();
using (var drawingContext = drawingVisual.RenderOpen())
{
var rectangleGeometry = new RectangleGeometry(new Rect(0, 0, 10, 10)); for (int i = 0; i < 10; i++)
{
translateTransform.X = i * 15; drawingContext.PushTransform(translateTransform); drawingContext.DrawGeometry(Brushes.Red, null, rectangleGeometry); drawingContext.Pop();
}
} translateTransform.X = 500; Visual = drawingVisual; SetTranslateTransform(translateTransform);
} private async void SetTranslateTransform(TranslateTransform translateTransform)
{
while (true)
{
translateTransform.X++; if (translateTransform.X > 700)
{
translateTransform.X = 0;
} await Task.Delay(TimeSpan.FromMilliseconds(10));
}
} protected override Visual GetVisualChild(int index) => Visual;
protected override int VisualChildrenCount => 1; private Visual Visual { get; }
}

以上代码的预期行为是什么?还请大家跑跑试试

其实就是界面在做动画,只是此动画有些有趣,需要在界面有其他逻辑进行界面刷新的时候,或者说触发渲染线程进行渲染时,才会进行动画刷新

本文所有代码放在 githubgitee 欢迎小伙伴访问

WPF 更改 DrawingVisual 的 RenderOpen 用到的对象的内容将持续影响渲染效果的更多相关文章

  1. wpf 客户端【JDAgent桌面助手】开发详解(三) 瀑布流效果实现与UI虚拟化优化大数据显示

    目录区域: 业余开发的wpf 客户端终于完工了..晒晒截图 wpf 客户端[JDAgent桌面助手]开发详解-开篇 wpf 客户端[JDAgent桌面助手]详解(一)主窗口 圆形菜单... wpf 客 ...

  2. WPF中实现图片文件转换成Visual对象,Viewport3D对象转换成图片

    原文:WPF中实现图片文件转换成Visual对象,Viewport3D对象转换成图片 1.图片文件转换成Visual对象 private Visual CreateVisual(string imag ...

  3. [WPF系列]-数据邦定之DataTemplate 根据对象属性切换模板

      引言 书接上回[WPF系列-数据邦定之DataTemplate],本篇介绍如何根据属性切换模板(DataTemplate)   切换模板的两种方式:   使用DataTemplateSelecto ...

  4. wpf 中 Ellipse 对象对动画性能的影响

    vs2019 .NetFramework 4.8 win10-64 1909 接手一个wpf项目,某窗口中包含大量的 Shape 对象(线,矩形,圆形等). 这些内容要匀速的向左平移,类似于游戏&qu ...

  5. WinForm小白的WPF初试一:从PropertyGrid控件,输出内容到Word(上)

    学WinForm也就半年,然后转到WPF,还在熟悉中.最近拿到一个任务:从PropertyGrid控件,输出内容到Word.难点有: 一.PropertyGrid控件是WinForm控件,在WPF中并 ...

  6. WPF MVVM模式下实现ListView下拉显示更多内容

    在手机App中,如果有一个展示信息的列表,通常会展示很少一部分,当用户滑动到列表底部时,再加载更多内容.这样有两个好处,提高程序性能,减少网络流量.这篇博客中,将介绍如何在WPF ListView中实 ...

  7. WPF前台界面显示“未将对象引用设置到对象的实例”

    在做即时通信项目中,使用WPF的MVVM模式,如果在前台绑定VM,经常会显示波浪线,鼠标放上去提示未将对象引用设置到对象的实例,但程序能正常运行,后来发现如果前台不绑定VM,在后台cs里绑定就不会出现 ...

  8. C# WPF 登录多线程中 “调用线程无法访问对象,因为另一个线程拥有该对象“

    造成这个错误的原因很多,以下是我遇到的 我的思路,开启一个线程A登录.因为服务器响应登录成功需要在主线程做一些操作,我这边需要用到主线程的窗口对象,我把窗口对象传到线程 A,直接用实例方法会有这个错误 ...

  9. WPF 远程显示原图 当前主页面 工具栏 一个Window页面的元素适用一个效果

    http://www.jb51.net/article/98384.htm 1.wpf远程显示原图: Stretch="Fill" + ; 主要是因为那个950和650,据显示位置 ...

  10. WPF:将HTML RGB颜色值转化为Color对象的两种方式

    (1)方式一: Color color1 = (Color)System.Windows.Media.ColorConverter.ConvertFromString("#E0E0E0&qu ...

随机推荐

  1. 李沐动手学深度学习pytorch实践笔记

    1.pytorch中的矩阵乘法: 2.标量对向量求导: 3.pytorch的backward函数: 4.如何直观理解梯度下降: 梯度,是个向量,有方向和长度就是向量,向量里的各个元素是偏导.是标量对向 ...

  2. BWO白鲸优化算法

    白鲸算法 ​ 白鲸算法(BWO)是一种新的元启发式算法,是一种基于群体的算法,其灵感来自于白鲸的行为,包括游泳,猎物和鲸落.在BWO的数学模型中构建了勘探,开发和鲸落阶段,并在开发阶段利用Levy飞行 ...

  3. 不用写一行代码!Python最强自动化神器!

    1.Playwright介绍 Playwright是一个由Microsoft开发的开源自动化测试工具,它可以用于测试Web应用程序.Playwright支持多种浏览器,包括Chrome.Firefox ...

  4. KingbaseES 数据库使用Limit子句查询结果返回顺序不一致

    一.KingbaseES数据库limit查询子句: 在KingbaseES数据库使用LIMIT子句限制查询结果的行数,从而实现分段显示数据的功能. 使用LIMIT子句在KingbaseES数据库中进行 ...

  5. KingbaseES 原生XML系列二 -- XML数据操作函数

    KingbaseES 原生XML系列二--XML数据操作函数(DELETEXML,APPENDCHILDXML,INSERTCHILDXML,INSERTCHILDXMLAFTER,INSERTCHI ...

  6. 3D Object Detection Essay Reading 2024.04.01

    Swin Transformer paper: https://arxiv.org/abs/2103.14030 (ICCV 2021) code:https://github.com/microso ...

  7. 提升面试成功率:深入理解 C++ 11 新特性

    C++11是C++语言的一个重大更新,引入了许多新特性,包括自动类型推导.lambda表达式.右值引用.智能指针等等.这些新特性使得C++更加现代化.高效.易用.也是面试容很容易被问到一个问题,下面我 ...

  8. Regex中Replace方法的简单实用

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  9. 【有奖互动】开发者版本新特性,你期待哪些更新?#HDC.Together2023#

    <hdc.together< span="">>华为开发者大会2023再次启航,将于8月4日~6日在中国松山湖举办,承载万千期待,开启崭新时代.聚焦新版本. ...

  10. “AI虚拟数字人+线下大屏互动”升级智能人机交互服务!

    如今AIGC 强势爆发. ChatGPT 语言大模型横空出世,使得数字人的"大脑"水平得到了极大提升,AI技术赋能下的虚拟数字人拥有了更加精准的语言表达.思考逻辑.帮助各个行业实现 ...