Browse Source

Abstracted storage of styled values into ValueStore.

pull/1703/head
Steven Kirk 8 years ago
parent
commit
27daffeb3e
  1. 124
      src/Avalonia.Base/AvaloniaObject.cs
  2. 9
      src/Avalonia.Base/IPriorityValueOwner.cs
  3. 4
      src/Avalonia.Base/PriorityValue.cs
  4. 134
      src/Avalonia.Base/ValueStore.cs
  5. 4
      tests/Avalonia.Base.UnitTests/PriorityValueTests.cs

124
src/Avalonia.Base/AvaloniaObject.cs

@ -32,8 +32,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// The set values/bindings on this object. /// The set values/bindings on this object.
/// </summary> /// </summary>
private readonly Dictionary<AvaloniaProperty, PriorityValue> _values = private readonly ValueStore _values;
new Dictionary<AvaloniaProperty, PriorityValue>();
/// <summary> /// <summary>
/// Maintains a list of direct property binding subscriptions so that the binding source /// Maintains a list of direct property binding subscriptions so that the binding source
@ -73,6 +72,8 @@ namespace Avalonia
{ {
VerifyAccess(); VerifyAccess();
_values = new ValueStore(this);
void Notify(AvaloniaProperty property) void Notify(AvaloniaProperty property)
{ {
object value = property.IsDirect ? object value = property.IsDirect ?
@ -230,7 +231,7 @@ namespace Avalonia
} }
else else
{ {
return GetValueInternal(property); return _values.GetValue(property);
} }
} }
@ -257,7 +258,7 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(property != null); Contract.Requires<ArgumentNullException>(property != null);
VerifyAccess(); VerifyAccess();
return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false; return _values.IsAnimating(property);
} }
/// <summary> /// <summary>
@ -274,14 +275,7 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(property != null); Contract.Requires<ArgumentNullException>(property != null);
VerifyAccess(); VerifyAccess();
PriorityValue value; return _values.IsSet(property);
if (_values.TryGetValue(property, out value))
{
return value.Value != AvaloniaProperty.UnsetValue;
}
return false;
} }
/// <summary> /// <summary>
@ -369,14 +363,6 @@ namespace Avalonia
} }
else else
{ {
PriorityValue v;
if (!_values.TryGetValue(property, out v))
{
v = CreatePriorityValue(property);
_values.Add(property, v);
}
Logger.Verbose( Logger.Verbose(
LogArea.Property, LogArea.Property,
this, this,
@ -385,7 +371,7 @@ namespace Avalonia
description, description,
priority); priority);
return v.Add(source, (int)priority); return _values.AddBinding(property, source, priority);
} }
} }
@ -416,20 +402,12 @@ namespace Avalonia
public void Revalidate(AvaloniaProperty property) public void Revalidate(AvaloniaProperty property)
{ {
VerifyAccess(); VerifyAccess();
PriorityValue value; _values.Revalidate(property);
if (_values.TryGetValue(property, out value))
{
value.Revalidate();
}
} }
/// <inheritdoc/> /// <inheritdoc/>
void IPriorityValueOwner.Changed(PriorityValue sender, object oldValue, object newValue) void IPriorityValueOwner.Changed(AvaloniaProperty property, int priority, object oldValue, object newValue)
{ {
var property = sender.Property;
var priority = (BindingPriority)sender.ValuePriority;
oldValue = (oldValue == AvaloniaProperty.UnsetValue) ? oldValue = (oldValue == AvaloniaProperty.UnsetValue) ?
GetDefaultValue(property) : GetDefaultValue(property) :
oldValue; oldValue;
@ -439,7 +417,7 @@ namespace Avalonia
if (!Equals(oldValue, newValue)) if (!Equals(oldValue, newValue))
{ {
RaisePropertyChanged(property, oldValue, newValue, priority); RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority);
Logger.Verbose( Logger.Verbose(
LogArea.Property, LogArea.Property,
@ -448,14 +426,14 @@ namespace Avalonia
property, property,
oldValue, oldValue,
newValue, newValue,
priority); (BindingPriority)priority);
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification) void IPriorityValueOwner.BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
{ {
UpdateDataValidation(sender.Property, notification); UpdateDataValidation(property, notification);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -468,10 +446,7 @@ namespace Avalonia
/// Gets all priority values set on the object. /// Gets all priority values set on the object.
/// </summary> /// </summary>
/// <returns>A collection of property/value tuples.</returns> /// <returns>A collection of property/value tuples.</returns>
internal IDictionary<AvaloniaProperty, PriorityValue> GetSetValues() internal IDictionary<AvaloniaProperty, PriorityValue> GetSetValues() => _values.GetSetValues();
{
return _values;
}
/// <summary> /// <summary>
/// Forces revalidation of properties when a property value changes. /// Forces revalidation of properties when a property value changes.
@ -660,68 +635,18 @@ namespace Avalonia
} }
} }
/// <summary>
/// Creates a <see cref="PriorityValue"/> for a <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The <see cref="PriorityValue"/>.</returns>
private PriorityValue CreatePriorityValue(AvaloniaProperty property)
{
var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(GetType());
Func<object, object> validate2 = null;
if (validate != null)
{
validate2 = v => validate(this, v);
}
PriorityValue result = new PriorityValue(
this,
property,
property.PropertyType,
validate2);
return result;
}
/// <summary> /// <summary>
/// Gets the default value for a property. /// Gets the default value for a property.
/// </summary> /// </summary>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <returns>The default value.</returns> /// <returns>The default value.</returns>
private object GetDefaultValue(AvaloniaProperty property) internal object GetDefaultValue(AvaloniaProperty property)
{ {
if (property.Inherits && InheritanceParent is AvaloniaObject aobj) if (property.Inherits && InheritanceParent is AvaloniaObject aobj)
return aobj.GetValueInternal(property); return aobj._values.GetValue(property);
return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType());
} }
/// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value
/// without check for registered as this can slow getting the value
/// this method is intended for internal usage in AvaloniaObject only
/// it's called only after check the property is registered
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
private object GetValueInternal(AvaloniaProperty property)
{
object result = AvaloniaProperty.UnsetValue;
PriorityValue value;
if (_values.TryGetValue(property, out value))
{
result = value.Value;
}
if (result == AvaloniaProperty.UnsetValue)
{
result = GetDefaultValue(property);
}
return result;
}
/// <summary> /// <summary>
/// Sets the value of a direct property. /// Sets the value of a direct property.
/// </summary> /// </summary>
@ -801,22 +726,9 @@ namespace Avalonia
originalValue, originalValue,
originalValue?.GetType().FullName ?? "(null)")); originalValue?.GetType().FullName ?? "(null)"));
} }
PriorityValue v;
if (!_values.TryGetValue(property, out v))
{
if (value == AvaloniaProperty.UnsetValue)
{
return;
}
v = CreatePriorityValue(property);
_values.Add(property, v);
}
LogPropertySet(property, value, priority); LogPropertySet(property, value, priority);
v.SetValue(value, (int)priority); _values.AddValue(property, value, (int)priority);
} }
/// <summary> /// <summary>

