如何强制一个自定义的 FrameworkElement 渲染已更改的内容?

3
我有一个 Visuals 的树。主窗口有一个全屏的画布,画布持有自定义 Visuals 的根节点。第一个自定义 Visual (A) 可以展开或收缩,根据用户浏览的子节点 (Bs, 是 DrawingVisuals)。当主窗口改变大小时,我想要添加或移除 A 的子节点。当用户在视图中向任何方向滚动时,情况也可能发生变化。当添加子节点 (Bs) 时,我希望它们被呈现出来。(被移除的 Bs 不是问题,因为它们已经不可见了。)
举个例子:主窗口的尺寸为 640x480。我将其调整为两倍大小,这会显示更多的 Bs,然后将它们添加到 A 中。迄今为止,我无法使新添加的 Bs 呈现,即使在 FrameworkElement 的 Invalidate??() 方法中使用也无效。我已经尝试在主窗口和 A 中添加 Bs 时使用过 Invalidate??() 方法(我并没有尝试在 Bs 中使用,因为它们是新的)。看起来 Invalidate??() 不是解决问题的方法。
我发现了一个 hack,在调整大小时可以让新的 Bs 被渲染。在主窗口中,我可以删除 A 并重新添加,这样可以强制重新绘制树:
this.contentCanvas.Children.RemoveRange(0, contentCanvas.Children.Count); // 删除根节点 this.contentCanvas.Children.Add(this.CustomVisual_A); // 重新添加根节点
出于明显的原因,这是不可取的,特别是在滚动时。具体来说,被添加和删除的 Bs 是以那种方式管理的,因为它们相当大,我需要尽可能地减少内存占用并提高渲染速度。这就是为什么我要费这么大的劲。
有人可以帮助我理解如何使新添加的 Bs 被呈现吗?非常感谢!
2个回答

3
我解决了这个问题。即使FrameworkElement是自定义的,你仍需要使用标准的AddVisualChild()和RemoveVisualChild()方法来管理你的子元素:
this.AddVisualChild(new B());
...
this.RemoveVisualChild(existingB);

你仍然维护自己的B集合,但似乎除非你使用AddVisualChild()RemoveVisualChild(),否则基础的FrameworkElement不会看到这种变化(实际上是Canvas的超类Panel)。这可能还涉及父级以某种方式进行通知;不确定。最终,这些集合管理方法会导致在Panel上定义的OnVisualChildrenChanged()方法被调用,因此应该在A中实现它。在那时,我将子项添加/删除到我的内部集合中,然后调用基类:
protected override void OnVisualChildrenChanged(DependencyObject added, DependencyObject removed)
{
     if (added != null)
     {
        this.children.Add((B) added);
     }

     if (removed != null)
     {
        this.children.Remove((B) removed);
     }

     base.OnVisualChildrenChanged(added, removed);
}

这会引起内部指示需要进行渲染。
我想肯定是这种情况,因为当我从我的自定义Canvas中移除A并重新添加A时,它强制进行了渲染。

2

有几个调用需要这样做...

        element.UpdateLayout();
        element.InvalidateArrange();
        element.InvalidateMeasure();
        element.InvalidateVisual();

谢谢。是的,我尝试过使用所有这些(由上面的Invalidate???()指示)。我还尝试了UpdateLayout()。关键缺失的部分在我的下面回答中找到了。 - Vaughn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接