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/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 diff --git a/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs b/src/Dock.Avalonia/Controls/Recycling/ControlRecycling.cs new file mode 100644 index 000000000..3d875ebed --- /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)