9
src/Avalonia.Base/IPriorityValueOwner.cs

@ -13,18 +13,19 @@ namespace Avalonia
/// <summary> /// <summary>
/// Called when a <see cref="PriorityValue"/>'s value changes. /// Called when a <see cref="PriorityValue"/>'s value changes.
/// </summary> /// </summary>
/// <param name="sender">The source of the change.</param> /// <param name="property">The the property that has changed.</param>
/// <param name="priority">The priority of the value.</param>
/// <param name="oldValue">The old value.</param> /// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param> /// <param name="newValue">The new value.</param>
void Changed(PriorityValue sender, object oldValue, object newValue); void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue);
/// <summary> /// <summary>
/// Called when a <see cref="BindingNotification"/> is received by a /// Called when a <see cref="BindingNotification"/> is received by a
/// <see cref="PriorityValue"/>. /// <see cref="PriorityValue"/>.
/// </summary> /// </summary>
/// <param name="sender">The source of the change.</param> /// <param name="property">The the property that has changed.</param>
/// <param name="notification">The notification.</param> /// <param name="notification">The notification.</param>
void BindingNotificationReceived(PriorityValue sender, BindingNotification notification); void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification);
/// <summary> /// <summary>
/// Ensures that the current thread is the UI thread. /// Ensures that the current thread is the UI thread.

4
src/Avalonia.Base/PriorityValue.cs

