WPF有每当相应的内置机制,使所有Adorners
被重新测量,重新排列,并重新呈现AdornedElement
更改大小,位置或变换。这个机制要求你在编写你的装饰者时遵循一定的规则,但并不是所有的文件都应该清楚地记录下来。
我会先回答你的标题问题为什么你的装潢不符合重新渲染,然后说明解决它的最好方法。
为什么装饰器不重新渲染
每当AdornerLayer接收它扫描它的每一个装饰器,看看该AdornedElement
的大小,位置改变或变换LayoutChanged通知。如果是这样,它将设置标志来强制Adorner
进行测量,排列和再次渲染 - 大致相当于InvalidateMeasure(); InvaliateArrange(); InvalidateVisual();
。
在这种情况下通常会发生的情况是控制首先被测量,然后进行排列,然后进行渲染。事实上,WPF试图使这是最常见的情况,因为它是最有效的序列。然而,在重新测量之前,很多情况下控制可能会重新排列和/或重新排列。这是WPF中事件的合法顺序(允许灵活的布局技术),但它并不常见,所以通常不会进行测试。
一个正确实施Adorner
或其他UIElement
会小心调用InvalidateVisual()
任何时候渲染可能会受到影响,除非仅AffectsRender
依赖属性发生了变化。
在你的情况下,你的装饰大小显然会影响渲染。大小属性不是AffectsRender
依赖项属性,因此在更改时需要手动调用InvalidateVisual()
。如果你不这样做,WPF可能永远不知道重新渲染你的装饰者。
什么在你的情况正在发生的事情大概是这样的:
- 布局完成和
LayoutChanged
事件触发
AdornerLayer
发现你的AdornedElement
AdornerLayer
时间表大小改变你对重新测量装饰器,重新布局和重新渲染
- 某些原因
Arrange()
被调用,导致在重新测量之前重新布局和重新渲染。这导致WPF认为装饰者不再需要重新布局或重新渲染。
- 布局引擎检测到装饰器需要测量,并呼吁
Measure
- 装饰器的
MeasureOverride
重新计算所需的大小,但确实没什么好说的WPF装饰器需要重新渲染
- 布局引擎决定有什么更多的工作要做,因此装饰器永远不会重新呈现
你能做些什么来解决它
解决的办法是,当然,通过调用InvalidateVisual()
以修复Adorner
的bug。只要控制重新测量,像这样:
protected override Size MeasureOverride(Size constraint)
{
var result = base.MeasureOverride(constraint);
// ... add custom measure code here if desired ...
InvalidateVisual();
return result;
}
这样做将使你的装饰器始终如一地遵守WPF的所有规则,所以它会在所有情况下按预期工作。这也是最有效的解决方案,因为InvalidateVisual()
什么都不会做,除非在真正需要的情况下。
尺寸属性不会影响渲染有多奇怪 - 以前没有碰到过,但是看起来像是一个讨厌的小问题,我很惊讶它不会更经常出现!你知道(或者你可以推测)为什么WPF设计者以这种方式构建它?无论如何,谢谢你提供了这样一个清晰,详细和翔实的答案。 – itowlson 2010-03-27 01:48:49
@itowlson:因为在最常见的情况下(RenderSize仅在ArrangeCore中更新并且渲染不依赖于任何其他大小),您可能从来没有碰到过它,因为在不需要InvalidateVisual()调用的情况下始终触发渲染。记录一系列ArrangeOverride和OnRender调用,以更好地感受这种工作方式。我的猜测是,设计师不想在每次设置RenderSize时自动调度OnRender,因为他们设想RenderSize会被更改并立即改回的场景。 – 2010-03-29 21:13:28
不渲染的另一个原因可能是您必须传递给Adorner基础构造函数的'UIElement'不在adorner层中。例如如果使用'AdornerDecorator'来创建本地装饰器图层,然后错误地引用超出视觉/逻辑树的'UIElement'。 – Dennis 2012-08-15 10:07:16