diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index 1de5cb06c6..56ad241187 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -492,6 +492,11 @@ namespace Avalonia
return Name;
}
+ ///
+ /// True if has any observers.
+ ///
+ internal bool HasNotifyInitializedObservers => _initialized.HasObservers;
+
///
/// Notifies the observable.
///
diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
index 037e0dd72e..88b0201fcb 100644
--- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
+++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
@@ -24,8 +24,8 @@ namespace Avalonia
new Dictionary>();
private readonly Dictionary> _attachedCache =
new Dictionary>();
- private readonly Dictionary>> _initializedCache =
- new Dictionary>>();
+ private readonly Dictionary> _initializedCache =
+ new Dictionary>();
///
/// Gets the instance
@@ -286,35 +286,73 @@ namespace Avalonia
property.NotifyInitialized(e);
}
- if (!_initializedCache.TryGetValue(type, out var items))
+ if (!_initializedCache.TryGetValue(type, out var initializationData))
{
- var build = new Dictionary();
+ var visited = new HashSet();
- foreach (var property in GetRegistered(type))
+ initializationData = new List();
+
+ foreach (AvaloniaProperty property in GetRegistered(type))
{
- var value = !property.IsDirect ?
- ((IStyledPropertyAccessor)property).GetDefaultValue(type) :
- null;
- build.Add(property, value);
+ if (property.IsDirect)
+ {
+ initializationData.Add(new PropertyInitializationData(property, (IDirectPropertyAccessor)property));
+ }
+ else
+ {
+ initializationData.Add(new PropertyInitializationData(property, (IStyledPropertyAccessor)property, type));
+ }
+
+ visited.Add(property);
}
- foreach (var property in GetRegisteredAttached(type))
+ foreach (AvaloniaProperty property in GetRegisteredAttached(type))
{
- if (!build.ContainsKey(property))
+ if (!visited.Contains(property))
{
- var value = ((IStyledPropertyAccessor)property).GetDefaultValue(type);
- build.Add(property, value);
+ initializationData.Add(new PropertyInitializationData(property, (IStyledPropertyAccessor)property, type));
+
+ visited.Add(property);
}
}
- items = build.ToList();
- _initializedCache.Add(type, items);
+ _initializedCache.Add(type, initializationData);
+ }
+
+ foreach (PropertyInitializationData data in initializationData)
+ {
+ if (!data.Property.HasNotifyInitializedObservers)
+ {
+ continue;
+ }
+
+ object value = data.IsDirect ? data.DirectAccessor.GetValue(o) : data.Value;
+
+ Notify(data.Property, value);
+ }
+ }
+
+ private readonly struct PropertyInitializationData
+ {
+ public AvaloniaProperty Property { get; }
+ public object Value { get; }
+ public bool IsDirect { get; }
+ public IDirectPropertyAccessor DirectAccessor { get; }
+
+ public PropertyInitializationData(AvaloniaProperty property, IDirectPropertyAccessor directAccessor)
+ {
+ Property = property;
+ Value = null;
+ IsDirect = true;
+ DirectAccessor = directAccessor;
}
- foreach (var i in items)
+ public PropertyInitializationData(AvaloniaProperty property, IStyledPropertyAccessor styledAccessor, Type type)
{
- var value = i.Key.IsDirect ? o.GetValue(i.Key) : i.Value;
- Notify(i.Key, value);
+ Property = property;
+ Value = styledAccessor.GetDefaultValue(type);
+ IsDirect = false;
+ DirectAccessor = null;
}
}
}
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index 4514109e12..eb68ba647e 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -105,11 +105,13 @@ namespace Avalonia.Controls
get => _selectedItem;
set
{
+ var selectedItems = SelectedItems;
+
SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
if (value != null)
{
- if (SelectedItems.Count != 1 || SelectedItems[0] != value)
+ if (selectedItems.Count != 1 || selectedItems[0] != value)
{
_syncingSelectedItems = true;
SelectSingleItem(value);
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs
new file mode 100644
index 0000000000..06716f7102
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs
@@ -0,0 +1,15 @@
+using Avalonia.Controls;
+using BenchmarkDotNet.Attributes;
+
+namespace Avalonia.Benchmarks.Base
+{
+ [MemoryDiagnoser]
+ public class AvaloniaObjectInitializationBenchmark
+ {
+ [Benchmark(OperationsPerInvoke = 1000)]
+ public Button InitializeButton()
+ {
+ return new Button();
+ }
+ }
+}