Nested MDIs serialization

Docking/MDI for WPF Forum

Posted 3 years ago by Tomer
Version: 20.1.0
Platform: .NET 4.0
Environment: Windows 10 (64-bit)
Avatar

Hey, 

I'm trying to serialize a DockSite with TabbedMdiHost that hosts several DockSites with StandardMdiHost and DocumentWindows (basically, tabs that contain windows with separated Views), the serialization looks like this:

<DockSiteLayout xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SerializationFormat="All" Version="2">
	<AutoHideHost />
	<Content xsi:type="Workspace">
		<Content xsi:type="TabbedMdiHost">
			<Content xsi:type="TabbedMdiContainer" DockedSize="772,363" SelectedWindowUniqueId="b477fd4f-74b3-4074-a8cc-675ec8fe2692">
				<UIElement xsi:type="DocumentWindowRef" UniqueId="499fb5f3-008f-4b79-a4ee-fdfa26de4b36" />
				<UIElement xsi:type="DocumentWindowRef" UniqueId="b477fd4f-74b3-4074-a8cc-675ec8fe2692" />
			</Content>
		</Content>
	</Content>
	<DocumentWindows>
		<DocumentWindow UniqueId="499fb5f3-008f-4b79-a4ee-fdfa26de4b36" SerializationId="Page1" ContainerDockedSize="772,363" IsOpen="true" LastActiveDateTime="2021-05-16T15:55:20.7123613+03:00" State="Document" />
		<DocumentWindow UniqueId="b477fd4f-74b3-4074-a8cc-675ec8fe2692" SerializationId="Page2" ContainerDockedSize="772,363" IsOpen="true" LastActiveDateTime="2021-05-16T15:55:22.8691845+03:00" State="Document" />
	</DocumentWindows>
</DockSiteLayout>

But, each DocumentWindow contains DockSite with StandardMdiHost and more DocumentWindow and its information is discarded when serialzing. I tried to save the inner DockSite info in the Tag property, it looks like the information is saved correctly:

<DockSiteLayout xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SerializationFormat="All" Version="2">
	<AutoHideHost />
	<Content xsi:type="Workspace">
		<Content xsi:type="TabbedMdiHost">
			<Content xsi:type="TabbedMdiContainer" DockedSize="772,363" SelectedWindowUniqueId="5758f673-96cd-4d46-9a87-70d9d61c9477">
				<UIElement xsi:type="DocumentWindowRef" UniqueId="1ba85ba1-244d-4421-aa26-4778ab58202d" />
				<UIElement xsi:type="DocumentWindowRef" UniqueId="5758f673-96cd-4d46-9a87-70d9d61c9477" />
			</Content>
		</Content>
	</Content>
	<DocumentWindows>
		<DocumentWindow UniqueId="1ba85ba1-244d-4421-aa26-4778ab58202d" SerializationId="Page1" ContainerDockedSize="772,363" IsOpen="true" LastActiveDateTime="2021-05-17T13:59:18.2278161+03:00" State="Document">
			<Tag xsi:type="DockSiteLayout" SerializationFormat="All" Version="2">
				<AutoHideHost />
				<Content xsi:type="Workspace">
					<Content xsi:type="StandardMdiHost" AreWindowsMaximized="false">
						<UIElement xsi:type="DocumentWindowRef" UniqueId="519c839b-06b8-4c6e-91d6-0c92304d47fc" />
					</Content>
				</Content>
				<DocumentWindows>
					<DocumentWindow UniqueId="519c839b-06b8-4c6e-91d6-0c92304d47fc" SerializationId="Window1" IsOpen="true" LastActiveDateTime="2021-05-17T13:59:20.5921809+03:00" StandardMdiBounds="1,1,546,90" State="Document" />
				</DocumentWindows>
			</Tag>
		</DocumentWindow>
		<DocumentWindow UniqueId="5758f673-96cd-4d46-9a87-70d9d61c9477" SerializationId="Page2" ContainerDockedSize="772,363" IsOpen="true" LastActiveDateTime="2021-05-17T13:59:21.4127636+03:00" State="Document">
			<Tag xsi:type="DockSiteLayout" SerializationFormat="All" Version="2">
				<AutoHideHost />
				<Content xsi:type="Workspace">
					<Content xsi:type="StandardMdiHost" AreWindowsMaximized="false">
						<UIElement xsi:type="DocumentWindowRef" UniqueId="6b10af78-cd2b-4c9f-810e-5ff37f8f0588" />
						<UIElement xsi:type="DocumentWindowRef" UniqueId="f267ef22-37c0-46b2-a754-0eace240e544" />
					</Content>
				</Content>
				<DocumentWindows>
					<DocumentWindow UniqueId="6b10af78-cd2b-4c9f-810e-5ff37f8f0588" SerializationId="Window2" IsOpen="true" LastActiveDateTime="2021-05-17T13:59:23.2578814+03:00" StandardMdiBounds="1,1,546,90" State="Document" />
					<DocumentWindow UniqueId="f267ef22-37c0-46b2-a754-0eace240e544" SerializationId="Window3" IsOpen="true" LastActiveDateTime="2021-05-17T13:59:23.6519756+03:00" StandardMdiBounds="31,31,546,90" State="Document" />
				</DocumentWindows>
			</Tag>
		</DocumentWindow>
	</DocumentWindows>
</DockSiteLayout>

