在可视化树中移动 UIElement 而不重新计算布局

发布于 2024-09-27 18:01:31 字数 432 浏览 9 评论 0原文

我们有一个使用 Docking Control (Actipro) 的 WPF 应用程序 (.Net 4.0)。我们可以关闭对接窗口。在这种情况下,将创建一个“真实”窗口并将内容分配给该窗口。

当然,移动视觉树中的内容将重新触发完整的布局。这是有问题的,因为在这些停靠窗口之一中,我们有一个图表控件(Mindfusion Chartming,WPF 控件),它可能需要长达 10 秒的时间才能完全布局自身(非常大的图表)。

我认为这个问题没有任何直接的解决方案。然而我想知道其他有类似问题的程序员是如何解决这个问题的。有什么聪明的方法可以避免重新计算布局吗?

从理论上讲,由于图表位于 ScrollViewer 内部,因此实际上没有任何变化,因此无论何时放置它,可用空间量都保持不变(无限)。

编辑:另请注意,内部的图表控件是交互式的。我们需要拖放。

We have a WPF application (.Net 4.0) using a Docking Control (Actipro). We can dock out the docking windows. In that case, a "real" Window is created and the content is assigned to that window.

Of course, moving stuff in the Visual Tree will re-trigger the complete layouting. This is problematic, because in one of these docking windows, we have a diagramming control (Mindfusion Diagramming,WPF control) that can take up to 10 seconds to completely layout itself (very large diagrams).

I don't think that there's any direct solution to this problem. I wonder however how other programmers with similar issues approached this problem. Is there any clever way to avoid recalculating the layout?

In theory, nothing really changes since the diagram is inside a ScrollViewer, so whenever it is placed, the amount of avaiable space remains the same(infinite).

Edit: Also note that the diagram control inside is interactive. We need Drag&Drop.

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

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

发布评论

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

评论(3

泪痕残 2024-10-04 18:01:31

这是一个想法。

  1. 创建一个继承自 Decorator 的自定义类。
  2. 将图表控件包装在装饰器内。
  3. 覆盖 MeasureOverride 并简单地调用 base.Measure 但在返回之前将结果存储在字段中。
  4. 添加一个属性,使您能够禁用测量调用。如果该属性为 true,则只需在 MeasureOverride 中返回先前的大小,而不是调用 base.Measure。
  5. 在更改视觉层次结构时设置属性。

从我的头脑中,我想不出任何理由为什么这不起作用。

实际上不久前我也做过类似的事情。在 NovaMind 中实现侧面板的滑动动画时,我使用了装饰器来防止内容在面板时执行布局动画其宽度。我使用最终宽度计算了大小,将其存储,然后使用 MeasureOverride 来伪造当前大小...这可以防止在尝试对复杂控件的宽度进行动画处理时涉及的性能问题。 :)

Here is an idea.

  1. Create a custom class inheriting from Decorator.
  2. Wrap your diagramming control inside the decorator.
  3. Override MeasureOverride and simply call base.Measure but store the result in a field before returning.
  4. Add a property which enables you to disable the measure call. If the property is true simply return the previous size in MeasureOverride instead of calling base.Measure.
  5. Set the property while changing the visual hierarchy.

From the top of my head I can't think of any reason why this shouldn't work.

I have actually done something very similar not too long ago. When implementing the sliding animation for the side panels in NovaMind I used a Decorator to prevent the content from performing layout while the panel animates its width. I calculated the size with the final width, stored it and then used MeasureOverride to fake the current size... This prevented the performance issues involved when trying to animate the width of a complex control. :)

倾其所爱 2024-10-04 18:01:31

另一种可能性是,问题与布局无关,而是与将内容从一个窗口移动到另一个窗口时可视化树的“切断”有关。这似乎会导致依赖属性的大量重新计算,如果您的内容可视化树像我的一样,有 2000 个以上的控件,那么速度真的很慢。

