diff --git a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs
new file mode 100644
index 00000000..b6cb9612
--- /dev/null
+++ b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs
@@ -0,0 +1,112 @@
+/************************************************************************
+ AvalonDock
+
+ Copyright (C) 2007-2013 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at https://opensource.org/licenses/MS-PL
+ ************************************************************************/
+
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace AvalonDock.Layout.Serialization
+{
+ /// Implements a layout serialization/deserialization method of the docking framework.
+ public class AsyncXmlLayoutSerializer : LayoutSerializerBase
+ {
+ #region Constructors
+
+ ///
+ /// Class constructor from instance.
+ ///
+ ///
+ public AsyncXmlLayoutSerializer(DockingManager manager)
+ : base(manager)
+ {
+ }
+
+ #endregion Constructors
+
+ #region Public Methods
+
+ /// Serialize the layout into a .
+ ///
+ public void Serialize(XmlWriter writer)
+ {
+ var serializer = new XmlSerializer(typeof(LayoutRoot));
+ serializer.Serialize(writer, Manager.Layout);
+ }
+
+ /// Serialize the layout into a .
+ ///
+ public void Serialize(TextWriter writer)
+ {
+ var serializer = new XmlSerializer(typeof(LayoutRoot));
+ serializer.Serialize(writer, Manager.Layout);
+ }
+
+ /// Serialize the layout into a .
+ ///
+ public void Serialize(Stream stream)
+ {
+ var serializer = new XmlSerializer(typeof(LayoutRoot));
+ serializer.Serialize(stream, Manager.Layout);
+ }
+
+ /// Serialize the layout into a file using a .
+ ///
+ public void Serialize(string filepath)
+ {
+ using (var stream = new StreamWriter(filepath))
+ {
+ Serialize(stream);
+ }
+ }
+
+ /// Deserialize the layout a file from a .
+ ///
+ public async Task Deserialize(System.IO.Stream stream)
+ {
+ var serializer = new XmlSerializer(typeof(LayoutRoot));
+ var layout = (LayoutRoot)serializer.Deserialize(stream);
+ await FixupLayout(layout);
+ Manager.Layout = layout;
+ }
+
+ /// Deserialize the layout a file from a .
+ ///
+ public async Task Deserialize(TextReader reader)
+ {
+ var serializer = new XmlSerializer(typeof(LayoutRoot));
+ var layout = (LayoutRoot)serializer.Deserialize(reader);
+ await FixupLayout(layout);
+ Manager.Layout = layout;
+ }
+
+ /// Deserialize the layout a file from a .
+ ///
+ public async Task Deserialize(XmlReader reader)
+ {
+ var serializer = new XmlSerializer(typeof(LayoutRoot));
+ var layout = (LayoutRoot)serializer.Deserialize(reader);
+ await FixupLayout(layout);
+ Manager.Layout = layout;
+ }
+
+ /// Deserialize the layout from a file using a .
+ ///
+ public async Task Deserialize(string filepath)
+ {
+ using (var stream = new StreamReader(filepath))
+ {
+ await Deserialize(stream);
+ }
+ }
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutRestoreEventArgs.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutRestoreEventArgs.cs
new file mode 100644
index 00000000..d97dbf31
--- /dev/null
+++ b/source/Components/AvalonDock/Layout/Serialization/LayoutRestoreEventArgs.cs
@@ -0,0 +1,76 @@
+/************************************************************************
+ AvalonDock
+
+ Copyright (C) 2007-2013 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at https://opensource.org/licenses/MS-PL
+ ************************************************************************/
+
+using System.ComponentModel;
+
+namespace AvalonDock.Layout.Serialization
+{
+ ///
+ /// Implements an event that can be used to communicate between deserialization method
+ /// and client application that a new item (LayoutAnchorable or Document) is about to
+ /// be constructed and should be attached to a corresponding viewmodel.
+ ///
+ /// The client application can use this event to Cancel reloading the item or
+ /// attach (a viewmodel) content to the view item that is about to be reloaded and presented in the UI.
+ ///
+ /// Use the Cancel property to indicate the case in which an item should not be deserialized.
+ ///
+ public class LayoutRestoreEventArgs
+ {
+ #region constructors
+
+ ///
+ /// Class constructor from and object.
+ ///
+ /// The model of the view that has been deserialized.
+ /// The content if it was available in previous layout.
+ public LayoutRestoreEventArgs(LayoutContent model, object previousContent)
+ {
+ Cancel = false; // reloading an item is not by cancelled by default
+ Handled = false; // an item is not handled by default
+ Model = model;
+ Content = previousContent;
+ }
+
+ #endregion constructors
+
+ #region Properties
+
+ ///
+ /// Gets or sets a value indicating whether the event should be canceled.
+ ///
+ public bool Cancel
+ {
+ get; private set;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the event should continue processing.
+ ///
+ public bool Handled
+ {
+ get; private set;
+ }
+
+ ///
+ /// Gets the model of the view that is about to be deserialized.
+ ///
+ public LayoutContent Model
+ {
+ get; private set;
+ }
+
+ ///
+ /// Gets/sets the content for the that is about to be deserialized.
+ ///
+ public object Content { get; set; }
+
+ #endregion Properties
+ }
+}
\ No newline at end of file
diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs
new file mode 100644
index 00000000..4ff702d5
--- /dev/null
+++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs
@@ -0,0 +1,253 @@
+/************************************************************************
+ AvalonDock
+
+ Copyright (C) 2007-2013 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at https://opensource.org/licenses/MS-PL
+ ************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace AvalonDock.Layout.Serialization
+{
+ /// Implements a base class for the layout serialization/deserialization of the docking framework.
+ public abstract class LayoutSerializerBase : IDisposable
+ {
+ #region Properties
+
+ #region PreviousAnchorables
+ protected IEnumerable PreviousAnchorables => _previousAnchorables;
+ private readonly LayoutAnchorable[] _previousAnchorables = null;
+
+ #endregion PreviousAnchorables
+
+ #region PreviousDocuments
+
+ protected IEnumerable PreviousDocuments => _previousDocuments;
+ private readonly LayoutDocument[] _previousDocuments = null;
+
+ #endregion PreviousDocuments
+
+ #endregion fields
+
+ #region Constructors
+
+ ///
+ /// Class constructor from instance.
+ ///
+ ///
+ public LayoutSerializerBase(DockingManager manager)
+ {
+ Manager = manager ?? throw new ArgumentNullException(nameof(manager));
+ Manager.SuspendDocumentsSourceBinding = true;
+ Manager.SuspendAnchorablesSourceBinding = true;
+
+ _previousAnchorables = Manager.Layout.Descendents().OfType().ToArray();
+ _previousDocuments = Manager.Layout.Descendents().OfType().ToArray();
+ _layoutRestore = new List();
+ }
+
+ #endregion Constructors
+
+ #region Delegates
+
+ ///
+ /// Method descriptor for .
+ ///
+ /// The layout serializer.
+ /// An instance of that allows to cancel the creation and/or the further processing.
+ ///
+ public delegate Task LayoutRestoreDelegate(object sender, LayoutRestoreEventArgs e);
+
+ #endregion
+
+ #region Events
+
+
+ ///
+ /// Raises an event when the layout serializer is about to deserialize an item to ask the
+ /// client application whether the item should be deserialized and re-displayed and what content
+ /// should be used if so.
+ ///
+ public event LayoutRestoreDelegate LayoutRestore
+ {
+ add => _layoutRestore.Add(value);
+ remove => _layoutRestore.Remove(value);
+ }
+ private List _layoutRestore;
+
+ #endregion Events
+
+ #region Properties
+
+ ///
+ /// Gets the root of the docking library.
+ ///
+ public DockingManager Manager { get; }
+
+ #endregion Properties
+
+ #region Protected Methods
+
+ protected virtual void Dispose()
+ {
+ Manager.SuspendDocumentsSourceBinding = false;
+ Manager.SuspendAnchorablesSourceBinding = false;
+ }
+
+ ///
+ /// Fixes the reference after
+ /// deserializing the to point towards the matching container again.
+ ///
+ ///
+ /// Uses first occurance where
+ /// is equivalent to .
+ ///
+ ///
+ protected virtual async Task FixupPreviousContainerReference(LayoutRoot layoutRoot)
+ {
+ foreach (var lcToAttach in layoutRoot.Descendents()
+ .OfType().Where(lc => lc.PreviousContainerId != null))
+ {
+ var paneContainerToAttach = layoutRoot.Descendents()
+ .OfType().FirstOrDefault(lps => lps.Id == lcToAttach.PreviousContainerId);
+ if (!(paneContainerToAttach is ILayoutContainer layoutContainer))
+ {
+ throw new ArgumentException($"Unable to find a pane with id ='{lcToAttach.PreviousContainerId}'");
+ }
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToAttach.PreviousContainer = layoutContainer);
+ }
+ }
+
+ protected virtual async Task ReapplyAnchorablesContent(LayoutRoot layout)
+ {
+ foreach (var lcToFix in layout.Descendents().OfType().Where(lc => lc.Content == null).ToArray())
+ {
+ // Try find the content in replaced layout
+ LayoutAnchorable previousAchorable = null;
+ if (lcToFix.ContentId != null)
+ {
+ previousAchorable = _previousAnchorables.FirstOrDefault(a => a.ContentId == lcToFix.ContentId);
+ }
+
+ if (_layoutRestore.Any())
+ {
+ // Ask client application via callback if item should be deserialized
+ var eventArgs = new LayoutRestoreEventArgs(lcToFix, previousAchorable?.Content);
+ foreach (var callback in _layoutRestore)
+ {
+ await callback(this, eventArgs);
+ if (eventArgs.Cancel || eventArgs.Handled)
+ {
+ break;
+ }
+ }
+ // Close anchorable if client app decided to cancel it
+ if (eventArgs.Cancel)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Close());
+ }
+ // update anchorable content if client provided content
+ else if (eventArgs.Content != null)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Content = eventArgs.Content);
+ }
+ // If client has not provided any content and
+ // has not explicitly set the content on the LayoutContent
+ // then hide the anchorable
+ else if (eventArgs.Model.Content != null)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Hide(false));
+ }
+ }
+ // Ensure a previousAnchorable exists, otherwise hide this (skip)
+ else if (previousAchorable == null)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Hide(false));
+ }
+ // Load content from previous anchorable
+ else
+ {
+ lcToFix.Content = previousAchorable.Content;
+ lcToFix.IconSource = previousAchorable.IconSource;
+ }
+ }
+ }
+ protected virtual async Task ReapplyDocumentsContent(LayoutRoot layout)
+ {
+ foreach (var lcToFix in layout.Descendents().OfType().Where(lc => lc.Content == null).ToArray())
+ {
+ // Try find the content in replaced layout
+ LayoutDocument previousDocument = null;
+ if (lcToFix.ContentId != null)
+ {
+ previousDocument = _previousDocuments.FirstOrDefault(a => a.ContentId == lcToFix.ContentId);
+ }
+
+ if (_layoutRestore.Any())
+ {
+ // Ask client application via callback if item should be deserialized
+ var eventArgs = new LayoutRestoreEventArgs(lcToFix, previousDocument?.Content);
+ foreach (var callback in _layoutRestore)
+ {
+ await callback(this, eventArgs);
+ if (eventArgs.Cancel || eventArgs.Handled)
+ {
+ break;
+ }
+ }
+ // Close anchorable if client app decided to cancel it
+ if (eventArgs.Cancel)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Close());
+ }
+ // update anchorable content if client provided content
+ else if (eventArgs.Content != null)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Content = eventArgs.Content);
+ }
+ // If client has not provided any content and
+ // has not explicitly set the content on the LayoutContent
+ // then hide the anchorable
+ else if (eventArgs.Model.Content != null)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Close());
+ }
+ }
+ // Ensure a previousAnchorable exists, otherwise hide this (skip)
+ else if (previousDocument == null)
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() => lcToFix.Close());
+
+ }
+ // Load content from previous anchorable
+ else
+ {
+ lcToFix.Content = previousDocument.Content;
+ lcToFix.IconSource = previousDocument.IconSource;
+ }
+ }
+ }
+ protected virtual async Task FixupLayout(LayoutRoot layout)
+ {
+ await FixupPreviousContainerReference(layout);
+ await ReapplyAnchorablesContent(layout);
+ await ReapplyDocumentsContent(layout);
+
+ await Application.Current.Dispatcher.InvokeAsync(() => layout.CollectGarbage());
+ }
+
+ #endregion Methods
+
+ #region IDisposable
+
+ void IDisposable.Dispose() => this.Dispose();
+
+ #endregion IDisposable
+ }
+}
\ No newline at end of file