我昨天和头像大人在解决一个坑,发现调用了 InvalidateVisual 的时候,不会触发 OnRender 方法。 那么在什么时候会触发 OnRender 方法,在什么时候不会触发 在 WPF 中通过 InvalidateVisual 方法可以告诉 WPF 框架,当前这个控件需要重新绘制元素,但是调用这个方法不是立刻进行绘制 可以看到时间没有更新,也就是 OnRender 没有触发 ? 原理是在控件的 OnRender 触发条件是控件需要在视觉树上,如果控件不在视觉树上,如被从上层元素移除或元素被设置 Collapsed 那么 OnRender 将不会触发 为什么此时设计让 OnRender 原因是既然这个控件就不想显示出来了,那么还调用他的 OnRender 方法做什么 有没有例外项?
使用方式 Profiler是个内置组件,用他包裹需要检测性能的组件即可: <Profiler id="App" onRender={onRender}> <App /> </Profiler> 嵌套使用也是可以的 : <App> <Profiler id="Sidebar" onRender={onRender}> <Sidebar /> </Profiler> <Profiler id="Content " onRender={onRender}> <Content /> </Profiler> </App> Profiler会检测被他包裹的组件树的性能,检测结果会作为onRender回调的参数 App" onRender={onRender}> <App /> </Profiler> 这个值越高,代表性能优化效果越好。 > <Profiler id="Content" onRender={onRender}> <Content /> </Profiler> </App> 再分别在onRender中衡量
先写一个简单的 OnRender ,创建一个类 GearcawralSarBule 继承 FrameworkElement 就可以重写 OnRender 方法,为了让WPF调用 OnRender 方法就需要把 GearcawralSarBule : FrameworkElement { /// <inheritdoc /> protected override void OnRender (DrawingContext drawingContext) { base.OnRender(drawingContext); } } { drawingContext.DrawDrawing(DrawingVisual.Drawing); base.OnRender(drawingContext 如对 DrawingVisual 进行变换的代码 protected override void OnRender(DrawingContext drawingContext)
首先创建一个类继承 UIElement 这样就可以重写 OnRender 方法在里面画出文字 假设需要画出的文字是 欢迎访问我博客 http://lindexi.gitee.io 里面有大量 UWP WPF 博客 protected override void OnRender(DrawingContext drawingContext) { var str = "欢迎访问我博客 http://lindexi.gitee.io 里面有大量 UWP WPF 博客"; base.OnRender(drawingContext); 添加这个控件运行代码就可以看到上面界面 <local:CureekaMasar></local:CureekaMasar> 所有代码请看下面 protected override void OnRender Pen(new SolidColorBrush(Colors.Black), 1), geometry ); base.OnRender
WPF如果需要写入描边需要使用 FormattedText 将文字转换为 Geometry 然后通过画出 Geometry 的边框和填充画出描边 首先创建一个类继承 UIElement 这样就可以重写 OnRender 方法在里面画出文字 假设需要画出的文字是 欢迎访问我博客 http://lindexi.gitee.io 里面有大量 UWP WPF 博客 protected override void OnRender { var str = "欢迎访问我博客 http://lindexi.gitee.io 里面有大量 UWP WPF 博客"; base.OnRender 添加这个控件运行代码就可以看到上面界面 <local:CureekaMasar></local:CureekaMasar> 所有代码请看下面 protected override void OnRender Pen(new SolidColorBrush(Colors.Black), 1), geometry ); base.OnRender
先写一个简单的 OnRender ,创建一个类 GearcawralSarBule 继承 FrameworkElement 就可以重写 OnRender 方法,为了让WPF调用 OnRender 方法就需要把 GearcawralSarBule : FrameworkElement { /// <inheritdoc /> protected override void OnRender (DrawingContext drawingContext) { base.OnRender(drawingContext); } } { drawingContext.DrawDrawing(DrawingVisual.Drawing); base.OnRender(drawingContext 如对 DrawingVisual 进行变换的代码 protected override void OnRender(DrawingContext drawingContext)
下面是 SharpDxMaynumaSejair 类的 OnRender 方法,通过继承他就可以使用 SharpDx 画出来。 protected abstract void OnRender(SharpDX.Direct2D1.RenderTarget renderTarget); 其他的代码和WPF 使用封装的 SharpDx 控件使用的差不多 直接通过 OnRender 就可以进行渲染,但是 OnRender 是被触发的,触发的方法是调用基类 Rendering 函数,调用了这个函数会进入异步的 SharpDx 渲染,渲染完成再通过 继承 FrameworkElement 可以重写 OnRender 。 通过 OnRender 可以画出图片,而 D3Dimage 就是 ImageSource,虽然可以看到我自己定义的也是 OnRender, 这个函数和自己定义的不相同,虽然我把自己定义的函数也是和他使用相同的命名
使用 DrawingContext WPF 中的 DrawingContext 是一个基础的绘图对象,用于绘制各种图形,它的一个最简单的使用方式是重载 UIElement 的 OnRender 方法,在这个方法中绘制 UIElement 的UI: // Override the OnRender call to add a Background and Border to the OffSetPanel protected override void OnRender(DrawingContext dc) { SolidColorBrush mySolidColorBrush = new SolidColorBrush 在上面的代码中我们已经将文字转为一个 Geometry,接下来直接调用 DrawGeometry 并加上边框: protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); var geometry = CreateTextGeometry(); //
下面是 SharpDxMaynumaSejair 类的 OnRender 方法,通过继承他就可以使用 SharpDx 画出来。 protected abstract void OnRender(SharpDX.Direct2D1.RenderTarget renderTarget); 其他的代码和WPF 使用封装的 SharpDx 控件使用的差不多 直接通过 OnRender 就可以进行渲染,但是 OnRender 是被触发的,触发的方法是调用基类 Rendering 函数,调用了这个函数会进入异步的 SharpDx 渲染,渲染完成再通过 继承 FrameworkElement 可以重写 OnRender 。 通过 OnRender 可以画出图片,而 D3Dimage 就是 ImageSource,虽然可以看到我自己定义的也是 OnRender, 这个函数和自己定义的不相同,虽然我把自己定义的函数也是和他使用相同的命名
原因是在图片继承的 UIElement 的布局方法会调用 OnRender 方法,而图片通过 DrawContext 的方式绘制了 Source 但是这个 DrawContext 的上下文被 UIElement RenderData 类,而绘制完成之后将对应的值放在 _drawingContent 字段,也就是在 _drawingContent 引用了图片资源 此时设置图片的源为空,如果图片还在视觉树上,那么将会再次触发 OnRender 方法,在 OnRender 方法里面将会更新 RenderData 对图片源的引用 但是如果图片是被移除视觉树之后设置图片的源为空,那么不会再次触发 OnRender 方法,这样在 RenderData 如果在设置图片的源为空,然后不等待 OnRender 方法执行就将图片移除视觉树也是会内存泄漏。 所以需要设置图片的源为空,然后调用 UpdateLayout 方法执行 OnRender 方法 其实这个内存泄漏问题很小,原因是如果 Image 元素对象没有被引用,那么图片就可以被释放,此时图片的源也可以释放
但是无法对于自己的控件
如果自己创建一个控件,那么直接使用 dc.DrawLine 得到不是清晰的
创建一个类自定义控件,添加下面的代码画出线
protected override void OnRender ///
={onRenderCallback}>
render必须不能被重写,但是在处理过程中调用onRender允许子类的实现添加一个onRender方法去执行特定于类的处理。 每一个onRender方法在“贡献”它额外的逻辑之前必须先调用它父类的onRender方法。 下面这个图描绘了onRender模板方法的功能。 它调用了 this.onRender,this.onRender是现在的子类的实现(如果实现了的话)。调用父类的版本又调用父类的版本,等等。 onRender: function() { this.callParent(arguments); // call the superclass onRender method 7 onRender - 允许渲染情形下有附加的行为。 8 afterRender - 允许渲染被完成之后又附加的行为。
但是无法对于自己的控件
如果自己创建一个控件,那么直接使用 dc.DrawLine 得到不是清晰的
创建一个类自定义控件,添加下面的代码画出线
protected override void OnRender ///
docker.io" insecure = true location = "registry-1.docker.io" [[registry.mirror]] location = "https://xxxx.onrender.com "k8s.gcr.io" insecure = true location = "k8s.gcr.io" [[registry.mirror]] location = "https://xxxx.onrender.com prefix = "gcr.io" insecure = true location = "gcr.io" [[registry.mirror]] location = "https://xxxx.onrender.com prefix = "ghcr.io" insecure = true location = "ghcr.io" [[registry.mirror]] location = "https://xxxx.onrender.com prefix = "quay.io" insecure = true location = "quay.io" [[registry.mirror]] location = "https://xxxx.onrender.com
大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移。 如有两个放入到 StackPanel 的自定义 UIElement 控件,这两个控件都在 OnRender 方法里面,画出一条从 0 到 100 的线段,此时两个控件画出的直线在窗口里面没有重叠。 也就是说在 OnRender 里面绘制的内容将会叠加上元素被布局控件布局的偏移的值 阅读本文,你将了解布局控件是如何影响到里层控件的渲染,以及渲染收集过程中将会如何受到元素坐标的影响 如本文开始的问题, 的 OnRender 方法。 调用到 OnRender 方法,此方法是给开发者进行重写的,绘制开发者业务上的界面使用。
这不是一篇深入底层的博客,很多细节还请看 DX 底层相关 小伙伴都知道 在 WPF 里面使用了 DX 作为底层的渲染,在说到 WPF 卡顿的时候,还请小伙伴不要忘记 dx 部分也是可能存在卡顿的 在 WPF 的 OnRender dx 将会传递这些绘制命令到 UMD (User Mode Driver) 层 上面这句话仅在单 UI 线程时生效,如果采用多 UI 线程将会复杂一些,本文也不讨论多 UI 线程 而在 WPF 的 OnRender - 知乎 绘制完成之后将会在 GPU 缓存里面绘制出一帧完整的图像,而此时依然还不是在屏幕显示,需要等待 Present 命令才会让屏幕输出 通过上文,当然需要您读一下附加的博客,如果 WPF 的 OnRender 我能讲的只是一个大概,更多还请小伙伴看本文引用的链接,特别是官方文档 我认为我写的内容最多只能算官方文档的笔记,唯一有点用的就是和 WPF 的关联 在 WPF 中,从 dx 层面出现的卡顿调试顺序建议如下 在 OnRender
先在以上代码的基础上,添加 HitTestCore 和 OnRender 方法,同时为了展现效果,也在 OnRender 里面绘制一个圆形,代码如下 class F2 : FrameworkElement { ... // 忽略其他代码 protected override void OnRender(DrawingContext drawingContext) { 在有需要将子自定义控件的 OnRender 方法的内容打到界面上以及让子自定义控件参与命中测试时,还需要加上更多的代码 先分析一下为什么 F2 的 OnRender 方法没有在界面打出来绘制的圆形。 方法里面调用 OnRender 方法的。 换句话说就是,想要 OnRender 方法被调用,那就需要调用 Arrange 方法 了解了这个问题之后,解决方法也就自然知道了,既然没有调用 Arrange 方法,那就不妨调用一下。
好在计算机是有精度限制的,但即使有精度限制,所需要计算量也是非常大的,这也就让渲染线程炸掉了 如下面的逗比代码,我在定义的 Foo 类的 OnRender 方法里面,加上如下代码 class Foo { Width = 500; Height = 100; } protected override void OnRender 500, 0)); drawingContext.DrawGeometry(Brushes.Beige, pen, geometry); base.OnRender
Profiler API 可以测试 React 组件的渲染性能,如果我们要测试一个组件,可以把它放到 Profiler 组件中,组件接收一个 onRender 函数,当组件每次 commit 更新时,函数都会执行 : render( <App> <Profiler id="Navigation" onRender={callback}> <Navigation {...props} /> </Profiler> <Main {...props} /> </App> ); onRender 中的下面两个参数我们可能会用到: phase: "mount" | "update console.table({ commitTime, }); console.groupEnd(); } return ( <Profiler id="users" onRender