我无法使用 Actipro 对接库本身找到一个优雅的解决方案,因此我想如何使 WPF 不再执行此行为。我提出的解决方案是将我的内容创建为具有 WinForms UserControl 的单个子级的单个 WinFormsHost 控件。然后,我使 WinForms UserControl 的内容成为基于 WPF 的内容,该内容应显示为停靠窗口内容。我认为当 WPF 开始从顶部遍历可视化树以在树被“剪断”时重新评估所有依赖属性时,它会遇到 WinForms 控件并停止。

我的 Actipro 对接工具窗口过去需要 6 秒左右才能切换选项卡或浮动。现在它们基本上是瞬时的。您必须确保任何命令处理程序都不是在应用程序级别,而是在您的 WPF 内容级别,并且您可能必须调整某些样式文件的位置,但效果非常好。

Another possibility is that the problem isn't related to layout so much as the "severing" of the visual tree when moving the content from one window to another. This seems to cause a slew of recalculations for dependency properties, which if your visual tree for content was like mine, upwards of 2000 controls, it was really slow.

I couldn't find an elegant solution to this using Actipro docking library itself, so I thought how I could divert WPF from doing this behavior. The solution I came up with was to create my content as a single WinFormsHost control with a single child of a WinForms UserControl. Then, I made that WinForms UserControl have it's content be the WPF based content that should appear as the docking window content. I figured that when WPF started walking the visual tree from the top to re-evaluate all dependency properties when the tree was "snipped", it would run into the WinForms control and stop.

My Actipro docking tool windows used to take 6 seconds or so just to switch tabs or to float. Now they are essentially instaneous. You have to ensure that any command handlers are not at the application level, but instead at your WPF content level and you might have to finagle wtih the location of some style files, but it worked fantastic.

挽袖吟 2024-10-04 18:01:31

您可能希望将可视化树中的图表控件替换为图像,在屏幕外渲染图表并使用 rendertargetbitmap 将渲染的图表转换为图像,您可以将其用作可视化树中图像的源。

像这样的事情:

// image is the Image from the visual tree
int h = image.ActualHeight;
int w = image.ActualWidth;

// layout the diagram to the size of the image
diagram.Measure(new Size(w, h));
diagram.Arrange(new Rect(newSize(w,h)));
diagram.UpdateLayout();

// render the diagram to a bitmap
RenderTargetBitmap bmp = new RenderTargetBitmap((int)w, (int)h, 96, 96, PixelFormats.Default);
bmp.Render(diagram);

// set the source of your image to the bitmap
image.Source = bmp;

在示例中,如果 PixelFormats.Default 似乎不起作用,您可以尝试 PixelFormats.Pbgra32,我认为这是在此类事情中使用的更常见的格式。

您也许还可以以类似的方式使用 VisualBrush。我可以想象,从长远来看,您可能可以为图表创建一个包装类,以便仅在发生变化(即图表的一部分或大小)时自动显示图像副本并重新布局图表。

you might want to replace your diagramming control in the visual tree with an Image, render the diagram offscreen and use rendertargetbitmap to convert the rendered diagram to an image, which you can use as the source for the Image in the visual tree.

something like this:

// image is the Image from the visual tree
int h = image.ActualHeight;
int w = image.ActualWidth;

// layout the diagram to the size of the image
diagram.Measure(new Size(w, h));
diagram.Arrange(new Rect(newSize(w,h)));
diagram.UpdateLayout();

// render the diagram to a bitmap
RenderTargetBitmap bmp = new RenderTargetBitmap((int)w, (int)h, 96, 96, PixelFormats.Default);
bmp.Render(diagram);

// set the source of your image to the bitmap
image.Source = bmp;

in the example if PixelFormats.Default doesn't seem to work, you might try PixelFormats.Pbgra32, which I think is a more common format to use in this type of thing.

you might also be able to use a VisualBrush in a similar manner. I can imagine in the long run you could probably create a wrapper class for the diagram to automatically display the image copy and re-layout the diagram only if something changes (ie a part of the diagram or the size).

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