From 2036c5c8ef3cebf38a90d738405179f6a51ec2b3 Mon Sep 17 00:00:00 2001 From: X39 Date: Mon, 19 Apr 2021 22:58:49 +0200 Subject: [PATCH 1/9] Changed encapsulation on interfaces from internal to public --- .../AvalonDock/Layout/ILayoutPaneSerializable.cs | 4 ++-- .../AvalonDock/Layout/ILayoutPositionableElement.cs | 8 ++++---- .../AvalonDock/Layout/ILayoutPreviousContainer.cs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) 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; } From a3bfc89b75cd1f259054b7805bef934bd29fe7bd Mon Sep 17 00:00:00 2001 From: X39 Date: Mon, 19 Apr 2021 23:44:05 +0200 Subject: [PATCH 2/9] Marked now obsolete serialization classes with [Obsolete] --- .../Serialization/LayoutSerializationCallbackEventArgs.cs | 4 +++- .../AvalonDock/Layout/Serialization/LayoutSerializer.cs | 3 ++- .../AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) 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/LayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs index 7e7d0466..75bbd5e9 100644 --- a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -13,6 +13,7 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout.Serialization { /// Implements a base class for the layout serialization/deserialization of the docking framework. + [Obsolete("This class was replaced with " + nameof(LayoutSerializerBase))] public abstract class LayoutSerializer { #region fields diff --git a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs index 438c472f..c9d1b597 100644 --- a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.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.IO; using System.Xml; using System.Xml.Serialization; @@ -14,6 +15,7 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout.Serialization { /// Implements a layout serialization/deserialization method of the docking framework. + [Obsolete("This class was replaced with " + nameof(AsyncXmlLayoutSerializer))] public class XmlLayoutSerializer : LayoutSerializer { #region Constructors From a204d490719d7f315cc5710447124f8301c53698 Mon Sep 17 00:00:00 2001 From: X39 Date: Mon, 19 Apr 2021 23:43:43 +0200 Subject: [PATCH 3/9] Added new serializer classes, supporting async serialization scheme --- .../Serialization/AsyncXmlLayoutSerializer.cs | 112 ++++++++ .../Serialization/LayoutRestoreEventArgs.cs | 76 ++++++ .../Serialization/LayoutSerializerBase.cs | 253 ++++++++++++++++++ 3 files changed, 441 insertions(+) create mode 100644 source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs create mode 100644 source/Components/AvalonDock/Layout/Serialization/LayoutRestoreEventArgs.cs create mode 100644 source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs 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 From 0dbfe9a55f37e1c44064497d1c7faa888c887d8f Mon Sep 17 00:00:00 2001 From: X39 Date: Mon, 27 Dec 2021 18:07:06 +0100 Subject: [PATCH 4/9] Upgraded .net framework dependency to 4.5.2 --- .../AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj | 2 +- .../AvalonDock.Themes.Expression.csproj | 2 +- .../AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj | 2 +- .../AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj | 2 +- .../AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj | 2 +- source/Components/AvalonDock/AvalonDock.csproj | 2 +- .../MLibTest_Components/ServiceLocator/ServiceLocator.csproj | 2 +- source/MVVMTestApp/MVVMTestApp.csproj | 2 +- source/TestApp/TestApp.csproj | 2 +- source/WinFormsTestApp/WinFormsTestApp.csproj | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index 9773deb7..c394a99e 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 49aba489..25518bb1 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 c37ab38e..2e974075 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 fb687324..d004617a 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 a2aa474e..9769519d 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 c8142e4c..5f164493 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/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/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 From af5942908f6a121775b837c92e141931ca8dc356 Mon Sep 17 00:00:00 2001 From: X39 Date: Fri, 14 Jan 2022 19:12:07 +0100 Subject: [PATCH 5/9] cleanup --- .../.idea/.idea.AvalonDock/.idea/.gitignore | 13 + source/.idea/.idea.AvalonDock/.idea/.name | 1 + source/.idea/.idea.AvalonDock/.idea/aws.xml | 11 + .../.idea.AvalonDock/.idea/encodings.xml | 4 + .../.idea.AvalonDock/.idea/indexLayout.xml | 8 + source/.idea/.idea.AvalonDock/.idea/misc.xml | 6 + source/.idea/.idea.AvalonDock/.idea/vcs.xml | 6 + source/AvalonDock.sln.DotSettings | 3 + .../Serialization/AsyncXmlLayoutSerializer.cs | 45 +-- .../Serialization/LayoutSerializerBase.cs | 286 +++++++++++------- 10 files changed, 255 insertions(+), 128 deletions(-) create mode 100644 source/.idea/.idea.AvalonDock/.idea/.gitignore create mode 100644 source/.idea/.idea.AvalonDock/.idea/.name create mode 100644 source/.idea/.idea.AvalonDock/.idea/aws.xml create mode 100644 source/.idea/.idea.AvalonDock/.idea/encodings.xml create mode 100644 source/.idea/.idea.AvalonDock/.idea/indexLayout.xml create mode 100644 source/.idea/.idea.AvalonDock/.idea/misc.xml create mode 100644 source/.idea/.idea.AvalonDock/.idea/vcs.xml create mode 100644 source/AvalonDock.sln.DotSettings 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/Layout/Serialization/AsyncXmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs index b6cb9612..3d4b093e 100644 --- a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs @@ -9,6 +9,7 @@ This program is provided to you under the terms of the Microsoft Public using System; using System.IO; +using System.Linq; using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; @@ -31,6 +32,23 @@ public AsyncXmlLayoutSerializer(DockingManager 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 DeserializeCommon(Func function) + => DeserializeCommon(() => + { + var serializer = XmlSerializer.FromTypes(new[] {typeof(LayoutRoot)}).First(); + return function(serializer); + }); + + #endregion + #region Public Methods /// Serialize the layout into a . @@ -69,33 +87,18 @@ public void Serialize(string filepath) /// 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; - } + public Task Deserialize(Stream stream) + => DeserializeCommon((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(stream)); /// 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; - } + public Task Deserialize(TextReader reader) + => DeserializeCommon((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); /// 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; - } + public Task Deserialize(XmlReader reader) + => DeserializeCommon((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); /// Deserialize the layout from a file using a . /// diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs index 4ff702d5..ecfd76b6 100644 --- a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs @@ -12,6 +12,10 @@ This program is provided to you under the terms of the Microsoft Public 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 { @@ -21,6 +25,7 @@ public abstract class LayoutSerializerBase : IDisposable #region Properties #region PreviousAnchorables + protected IEnumerable PreviousAnchorables => _previousAnchorables; private readonly LayoutAnchorable[] _previousAnchorables = null; @@ -41,7 +46,16 @@ public abstract class LayoutSerializerBase : IDisposable /// Class constructor from instance. /// /// - public LayoutSerializerBase(DockingManager manager) + 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; @@ -50,6 +64,7 @@ public LayoutSerializerBase(DockingManager manager) _previousAnchorables = Manager.Layout.Descendents().OfType().ToArray(); _previousDocuments = Manager.Layout.Descendents().OfType().ToArray(); _layoutRestore = new List(); + Dispatcher = staDispatcher; } #endregion Constructors @@ -68,7 +83,6 @@ public LayoutSerializerBase(DockingManager manager) #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 @@ -79,7 +93,8 @@ public event LayoutRestoreDelegate LayoutRestore add => _layoutRestore.Add(value); remove => _layoutRestore.Remove(value); } - private List _layoutRestore; + + private readonly List _layoutRestore; #endregion Events @@ -90,10 +105,150 @@ public event LayoutRestoreDelegate LayoutRestore /// 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 HandleLayoutRestoreFallback( + LayoutContent previousContent, + LayoutContent lcToFix, + Action onPreviousContentNull) + { + if (previousContent == null) + { + await Dispatcher.InvokeAsync(onPreviousContentNull); + } + else + { + lcToFix.Content = previousContent.Content; + lcToFix.IconSource = previousContent.IconSource; + } + } + + private Task HandleLayoutRestoreFallbackOfAnchorable( + LayoutAnchorable previousAnchorable, + LayoutAnchorable lcToFix) + => HandleLayoutRestoreFallback( + previousAnchorable, + lcToFix, + () => lcToFix.Hide(false)); + private Task HandleLayoutRestoreFallbackOfDocument( + LayoutDocument previousAnchorable, + LayoutDocument lcToFix) + => HandleLayoutRestoreFallback( + previousAnchorable, + lcToFix, + // ReSharper disable once ConvertClosureToMethodGroup + () => lcToFix.Close()); + + private async Task TryHandleLayoutRestoreByUserCallback( + 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 TryHandleLayoutRestoreOfAnchorableByUserCallback( + LayoutAnchorable previousAnchorable, + LayoutAnchorable lcToFix) + => TryHandleLayoutRestoreByUserCallback( + previousAnchorable, + lcToFix, + () => lcToFix.Hide(false)); + private Task TryHandleLayoutRestoreOfDocumentByUserCallback( + LayoutDocument previousDocument, + LayoutDocument lcToFix) + => TryHandleLayoutRestoreByUserCallback( + 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 DeserializeCommon(Func function) + { + var layout = function(); + await FixupLayout(layout); + Manager.Layout = layout; + } + protected virtual void Dispose() { Manager.SuspendDocumentsSourceBinding = false; @@ -105,141 +260,58 @@ protected virtual void Dispose() /// deserializing the to point towards the matching container again. /// /// - /// Uses first occurance where + /// Uses first occurrence where /// is equivalent to . /// /// protected virtual async Task FixupPreviousContainerReference(LayoutRoot layoutRoot) { - foreach (var lcToAttach in layoutRoot.Descendents() - .OfType().Where(lc => lc.PreviousContainerId != null)) + foreach (var lcToAttach in GetDescendentsWithPreviousContainerId(layoutRoot)) { var paneContainerToAttach = layoutRoot.Descendents() - .OfType().FirstOrDefault(lps => lps.Id == lcToAttach.PreviousContainerId); + .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); + + await 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()) + foreach (var lcToFix in GetDescendentsWithoutContent(layout) + .ToArray()) { - // Try find the content in replaced layout - LayoutAnchorable previousAchorable = null; - if (lcToFix.ContentId != null) - { - previousAchorable = _previousAnchorables.FirstOrDefault(a => a.ContentId == lcToFix.ContentId); - } + var previousAnchorable = GetPreviousAnchorableByContentIdOrDefault(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; - } + if (await TryHandleLayoutRestoreOfAnchorableByUserCallback(previousAnchorable, lcToFix)) + continue; + await HandleLayoutRestoreFallbackOfAnchorable(previousAnchorable, lcToFix); } } + protected virtual async Task ReapplyDocumentsContent(LayoutRoot layout) { - foreach (var lcToFix in layout.Descendents().OfType().Where(lc => lc.Content == null).ToArray()) + foreach (var lcToFix in GetDescendentsWithoutContent(layout) + .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; - } + var previousDocument = GetPreviousDocumentByContentIdOrDefault(lcToFix.ContentId); + if (await TryHandleLayoutRestoreOfDocumentByUserCallback(lcToFix, previousDocument)) + continue; + await HandleLayoutRestoreFallbackOfDocument(previousDocument, lcToFix); } } + protected virtual async Task FixupLayout(LayoutRoot layout) { await FixupPreviousContainerReference(layout); await ReapplyAnchorablesContent(layout); await ReapplyDocumentsContent(layout); - await Application.Current.Dispatcher.InvokeAsync(() => layout.CollectGarbage()); + await Dispatcher.InvokeAsync(layout.CollectGarbage); } #endregion Methods From 643fd7786474f4a535f3f0049bfc39ed9b48ab8d Mon Sep 17 00:00:00 2001 From: X39 Date: Thu, 10 Mar 2022 22:46:41 +0100 Subject: [PATCH 6/9] Removed obsolete marker of non-async variants --- .../AvalonDock/Layout/Serialization/LayoutSerializer.cs | 1 - .../AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs index e6affec3..7244045f 100644 --- a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs @@ -13,7 +13,6 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout.Serialization { /// Implements a base class for the layout serialization/deserialization of the docking framework. - [Obsolete("This class was replaced with " + nameof(LayoutSerializerBase))] public abstract class LayoutSerializer { #region fields diff --git a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs index 667e3868..25b303e6 100644 --- a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs @@ -15,7 +15,6 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout.Serialization { /// Implements a layout serialization/deserialization method of the docking framework. - [Obsolete("This class was replaced with " + nameof(AsyncXmlLayoutSerializer))] public class XmlLayoutSerializer : LayoutSerializer { #region Constructors From f8af68517651775e59b43a6a8dc1e602affaf5a2 Mon Sep 17 00:00:00 2001 From: X39 Date: Thu, 10 Mar 2022 22:54:31 +0100 Subject: [PATCH 7/9] fixes naming scheme of async xml layout serializer --- .../Serialization/AsyncXmlLayoutSerializer.cs | 20 ++++----- .../Serialization/LayoutSerializerBase.cs | 42 +++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs index 3d4b093e..ffa086ab 100644 --- a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs @@ -40,8 +40,8 @@ public AsyncXmlLayoutSerializer(DockingManager manager) /// A function, receiving the , /// that is supposed to call a deserialize method. /// - private Task DeserializeCommon(Func function) - => DeserializeCommon(() => + private Task DeserializeCommonAsync(Func function) + => base.DeserializeCommonAsync(() => { var serializer = XmlSerializer.FromTypes(new[] {typeof(LayoutRoot)}).First(); return function(serializer); @@ -87,26 +87,26 @@ public void Serialize(string filepath) /// Deserialize the layout a file from a . /// - public Task Deserialize(Stream stream) - => DeserializeCommon((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(stream)); + public Task DeserializeAsync(Stream stream) + => DeserializeCommonAsync((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(stream)); /// Deserialize the layout a file from a . /// - public Task Deserialize(TextReader reader) - => DeserializeCommon((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); + public Task DeserializeAsync(TextReader reader) + => DeserializeCommonAsync((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); /// Deserialize the layout a file from a . /// - public Task Deserialize(XmlReader reader) - => DeserializeCommon((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); + public Task DeserializeAsync(XmlReader reader) + => DeserializeCommonAsync((xmlSerializer) => (LayoutRoot) xmlSerializer.Deserialize(reader)); /// Deserialize the layout from a file using a . /// - public async Task Deserialize(string filepath) + public async Task DeserializeAsync(string filepath) { using (var stream = new StreamReader(filepath)) { - await Deserialize(stream); + await DeserializeAsync(stream); } } diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs index ecfd76b6..285359e0 100644 --- a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializerBase.cs @@ -128,7 +128,7 @@ private static IEnumerable GetDescendentsWithoutContent(LayoutRoot layout) .Where(lc => lc.Content == null); } - private async Task HandleLayoutRestoreFallback( + private async Task HandleLayoutRestoreFallbackAsync( LayoutContent previousContent, LayoutContent lcToFix, Action onPreviousContentNull) @@ -144,23 +144,23 @@ private async Task HandleLayoutRestoreFallback( } } - private Task HandleLayoutRestoreFallbackOfAnchorable( + private Task HandleLayoutRestoreFallbackOfAnchorableAsync( LayoutAnchorable previousAnchorable, LayoutAnchorable lcToFix) - => HandleLayoutRestoreFallback( + => HandleLayoutRestoreFallbackAsync( previousAnchorable, lcToFix, () => lcToFix.Hide(false)); private Task HandleLayoutRestoreFallbackOfDocument( LayoutDocument previousAnchorable, LayoutDocument lcToFix) - => HandleLayoutRestoreFallback( + => HandleLayoutRestoreFallbackAsync( previousAnchorable, lcToFix, // ReSharper disable once ConvertClosureToMethodGroup () => lcToFix.Close()); - private async Task TryHandleLayoutRestoreByUserCallback( + private async Task TryHandleLayoutRestoreByUserCallbackAsync( LayoutContent previousContent, LayoutContent lcToFix, Action onNoContentAvailable) @@ -190,17 +190,17 @@ private async Task TryHandleLayoutRestoreByUserCallback( return true; } - private Task TryHandleLayoutRestoreOfAnchorableByUserCallback( + private Task TryHandleLayoutRestoreOfAnchorableByUserCallbackAsync( LayoutAnchorable previousAnchorable, LayoutAnchorable lcToFix) - => TryHandleLayoutRestoreByUserCallback( + => TryHandleLayoutRestoreByUserCallbackAsync( previousAnchorable, lcToFix, () => lcToFix.Hide(false)); - private Task TryHandleLayoutRestoreOfDocumentByUserCallback( + private Task TryHandleLayoutRestoreOfDocumentByUserCallbackAsync( LayoutDocument previousDocument, LayoutDocument lcToFix) - => TryHandleLayoutRestoreByUserCallback( + => TryHandleLayoutRestoreByUserCallbackAsync( previousDocument, lcToFix, // ReSharper disable once ConvertClosureToMethodGroup @@ -242,10 +242,10 @@ protected async Task RaiseLayoutRestoreAsync( /// A function, returning the deserialized , /// that is supposed to call a deserialize method. /// - protected async Task DeserializeCommon(Func function) + protected async Task DeserializeCommonAsync(Func function) { var layout = function(); - await FixupLayout(layout); + await FixupLayoutAsync(layout); Manager.Layout = layout; } @@ -264,7 +264,7 @@ protected virtual void Dispose() /// is equivalent to . /// /// - protected virtual async Task FixupPreviousContainerReference(LayoutRoot layoutRoot) + protected virtual async Task FixupPreviousContainerReferenceAsync(LayoutRoot layoutRoot) { foreach (var lcToAttach in GetDescendentsWithPreviousContainerId(layoutRoot)) { @@ -280,36 +280,36 @@ protected virtual async Task FixupPreviousContainerReference(LayoutRoot layoutRo } } - protected virtual async Task ReapplyAnchorablesContent(LayoutRoot layout) + protected virtual async Task ReapplyAnchorablesContentAsync(LayoutRoot layout) { foreach (var lcToFix in GetDescendentsWithoutContent(layout) .ToArray()) { var previousAnchorable = GetPreviousAnchorableByContentIdOrDefault(lcToFix.ContentId); - if (await TryHandleLayoutRestoreOfAnchorableByUserCallback(previousAnchorable, lcToFix)) + if (await TryHandleLayoutRestoreOfAnchorableByUserCallbackAsync(previousAnchorable, lcToFix)) continue; - await HandleLayoutRestoreFallbackOfAnchorable(previousAnchorable, lcToFix); + await HandleLayoutRestoreFallbackOfAnchorableAsync(previousAnchorable, lcToFix); } } - protected virtual async Task ReapplyDocumentsContent(LayoutRoot layout) + protected virtual async Task ReapplyDocumentsContentAsync(LayoutRoot layout) { foreach (var lcToFix in GetDescendentsWithoutContent(layout) .ToArray()) { var previousDocument = GetPreviousDocumentByContentIdOrDefault(lcToFix.ContentId); - if (await TryHandleLayoutRestoreOfDocumentByUserCallback(lcToFix, previousDocument)) + if (await TryHandleLayoutRestoreOfDocumentByUserCallbackAsync(lcToFix, previousDocument)) continue; await HandleLayoutRestoreFallbackOfDocument(previousDocument, lcToFix); } } - protected virtual async Task FixupLayout(LayoutRoot layout) + protected virtual async Task FixupLayoutAsync(LayoutRoot layout) { - await FixupPreviousContainerReference(layout); - await ReapplyAnchorablesContent(layout); - await ReapplyDocumentsContent(layout); + await FixupPreviousContainerReferenceAsync(layout); + await ReapplyAnchorablesContentAsync(layout); + await ReapplyDocumentsContentAsync(layout); await Dispatcher.InvokeAsync(layout.CollectGarbage); } From 80c484d4b9eaa481a2fe999335c67ae4e35c6bbc Mon Sep 17 00:00:00 2001 From: X39 Date: Thu, 10 Mar 2022 23:01:43 +0100 Subject: [PATCH 8/9] addition of samples for AsyncXmlLayoutSerializer --- .../Serialization/AsyncXmlLayoutSerializer.cs | 33 +++++++++++++++- source/MVVMTestApp/MainWindow.xaml.cs | 38 +++++++++++++++++-- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs index ffa086ab..142c8ade 100644 --- a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs @@ -16,7 +16,38 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Layout.Serialization { - /// Implements a layout serialization/deserialization method of the docking framework. + /// + /// 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 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 From 319bb05751fa19b7c46eaa3d3e2302c0c2f277fc Mon Sep 17 00:00:00 2001 From: X39 Date: Thu, 10 Mar 2022 23:07:13 +0100 Subject: [PATCH 9/9] fix indentation from space to tab --- .../Layout/Serialization/AsyncXmlLayoutSerializer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs index 142c8ade..849ebf7e 100644 --- a/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/AsyncXmlLayoutSerializer.cs @@ -21,12 +21,12 @@ namespace AvalonDock.Layout.Serialization /// /// /// - /// 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 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"); + /// } /// /// ///