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);