From 868340b6f21b9f4918e000467b03944d55bba219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 30 Aug 2023 21:11:33 +0200 Subject: [PATCH 1/3] Add control cache support --- .../Controls/DocumentControl.axaml | 6 +- .../Controls/Recycling/ControlRecycling.cs | 67 +++++++++++++++++++ .../Recycling/RecylingDataTemplate.cs | 59 ++++++++++++++++ src/Dock.Avalonia/Controls/ToolControl.axaml | 6 +- src/Dock.Avalonia/Properties/AssemblyInfo.cs | 1 + src/Dock.Model.Avalonia/Controls/Document.cs | 13 +++- src/Dock.Model.Avalonia/Controls/Tool.cs | 13 +++- 7 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs create mode 100644 src/Dock.Avalonia/Controls/Recycling/RecylingDataTemplate.cs diff --git a/src/Dock.Avalonia/Controls/DocumentControl.axaml b/src/Dock.Avalonia/Controls/DocumentControl.axaml index 7ff353943..870a124dd 100644 --- a/src/Dock.Avalonia/Controls/DocumentControl.axaml +++ b/src/Dock.Avalonia/Controls/DocumentControl.axaml @@ -44,7 +44,11 @@ + VerticalAlignment="Stretch"> + + + + diff --git a/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs b/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs new file mode 100644 index 000000000..ede6affd9 --- /dev/null +++ b/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Controls.Templates; + +namespace Dock.Avalonia.Controls.Recycling; + +/// +/// +/// +public class ControlRecycling +{ + private readonly Dictionary _cache = new(); + + private bool TryGetValue(object? data, out Control? control) + { + if (data is null) + { + control = null; + return false; + } + + return _cache.TryGetValue(data, out control); + } + + private void Add(object data, Control control) + { + _cache[data] = control; + } + + /// + /// + /// + /// + /// + /// + /// + public Control? Build(object? data, Control? existing, Control? parent) + { + if (data is null) + { + return null; + } + + if (TryGetValue(data, out var control)) + { +#if DEBUG + Console.WriteLine($"[Cached] {data}, {control}"); +#endif + return control; + } + + var dataTemplate = parent?.FindDataTemplate(data, null); + + control = dataTemplate?.Build(data); + if (control is null) + { + return null; + } + + Add(data, control); +#if DEBUG + Console.WriteLine($"[Added] {data}, {control}"); +#endif + return control; + } +} diff --git a/src/Dock.Avalonia/Controls/Recycling/RecylingDataTemplate.cs b/src/Dock.Avalonia/Controls/Recycling/RecylingDataTemplate.cs new file mode 100644 index 000000000..9c174cbda --- /dev/null +++ b/src/Dock.Avalonia/Controls/Recycling/RecylingDataTemplate.cs @@ -0,0 +1,59 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Templates; + +namespace Dock.Avalonia.Controls.Recycling; + +/// +/// +/// +public class ControlRecyclingDataTemplate : AvaloniaObject, IRecyclingDataTemplate +{ + private static readonly ControlRecycling s_controlRecycling = new(); + + /// + /// + /// + public static readonly StyledProperty ParentProperty = + AvaloniaProperty.Register("Parent"); + + /// + /// + /// + public Control? Parent + { + get => GetValue(ParentProperty); + set => SetValue(ParentProperty, value); + } + + /// + /// + /// + /// + /// + public Control? Build(object? param) + { + return null; + } + + /// + /// + /// + /// + /// + public bool Match(object? data) + { + return true; + } + + /// + /// + /// + /// + /// + /// + public Control? Build(object? data, Control? existing) + { + return s_controlRecycling.Build(data, existing, Parent); + } +} diff --git a/src/Dock.Avalonia/Controls/ToolControl.axaml b/src/Dock.Avalonia/Controls/ToolControl.axaml index 2ba982ad4..465b499f7 100644 --- a/src/Dock.Avalonia/Controls/ToolControl.axaml +++ b/src/Dock.Avalonia/Controls/ToolControl.axaml @@ -32,7 +32,11 @@ + VerticalAlignment="Stretch"> + + + + diff --git a/src/Dock.Avalonia/Properties/AssemblyInfo.cs b/src/Dock.Avalonia/Properties/AssemblyInfo.cs index 2ac0ebaef..6ec0ec8e0 100644 --- a/src/Dock.Avalonia/Properties/AssemblyInfo.cs +++ b/src/Dock.Avalonia/Properties/AssemblyInfo.cs @@ -2,6 +2,7 @@ [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Dock.Avalonia")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Dock.Avalonia.Controls")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Dock.Avalonia.Controls.Recycling")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Dock.Avalonia.Converters")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Dock.Avalonia.MarkupExtension")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Dock.Avalonia.Themes")] diff --git a/src/Dock.Model.Avalonia/Controls/Document.cs b/src/Dock.Model.Avalonia/Controls/Document.cs index 54bb53b16..db5c086b2 100644 --- a/src/Dock.Model.Avalonia/Controls/Document.cs +++ b/src/Dock.Model.Avalonia/Controls/Document.cs @@ -24,6 +24,8 @@ public class Document : DockableBase, IDocument, IDocumentContent, ITemplate ContentProperty = AvaloniaProperty.Register(nameof(Content)); + private Control? _cached; + /// /// Initializes new instance of the class. /// @@ -97,7 +99,16 @@ public bool Match(object? data) /// public Control? Build(object? data, Control? existing) { - return existing ?? TemplateContent.Load(Content)?.Result; + if (_cached is not null) + { + return _cached; + } + var control = TemplateContent.Load(Content)?.Result; + if (control is not null) + { + _cached = control; + } + return control; } private static TemplateResult? Load(object? templateContent) diff --git a/src/Dock.Model.Avalonia/Controls/Tool.cs b/src/Dock.Model.Avalonia/Controls/Tool.cs index 48043bfd2..738d1d494 100644 --- a/src/Dock.Model.Avalonia/Controls/Tool.cs +++ b/src/Dock.Model.Avalonia/Controls/Tool.cs @@ -24,6 +24,8 @@ public class Tool : DockableBase, ITool, IDocument, IToolContent, ITemplate ContentProperty = AvaloniaProperty.Register(nameof(Content)); + private Control? _cached; + /// /// Initializes new instance of the class. /// @@ -99,7 +101,16 @@ public bool Match(object? data) /// public Control? Build(object? data, Control? existing) { - return existing ?? TemplateContent.Load(Content)?.Result; + if (_cached is not null) + { + return _cached; + } + var control = TemplateContent.Load(Content)?.Result; + if (control is not null) + { + _cached = control; + } + return control; } private static TemplateResult? Load(object? templateContent) From 8073e0abbe03b10c8ecee9f8c26a02306f54b7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 31 Aug 2023 09:50:59 +0200 Subject: [PATCH 2/3] Update ControlRecycling.cs --- src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs b/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs index ede6affd9..3d875ebed 100644 --- a/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs +++ b/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs @@ -10,7 +10,7 @@ namespace Dock.Avalonia.Controls.Recycling; /// public class ControlRecycling { - private readonly Dictionary _cache = new(); + private readonly Dictionary _cache = new(); private bool TryGetValue(object? data, out Control? control) { From fc2ea318c6c03f5f789f2a14a1142a9a5c2ff173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 31 Aug 2023 10:09:33 +0200 Subject: [PATCH 3/3] Update HostWindow.axaml.cs --- src/Dock.Avalonia/Controls/HostWindow.axaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dock.Avalonia/Controls/HostWindow.axaml.cs b/src/Dock.Avalonia/Controls/HostWindow.axaml.cs index 1b86c50a9..dfca3875c 100644 --- a/src/Dock.Avalonia/Controls/HostWindow.axaml.cs +++ b/src/Dock.Avalonia/Controls/HostWindow.axaml.cs @@ -267,7 +267,7 @@ public void Present(bool isDialog) Window.Factory?.OnWindowOpened(Window); } - ShowDialog(null); // FIXME: Set correct parent window. + ShowDialog(null!); // FIXME: Set correct parent window. } } else