This project is read-only.

Layout persistance for individual windows

Jan 31, 2016 at 2:01 AM

I'm building a modular application, in which modules can restore ther view states. For example, a plugin view will attach to the dock manager in that stage (or rather, it will attach itself to a PRISM Region, which in turn is bound to the dockmanager and attaches the element). And another module managing avalondock layout is supposed to restore the dock manager's layout in that stage, too.

Now the normal order to do so would be: Load and instantiate plugins - attach their views to the dock manager - restore (deserialize) the dock manager's layout.

The issue is that I would ideally like to treat the part of code handling the dock manager's layout as just another plugin hooking into the standard imported "restore layout" functions. By doing that, I would not be able to easily impose any restrictions on the order of execution - so I could not guarantee that the dock manager would restore its layout after all of its views have been created and attached.
Depending on the load order, it might happen that the dock manager deserializes its layout, and afterwards another plugin is loaded which attaches a view to the dock manager.

The current AD behavior is that when your layout file specifies e.g. and item with ContentId = "Test" to be a floating window at a certain position, that state will be restored if the layout is deserialized only AFTER the test-item is created and attached to the dockmanager.
If we deserialize the layout first, and afterwards create and attach the test-item, its previously loaded layout information will be ignored and the window will just pop up at its default location.

I'm esentially looking for a way to apply the stored layout information even if objects are attached after the layout has been serialized.
So for example, assume your layout contains information about a ContentId=Test element, but that element hasn't been created at the time of deserialization yet.
Upon deserialization, AvalonDock could realize "ok, there's some layout information for a ContentId=Test window. Hmm, that window doesn't exist (yet) though.. let's just ignore that for now".
And later on, when the Test-view is created and attached, the dock manager should notice "ah, the previously loaded layout contained some information about a view with ContentId=Test .. let's use that to create and position the new element" - but without deserializing the entire dock managers layout again of course, rather just applying the layout to the newly attached element.

Would this be possible in any way? Tips are very appreciated.

My closest approach was writing an ILayoutUpdateStrategy in which I parsed the layout XML file again when adding a new element, sorted out the node with the element's content id, and restored its layout from XML.
public class AvalonDockLayoutUpdateStrategy : ILayoutUpdateStrategy
    // ...........

    public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument anchorableToShow, ILayoutContainer destinationContainer)
        var contentId = ((IView)anchorableToShow.Content).ID;
        if (contentId != null)
            XmlDocument doc = new XmlDocument();
            var node = doc.SelectSingleNode("//node()[@ContentId='"+contentId+"']");

            var reader = new XmlNodeReader(node);

            anchorableToShow.ReadXml(reader); // restore state of the element from xml

        return false;

Deserialization of the element seems to work, but that way still doesn't really work very well; e.g. when the new element is attached as a document, but was previously saved as an Anchorable in the Layout.xml, the above would fail.