Request for MVVM-Based Nested Dock Sites Layout Serialization Example

Docking/MDI for WPF Forum

Posted 6 months ago by Jiho Kim - Ansys
Version: 23.1.4
Avatar

Dear Actipro Team,

I hope this message finds you well.

We are currently using Nested Dock Sites in our project. When we save and load the program, we also save and load the layout. While the saved data appears to be correct, we are encountering issues upon loading. Specifically, the order of the ToolWindows in the nested DockSites changes (this happens when the title is modified), and the sizes of the ToolWindows are not applied correctly (this issue occurs consistently).

We are utilizing the MVVM pattern to bind all actions, and there is no code-behind for docking-related operations. We would like to resolve these issues with the help of a proper example.

Your assistance in providing an example for layout serialization with Nested Dock Sites using the MVVM pattern would be greatly appreciated.

Thank you for your time and support. I look forward to your response.

 

Comments (6)

Posted 6 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

I'm sorry you're having trouble.  I would not expect the title of a tool window to affect anything as long as you use the SerializationId for each tool window to give it a unique consistent ID that isn't changed when titles are changed.  As for the sizes being different, you mentioned using nested dock sites.  You would want to ensure the outer dock site's layout is fully deserialized, loaded, and arranged before deserializing the inner dock sites' layouts.  It should work ok if the outer dock site is fully loaded/arranged first.

If you try those things and still can't figure out the problem, kindly put together a new simple sample project that shows the issue happening and send that to us in a support ticket, referencing this thread.  Be sure to exclude the bin/obj folders from the .zip you send so it doesn't get spam blocked.  Then we can debug with that and see what's happening and point you in the right direction.  Thanks!


Actipro Software Support

Posted 6 months ago by Jiho Kim - Ansys
Avatar

Hello,

I have identified the cause of the issue with different sizes.

Before

    <controls:MainDockSite
        x:Name="DockSite"
        DocumentItemContainerStyle="{StaticResource SlotGroupWindowStyle}"
        DocumentItemTemplate="{StaticResource SlotGroupTemplate}"
        DocumentItemsSource="{Binding Items}"
        Layout="{Binding Layout}"
        Style="{StaticResource DockSiteStyle}">
        <docking:Workspace>
            <docking:TabbedMdiHost ContainersHaveNewTabButtons="True" TabOverflowBehavior="ScrollWithMenu" />
        </docking:Workspace>
    </controls:MainDockSite>

After

    <controls:MainDockSite
        x:Name="DockSite"
        DocumentItemContainerStyle="{StaticResource SlotGroupWindowStyle}"
        DocumentItemTemplate="{StaticResource SlotGroupTemplate}"
        DocumentItemsSource="{Binding Items}"
        Layout="{Binding Layout}"
        Style="{StaticResource DockSiteStyle}">
        <docking:SplitContainer>
            <docking:Workspace>
                <docking:TabbedMdiHost ContainersHaveNewTabButtons="True" TabOverflowBehavior="ScrollWithMenu">
                    <docking:TabbedMdiContainer />
                </docking:TabbedMdiHost>
            </docking:Workspace>
        </docking:SplitContainer>
    </controls:MainDockSite>

We have modified the Outer DockSite hierarchy to follow the order shown in the Actipro example.
And here is the serialization result of the Inner DockSite.

Before

<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="SplitContainer" Orientation="Horizontal" DockedSize="1462,894.666666666667">
		<UIElement xsi:type="ToolWindowContainer" DockedSize="0,894.666666666667" SelectedWindowUniqueId="8ecb4b90-0caf-4a40-be99-01ec40823521">
			<UIElement xsi:type="ToolWindowRef" UniqueId="8ecb4b90-0caf-4a40-be99-01ec40823521" />
		</UIElement>
		<UIElement xsi:type="ToolWindowContainer" DockedSize="0,200" SelectedWindowUniqueId="b5b8bf55-cf35-4a2b-980d-1bf319b6b1dd">
			<UIElement xsi:type="ToolWindowRef" UniqueId="b5b8bf55-cf35-4a2b-980d-1bf319b6b1dd" />
		</UIElement>
	</Content>
	<ToolWindows>
		<ToolWindow UniqueId="8ecb4b90-0caf-4a40-be99-01ec40823521" SerializationId="21" ContainerDockedSize="0,894.666666666667" IsOpen="true" LastActiveDateTime="2024-10-18T16:45:23.4502985+09:00" State="Docked" Type="VM.Windows.Post.Controls.SlotWindow, Postprocessor" />
		<ToolWindow UniqueId="b5b8bf55-cf35-4a2b-980d-1bf319b6b1dd" SerializationId="22" ContainerDockedSize="0,200" IsOpen="true" LastActiveDateTime="2024-10-18T16:45:16.6083189+09:00" State="Docked" Type="VM.Windows.Post.Controls.SlotWindow, Postprocessor" />
	</ToolWindows>
</DockSiteLayout>

After

