Move UIElement in Visual Tree without recalculating layout

↘锁芯ラ 提交于 2020-01-13 05:50:12

问题


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.


回答1:


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. :)




回答2:


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.




回答3:


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).



来源:https://stackoverflow.com/questions/3968072/move-uielement-in-visual-tree-without-recalculating-layout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!