当将应用于元素的元素更改时,为什么我的装饰器不重新渲染? [英] Why does my adorner not re-render when the element it's applied to changes?

查看:95
本文介绍了当将应用于元素的元素更改时,为什么我的装饰器不重新渲染?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我正在构建的UI中,只要面板中的一个控件具有焦点,我就希望装饰一个面板。因此,我处理了 IsKeyboardFocusWithinChanged 事件,并在获得焦点时在元素中添加了修饰符,并在失去焦点时将其移除。

In a UI I'm building, I want to adorn a panel whenever one of the controls in the panel has the focus. So I handle the IsKeyboardFocusWithinChanged event, and add an adorner to the element when it gains the focus and remove the adorner when it loses focus. This seems to work OK.

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

The problem I'm having is that the adorner isn't getting re-rendered if the bounds of the adorned element changes. For instance, in this simple case:

<WrapPanel Orientation="Horizontal"
           IsKeyboardFocusChanged="Panel_IsKeyboardFocusChanged">
   <Label>Caption</Label>
   <TextBox>Data</TextBox>
</WrapPanel>

装饰器正确装饰了 WrapPanel 当 TextBox 获得焦点时,但是当我输入文本时, TextBox 会在装饰边缘下方展开。当然,一旦我做任何迫使装饰器渲染的事情,例如ALT-TAB退出应用程序或将焦点移到另一个面板上,它便会自行纠正。但是,当装饰元素的边界发生变化时,如何才能重新渲染它呢?

the adorner correctly decorates the bounds of the WrapPanel when the TextBox receives the focus, but as I type in text, the TextBox expands underneath the edge of the adorner. Of course as soon as I do anything that forces the adorner to render, like ALT-TAB out of the application or give another panel the focus, it corrects itself. But how can I get it to re-render when the bounds of the adorned element change?

推荐答案

WPF具有内置功能每当相应的 AdornedElement 更改大小,位置或变换时,都会重新测量,重新排列和重新呈现所有装饰器的机制。这种机制要求您在对装饰者进行编码时遵循某些规则,但并不是所有的文件都应有应有的清晰记录。

WPF has a built-in mechanism to cause all Adorners to be remeasured, rearranged, and rerendered whenever the corresponding AdornedElement changes size, position, or transform. This mechanism requires you to follow certain rules when coding your adorner, not all of which are documented as clearly as they ought to be.

我将首先回答您的标题问题为什么,您的装饰者不一致地重新渲染,然后解释修复它的最佳方法。

I will first answer your title question of why your adorner doesn't consistenty re-render, then explain the best way to fix it.

为什么装饰者没有t重新呈现

每当AdornerLayer收到LayoutChanged通知时,它就会扫描其每个装饰器,以查看 AdornedElement 的大小,位置或变换已更改。如果是这样,它将设置标志以强制 Adorner 再次进行测量,排列和渲染-大致等效于 InvalidateMeasure();。 InvaliateArrange(); InvalidateVisual();

Whenever an AdornerLayer receives a LayoutChanged notification it scans each of its Adorners to see if the AdornedElement has changed in size, position or transform. If so, it sets flags to force the Adorner to measure, arrange, and render again -- roughly equivalent to InvalidateMeasure(); InvaliateArrange(); InvalidateVisual();.

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

What normally happens in this situation is that the control is first measured, then arranged, then rendered. In fact, WPF tries to make this the most common case because it is the most efficient sequence. However there are many situations where a control can end up being rearranged and/or rerendered before it is remeasured. This is a legitimate order of events in WPF (to allow flexible layout techniques), but it is not common so it is often not tested.

正确实现的 Adorner 或其他 UIElement 会谨慎地调用 InvalidateVisual() 任何时候渲染都可能受到影响,除非仅更改了 AffectsRender 依赖项属性。

A correctly implemented Adorner or other UIElement will be careful to call InvalidateVisual() any time the rendering may be affected unless only AffectsRender dependency properties were changed.

情况下,装饰者的大小显然会影响渲染。大小属性不是 AffectsRender 依赖项属性,因此有必要在更改 InvalidateVisual()时手动调用它们。如果您不这样做,则WPF可能永远不会知道重新渲染装饰器。

In your case, your adorner's size clearly affect rendering. The size properties are not AffectsRender dependency properties, so it is necessary to manualy call InvalidateVisual() when they change. If you don't, WPF may never know to re-render your adorner.

您所处的情况可能是这样的:

What is happening in your situation is probably this:


  • 布局完成,并且 LayoutChanged 事件触发

  • AdornerLayer 发现您的 AdornedElement

  • AdornerLayer 安排您的装饰器重新测量,重新布局和重新渲染

  • 某些原因导致 Arrange()被调用,导致重新布局和重新渲染在重新测量之前发生。这使WPF认为装饰器不再需要重新布局或重新渲染。

  • 布局引擎检测到装饰器需要测量并调用 Measure code>

  • 装饰者的 MeasureOverride 重新计算所需的大小,但并没有告诉WPF装饰者需要重新设置大小-render

  • 布局引擎决定无事可做,因此装饰者永远不会重新渲染

  • Layout completes and the LayoutChanged event fires
  • AdornerLayer discovers the size change on your AdornedElement
  • AdornerLayer schedules your adorner for re-measure, re-layout, and re-render
  • Something causes Arrange() to be called which causes the re-layout and re-render to happen before the re-measure. This causes WPF to think the adorner no longer needs a re-layout or re-render.
  • The layout engine detects that the adorner needs measuring and calls Measure
  • The adorner's MeasureOverride recomputes the desired size but does nothing to tell WPF the adorner needs to re-render
  • The layout engine decides there is nothing more to be done and so the adorner never re-renders

您可以采取哪些措施对其进行修复

解决方案当然是要修复每当重新测量控件时,通过调用 InvalidateVisual()来调用 Adorner ,例如:

The solution is, of course, to fix the bug in the Adorner by calling InvalidateVisual() whenever the control is re-measured, like this:

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

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

Doing this will cause your Adorner to consistently obey all the rules of WPF, so it will work as expected in all situations. This is also the most efficient solution, since InvalidateVisual() will do nothing at all except in those cases where it is really needed.

这篇关于当将应用于元素的元素更改时,为什么我的装饰器不重新渲染?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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