Mar*_*eIV 22 wpf custom-controls redraw invalidation onrender
我们正在控制子类中进行自定义绘制OnRender.此绘图代码基于外部触发器和数据.因此,每当触发器触发时,我们都需要根据该数据重新呈现控件.我们要做的是找出如何强制控件重新渲染但不经过整个布局传递.
如上所述,我所看到的大多数答案都围绕着使Visual无效的布局无效,这会迫使新的测量并安排非常昂贵的通行证,特别是对于像我们这样非常复杂的视觉树.但同样,布局不会改变,VisualTree 也不会改变.唯一能做的就是外部数据的呈现方式不同.因此,这严格来说是纯粹的渲染问题.
同样,我们只是在寻找一种简单的方法来告诉控件它需要重新执行OnRender.我见过一个"黑客"在您创建一个新的DependencyProperty,并用你刚才设置的时候要刷新的控制一定的价值"AffectsRender"注册它,但我更感兴趣的是发生了什么事情的默认实现内那些属性:他们所谓的影响那种行为.
好了,它看起来像没有任何这样的呼叫,因为即使是AffectsRender国旗还是引起内部安排通(按照以下CodeNaked的答案),但我已经张贴,显示了内置行为,以及一个工作-第二个答案周围禁止你的布局传递代码从一个简单的可空大小作为标志运行.见下文.
Cod*_*ked 19
不幸的是,您必须调用InvalidateVisual,它在内部调用InvalidateArrange.该OnRender方法作为排列阶段的一部分被调用,因此您需要告诉WPF重新排列控件(InvalidateArrange所做的)并且需要重绘(InvalidateVisual会这样做).
该FrameworkPropertyMetadata.AffectsRender选项只是告诉WPF InvalidateVisual在关联属性更改时调用.
如果你有一个控件(让我们调用这个MainControl)覆盖OnRender并包含几个后代控件,那么调用InvalidateVisual可能需要重新安排后代控件,甚至重新测量.但我相信WPF已经进行了优化,以防止后代控件在可用空间不变的情况下重新排列.
您可以通过将渲染逻辑移动到单独的控件(比如NestedControl)来解决这个问题,该控件将是MainControl的可视子级.MainControl可以将其自动添加为可视子项,也可以将其作为ControlTemplate的一部分添加,但它必须是z顺序中的最低子项.然后,您可以InvalidateNestedControl在MainControl上公开一个类型方法,该方法将在NestedControl上调用InvalidateVisual.
Mar*_*eIV 11
好的,我正在回答这个问题,向人们展示为什么CodeNaked的答案是正确的,但如果你愿意,可以用星号表示,并提供解决方法.但是,在良好的SO公民身份中,我仍然标记着他的答案,因为他的回答使我在这里.
更新:我已经将接受的答案移到这里有两个原因.第一,我想让人们知道有是到了一个解决方案(大多数人只读接受的答案,继续前进)和二,考虑到他有25K一个代表,我不认为如果我带回去,他会介意!:)
这就是我做的.为了测试这个,我创建了这个子类......
public class TestPanel : DockPanel
{
protected override Size MeasureOverride(Size constraint)
{
System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
return base.MeasureOverride(constraint);
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
return base.ArrangeOverride(arrangeSize);
}
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
System.Console.WriteLine("OnRender called for " + this.Name + ".");
base.OnRender(dc);
}
}
Run Code Online (Sandbox Code Playgroud)
...我这样布局(注意它们是嵌套的):
<l:TestPanel x:Name="MainTestPanel" Background="Yellow">
<Button Content="Test" Click="Button_Click" DockPanel.Dock="Top" HorizontalAlignment="Left" />
<l:TestPanel x:Name="InnerPanel" Background="Red" Margin="16" />
</l:TestPanel>
Run Code Online (Sandbox Code Playgroud)
当我调整窗口大小时,我得到了这个......
MeasureOverride called for MainTestPanel.
MeasureOverride called for InnerPanel.
ArrangeOverride called for MainTestPanel.
ArrangeOverride called for InnerPanel.
OnRender called for InnerPanel.
OnRender called for MainTestPanel.
Run Code Online (Sandbox Code Playgroud)
但当我打电话InvalidateVisual给'MainTestPanel'时(在按钮的'Click'事件中),我得到了这个......
ArrangeOverride called for MainTestPanel.
OnRender called for MainTestPanel.
Run Code Online (Sandbox Code Playgroud)
请注意,没有调用任何测量覆盖,只调用外部控件的ArrangeOverride.
它并不完美,好像你ArrangeOverride的子类中有一个非常繁重的计算(不幸的是我们这样做)仍然得到(重新)执行,但至少孩子们不会陷入同样的命运.
但是,如果您知道没有任何子控件具有设置了AffectsParentArrange位的属性(我们这样做了),您可以更好地使用Nullable Size作为标志来禁止重新进入的ArrangeOverride逻辑,除非需要,像这样......
public class TestPanel : DockPanel
{
Size? arrangeResult;
protected override Size MeasureOverride(Size constraint)
{
arrangeResult = null;
System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
return base.MeasureOverride(constraint);
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
if(!arrangeResult.HasValue)
{
System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
// Do your arrange work here
arrangeResult = base.ArrangeOverride(arrangeSize);
}
return arrangeResult.Value;
}
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
System.Console.WriteLine("OnRender called for " + this.Name + ".");
base.OnRender(dc);
}
}
Run Code Online (Sandbox Code Playgroud)
现在除非特别需要重新执行排列逻辑(作为对MeasureOverride的调用),否则只能获得OnRender,如果你想明确强制排列逻辑,只需将大小归零,调用InvalidateVisual,Bob就是你的叔叔!:)
希望这可以帮助!
InvalidateVisual()除非控件的大小发生变化,否则您不应调用,即使如此,仍有其他方法可以导致重新布局。
有效地更新控件的视觉效果而不改变其大小。使用一个DrawingGroup. 您创建DrawingGroup,并把它放入DrawingContext时OnRender(),然后之后的任何时间,你可以Open()在DrawingGroup改变它的视觉绘图命令,和WPF将自动高效地重新呈现UI的部分。(RenderTargetBitmap如果您更喜欢可以对位图进行增量更改,而不是每次都重新绘制,则也可以使用此技术)
这是它的样子:
DrawingGroup backingStore = new DrawingGroup();
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
Render(); // put content into our backingStore
drawingContext.DrawDrawing(backingStore);
}
// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {
var drawingContext = backingStore.Open();
Render(drawingContext);
drawingContext.Close();
}
private void Render(DrawingContext drawingContext) {
// put your render code here
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
23749 次 |
| 最近记录: |