diff --git a/src/Avalonia.Styling/Styling/PropertySetterInstance.cs b/src/Avalonia.Styling/Styling/PropertySetterInstance.cs
index 689438dfd7..b52ae6b146 100644
--- a/src/Avalonia.Styling/Styling/PropertySetterInstance.cs
+++ b/src/Avalonia.Styling/Styling/PropertySetterInstance.cs
@@ -7,7 +7,7 @@ using Avalonia.Reactive;
namespace Avalonia.Styling
{
///
- /// A which has been instance on a control.
+ /// A which has been instanced on a control.
///
/// The target property type.
internal class PropertySetterInstance : SingleSubscriberObservableBase>,
diff --git a/src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs
new file mode 100644
index 0000000000..03d33faff9
--- /dev/null
+++ b/src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs
@@ -0,0 +1,128 @@
+using System;
+using Avalonia.Data;
+using Avalonia.Reactive;
+
+#nullable enable
+
+namespace Avalonia.Styling
+{
+ ///
+ /// A which has been instanced on a control and whose value is lazily
+ /// evaluated.
+ ///
+ /// The target property type.
+ internal class PropertySetterLazyInstance : SingleSubscriberObservableBase>,
+ ISetterInstance
+ {
+ private readonly IStyleable _target;
+ private readonly StyledPropertyBase? _styledProperty;
+ private readonly DirectPropertyBase? _directProperty;
+ private readonly Func _valueFactory;
+ private BindingValue _value;
+ private IDisposable? _subscription;
+ private bool _isActive;
+
+ public PropertySetterLazyInstance(
+ IStyleable target,
+ StyledPropertyBase property,
+ Func valueFactory)
+ {
+ _target = target;
+ _styledProperty = property;
+ _valueFactory = valueFactory;
+ }
+
+ public PropertySetterLazyInstance(
+ IStyleable target,
+ DirectPropertyBase property,
+ Func valueFactory)
+ {
+ _target = target;
+ _directProperty = property;
+ _valueFactory = valueFactory;
+ }
+
+ public void Start(bool hasActivator)
+ {
+ _isActive = !hasActivator;
+
+ if (_styledProperty is object)
+ {
+ var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style;
+ _subscription = _target.Bind(_styledProperty, this, priority);
+ }
+ else
+ {
+ _subscription = _target.Bind(_directProperty, this);
+ }
+ }
+
+ public void Activate()
+ {
+ if (!_isActive)
+ {
+ _isActive = true;
+ PublishNext();
+ }
+ }
+
+ public void Deactivate()
+ {
+ if (_isActive)
+ {
+ _isActive = false;
+ PublishNext();
+ }
+ }
+
+ public override void Dispose()
+ {
+ if (_subscription is object)
+ {
+ var sub = _subscription;
+ _subscription = null;
+ sub.Dispose();
+ }
+ else if (_isActive)
+ {
+ if (_styledProperty is object)
+ {
+ _target.ClearValue(_styledProperty);
+ }
+ else
+ {
+ _target.ClearValue(_directProperty);
+ }
+ }
+
+ base.Dispose();
+ }
+
+ protected override void Subscribed() => PublishNext();
+ protected override void Unsubscribed() { }
+
+ private T GetValue()
+ {
+ if (_value.HasValue)
+ {
+ return _value.Value;
+ }
+
+ _value = _valueFactory();
+ return _value.Value;
+ }
+
+ private void PublishNext()
+ {
+ if (_isActive)
+ {
+ GetValue();
+ PublishNext(_value);
+ }
+ else
+ {
+ PublishNext(default);
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Styling/Styling/Setter.cs b/src/Avalonia.Styling/Styling/Setter.cs
index c6bdeef317..1f3d6335a9 100644
--- a/src/Avalonia.Styling/Styling/Setter.cs
+++ b/src/Avalonia.Styling/Styling/Setter.cs
@@ -68,18 +68,10 @@ namespace Avalonia.Styling
throw new InvalidOperationException("Setter.Property must be set.");
}
- var value = Value;
-
- if (value is ITemplate template &&
- !typeof(ITemplate).IsAssignableFrom(Property.PropertyType))
- {
- value = template.Build();
- }
-
var data = new SetterVisitorData
{
target = target,
- value = value,
+ value = Value,
};
Property.Accept(this, ref data);
@@ -97,6 +89,13 @@ namespace Avalonia.Styling
property,
binding);
}
+ else if (data.value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(property.PropertyType))
+ {
+ data.result = new PropertySetterLazyInstance(
+ data.target,
+ property,
+ () => (T)template.Build());
+ }
else
{
data.result = new PropertySetterInstance(
@@ -117,6 +116,13 @@ namespace Avalonia.Styling
property,
binding);
}
+ else if (data.value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(property.PropertyType))
+ {
+ data.result = new PropertySetterLazyInstance(
+ data.target,
+ property,
+ () => (T)template.Build());
+ }
else
{
data.result = new PropertySetterInstance(
diff --git a/tests/Avalonia.Styling.UnitTests/StyleTests.cs b/tests/Avalonia.Styling.UnitTests/StyleTests.cs
index 61d9f961f3..a6bbe63cd0 100644
--- a/tests/Avalonia.Styling.UnitTests/StyleTests.cs
+++ b/tests/Avalonia.Styling.UnitTests/StyleTests.cs
@@ -284,7 +284,9 @@ namespace Avalonia.Styling.UnitTests
};
var target = new Class1();
+ target.BeginInit();
styles.TryAttach(target, null);
+ target.EndInit();
Assert.NotNull(target.Child);
Assert.Equal(1, instantiationCount);