在我正在构建的用户界面中,只要面板中的某个控件具有焦点,我就会想要装饰面板。因此,我处理IsKeyboardFocusWithinChanged
事件,并在元素获得焦点时向元素添加元素,并在焦点失去焦点时移除元素。这似乎工作正常。
我遇到的问题是,如果装饰元素的边界发生变化,则不会重新渲染装饰器。例如,在这个简单的例子中:
<WrapPanel Orientation="Horizontal"
IsKeyboardFocusChanged="Panel_IsKeyboardFocusChanged">
<Label>Caption</Label>
<TextBox>Data</TextBox>
</WrapPanel>
当WrapPanel
收到焦点时,装饰器会正确地装饰TextBox
的边界,但当我输入文字时,TextBox
会在装饰边缘下方展开。当然,只要我做任何迫使装饰者渲染的东西,例如ALT-TAB从应用程序中移出或给予另一个小组焦点,它就会自行纠正。但是,当装饰元素的边界发生变化时,如何让它重新渲染?
答案 0 :(得分:58)
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()
除了真正需要它的情况外什么都不做。
答案 1 :(得分:1)
您需要在面板上调用调度程序。向TextBox SizeChanged事件添加处理程序:
private void myTextBox_SizeChanged(object sender, SizeChangedEventArgs e)
{
panel.Dispatcher.Invoke((Action)(() =>
{
if (panel.IsKeyboardFocusWithin)
{
// remove and add adorner to reset
myAdornerLayer.Remove(myAdorner);
myAdornerLayer.Add(myAdorner);
}
}), DispatcherPriority.Render, null);
}
这主要来自这篇文章:http://geekswithblogs.net/NewThingsILearned/archive/2008/08/25/refresh--update-wpf-controls.aspx