为什么我的装饰者在应用它的元素发生变化时不会重新渲染?

Rob*_*ney 16 wpf adorner

在我正在构建的用户界面中,只要面板中的一个控件具有焦点,我就想要装饰面板.因此,我处理IsKeyboardFocusWithinChanged事件,并在元素获得焦点时添加元素,并在焦点失去焦点时移除元素.这似乎工作正常.

我遇到的问题是,如果装饰元素的边界发生变化,则不会重新渲染装饰器.例如,在这个简单的情况下:

<WrapPanel Orientation="Horizontal"
           IsKeyboardFocusChanged="Panel_IsKeyboardFocusChanged">
   <Label>Caption</Label>
   <TextBox>Data</TextBox>
</WrapPanel>
Run Code Online (Sandbox Code Playgroud)

装饰器正确地装饰了接收焦点WrapPanel时的边界TextBox,但是当我输入文本时,TextBox会在装饰边缘下面展开.当然,只要我做任何迫使装饰者渲染的东西,例如ALT-TAB从应用程序中移出或给予另一个小组焦点,它就会自行纠正.但是,如果装饰元素的边界发生变化,我怎样才能重新渲染它?

Ray*_*rns 58

WPF有一个内置机制,Adorners可以在相应的AdornedElement大小,位置或变换发生变化时重新计算,重新排列和重新渲染所有内容.这种机制要求您在对您的装饰者进行编码时遵循某些规则,而不是所有这些都按照应有的清晰记录.

我将首先回答你的标题问题,说明为什么你的装饰师不能一致地重新渲染,然后解释解决它的最佳方法.

为什么装饰者不会重新渲染

每当AdornerLayer收到LayoutChanged通知时,它会扫描每个Adorner以查看其AdornedElement大小,位置或变换是否发生了变化.如果是这样,它会设置标志以强制Adorner再次测量,排列和渲染 - 大致相当于InvalidateMeasure(); InvaliateArrange(); InvalidateVisual();.

在这种情况下通常发生的是首先测量控制,然后排列,然后再渲染.事实上,WPF试图将其作为最常见的情况,因为它是最有效的序列.然而,在许多情况下,控件最终可能会在重新测量之前重新排列和/或重新渲染.这是WPF中合法的事件顺序(允许灵活的布局技术),但它并不常见,因此通常不进行测试.

除非只更改了依赖项属性,否则在任何时候渲染都可能受到影响时,正确实现Adorner或其他操作UIElement都会小心.InvalidateVisual() AffectsRender

在您的情况下,您的装饰者尺寸明显会影响渲染.size属性不是AffectsRender依赖属性,因此InvalidateVisual()在更改时必须进行manualy调用.如果不这样做,WPF可能永远不会知道重新渲染您的装饰者.

在你的情况下发生的事情可能是这样的:

  • 布局完成,LayoutChanged事件触发
  • AdornerLayer 发现你的尺寸变化 AdornedElement
  • AdornerLayer 安排您的装饰者重新测量,重新布局和重新渲染
  • 导致Arrange()被调用的东西导致重新布局并在重新测量之前重新渲染.这会导致WPF认为装饰者不再需要重新布局或重新渲染.
  • 布局引擎检测到装饰者需要测量和调用 Measure
  • 装饰者MeasureOverride重新计算所需的大小,但没有告诉WPF装饰者需要重新渲染
  • 布局引擎决定不再需要做任何事情,因此装饰器永远不会重新渲染

你可以做些什么来解决它

当然,解决方案是在重新测量控件时Adorner通过调用修复bug InvalidateVisual(),如下所示:

protected override Size MeasureOverride(Size constraint)
{
  var result = base.MeasureOverride(constraint);
  // ... add custom measure code here if desired ...
  InvalidateVisual();
  return result;
}
Run Code Online (Sandbox Code Playgroud)

这样做会使您的Adorner始终遵守WPF的所有规则,因此它将在所有情况下按预期工作.这也是最有效的解决方案,因为InvalidateVisual()除了真正需要它的情况外,它什么都不做.

  • 大小属性不会影响渲染有多奇怪 - 以前没有碰过这个,但看起来像一个令人讨厌的小问题而且我很惊讶它不会经常出现!你知道(或者你能推测)为什么WPF设计师以这种方式构建它吗?无论如何,感谢您提供如此清晰,详尽和翔实的答案. (2认同)