Skip to content

Commit

Permalink
Merge pull request #1703 from AvaloniaUI/valuestore
Browse files Browse the repository at this point in the history
Store LocalValues directly without creating a PriorityValue.
  • Loading branch information
jkoritzinsky committed Jul 3, 2018
2 parents 7a6b3e3 + 6d413c2 commit 5d8b084
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 114 deletions.
139 changes: 33 additions & 106 deletions src/Avalonia.Base/AvaloniaObject.cs
Expand Up @@ -29,12 +29,6 @@ public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyProp
/// </summary>
private IAvaloniaObject _inheritanceParent;

/// <summary>
/// The set values/bindings on this object.
/// </summary>
private readonly Dictionary<AvaloniaProperty, PriorityValue> _values =
new Dictionary<AvaloniaProperty, PriorityValue>();

/// <summary>
/// Maintains a list of direct property binding subscriptions so that the binding source
/// doesn't get collected.
Expand All @@ -52,6 +46,7 @@ public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyProp
private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;

private DeferredSetter<AvaloniaProperty, object> _directDeferredSetter;
private ValueStore _values;

/// <summary>
/// Delayed setter helper for direct properties. Used to fix #855.
Expand Down Expand Up @@ -228,9 +223,20 @@ public object GetValue(AvaloniaProperty property)
{
return ((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this);
}
else if (_values != null)
{
var result = _values.GetValue(property);

if (result == AvaloniaProperty.UnsetValue)
{
result = GetDefaultValue(property);
}

return result;
}
else
{
return GetValueInternal(property);
return GetDefaultValue(property);
}
}

Expand All @@ -257,7 +263,7 @@ public bool IsAnimating(AvaloniaProperty property)
Contract.Requires<ArgumentNullException>(property != null);
VerifyAccess();

return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false;
return _values?.IsAnimating(property) ?? false;
}

/// <summary>
Expand All @@ -274,14 +280,7 @@ public bool IsSet(AvaloniaProperty property)
Contract.Requires<ArgumentNullException>(property != null);
VerifyAccess();

PriorityValue value;

if (_values.TryGetValue(property, out value))
{
return value.Value != AvaloniaProperty.UnsetValue;
}

return false;
return _values?.IsSet(property) ?? false;
}

/// <summary>
Expand Down Expand Up @@ -369,14 +368,6 @@ public bool IsSet(AvaloniaProperty property)
}
else
{
PriorityValue v;

if (!_values.TryGetValue(property, out v))
{
v = CreatePriorityValue(property);
_values.Add(property, v);
}

Logger.Verbose(
LogArea.Property,
this,
Expand All @@ -385,7 +376,12 @@ public bool IsSet(AvaloniaProperty property)
description,
priority);

return v.Add(source, (int)priority);
if (_values == null)
{
_values = new ValueStore(this);
}

return _values.AddBinding(property, source, priority);
}
}

Expand Down Expand Up @@ -416,20 +412,12 @@ public bool IsSet(AvaloniaProperty property)
public void Revalidate(AvaloniaProperty property)
{
VerifyAccess();
PriorityValue value;

if (_values.TryGetValue(property, out value))
{
value.Revalidate();
}
_values?.Revalidate(property);
}

/// <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) ?
GetDefaultValue(property) :
oldValue;
Expand All @@ -439,7 +427,7 @@ void IPriorityValueOwner.Changed(PriorityValue sender, object oldValue, object n

if (!Equals(oldValue, newValue))
{
RaisePropertyChanged(property, oldValue, newValue, priority);
RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority);

Logger.Verbose(
LogArea.Property,
Expand All @@ -448,14 +436,14 @@ void IPriorityValueOwner.Changed(PriorityValue sender, object oldValue, object n
property,
oldValue,
newValue,
priority);
(BindingPriority)priority);
}
}

/// <inheritdoc/>
void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification)
void IPriorityValueOwner.BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
{
UpdateDataValidation(sender.Property, notification);
UpdateDataValidation(property, notification);
}

/// <inheritdoc/>
Expand All @@ -468,10 +456,7 @@ Delegate[] IAvaloniaObjectDebug.GetPropertyChangedSubscribers()
/// Gets all priority values set on the object.
/// </summary>
/// <returns>A collection of property/value tuples.</returns>
internal IDictionary<AvaloniaProperty, PriorityValue> GetSetValues()
{
return _values;
}
internal IDictionary<AvaloniaProperty, PriorityValue> GetSetValues() => _values?.GetSetValues();

/// <summary>
/// Forces revalidation of properties when a property value changes.
Expand Down Expand Up @@ -660,68 +645,18 @@ private static object CastOrDefault(object value, Type type)
}
}

/// <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>
/// Gets the default value for a property.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The default value.</returns>
private object GetDefaultValue(AvaloniaProperty property)
internal object GetDefaultValue(AvaloniaProperty property)
{
if (property.Inherits && InheritanceParent is AvaloniaObject aobj)
return aobj.GetValueInternal(property);
return aobj.GetValue(property);
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>
/// Sets the value of a direct property.
/// </summary>
Expand Down Expand Up @@ -802,21 +737,13 @@ private void SetStyledValue(AvaloniaProperty property, object value, BindingPrio
originalValue?.GetType().FullName ?? "(null)"));
}

PriorityValue v;

if (!_values.TryGetValue(property, out v))
if (_values == null)
{
if (value == AvaloniaProperty.UnsetValue)
{
return;
}

v = CreatePriorityValue(property);
_values.Add(property, v);
_values = new ValueStore(this);
}

LogPropertySet(property, value, priority);
v.SetValue(value, (int)priority);
_values.AddValue(property, value, (int)priority);
}

/// <summary>
Expand Down
9 changes: 5 additions & 4 deletions src/Avalonia.Base/IPriorityValueOwner.cs
Expand Up @@ -13,18 +13,19 @@ internal interface IPriorityValueOwner
/// <summary>
/// Called when a <see cref="PriorityValue"/>'s value changes.
/// </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="newValue">The new value.</param>
void Changed(PriorityValue sender, object oldValue, object newValue);
void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue);

/// <summary>
/// Called when a <see cref="BindingNotification"/> is received by a
/// <see cref="PriorityValue"/>.
/// </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>
void BindingNotificationReceived(PriorityValue sender, BindingNotification notification);
void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification);

/// <summary>
/// Ensures that the current thread is the UI thread.
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Base/PriorityValue.cs
Expand Up @@ -281,12 +281,12 @@ private void UpdateValue(object value, int priority)

if (notification == null || notification.HasValue)
{
notify(() => Owner?.Changed(this, old, Value));
notify(() => Owner?.Changed(Property, ValuePriority, old, Value));
}

if (notification != null)
{
Owner?.BindingNotificationReceived(this, notification);
Owner?.BindingNotificationReceived(Property, notification);
}
}
else
Expand Down

0 comments on commit 5d8b084

Please sign in to comment.