在我正在构建的UI中,每当面板中的一个控件具有焦点时,我希望装饰面板。因此,我处理IsKeyboardFocusWithinChanged
事件,并在元素获得焦点时向元素添加装饰器,并在失去焦点时删除装饰器。这似乎可以正常工作。
我遇到的问题是,如果被装饰元素的边界发生变化,装饰器不会被重新呈现。例如,在这个简单的例子中:
<WrapPanel Orientation="Horizontal"
IsKeyboardFocusChanged="Panel_IsKeyboardFocusChanged">
<Label>Caption</Label>
<TextBox>Data</TextBox>
</WrapPanel>
当TextBox
接收到焦点时,装饰器正确地装饰了WrapPanel
的边界,但是当我输入文本时,TextBox
在装饰器的边缘下方展开。当然,只要我做任何迫使装饰器渲染的事情,比如ALT-TAB退出应用程序或给予另一个面板焦点,但是当被修饰的元素的边界改变时,我怎样才能让它重新呈现呢?
2条答案
按热度按时间balp4ylt1#
WPF有一个内置的机制,当相应的
AdornedElement
改变大小、位置或转换时,会导致所有Adorners
被重新测量、重新排列和重新呈现。这种机制要求您在编写装饰器时遵循某些规则,但并非所有规则都有明确的文档说明。我将首先回答你的标题问题为什么你的装饰器不一致重新渲染,然后解释最好的方法来解决它。
为什么装饰器不重新渲染
每当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装饰器需要重新呈现 *你能做些什么来弥补
当然,解决方案是通过在每次重新测量控件时调用
InvalidateVisual()
来修复Adorner
中的错误,如下所示:这样做会使你的Adorner始终遵守WPF的所有规则,所以它在所有情况下都能按预期工作。这也是最有效的解决方案,因为
InvalidateVisual()
除了在真正需要它的情况下,什么也不做。jecbmhm32#
您需要调用面板上的调度程序。向TextBox SizeChanged事件添加一个处理程序:
这基本上来自这篇文章:Link