xceedsoftware / wpftoolkit

All the controls missing in WPF. Over 1 million downloads.
Other
3.89k stars 877 forks source link

Custom serialization of AvalonDock.Layout.LayoutDocumentFloatingWindow #1047

Open xceedsoftware opened 7 years ago

xceedsoftware commented 7 years ago

EricBakx[CodePlex]
Saving and loading of the layout is possible with custom LayoutDocuments (class derived from LayoutDocument) as long as they are docked in the LayoutDocumentPane. But if a LayoutDocument is dragged onto an other screen, the custom LayoutDocument is stored in a LayoutDocumentFloatingWindow. If now the custom LayoutDocument is saved/loaded the custom type isn't stored in the XML file. The XML file contains a RootDocument instead of the custom derived class name. Therefore saving/load a CUSTOM LayoutDocument in a LayoutDocumentFloatingWindow is not possible.

To solve this, I made a proposal for serializing LayoutDocumentFloatingWindow, LayoutFloatingWindow and LayoutAnchorableFloatingWindow. Serialization is performed by implementing the IXmlSerializable interface.

The nasty thing was that in the layout XML file I wanted the following structure:

ltFloatingWindowsgt ltLayoutDocumentFloatingWindowgt ltCustomLayoutDocument1 /gt ltLayoutDocumentFloatingWindow /gt ltLayoutDocumentFloatingWindowgt ltCustomLayoutDocument2 /gt ltLayoutDocumentFloatingWindow /gt ltFloatingWindows /gt

Therefore I had to implement the IXmlSerializable in LayoutRoot. See attached files with the solutions and an example of the resulting XML file.

xceedsoftware commented 7 years ago

BoucherS[CodePlex]
You could expect this in v3.2. Thanks !

xceedsoftware commented 7 years ago

EricBakx[CodePlex]
Hi BoucherS,

I tested your suggestion with my code, and now it works fine. You were right, I was missing that part of the solution.

With this solution I can now save the layout of my custom LayoutDocument in a LayoutDocumentFloatingWindow.

Are you planning to integrate my proposal into AvalonDock? In which version can I expect it?

Thanks for your help!

xceedsoftware commented 7 years ago

BoucherS[CodePlex]
Hi,

Your solution will work, but not in the WpfApplication118 sample attached. The reason is that this sample has a RootPanel containing 2 children (a LayoutAnchorablePane and a LayoutDocumentPane). Upon loading, your solution only reads the first child and then crashes in LayoutRoot.ReadRootPanel() at reader.ReadEndElement().

Here's a modification to your solution : 1) LayoutRoot.ReadRootPanel() will now read all RootPanel children and return a list of ILayoutPanelElement : private ListILayoutPanelElement ReadRootPanel( XmlReader reader ) { ListILayoutPanelElement result = new ListILayoutPanelElement();

  string startElementName = reader.LocalName;
  reader.Read();
  if( reader.LocalName == startElementName  reader.NodeType == XmlNodeType.EndElement )
  {
    return null;
  }

  while( reader.NodeType == XmlNodeType.Whitespace )
  {
    reader.Read();
  }

  if( reader.LocalName == RootPanel )
  {
    reader.Read();

    while( true )
    {
      //Read all RootPanel children
      var element = ReadElement( reader ) as ILayoutPanelElement;
      if( element != null )
      {
        result.Add( element );
      }
      else
      {
        break;
      }
    }
  }

  reader.ReadEndElement();

  return result;
}

2) In LayoutRoot.ReadXml(), all children will now be added to the RootPanel : public void ReadXml( XmlReader reader ) { reader.MoveToContent(); if( reader.IsEmptyElement ) { reader.Read(); return; }

  ListILayoutPanelElement layoutPanelElement = ReadRootPanel( reader );
  if( layoutPanelElement != null )
  {
    //Create the RootPanel with the first child
    RootPanel = new LayoutPanel( layoutPanelElement.First() );
    //Add all children to RootPanel
    for( int i = 1; i  layoutPanelElement.Count; ++i )
    {
      RootPanel.Children.Add( layoutPanelElement[ i ] );
    }
  }

........ }

xceedsoftware commented 7 years ago

EricBakx[CodePlex]
Hi BoucherS,

The way you describe is indeed exactly how it works now. The problem is that in the XML file you have a class type RootDocument (so not my custom class derived from LayoutDocument). If you de-serialize the RootDocument from the XML file you won't get my derived class back because that class type isn't specified in the XML file. You will get a LayoutDocument (base class), and not my custom derived class.

xceedsoftware commented 7 years ago

BoucherS[CodePlex]
Hi,

using Toolkit v2.9 with a small sample, I was able to serialize a custom LayoutDocument which is floating. You can find the sample in attachment (WpfApplication118.zip). Simply add a reference to Xceed.Wpf.AvalonDock.dll.

The resulting file : SavedFile.xml contains a LayoutFloatingWindow with a RootDocument (its content property). If I move the LayoutDocuments and LayoutAnchorables around and then load the saved file, the FloatingWindow is restored with its custom LayoutDocument.