Search code examples
c#wpfavalondock

AvalonDock 2.0: adding LayoutDocument not working after Deserialize layout


Simple AvalonDock application with only LayoutDocumentPane. Program sample

XAML:

<Window x:Name="MainWindow1" x:Class="AvalonTest2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
        Title="MainWindow" Height="350" Width="525" Loaded="MainWindow1_Loaded">
    <Grid Name="MainGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <ToolBar Grid.Row="0" Grid.Column="0">
            <Button Content="New Tab" Margin="10,0,0,0" Click="NewTabClick"/>
            <Button Content="Save layout" Margin="10,0,0,0" Click="Button_Click"/>
        </ToolBar>

        <xcad:DockingManager x:Name="dockManager" Grid.Row="1">
            <xcad:LayoutRoot x:Name="_layoutRoot">
                <xcad:LayoutPanel Orientation="Horizontal" x:Name="_layoutPanel">
                    <xcad:LayoutDocumentPane x:Name="workSpace">
                        <xcad:LayoutDocument ContentId="dummy" Title="Dummy" >
                            <Button x:Name="dummyButton" Content="Dummy Content" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </xcad:LayoutDocument>
                    </xcad:LayoutDocumentPane>
                </xcad:LayoutPanel>
            </xcad:LayoutRoot>
        </xcad:DockingManager>
    </Grid>
</Window>

Code:

using System;
using System.IO;
using System.Windows;
using Xceed.Wpf.AvalonDock.Layout;
using Xceed.Wpf.AvalonDock.Layout.Serialization;

namespace AvalonTest2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void NewTabClick(object sender, RoutedEventArgs e)
        {
            var ld = new LayoutDocument();
            var x = new Random().Next().ToString();
            ld.Title = x;
            ld.ContentId = x;
             workSpace.Children.Add(ld);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var layoutSerial = new XmlLayoutSerializer(dockManager);
            layoutSerial.Serialize("layout.xml");
        }

        private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
        {
            if (File.Exists("layout.xml"))
            {
               // var serializer = new XmlLayoutSerializer(dockManager);
               // serializer.Deserialize("layout.xml");
            }
        }
    }
}

I press "New tab" button and everything is fine: new tab appears.

I restart the program, save layout ("Save layout" button), close program and remove comments on Deserialize lines.

I start the program and on pressing "New tab", no tabs appears. I'd look inside LayoutDocumentPane (workSpace), "Children" property is filled with new Documents.

What's wrong?


Solution

  • Unfortunately, the (de-)serializing functionality is implemented not very nice in the AvalonDock suite.

    On deserializing, a completely new LayoutRoot object will be created. You define in XAML a LayoutDocumentPane with name workSpace and add the newly created LayoutDocument into this pane. But, after deserialization, this LayoutDocumentPane does not belong to the layout anymore - it has been removed; a new LayoutDocumentPane has been created instead.

    Therefore you cannot see any LayoutDocument views added into the workSpace container.

    Don't reference the instance of your LayoutDocumentPane by name, but rather find it dynamically instead. You have to change your code to something like:

    var ld = new LayoutDocument();
    var x = new Random().Next().ToString();
    ld.Title = x;
    ld.ContentId = x;
    
    LayoutDocumentPane documentPane = this.dockManager.Layout.Descendents().OfType<LayoutDocumentPane>().SingleOrDefault();
    if (documentPane != null)
    {
        documentPane.Children.Add(ld);
    }