Skip to content

Commit

Permalink
Lazily evaluate <Template>s in setters.
Browse files Browse the repository at this point in the history
Part of fixing #5027.
  • Loading branch information
grokys committed Nov 17, 2020
1 parent b3ed79a commit bc8a97a
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/Avalonia.Styling/Styling/PropertySetterInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
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>>,
Expand Down
128 changes: 128 additions & 0 deletions src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 9 deletions src/Avalonia.Styling/Styling/Setter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,10 @@ public ISetterInstance Instance(IStyleable target)
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);
Expand All @@ -97,6 +89,13 @@ void IAvaloniaPropertyVisitor<SetterVisitorData>.Visit<T>(
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>(
Expand All @@ -117,6 +116,13 @@ void IAvaloniaPropertyVisitor<SetterVisitorData>.Visit<T>(
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>(
Expand Down
2 changes: 2 additions & 0 deletions tests/Avalonia.Styling.UnitTests/StyleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ public void Template_In_Inactive_Style_Is_Not_Built()
};

var target = new Class1();
target.BeginInit();
styles.TryAttach(target, null);
target.EndInit();

Assert.NotNull(target.Child);
Assert.Equal(1, instantiationCount);
Expand Down

0 comments on commit bc8a97a

Please sign in to comment.