<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="SplitContainer" Orientation="Horizontal" DockedSize="1462,894.666666666667">
		<UIElement xsi:type="ToolWindowContainer" DockedSize="1268,894.666666666667" SelectedWindowUniqueId="9af0e507-9417-4619-a344-bb831c9c6a46">
			<UIElement xsi:type="ToolWindowRef" UniqueId="9af0e507-9417-4619-a344-bb831c9c6a46" />
		</UIElement>
		<UIElement xsi:type="ToolWindowContainer" DockedSize="188,200" SelectedWindowUniqueId="6042efb6-2569-42fd-b268-273d01a292fb">
			<UIElement xsi:type="ToolWindowRef" UniqueId="6042efb6-2569-42fd-b268-273d01a292fb" />
		</UIElement>
	</Content>
	<ToolWindows>
		<ToolWindow UniqueId="9af0e507-9417-4619-a344-bb831c9c6a46" SerializationId="5" ContainerDockedSize="1268,894.666666666667" IsOpen="true" LastActiveDateTime="2024-10-19T11:44:01.1113922+09:00" State="Docked" Type="VM.Windows.Post.Controls.SlotWindow, Postprocessor" />
		<ToolWindow UniqueId="6042efb6-2569-42fd-b268-273d01a292fb" SerializationId="6" ContainerDockedSize="188,200" IsOpen="true" LastActiveDateTime="2024-10-19T11:43:52.4503008+09:00" State="Docked" Type="VM.Windows.Post.Controls.SlotWindow, Postprocessor" />
	</ToolWindows>
</DockSiteLayout>

The difference is that, when looking at the Size values in the "Before" state, all the width values are 0. This suggests that the issue might be due to the DockSite hierarchy, which is preventing proper size calculation.

For reference, the XAML of the Inner DockSite has not been changed and is as follows:

<controls:SlotGroupDockSite
    x:Name="DockSite"
    Layout="{Binding Layout}"
    Style="{StaticResource DockSiteStyle}"
    ToolItemContainerStyle="{StaticResource SlotWindowStyle}"
    ToolItemTemplateSelector="{StaticResource SlotViewModelTemplateSelector}"
    ToolItemsSource="{Binding Items}">
    <docking:SplitContainer>
        <docking:ToolWindowContainer />
    </docking:SplitContainer>
</controls:SlotGroupDockSite>

[Modified 6 months ago]

Posted 6 months ago by Jiho Kim - Ansys
Avatar

Additionally, following your advice to load the Outer DockSite first and then the Inner DockSite has resolved the issue of the order being changed.

If possible, could you add a property to bind the serialized layout result to the DockSite, and at the appropriate time, when a value is present, load it to update the layout? Once the process is complete, it would be ideal if the DockSite could reset the Layout property.

As you might have inferred from my previous reply, I am using a control inherited from DockSite with a `Layout` DependencyProperty bound to it.

internal abstract class CustomDockSite : DockSite
{
    public static readonly DependencyProperty LayoutProperty = DependencyProperty.Register(
        nameof(Layout), typeof(string), typeof(CustomDockSite), new PropertyMetadata(default));

    static CustomDockSite()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomDockSite), new FrameworkPropertyMetadata(typeof(CustomDockSite)));
    }

    public string Layout
    {
        get { return (string)this.GetValue(LayoutProperty); }
        set { this.SetValue(LayoutProperty, value); }
    }
}

However, if I don’t call `DockSiteLayoutSerializer.LoadFromString` at the right time, the issue I’m experiencing occurs. I believe it would lead to better results if Actipro could ensure that this function is called at the appropriate time.

Thanks again for the great advice!

Posted 6 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

I'm very happy to hear you made some progress.  I will say that the XAML snippets you posted should not need all the inner controls you added.  For instance, in your Before/After scenario, everything that was added in the After XAML (SplitContainer and empty TabbedMdiContainer) is unnecessary and actually would be trimmed out if unused following a layout change.  Similar with the inner DockSite XAML, where the SplitContainer and ToolWindowContainer are unnecessary there unless you actually put other things in them.

All SplitContainer, ToolWindowContainer, and TabbedMdiContainer objects are transient controls that get added/removed as needed to support the layout changes made by the user.  Whereas the Workspace and TabbedMdiHost are persistent and remain between layout deserializations and other layout changes.

If you are seeing any changes in behavior due to the initial presence of those extra unnecessary container controls, then it's likely only because they trigger a measure/arrange cycle sooner than when you serialize the layout.  If you didn't have the container controls and would serialize the layout following everything being loaded and arranged, I would expect the same good results.

As for a bindable string Layout propety, I'm afraid we can't add something like that since the DockSiteLayoutSerializer has many options that can be configured.  You would need full access in many cases to those options.


Actipro Software Support

Posted 6 months ago by Jiho Kim - Ansys
Avatar

Final question then:
When exactly is the point you mentioned where "everything is being loaded and arranged"? Could you let me know when this occurs, and if there’s an event or method I can override to detect that point?

Thank you!

Answer - Posted 6 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

I would expect that when the containing Window's Loaded event fires, it has already loaded and arranged the outer DockSite.


Actipro Software Support

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