diff --git a/source/.idea/.idea.AvalonDock/.idea/.gitignore b/source/.idea/.idea.AvalonDock/.idea/.gitignore new file mode 100644 index 00000000..ad8aa732 --- /dev/null +++ b/source/.idea/.idea.AvalonDock/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/.idea.AvalonDock.iml +/contentModel.xml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/source/.idea/.idea.AvalonDock/.idea/.name b/source/.idea/.idea.AvalonDock/.idea/.name new file mode 100644 index 00000000..d257d125 --- /dev/null +++ b/source/.idea/.idea.AvalonDock/.idea/.name @@ -0,0 +1 @@ +AvalonDock \ No newline at end of file diff --git a/source/.idea/.idea.AvalonDock/.idea/aws.xml b/source/.idea/.idea.AvalonDock/.idea/aws.xml new file mode 100644 index 00000000..b63b642c --- /dev/null +++ b/source/.idea/.idea.AvalonDock/.idea/aws.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/source/.idea/.idea.AvalonDock/.idea/encodings.xml b/source/.idea/.idea.AvalonDock/.idea/encodings.xml new file mode 100644 index 00000000..df87cf95 --- /dev/null +++ b/source/.idea/.idea.AvalonDock/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/source/.idea/.idea.AvalonDock/.idea/indexLayout.xml b/source/.idea/.idea.AvalonDock/.idea/indexLayout.xml new file mode 100644 index 00000000..7b08163c --- /dev/null +++ b/source/.idea/.idea.AvalonDock/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/source/.idea/.idea.AvalonDock/.idea/misc.xml b/source/.idea/.idea.AvalonDock/.idea/misc.xml new file mode 100644 index 00000000..1d8c84d0 --- /dev/null +++ b/source/.idea/.idea.AvalonDock/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/source/.idea/.idea.AvalonDock/.idea/vcs.xml b/source/.idea/.idea.AvalonDock/.idea/vcs.xml new file mode 100644 index 00000000..6c0b8635 --- /dev/null +++ b/source/.idea/.idea.AvalonDock/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/source/AvalonDock.sln.DotSettings b/source/AvalonDock.sln.DotSettings new file mode 100644 index 00000000..5693e73d --- /dev/null +++ b/source/AvalonDock.sln.DotSettings @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index d1698281..049fd762 100644 --- a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj +++ b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj @@ -1,7 +1,7 @@ AvalonDock.Themes - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true sn.snk true diff --git a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj index a27eb523..61b82fbd 100644 --- a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj +++ b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj @@ -1,7 +1,7 @@ AvalonDock.Themes - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true sn.snk true diff --git a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj index 14703b6c..074363cd 100644 --- a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj +++ b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj @@ -1,7 +1,7 @@ AvalonDock.Themes - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true sn.snk true diff --git a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj index d466dd1a..1bc2d6ac 100644 --- a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj +++ b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj @@ -1,7 +1,7 @@ AvalonDock.Themes - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true sn.snk true diff --git a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj index 00e53323..d5bb6832 100644 --- a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj +++ b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj @@ -1,7 +1,7 @@ AvalonDock.Themes - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true sn.snk true diff --git a/source/Components/AvalonDock/AvalonDock.csproj b/source/Components/AvalonDock/AvalonDock.csproj index 64cd7486..9836e302 100644 --- a/source/Components/AvalonDock/AvalonDock.csproj +++ b/source/Components/AvalonDock/AvalonDock.csproj @@ -1,7 +1,7 @@ AvalonDock - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true true sn.snk diff --git a/source/Components/AvalonDock/Layout/ILayoutPaneSerializable.cs b/source/Components/AvalonDock/Layout/ILayoutPaneSerializable.cs index e8fcc66c..9f541559 100644 --- a/source/Components/AvalonDock/Layout/ILayoutPaneSerializable.cs +++ b/source/Components/AvalonDock/Layout/ILayoutPaneSerializable.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -10,7 +10,7 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout { /// Interface definition for a layout pane that can be identified by a unique id. - internal interface ILayoutPaneSerializable + public interface ILayoutPaneSerializable { /// Gets/sets the unique id for this layout pane. string Id { get; set; } diff --git a/source/Components/AvalonDock/Layout/ILayoutPositionableElement.cs b/source/Components/AvalonDock/Layout/ILayoutPositionableElement.cs index 29f8ddc6..277cb81d 100644 --- a/source/Components/AvalonDock/Layout/ILayoutPositionableElement.cs +++ b/source/Components/AvalonDock/Layout/ILayoutPositionableElement.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -12,7 +12,7 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout { /// Defines a layout element that can be positioned in a Grid like environment. - internal interface ILayoutPositionableElement : ILayoutElement, ILayoutElementForFloatingWindow + public interface ILayoutPositionableElement : ILayoutElement, ILayoutElementForFloatingWindow { /// Gets/sets the of the dock width for this positionable layout element. GridLength DockWidth { get; set; } @@ -52,7 +52,7 @@ internal interface ILayoutPositionableElement : ILayoutElement, ILayoutElementFo } /// Defines a layout element that supports actual width and height properties. - internal interface ILayoutPositionableElementWithActualSize : ILayoutPositionableElement + public interface ILayoutPositionableElementWithActualSize : ILayoutPositionableElement { /// Gets/sets the actual width the positionable layout element. double ActualWidth { get; set; } @@ -62,7 +62,7 @@ internal interface ILayoutPositionableElementWithActualSize : ILayoutPositionabl } /// Defines a layout element that supports position properties for a floating window. - internal interface ILayoutElementForFloatingWindow + public interface ILayoutElementForFloatingWindow { /// Invoke this method to raise the FloatingPropertiesUpdated event to inform subscribers of the change. void RaiseFloatingPropertiesUpdated(); diff --git a/source/Components/AvalonDock/Layout/ILayoutPreviousContainer.cs b/source/Components/AvalonDock/Layout/ILayoutPreviousContainer.cs index afab0935..fae84019 100644 --- a/source/Components/AvalonDock/Layout/ILayoutPreviousContainer.cs +++ b/source/Components/AvalonDock/Layout/ILayoutPreviousContainer.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -9,7 +9,7 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout { - internal interface ILayoutPreviousContainer + public interface ILayoutPreviousContainer { ILayoutContainer PreviousContainer { get; set; } diff --git a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs new file mode 100644 index 00000000..849ebf7e --- /dev/null +++ b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs @@ -0,0 +1,146 @@ +/************************************************************************ + 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.Linq; +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. + /// + /// + /// + /// private void Serialize() + /// { + /// // The serialization is not done async as the XmlSerializer used is not having async overloads + /// var serializer = new AvalonDock.Layout.Serialization.AsyncXmlLayoutSerializer(dockManager); + /// serializer.Serialize(@".\AvalonDock.config"); + /// } + /// + /// + /// + /// + /// private async Task DeserializeAsync(AvalonDock.DockingManager dockManager) + /// { + /// using (var serializer = new AvalonDock.Layout.Serialization.AsyncXmlLayoutSerializer(dockManager)) + /// { + /// serializer.LayoutRestore += async (s, args) => + /// { + /// // Emulate an async operation for this + /// await Task.Delay(1000); + /// // Required for each interaction with actual AvalonDock, as these are STA components + /// await Dispatcher.InvokeAsync(() => args.Content = args.Content); + /// }; + /// if (File.Exists(@".\AvalonDock.config")) + /// await serializer.DeserializeAsync(@".\AvalonDock.config"); + /// } + /// } + /// + /// + public class AsyncXmlLayoutSerializer : LayoutSerializerBase + { + #region Constructors + + /// + /// Class constructor from instance. + /// + /// + public AsyncXmlLayoutSerializer(DockingManager manager) + : base(manager) + { + } + + #endregion Constructors + + + #region Private Methods + + /// Performs all required actions for deserialization. + /// + /// A function, receiving the , + /// that is supposed to call a deserialize method. + /// + private Task DeserializeCommonAsync(Func function) + => base.DeserializeCommonAsync(() => + { + var serializer = XmlSerializer.FromTypes(new[] {typeof(LayoutRoot)}).First(); + return function(serializer); + }); + + #endregion + + #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 Task DeserializeAsync(Stream stream) + => DeserializeCommonAsync((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(stream)); + + /// Deserialize the layout a file from a . + /// + public Task DeserializeAsync(TextReader reader) + => DeserializeCommonAsync((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); + + /// Deserialize the layout a file from a . + /// + public Task DeserializeAsync(XmlReader reader) + => DeserializeCommonAsync((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); + + /// Deserialize the layout from a file using a . + /// + public async Task DeserializeAsync(string filepath) + { + using (var stream = new StreamReader(filepath)) + { + await DeserializeAsync(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/LayoutSerializationCallbackEventArgs.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializationCallbackEventArgs.cs index 976d1d0e..cb8d48bc 100644 --- a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializationCallbackEventArgs.cs +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializationCallbackEventArgs.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -7,6 +7,7 @@ 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.ComponentModel; namespace AvalonDock.Layout.Serialization @@ -21,6 +22,7 @@ namespace AvalonDock.Layout.Serialization /// /// Use the Cancel property to indicate the case in which an item should not be deserialized. /// + [Obsolete("The class utilizing this event got replaced.")] public class LayoutSerializationCallbackEventArgs : CancelEventArgs { #region constructors diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs new file mode 100644 index 00000000..285359e0 --- /dev/null +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs @@ -0,0 +1,325 @@ +/************************************************************************ + 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; +using System.Windows.Threading; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable MemberCanBeProtected.Global + +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. + /// + /// + protected LayoutSerializerBase(DockingManager manager) : this(manager, Application.Current.Dispatcher) + { + } + + /// + /// Class constructor from instance. + /// + /// + /// A that dispatches to the STA thread. + protected LayoutSerializerBase(DockingManager manager, Dispatcher staDispatcher) + { + 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(); + Dispatcher = staDispatcher; + } + + #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 readonly List _layoutRestore; + + #endregion Events + + #region Properties + + /// + /// Gets the root of the docking library. + /// + public DockingManager Manager { get; } + + protected Dispatcher Dispatcher { get; } + + #endregion Properties + + #region Private Methods + + + private static IEnumerable GetDescendentsWithPreviousContainerId(LayoutRoot layoutRoot) + { + return layoutRoot + .Descendents() + .OfType() + .Where(lc => lc.PreviousContainerId != null); + } + private static IEnumerable GetDescendentsWithoutContent(LayoutRoot layout) + where T : LayoutContent + { + return layout + .Descendents() + .OfType() + .Where(lc => lc.Content == null); + } + + private async Task HandleLayoutRestoreFallbackAsync( + LayoutContent previousContent, + LayoutContent lcToFix, + Action onPreviousContentNull) + { + if (previousContent == null) + { + await Dispatcher.InvokeAsync(onPreviousContentNull); + } + else + { + lcToFix.Content = previousContent.Content; + lcToFix.IconSource = previousContent.IconSource; + } + } + + private Task HandleLayoutRestoreFallbackOfAnchorableAsync( + LayoutAnchorable previousAnchorable, + LayoutAnchorable lcToFix) + => HandleLayoutRestoreFallbackAsync( + previousAnchorable, + lcToFix, + () => lcToFix.Hide(false)); + private Task HandleLayoutRestoreFallbackOfDocument( + LayoutDocument previousAnchorable, + LayoutDocument lcToFix) + => HandleLayoutRestoreFallbackAsync( + previousAnchorable, + lcToFix, + // ReSharper disable once ConvertClosureToMethodGroup + () => lcToFix.Close()); + + private async Task TryHandleLayoutRestoreByUserCallbackAsync( + LayoutContent previousContent, + LayoutContent lcToFix, + Action onNoContentAvailable) + { + if (!_layoutRestore.Any()) + return false; + // Ask client application via callback if item should be deserialized + var eventArgs = await RaiseLayoutRestoreAsync(lcToFix, previousContent); + // Close anchorable if client app decided to cancel it + if (eventArgs.Cancel) + { + await Dispatcher.InvokeAsync(lcToFix.Close); + } + // update anchorable content if client provided content + else if (eventArgs.Content != null) + { + await 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 Dispatcher.InvokeAsync(onNoContentAvailable); + } + + return true; + } + + private Task TryHandleLayoutRestoreOfAnchorableByUserCallbackAsync( + LayoutAnchorable previousAnchorable, + LayoutAnchorable lcToFix) + => TryHandleLayoutRestoreByUserCallbackAsync( + previousAnchorable, + lcToFix, + () => lcToFix.Hide(false)); + private Task TryHandleLayoutRestoreOfDocumentByUserCallbackAsync( + LayoutDocument previousDocument, + LayoutDocument lcToFix) + => TryHandleLayoutRestoreByUserCallbackAsync( + previousDocument, + lcToFix, + // ReSharper disable once ConvertClosureToMethodGroup + () => lcToFix.Close()); + + + private LayoutAnchorable GetPreviousAnchorableByContentIdOrDefault(string contentId) + => contentId != null + ? _previousAnchorables.FirstOrDefault(a => a.ContentId == contentId) + : null; + private LayoutDocument GetPreviousDocumentByContentIdOrDefault(string contentId) + => contentId != null + ? _previousDocuments.FirstOrDefault(a => a.ContentId == contentId) + : null; + + #endregion Private Methods + + #region Protected Methods + + protected async Task RaiseLayoutRestoreAsync( + LayoutContent layoutContent, + object content) + { + var eventArgs = new LayoutRestoreEventArgs(layoutContent, content); + foreach (var callback in _layoutRestore) + { + await callback(this, eventArgs); + if (eventArgs.Cancel || eventArgs.Handled) + { + break; + } + } + + return eventArgs; + } + + /// Performs all required actions for deserialization. + /// + /// A function, returning the deserialized , + /// that is supposed to call a deserialize method. + /// + protected async Task DeserializeCommonAsync(Func function) + { + var layout = function(); + await FixupLayoutAsync(layout); + Manager.Layout = layout; + } + + 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 occurrence where + /// is equivalent to . + /// + /// + protected virtual async Task FixupPreviousContainerReferenceAsync(LayoutRoot layoutRoot) + { + foreach (var lcToAttach in GetDescendentsWithPreviousContainerId(layoutRoot)) + { + 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 Dispatcher.InvokeAsync(() => lcToAttach.PreviousContainer = layoutContainer); + } + } + + protected virtual async Task ReapplyAnchorablesContentAsync(LayoutRoot layout) + { + foreach (var lcToFix in GetDescendentsWithoutContent(layout) + .ToArray()) + { + var previousAnchorable = GetPreviousAnchorableByContentIdOrDefault(lcToFix.ContentId); + + if (await TryHandleLayoutRestoreOfAnchorableByUserCallbackAsync(previousAnchorable, lcToFix)) + continue; + await HandleLayoutRestoreFallbackOfAnchorableAsync(previousAnchorable, lcToFix); + } + } + + protected virtual async Task ReapplyDocumentsContentAsync(LayoutRoot layout) + { + foreach (var lcToFix in GetDescendentsWithoutContent(layout) + .ToArray()) + { + var previousDocument = GetPreviousDocumentByContentIdOrDefault(lcToFix.ContentId); + if (await TryHandleLayoutRestoreOfDocumentByUserCallbackAsync(lcToFix, previousDocument)) + continue; + await HandleLayoutRestoreFallbackOfDocument(previousDocument, lcToFix); + } + } + + protected virtual async Task FixupLayoutAsync(LayoutRoot layout) + { + await FixupPreviousContainerReferenceAsync(layout); + await ReapplyAnchorablesContentAsync(layout); + await ReapplyDocumentsContentAsync(layout); + + await Dispatcher.InvokeAsync(layout.CollectGarbage); + } + + #endregion Methods + + #region IDisposable + + void IDisposable.Dispose() => this.Dispose(); + + #endregion IDisposable + } +} \ No newline at end of file diff --git a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs index d883b82d..25b303e6 100644 --- a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs @@ -7,6 +7,7 @@ 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.Xml; using System.Xml.Serialization; diff --git a/source/MLibTest/MLibTest_Components/ServiceLocator/ServiceLocator.csproj b/source/MLibTest/MLibTest_Components/ServiceLocator/ServiceLocator.csproj index df8ec47c..6b233fe7 100644 --- a/source/MLibTest/MLibTest_Components/ServiceLocator/ServiceLocator.csproj +++ b/source/MLibTest/MLibTest_Components/ServiceLocator/ServiceLocator.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0;net5.0;net45 + netcoreapp3.0;net5.0;net452 diff --git a/source/MVVMTestApp/MVVMTestApp.csproj b/source/MVVMTestApp/MVVMTestApp.csproj index 79c5b073..3d59990e 100644 --- a/source/MVVMTestApp/MVVMTestApp.csproj +++ b/source/MVVMTestApp/MVVMTestApp.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true diff --git a/source/MVVMTestApp/MainWindow.xaml.cs b/source/MVVMTestApp/MainWindow.xaml.cs index 61e03632..9d20f739 100644 --- a/source/MVVMTestApp/MainWindow.xaml.cs +++ b/source/MVVMTestApp/MainWindow.xaml.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -22,21 +23,50 @@ public MainWindow() } private void MainWindow_Loaded(object sender, RoutedEventArgs e) + { + Deserialize(); + // await DeserializeAsync(); + } + + private void Deserialize() { var serializer = new AvalonDock.Layout.Serialization.XmlLayoutSerializer(dockManager); - serializer.LayoutSerializationCallback += (s, args) => - { - args.Content = args.Content; - }; + serializer.LayoutSerializationCallback += (s, args) => { args.Content = args.Content; }; if (File.Exists(@".\AvalonDock.config")) serializer.Deserialize(@".\AvalonDock.config"); } + private async Task DeserializeAsync() + { + using (var serializer = new AvalonDock.Layout.Serialization.AsyncXmlLayoutSerializer(dockManager)) + { + serializer.LayoutRestore += async (s, args) => + { + // Emulate an async operation for this + await Task.Delay(1000); + // Required for each interaction with actual AvalonDock, as these are STA components + await Dispatcher.InvokeAsync(() => args.Content = args.Content); + }; + + if (File.Exists(@".\AvalonDock.config")) + await serializer.DeserializeAsync(@".\AvalonDock.config"); + } + } private void MainWindow_Unloaded(object sender, RoutedEventArgs e) + { + Serialize(); + } + + private void Serialize() { var serializer = new AvalonDock.Layout.Serialization.XmlLayoutSerializer(dockManager); serializer.Serialize(@".\AvalonDock.config"); + + // AsyncXmlLayoutSerializer may also be used here, as the serialization is not done async + // due to the XmlSerializer used not having async overloads + // var serializer = new AvalonDock.Layout.Serialization.AsyncXmlLayoutSerializer(dockManager); + // serializer.Serialize(@".\AvalonDock.config"); } #region LoadLayoutCommand diff --git a/source/TestApp/TestApp.csproj b/source/TestApp/TestApp.csproj index 95ac2e62..0b192fb3 100644 --- a/source/TestApp/TestApp.csproj +++ b/source/TestApp/TestApp.csproj @@ -1,7 +1,7 @@ TestApp - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true true diff --git a/source/WinFormsTestApp/WinFormsTestApp.csproj b/source/WinFormsTestApp/WinFormsTestApp.csproj index 05171c25..88163458 100644 --- a/source/WinFormsTestApp/WinFormsTestApp.csproj +++ b/source/WinFormsTestApp/WinFormsTestApp.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp3.0;net5.0-windows;net40 + netcoreapp3.0;net5.0-windows;net452 true true AvalonDock.WinFormsTestApp.Program