Persist VisualTree when switching tabs

Docking/MDI for WPF Forum

Posted 8 years ago by Gurpreet Bakshi
Version: 10.2.0532
Avatar
Hi,

I would like to the tab windows within a docksite to persist the visual tree when the tab switching happens as described in this article:

http://www.codeproject.com/KB/WPF/PersistTab.aspx

We are using the Actipro Docking and MDI suite to display views in a tabbed interface. The views are pretty heavy and the user has the need to switch the tabs extensively. The problem is that everytime you switch the tab, the visual tree is destroyed for the deselected tab and generated for the new one. There are two problems attached to it:

a) the slowness in switching tabs
b) for the elements that are not participating in databinding, they get reset.

The url above provides a fix for this when using the WPF TabControl. Can we achieve the same using the Actipro Docking Suite?

Many Thanks
Gurpreet

Comments (11)

Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Gurpreet,

If you are using our DocumentItemsSource/ToolItemsSource properties, such as with the MVVM pattern, then you can use code like the following to ensure the visuals for the individual windows are retained:
public class MyDockSite : DockSite {

    protected override void PrepareContainerForItemOverride(DockingWindow element, object item, DockingWindowItemType type) {
        base.PrepareContainerForItemOverride(element, item, type);

        if (!(item is Visual)) {
            var contentControl = new ContentControl();
            contentControl.SetBinding(ContentControl.ContentProperty, new Binding("DataContext") { Source = element });
            contentControl.SetBinding(ContentControl.ContentTemplateProperty, new Binding("ContentTemplate") { Source = element });
            contentControl.SetBinding(ContentControl.ContentTemplateSelectorProperty, new Binding("ContentTemplateSelector") { Source = element });

            // DataContext of element will already be set to item, so no need to explicitly set that here
            element.Content = contentControl;
        }
    }

}
When switching tabs, the visuals will still be removed from the visual tree. They are retained though, so when the tab is selected they are just added back in.


Actipro Software Support

Posted 8 years ago by Gurpreet Bakshi
Avatar
Thanks for the reply. However i am not using the DocumentItemsSource/ToolItemsSource. I am actually creating new windows by initializing DocumentWindow () and passing the DockSite to the instance.Can you tell me how your solution can be achieved using the approach we are using to create DocumentWindow(s)?

Thanks
Gurpreet
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Gurpreet,

If you are setting the Content to a visual, then the code above probably won't help. But you'd want to do something like:
var window = new DocumentWindow();
window.Content = new ContentControl {
    Content = item,
    ContentTemplate = dataTemplate,
};
this.dockSite.DocumentWindows.Add(window);
instead of:
var window = new DocumentWindow() {
    Content = item,
    ContentTemplate = dataTemplate,
};
this.dockSite.DocumentWindows.Add(window);


Actipro Software Support

Posted 8 years ago by Gurpreet Bakshi
Avatar
Can you please create a small sample and show how this can be achieved? Highly appreciate your assistance.

Gurpreet
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Gurpreet,

I'm not sure what you mean, I included an example above.


Actipro Software Support

Posted 8 years ago by Gurpreet Bakshi
Avatar
Sorry for being unclear. I am using a DocumentWindow descendant. The way the windows are created is by invoking one of the constructor overloads that accepts a docksite (see below)

 public partial class MyWindow : DocumentWindow, INotifyPropertyChanged
    {
        void initializeComponent()
        { 
            InitializeComponent();
        }
        public MyWindow(DockSite dockSite) : base(dockSite)
        {
            initializeComponent();
        }

        public MyWindow(DockSite dockSite, string name, string caption, ImageSource glyph,
            DependencyObject content) : base(dockSite, name, caption, glyph, content)
        {
            initializeComponent();
        }

        public MyWindow(DockSite dockSite, string name, string caption, ImageSource glyph, 
            System.Windows.Forms.Control content): base(dockSite)
        {
            initializeComponent();
            this.Content = WrapInteropContent(content);
            this.Title = caption;
            this.ImageSource = glyph;
        }
        public MyWindow(DockSite dockSite, string name, string caption, string glyphUri,
            DependencyObject content)
            : base(dockSite)
        {
            InitializeComponent();
            this.Content = content;
            this.Title = caption;
            base.ImageSource = new BitmapImage(new Uri(glyphUri, UriKind.RelativeOrAbsolute));
        }

        public MyWindow(DockSite dockSite, string name, string caption, string packUri,
           System.Windows.Forms.Control content)
            : base(dockSite)
        {
            InitializeComponent();
            this.Content = WrapInteropContent(content);
            this.Title = caption;
            base.ImageSource = new BitmapImage(new Uri(packUri));
        }
}
I somehow cannot find where your provided sample fits? Let's say i have a UserControl that needs to be added to the DockingWindow. In the sample that you provided ( quoted below), is the 'item' the UserControl instance? Where do i get the dataTemplate from?

var window = new DocumentWindow();
window.Content = new ContentControl {
    Content = item,
    ContentTemplate = dataTemplate,
};
this.dockSite.DocumentWindows.Add(window);
Please advise

Regards
G
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Gurpreet,

You'd want to do this:
this.Content = new ContentControl {
    Content = content,
};
But again, this only helps if the content is not a visual (i.e. a control, user control, etc) and has a DataTemplate applied.


Actipro Software Support

Posted 8 years ago by Robert A. McCarter
Avatar

Thanks Actipro Software Support!

I am using MVVM and the DocumentItemsSource property on the dock site, and your first solution worked very nicely.  I did have a custom layout control that worked on the initial tab open, but failed to show up when tabbing back to the tab (I don't know why).

My question is - the following code also works for me - which is "better"?  I'm especially concerned about memory leaks, and in your code it looks like you're creating a circular reference, which I know can sometimes cause WPF memory leaks:

protected override void PrepareContainerForItemOverride( DockingWindow window, object item, DockingWindowItemType type ) {
	// Let the base class do its work
	base.PrepareContainerForItemOverride(window, item, type);

	// If the item inside the tab is a visual, there's nothing to do
	if( item is Visual ) return;

	// Retain the data template instance by putting it inside another
	// visual and setting that as the docking window's content
	var contentControl = new ContentControl();
	contentControl.Content = item;

	// DataContext of element will already be set to item
	// so there is no need to explicitly set that again
	window.Content = contentControl;
}

 

Thanks,

Robert

Answer - Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Robert,

In more recent releases, you can simply set the DockSite.ItemContainerRetentionMode to DockingWindowItemContainerRetentionMode.Wrapped. This effectively does the same as our first post above.

Where do you see a circular reference? Is it just the fact that the ContentControl ends up being bound to the DockingWindow that hosts it? If so, this is a common occurance in WPF. We have profiled this and did not see any leaks, nor have we had any reports of issues.

What you have is fine, if it works in your case. Our solution has to be a bit more generic though, as what you have requires that you use implicit DataTemplate. While ours allows you to use explicit or implicit DataTemplates, as well as a DataTemplateSelector.


Actipro Software Support

Posted 3 years ago by James LaPenn - Senior Software Engineer, Loomis, Sayles, Company, L.P.
Avatar

Is this functionality still available in some form?

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi James,

In the 2016.1 version, this wrapping happens implicitly because docking window classes are now the ContentControl-based container of their child content in the UI.  In the previous versions, the docking windows rendered as the actual tabs in the UI (and not as the container of their content) so the retention mode was needed. 


Actipro Software Support

The latest build of this product (v2019.1 build 0683) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.