在WPF中,如何在控件实际呈现之前获取控件的呈现大小? [英] In WPF how can I get the rendered size of a control before it actually renders?

查看:705
本文介绍了在WPF中,如何在控件实际呈现之前获取控件的呈现大小?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Decorator子类中进行自定义渲染.我们的渲染需要创建复杂的几何图形,只有当实际渲染的尺寸发生变化时,才需要重新创建它们.这样,我将几何图形创建移到了自己的函数UpdateGeometry中,该函数创建然后冻结该几何图形以供在OnRender中使用.只需响应ActualWidthActualHeight的更改而调用此新功能.

更好的是,看来我们应该能够简单地重写OnRenderSizeChanged,根据文档说明,该信息...

"在派生类中重写时, 参与渲染操作 由布局指示 系统.此方法在 之后被调用 布局更新,并且在渲染之前 , 如果元素的RenderSize具有 布局更新导致更改."

但是,无论我使用覆盖还是监听ActualWidthActualHeight的属性更改通知,我的日志记录始终显示OnRender首先发生!嗯...哇?

为了确保这不是我在代码中所做的事情,我创建了一个准系统测试装饰器子类,并在覆盖的入口和出口处添加了日志记录.这是整个班级...

using System;
using System.Windows.Controls;

public class TestControl : Decorator
{

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
        Console.WriteLine("OnRender Entered");

        base.OnRender(drawingContext);

        Console.WriteLine("OnRender Exited");
    }

    protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
    {
        Console.WriteLine("OnRenderSizeChanged Entered");

        base.OnRenderSizeChanged(sizeInfo);

        Console.WriteLine("OnRenderSizeChanged Exited");
    }

}

而且我担心...这是输出...

OnRender Entered
OnRender Exited
OnRenderSizeChanged Entered
OnRenderSizeChanged Exited

那我在这里想念什么?

更重要的是,在布局子系统完成其工作之后,但在呈现控件之前,如何获得ActualWidthActualHeight值,这样我就可以在OnRender替代中需要它之前创建几何图形?

我最新的实现覆盖了ArrangeOverride,因为传入的值的大小包含ActualWidthActualHeight 应该 的大小布局系统将HorizontalAlignmentVerticalAlignment的值考虑为拉伸",最小值和最大值等,但它们的实际含义取决于该覆盖所返回的值,因此它要复杂得多.

无论哪种方式,我仍然想知道为什么OnRenderSizeChanged调用在应有的情况下没有发生.有想法吗?

标记

解决方案

通常,您应该能够从

在最终调用OnArrangeOverride之后,从Arrange方法调用OnRender方法.另一方面,从UpdateLayout调用OnRenderSizeChanged,对于视觉树的给定部分,可以有效地调度该OnRenderSizeChanged一次执行所有操作.这就是为什么在OnRender之后调用OnRenderSizeChanged的原因.

文档可能会引用渲染",就像在屏幕上实际渲染一样,而不是在调用OnRender时引用. WPF可以缓存给定元素的渲染指令,并在需要时执行它们.因此,在OnRenderSizeChanged之前调用OnRender的事实,并不意味着它实际的渲染指令在那时已提交到屏幕.

您可以使用以下方法修改OnRenderSizeChanged,以强制再次调用OnRender:

protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
{
    Console.WriteLine("OnRenderSizeChanged Entered");

    base.OnRenderSizeChanged(sizeInfo);
    this.InvalidateVisual();

    Console.WriteLine("OnRenderSizeChanged Exited");
}

如果RenderSize为"0,0",您可能还希望跳过OnRender代码.

I'm doing custom rendering in a Decorator subclass. Our rendering requires creating complex geometries which only neded to be re-created when the actual rendered size changes. As such, I have moved the geometry creation into its own function called UpdateGeometry which creates, then freezes the geometry for use in OnRender. This new function only needs to be called in response to a change in ActualWidth or ActualHeight.

Even better, it looks like we should be able to simply override OnRenderSizeChanged, which according to the documentation states...

"When overridden in a derived class, participates in rendering operations that are directed by the layout system. This method is invoked after layout update, and before rendering, if the element's RenderSize has changed as a result of layout update."

However, regardless if I'm using the override or listening to the property change notifications of ActualWidth and ActualHeight, my logging consistently shows OnRender as happening first! Um... Wha??

To be sure it wasn't something I was doing in my code, I created a bare-bones test decorator subclass and added logging there, both on entry and exit to the overrides. Here is the entire class...

using System;
using System.Windows.Controls;

public class TestControl : Decorator
{

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
        Console.WriteLine("OnRender Entered");

        base.OnRender(drawingContext);

        Console.WriteLine("OnRender Exited");
    }

    protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
    {
        Console.WriteLine("OnRenderSizeChanged Entered");

        base.OnRenderSizeChanged(sizeInfo);

        Console.WriteLine("OnRenderSizeChanged Exited");
    }

}

And as I feared... here is the output...

OnRender Entered
OnRender Exited
OnRenderSizeChanged Entered
OnRenderSizeChanged Exited

So what am I missing here?

More importantly, how can I get the ActualWidth and ActualHeight values after the layout subsystem has done its job, but before the control is rendered so I can create the geometry before it's needed in the OnRender override?

My latest implementation overrides ArrangeOverride as the value that's passed in there is a size containing what the ActualWidth and ActualHeight values should be after the core layout system takes into consideration HorizontalAlignment and VerticalAlignment with values of 'Stretch', minimums and maximums, etc, but what they actually are depends on the value that's returned from that override so it's a little more complex than that.

Either way, I'm still wondering why the OnRenderSizeChanged call doesn't happen when it's supposed to. Thoughts?

Mark

解决方案

In general, you should be able to get the correct size from ArrangeOverride. This doesn't include things like Margin, but that probably shouldn't be taken into account. You could either use the size passed as a parameter as your "render" size or use the return value of the base.ArrangeOverride call.

EDIT:

The OnRender method is called from the Arrange method, after OnArrangeOverride is ultimately called. The OnRenderSizeChanged on the other hand is called from UpdateLayout, which is effectively dispatched to be executed all at once for a given section of the visual tree. This is why the OnRenderSizeChanged is called after the OnRender.

The documentation may refer to the "rendering" as in actually rendered to the screen, not when OnRender is called. WPF can cache the rendering instructions for a given element and execute them when needed. So the fact that OnRender is called before OnRenderSizeChanged, doesn't mean it's actual rendering instructions are committed to the screen at that time.

You can modify your OnRenderSizeChanged to force OnRender to be called again using:

protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
{
    Console.WriteLine("OnRenderSizeChanged Entered");

    base.OnRenderSizeChanged(sizeInfo);
    this.InvalidateVisual();

    Console.WriteLine("OnRenderSizeChanged Exited");
}

You may also want to skip your OnRender code if RenderSize is "0,0".

这篇关于在WPF中,如何在控件实际呈现之前获取控件的呈现大小?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