Dirkster99 / AvalonDock

Our own development branch of the well known WPF document docking library
Microsoft Public License
1.41k stars 321 forks source link

Loading layout restores panes, but not tools inside #397

Open PierrePlaziat opened 2 years ago

PierrePlaziat commented 2 years ago

Hello, I am pretty new to AvalonDock, but i am starting to get less confuse with it.

I implemented the save / Load Layout commands into my app, Layout restores panes well, but not the tools inside them. I am clearly missing something

I tried to implement IXmlSerializable on my controls, but it didnt work, Maybe i didnt do it well, its the first time i encounter those layout serialization needs,

I'd like to know if this is a known behaviour and how this is supposed to be done.

Here is my code: `

    private void SaveLayout()
    {
        string xmlLayoutString = "";
        using (StringWriter fs = new StringWriter())
        {
            XmlLayoutSerializer xmlLayout = new XmlLayoutSerializer(this.dockingManager);
            xmlLayout.Serialize(fs);
            xmlLayoutString = fs.ToString();
        }
        File.WriteAllText("WriteText.txt", xmlLayoutString);
    }

    private void LoadLayout()
    {
        var serializer = new XmlLayoutSerializer(dockingManager);
        serializer.LayoutSerializationCallback += (s, args) => { };
        serializer.Deserialize("WriteText.txt");
    }`
romen-h commented 1 year ago

You don't need to serialize controls, that data is already captured in your XAML/code so controls can be re-made at runtime as many as needed. What you actually need to serialize is the layout of the docking panels, and just some information about what is inside them.

Each LayoutDocument or LayoutAnchorable has a "ContentId" and "Content" property. Content is the actual control that you can put in at runtime; ContentId is a string you can assign to keep track of what control was put as the content.

The XmlLayoutSerializer serializes the ContentId but not the Content.

This line here is your problem: serializer.LayoutSerializationCallback += (s, args) => { };

When loading a layout, the panels are being made without content and just have their ContentId which was saved from before.
That callback is your chance to look at the ContentId and set up the Content for the panel again. It gets called for every panel that was loaded.

PierrePlaziat commented 1 year ago

Thank you!

Here is the full solution:

public class MyLayoutAnchorable : LayoutAnchorable
{
    // Add a custom property to store the unique identifier for the panel
    public int PanelId { get; set; }
}

private void LoadLayout()
{
    var serializer = new XmlLayoutSerializer(dockingManager);
    serializer.LayoutSerializationCallback += (s, args) =>
    {
        // Check if the element being deserialized is a MyLayoutAnchorable
        if (args.Model is MyLayoutAnchorable anchorable)
        {
            // Use the PanelId property to determine which content to restore for the panel
            switch(anchorable.PanelId)
            {
                case 1: anchorable.Content = new Content1(); break;
                case 2: anchorable.Content = new Content2(); break;
                case 3: anchorable.Content = new Content3(); break;
            }
        }
    };
    serializer.Deserialize(App.RoamingFolder + "\\Layout.txt");
}