When I try to deserialize, the outer DockSite created correctly (creating its ViewModel and attaching it to DocumentItemsSource), but the inner DockSite is for some reason is null and created when the deserialization process is finished and I'm not able to deserialize the information stored in the Tag property.

Am I doing it totally wrong? Any suggestion? 

Comments (5)

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

Hello,

I'm sorry but the layout serialization mechanism is only designed to work for a single DockSite.  Other implementations could have any number of elements between the DocumentWindow and the nested DockSite.  That's why none of your nested DockSite info is persisted by default and why nothing is deserialized when you manually put the nested layout info in there.

My suggestion would be to store the layout data for each DockSite separately.  Then when loading the app later later, loop through those and deserialize each layout into the appropriate target DockSite.


Actipro Software Support

Posted 3 years ago by Tomer
Avatar

Thank you for the quick reply.

As I mentioned in the original post, I saved the nested information in the Tag property. I managed to deserialize the inner DockSite using the DockSite is loaded by assigning the stored Tag property to DocumentWindow Tag property while deserializing the outer DockSite. But now I have another problem, the deserialization process doesn't restore the properties of the saved DocumentsWindows (tabs order, active tab, inner windows size, etc)

I deserialize the outer DockSite like this:

private void DeserializeTabs()
{
    DockSiteLayoutSerializer layoutSerializer = new DockSiteLayoutSerializer
    {
        DocumentWindowDeserializationBehavior = DockingWindowDeserializationBehavior.LazyLoad,
        ToolWindowDeserializationBehavior = DockingWindowDeserializationBehavior.LazyLoad
    };

    var mainViewModel = (MainWindowViewModel)outerDockSite.DataContext;
    mainViewModel.TabItems.Clear();

    layoutSerializer.DockingWindowDeserializing += (obj, args) =>
    {

        var innerViewModel = new InnerMdiViewModel
        {
            SerializationId = args.Node.SerializationId,
            Title = args.Node.SerializationId,
            IsOpen = args.Node.IsOpen
        };
        mainViewModel.TabItems.Add(innerViewModel);
        args.Window = outerDockSite.ContainerFromDocumentItem(innerViewModel);
        args.Window.Tag = args.Node.Tag;
    };
    layoutSerializer.LoadFromFile(System.IO.Path.GetTempPath() + @"Serialization.xml", outerDockSite);
}

Inner DockSite deserilzation:

private void InnerDockSite_OnLoaded(object sender, RoutedEventArgs e)
{
    var parentDocumentWindow = (DocumentWindow)VisualTreeHelperExtended.GetAncestor(innerDockSite, typeof(DocumentWindow));
    if (parentDocumentWindow.Tag is XmlDockSiteLayout xmlDockSiteLayout)
    {
        DockSiteLayoutSerializer layoutSerializer = new DockSiteLayoutSerializer
        {
            CanKeepExistingDocumentWindowsOpen = false,
            DocumentWindowDeserializationBehavior = DockingWindowDeserializationBehavior.LazyLoad,
            ToolWindowDeserializationBehavior = DockingWindowDeserializationBehavior.LazyLoad
        };

        var innerMdiViewModel = (InnerMdiViewModel)innerDockSite.DataContext;
        innerMdiViewModel.WindowItems.Clear();

        layoutSerializer.DockingWindowDeserializing += (obj, args) =>
        {
            var innerWindowViewModel = new InnerWindowViewModel()
            {
                SerializationId = args.Node.SerializationId,
                Title = args.Node.SerializationId,
                IsOpen = args.Node.IsOpen
            };
            innerMdiViewModel.WindowItems.Add(innerWindowViewModel);
            args.Window = innerDockSite.ContainerFromDocumentItem(innerWindowViewModel);
            args.Window.Tag = args.Node.Tag;
        };
        layoutSerializer.RootNode = xmlDockSiteLayout;
        layoutSerializer.ApplyTo(innerDockSite);
        parentDocumentWindow.Tag = null;
    }
}

What am I doing wrong?

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

Hello,

Nothing stands out as being wrong in that code.  If you'd like us to look at it and debug what's going on, please make a new simple sample project that shows the issue you're seeing and send that to our support address, mentioning this thread.  Be sure to exclude the bin/obj folders so the .zip doesn't get spam blocked.  Thanks!


Actipro Software Support

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

Thank you for the sample.  After looking at the code in more detail, the DeserializeTabs method ( needs to have this set:

SerializationBehavior = DockSiteSerializationBehavior.All,

That tells the deserializer to make sure documents are positioned in the locations indicated by the layout file, instead of in a single default document location, which is what is happening now.

Also, you need to remove the line in that same method that sets IsOpen.  The deserialization process will take care of handling anything related to IsOpen/IsActivate and you setting either of those two properties will cause problems.

Those two changes will get the tabbed document layout deserializing properly.

The same two changes should be made in your InnerMdiView.xaml.cs file.  And also set IsAutoCascadeEnabled="False" on your StandardMdiHost in the InnerMdiView.xaml file.  Right now it's doing a Cascade when the DockSite is loaded (from IsAutoCascadeEnabled="True" by default), which is after your layout loads.

Those changes will get standard MDI deserializing properly.


Actipro Software Support

Posted 3 years ago by Tomer
Avatar

Worked like a charm, thank you :)

The latest build of this product (v24.1.1) 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.