Browse Source

Lazily evaluate <Template>s in setters.

Part of fixing #5027.
pull/5070/head
Steven Kirk 6 years ago
parent
commit
72a43174e1
  1. 2
      src/Avalonia.Styling/Styling/PropertySetterInstance.cs
  2. 128
      src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs
  3. 24
      src/Avalonia.Styling/Styling/Setter.cs
  4. 2
      tests/Avalonia.Styling.UnitTests/StyleTests.cs

2
src/Avalonia.Styling/Styling/PropertySetterInstance.cs

@ -7,7 +7,7 @@ using Avalonia.Reactive;
namespace Avalonia.Styling
{
/// <summary>
/// A <see cref="Setter"/> which has been instance on a control.
/// A <see cref="Setter"/> which has been instanced on a control.
/// </summary>
/// <typeparam name="T">The target property type.</typeparam>
internal class PropertySetterInstance<T> : SingleSubscriberObservableBase<BindingValue<T>>,

128
src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs

@ -0,0 +1,128 @@
using System;
using Avalonia.Data;
using Avalonia.Reactive;
#nullable enable
namespace Avalonia.Styling
{
/// <summary>
/// A <see cref="Setter"/> which has been instanced on a control and whose value is lazily
/// evaluated.
/// </summary>
/// <typeparam name="T">The target property type.</typeparam>
internal class PropertySetterLazyInstance<T> : SingleSubscriberObservableBase<BindingValue<T>>,
ISetterInstance
{
private readonly IStyleable _target;
private readonly StyledPropertyBase<T>? _styledProperty;
private readonly DirectPropertyBase<T>? _directProperty;
private readonly Func<T> _valueFactory;
private BindingValue<T> _value;
private IDisposable? _subscription;
private bool _isActive;
public PropertySetterLazyInstance(
IStyleable target,
StyledPropertyBase<T> property,
Func<T> valueFactory)
{
_target = target;
_styledProperty = property;
_valueFactory = valueFactory;
}
public PropertySetterLazyInstance(
IStyleable target,
DirectPropertyBase<T> property,
Func<T> 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);
}
}
}
}

24
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<T>(
data.target,
property,
() => (T)template.Build());
}
else
{
data.result = new PropertySetterInstance<T>(
@ -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<T>(
data.target,
property,
() => (T)template.Build());
}
else
{
data.result = new PropertySetterInstance<T>(

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

Loading…
Cancel
Save