WPF 更改 DrawingVisual 的 RenderOpen 用到的对象的内容将持续影响渲染效果
在 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; }
}
以上代码的预期行为是什么?还请大家跑跑试试
其实就是界面在做动画,只是此动画有些有趣,需要在界面有其他逻辑进行界面刷新的时候,或者说触发渲染线程进行渲染时,才会进行动画刷新
本文所有代码放在 github 和 gitee 欢迎小伙伴访问
WPF 更改 DrawingVisual 的 RenderOpen 用到的对象的内容将持续影响渲染效果的更多相关文章
- wpf 客户端【JDAgent桌面助手】开发详解(三) 瀑布流效果实现与UI虚拟化优化大数据显示
目录区域: 业余开发的wpf 客户端终于完工了..晒晒截图 wpf 客户端[JDAgent桌面助手]开发详解-开篇 wpf 客户端[JDAgent桌面助手]详解(一)主窗口 圆形菜单... wpf 客 ...
- WPF中实现图片文件转换成Visual对象,Viewport3D对象转换成图片
原文:WPF中实现图片文件转换成Visual对象,Viewport3D对象转换成图片 1.图片文件转换成Visual对象 private Visual CreateVisual(string imag ...
- [WPF系列]-数据邦定之DataTemplate 根据对象属性切换模板
引言 书接上回[WPF系列-数据邦定之DataTemplate],本篇介绍如何根据属性切换模板(DataTemplate) 切换模板的两种方式: 使用DataTemplateSelecto ...
- wpf 中 Ellipse 对象对动画性能的影响
vs2019 .NetFramework 4.8 win10-64 1909 接手一个wpf项目,某窗口中包含大量的 Shape 对象(线,矩形,圆形等). 这些内容要匀速的向左平移,类似于游戏&qu ...
- WinForm小白的WPF初试一:从PropertyGrid控件,输出内容到Word(上)
学WinForm也就半年,然后转到WPF,还在熟悉中.最近拿到一个任务:从PropertyGrid控件,输出内容到Word.难点有: 一.PropertyGrid控件是WinForm控件,在WPF中并 ...
- WPF MVVM模式下实现ListView下拉显示更多内容
在手机App中,如果有一个展示信息的列表,通常会展示很少一部分,当用户滑动到列表底部时,再加载更多内容.这样有两个好处,提高程序性能,减少网络流量.这篇博客中,将介绍如何在WPF ListView中实 ...
- WPF前台界面显示“未将对象引用设置到对象的实例”
在做即时通信项目中,使用WPF的MVVM模式,如果在前台绑定VM,经常会显示波浪线,鼠标放上去提示未将对象引用设置到对象的实例,但程序能正常运行,后来发现如果前台不绑定VM,在后台cs里绑定就不会出现 ...
- C# WPF 登录多线程中 “调用线程无法访问对象,因为另一个线程拥有该对象“
造成这个错误的原因很多,以下是我遇到的 我的思路,开启一个线程A登录.因为服务器响应登录成功需要在主线程做一些操作,我这边需要用到主线程的窗口对象,我把窗口对象传到线程 A,直接用实例方法会有这个错误 ...
- WPF 远程显示原图 当前主页面 工具栏 一个Window页面的元素适用一个效果
http://www.jb51.net/article/98384.htm 1.wpf远程显示原图: Stretch="Fill" + ; 主要是因为那个950和650,据显示位置 ...
- WPF:将HTML RGB颜色值转化为Color对象的两种方式
(1)方式一: Color color1 = (Color)System.Windows.Media.ColorConverter.ConvertFromString("#E0E0E0&qu ...
随机推荐
- 三维模型OBJ格式轻量化压缩并行计算处理方法浅析
三维模型OBJ格式轻量化压缩并行计算处理方法浅析 三维模型的轻量化是指通过一系列技术和算法来减小三维模型的文件大小,以提高模型在计算机中的加载.渲染和传输效率.并行计算是利用多个计算单元同时执行任务, ...
- Counts the number of the messages received and sent
我的博客园:https://www.cnblogs.com/CQman/ 本文版权归CQman和博客园共有,欢迎转载,但必须保留此段声明,并给出原文链接,谢谢合作. Symptom Counts t ...
- 自定义MarkupExtension的学习,以及WPF中Combobox绑定枚举类型
我们上一期讲到ComBobox绑定数据,https://www.cnblogs.com/guchen33/p/17630808.html 这次我们简单化一下, 正常来讲WPF中绑定不了枚举的像这样 / ...
- 用Vue3.0 写过组件吗?如果想实现一个 Modal你会怎么设计?
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.组件设计 组件就是把图形.非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式 现在有一个场景,点击新增与编辑都弹框出来进行 ...
- 记录--Cesium+Vue实战教程——地图导航
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 项目效果 我们今天要实现的是一个路径规划的功能,有两个输入框.输入起点终点,然后查询,得到规划的路径,效果如下: 我们会用到以下库: Ax ...
- pycharm 常见易错的PEP8规范
PEP8规范 ( Python Enhancement Proposal ) PEP 8: E231 missing whitespace after ','这个意思是逗号后面要有一个空格 PEP 8 ...
- r-nacos v0.4.0版本发布
r-nacos是一个用 rust重新实现的nacos. r-nacos比java实现的nacos更轻量.快速.稳定:合适在开发.测试.受资限服务等环境平替nacos服务使用. r-nacos v0.4 ...
- C#中字典集合的两种遍历
记录一下 Dictionary<string, string> dictionary = new Dictionary<string,string>(); foreach (s ...
- modelsim的工程文件结构
modelsim的工程文件结构 1.工程结构 modelsim中的工程包括一个库(这个库可以是空的,也可以包含器件延时信息的真实库),一个工程(以mpf为后缀的文件是工程的快捷打开方式)和若干源文件. ...
- KingbaseES V8R6备份恢复系列之 -- system-Id不匹配备份故障
KingbaseES V8R6备份恢复案例之---system-Id不匹配备份故障 案例说明: 在KingbaseES V8R6执行备份时,在sys_log日志中出现system-id不一致的故障 ...