@ -281,12 +281,12 @@ namespace Avalonia
if (notification == null || notification.HasValue) if (notification == null || notification.HasValue)
{ {
notify(() => Owner?.Changed(this, old, Value)); notify(() => Owner?.Changed(Property, ValuePriority, old, Value));
} }
if (notification != null) if (notification != null)
{ {
Owner?.BindingNotificationReceived(this, notification); Owner?.BindingNotificationReceived(Property, notification);
} }
} }
else else

134
src/Avalonia.Base/ValueStore.cs

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Data;
using Avalonia.Utilities;
namespace Avalonia
{
internal class ValueStore : IPriorityValueOwner
{
private readonly AvaloniaObject _owner;
private readonly Dictionary<AvaloniaProperty, PriorityValue> _values =
new Dictionary<AvaloniaProperty, PriorityValue>();
public ValueStore(AvaloniaObject owner)
{
_owner = owner;
}
public IDisposable AddBinding(
AvaloniaProperty property,
IObservable<object> source,
BindingPriority priority)
{
if (!_values.TryGetValue(property, out PriorityValue v))
{
v = CreatePriorityValue(property);
_values.Add(property, v);
}
return v.Add(source, (int)priority);
}
public void AddValue(AvaloniaProperty property, object value, int priority)
{
var originalValue = value;
if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value))
{
throw new ArgumentException(string.Format(
"Invalid value for Property '{0}': '{1}' ({2})",
property.Name,
originalValue,
originalValue?.GetType().FullName ?? "(null)"));
}
if (!_values.TryGetValue(property, out PriorityValue v))
{
if (value == AvaloniaProperty.UnsetValue)
{
return;
}
v = CreatePriorityValue(property);
_values.Add(property, v);
}
v.SetValue(value, priority);
}
public IDictionary<AvaloniaProperty, PriorityValue> GetSetValues() => _values;
public object GetValue(AvaloniaProperty property)
{
var result = AvaloniaProperty.UnsetValue;
if (_values.TryGetValue(property, out PriorityValue value))
{
result = value.Value;
}
if (result == AvaloniaProperty.UnsetValue)
{
result = _owner.GetDefaultValue(property);
}
return result;
}
public bool IsAnimating(AvaloniaProperty property)
{
return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false;
}
public bool IsSet(AvaloniaProperty property)
{
if (_values.TryGetValue(property, out PriorityValue value))
{
return value.Value != AvaloniaProperty.UnsetValue;
}
return false;
}
public void Revalidate(AvaloniaProperty property)
{
if (_values.TryGetValue(property, out PriorityValue value))
{
value.Revalidate();
}
}
void IPriorityValueOwner.BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
{
((IPriorityValueOwner)_owner).BindingNotificationReceived(property, notification);
}
void IPriorityValueOwner.Changed(AvaloniaProperty property, int priority, object oldValue, object newValue)
{
((IPriorityValueOwner)_owner).Changed(property, priority, oldValue, newValue);
}
void IPriorityValueOwner.VerifyAccess() => _owner.VerifyAccess();
private PriorityValue CreatePriorityValue(AvaloniaProperty property)
{
var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(_owner.GetType());
Func<object, object> validate2 = null;
if (validate != null)
{
validate2 = v => validate(_owner, v);
}
PriorityValue result = new PriorityValue(
this,
property,
property.PropertyType,
validate2);
return result;
}
}
}

4
tests/Avalonia.Base.UnitTests/PriorityValueTests.cs

@ -167,7 +167,7 @@ namespace Avalonia.Base.UnitTests
target.Add(Single("foo"), 0); target.Add(Single("foo"), 0);
owner.Verify(x => x.Changed(target, AvaloniaProperty.UnsetValue, "foo")); owner.Verify(x => x.Changed(target.Property, target.ValuePriority, AvaloniaProperty.UnsetValue, "foo"));
} }
[Fact] [Fact]
@ -180,7 +180,7 @@ namespace Avalonia.Base.UnitTests
target.Add(subject, 0); target.Add(subject, 0);
subject.OnNext("bar"); subject.OnNext("bar");
owner.Verify(x => x.Changed(target, "foo", "bar")); owner.Verify(x => x.Changed(target.Property, target.ValuePriority, "foo", "bar"));
} }
[Fact] [Fact]

Loading…
Cancel
Save