可以在给定区域而不是整个 WPF 控件上使用 InvalidateVisual() 吗?

发布于 2024-08-27 20:36:57 字数 298 浏览 7 评论 0原文

我有一个复杂的 WPF 控件,它在其 OnRender 中绘制许多图元(有点像地图)。当一小部分发生变化时,我只想为受影响的元素重新发出渲染命令,而不是运行整个 OnRender。虽然我对 OnRender 函数在调整大小或其他方面的性能感到满意,但对于基于鼠标悬停的图元突出显示来说,它的速度不够快。

目前我知道如何强制屏幕更新的唯一方法是调用 InvalidateVisual()。无法发送脏矩形区域以使其无效。

WPF屏幕构成的最低粒度是UI元素吗?我是否需要将基元渲染为中间目标,然后使用 InvalidateVisual() 更新到屏幕?

I have a complex WPF control that draws a lot of primitives in its OnRender (it's sort of like a map). When a small portion of it changes, I'd only like to re-issue render commands for the affected elements, instead of running the entire OnRender over. While I'm fine with my OnRender function's performance on a resize or whatever, it's not fast enough for mouse hover-based highlighting of primitives.

Currently the only way I know how to force a screen update is to call InvalidateVisual(). No way to send in a dirty rect region to invalidate.

Is the lowest granularity of WPF screen composition the UI element? Will I need to do my renders of primitives into an intermediate target and then have that use InvalidateVisual() to update to the screen?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

感情旳空白 2024-09-03 20:36:57

当您想要编写 WPF 自定义/复合控件时,您应该尽量避免重写 OnRender,特别是当您计划使其部分内容无效时。使用 AddVisualChild + 覆盖 VisualChildrenCount + 覆盖 GetVisualChild + 覆盖 Measure & 更容易。像这样排列(有 2 个子级的伪代码):

private void BuildMyControls()
{
  AddVisualChild(subControl1);
  AddVisualChild(subControl2);
}

protected override int VisualChildrenCount
{
  get
  {
    return 2;
  }
}

protected override Visual GetVisualChild(int index)
{
  if (index == 0) return subControl1;
  if (index == 1) return subControl2;
  return null; // should never be called in fact...
}

protected override Size MeasureCore(Size availableSize)
{
  base.Measure...
  BuildMyControls();
  .. measure them, probably call subControlX.Measure(...);
}

protected override void ArrangeCore(Rect finalRect)
{
  base.ArrangeCore(finalRect);
  ... arrange them, probably call subControlX.Arrange
}

使用这种代码,您可以使用类似 subControlX.InvalidateXXX(); 之类的方法仅使一部分无效。

When you want to write WPF custom/composite controls, you should try to avoid overriding OnRender as possible especially if you plan to invalidate portions of it. It's much easier to use AddVisualChild + override VisualChildrenCount + override GetVisualChild + override Measure & Arrange like this (pseudo code with 2 children):

private void BuildMyControls()
{
  AddVisualChild(subControl1);
  AddVisualChild(subControl2);
}

protected override int VisualChildrenCount
{
  get
  {
    return 2;
  }
}

protected override Visual GetVisualChild(int index)
{
  if (index == 0) return subControl1;
  if (index == 1) return subControl2;
  return null; // should never be called in fact...
}

protected override Size MeasureCore(Size availableSize)
{
  base.Measure...
  BuildMyControls();
  .. measure them, probably call subControlX.Measure(...);
}

protected override void ArrangeCore(Rect finalRect)
{
  base.ArrangeCore(finalRect);
  ... arrange them, probably call subControlX.Arrange
}

With this kind of code, you can invalidate just one portion, with something like subControlX.InvalidateXXX();

迷荒 2024-09-03 20:36:57

除非控件的大小发生变化,否则不应使用 InvalidateVisual(),因为这会导致 UI 重新布局的成本相当昂贵。

WPF 是一个保留绘图系统。这意味着 OnRender() 最好称为 AccumulateDrawingObjects()。它实际上积累了一棵实时绘图对象树,每个布局只需要发生一次。然后,它会在需要时使用这些对象来绘制您的 UI。要在不重新布局的情况下更改 UI 部分的外观,可以在 OnRender() 之后随时更新某些对象(例如 DrawingGroup、RenderTargetBitmap 和 WriteableBitmap)。

要稍后更新 UI 的一部分,请将这些命令包装在 DrawingGroup 中,并将对象放入 DrawingContext 中。然后您可以 Open() 并在需要时更新它,WPF 将自动重新绘制 UI 的该部分。

它看起来是这样的:

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();            
}

You shouldn't be using InvalidateVisual() unless the size of your control changes, as it causes a fairly expensive re-layout of your UI.

WPF is a retained drawing system. That means OnRender() might better be called AccumulateDrawingObjects(). It's actually accumulating a tree of live drawing objects, which only needs to happen once per layout. It then uses these objects to draw your UI whenever it needs to. To change how a portion of your UI looks without re-layout, some objects (like DrawingGroup, RenderTargetBitmap, and WriteableBitmap) can be updated after OnRender(), anytime you like.

To update a portion of your UI later, wrap those commands in a DrawingGroup and put that object into the DrawingContext. Then you can Open() and update it whenever you want, and WPF will automatically repaint that portion of the UI.

This is what it looks like:

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();            
}
难如初 2024-09-03 20:36:57

WPF 的工作方式与此不同,因此您无法使区域无效。但是,可以进行一些优化。有一个“测量”、“排列”和“渲染”通道。如果控件移动但实际呈现的内容没有改变,那么您可以告诉 WPF 仅执行排列过程。您可以使用 FrameworkPropertyMetadata 和 FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx)。

WPF doesn't work quite like that, so you can't invalidate regions. However, there are some optimizations that can be made. There is a Measure, Arrange, and then Render pass. If a control moves but what actually renders doesn't change then you can tell WPF to do only the arrange pass. You can trigger these invalidations off of dependency property value changes with FrameworkPropertyMetadata and FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文