WPF 有一个内置机制Adorners
,每当相应的AdornedElement
大小、位置或变换发生变化时,都会重新测量、重新排列和重新呈现所有内容。这种机制要求您在编码您的装饰器时遵循某些规则,并非所有规则都像应有的那样清楚地记录在案。
我将首先回答您的标题问题,即为什么您的装饰器不会一致地重新渲染,然后解释修复它的最佳方法。
为什么装饰器不重新渲染
每当 AdornerLayer 收到 LayoutChanged 通知时,它会扫描其每个 Adorner 以查看其AdornedElement
大小、位置或变换是否发生了变化。如果是这样,它会设置标志以强制Adorner
重新测量、排列和渲染 - 大致相当于InvalidateMeasure(); InvaliateArrange(); InvalidateVisual();
.
在这种情况下通常发生的情况是首先测量控件,然后排列,然后渲染。事实上,WPF 试图使这种情况成为最常见的情况,因为它是最有效的序列。但是,在许多情况下,控件最终可能会在重新测量之前重新排列和/或重新呈现。这是 WPF 中的合法事件顺序(以允许灵活的布局技术),但它并不常见,因此通常不进行测试。
除非仅更改了依赖项属性,否则任何时候渲染可能会受到影响,正确实现Adorner
或其他UIElement
人都会小心调用。InvalidateVisual()
AffectsRender
在您的情况下,您的装饰器的大小显然会影响渲染。尺寸属性不是AffectsRender
依赖属性,所以需要InvalidateVisual()
在改变时手动调用。如果你不这样做,WPF 可能永远不会知道重新渲染你的装饰器。
您的情况可能是这样的:
- 布局完成并
LayoutChanged
触发事件
AdornerLayer
发现您的尺寸变化AdornedElement
AdornerLayer
安排您的装饰器重新测量、重新布局和重新渲染
- 在重新测量之前发生了一些导致
Arrange()
重新布局和重新渲染的调用。这会导致 WPF 认为装饰器不再需要重新布局或重新渲染。
- 布局引擎检测到装饰器需要测量并调用
Measure
- 装饰器
MeasureOverride
重新计算所需的大小,但没有告诉 WPF 装饰器需要重新渲染
- 布局引擎决定没有更多工作要做,因此装饰器永远不会重新渲染
你可以做些什么来修复它
当然,解决方案是通过在重新测量控件时Adorner
调用来修复错误,如下所示:InvalidateVisual()
protected override Size MeasureOverride(Size constraint)
{
var result = base.MeasureOverride(constraint);
// ... add custom measure code here if desired ...
InvalidateVisual();
return result;
}
这样做会使您的 Adorner 始终遵守 WPF 的所有规则,因此它在所有情况下都能按预期工作。这也是最有效的解决方案,因为InvalidateVisual()
除了在真正需要的情况下什么都不做。