Pārlūkot izejas kodu

Abstracted storage of styled values into ValueStore.

Steven Kirk 7 gadi atpakaļ
vecāks
revīzija
27daffeb3e

+ 18 - 106
src/Avalonia.Base/AvaloniaObject.cs

@@ -32,8 +32,7 @@ namespace Avalonia
         /// <summary>
         /// The set values/bindings on this object.
         /// </summary>
-        private readonly Dictionary<AvaloniaProperty, PriorityValue> _values =
-            new Dictionary<AvaloniaProperty, PriorityValue>();
+        private readonly ValueStore _values;
 
         /// <summary>
         /// Maintains a list of direct property binding subscriptions so that the binding source
@@ -73,6 +72,8 @@ namespace Avalonia
         {
             VerifyAccess();
 
+            _values =  new ValueStore(this);
+
             void Notify(AvaloniaProperty property)
             {
                 object value = property.IsDirect ?
@@ -230,7 +231,7 @@ namespace Avalonia
             }
             else
             {
-                return GetValueInternal(property);
+                return _values.GetValue(property);
             }
         }
 
@@ -257,7 +258,7 @@ namespace Avalonia
             Contract.Requires<ArgumentNullException>(property != null);
             VerifyAccess();
 
-            return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false;
+            return _values.IsAnimating(property);
         }
 
         /// <summary>
@@ -274,14 +275,7 @@ namespace Avalonia
             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);
         }
 
         /// <summary>
@@ -369,14 +363,6 @@ namespace Avalonia
             }
             else
             {
-                PriorityValue v;
-
-                if (!_values.TryGetValue(property, out v))
-                {
-                    v = CreatePriorityValue(property);
-                    _values.Add(property, v);
-                }
-
                 Logger.Verbose(
                     LogArea.Property,
                     this,
@@ -385,7 +371,7 @@ namespace Avalonia
                     description,
                     priority);
 
-                return v.Add(source, (int)priority);
+                return _values.AddBinding(property, source, priority);
             }
         }
 
@@ -416,20 +402,12 @@ namespace Avalonia
         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;
@@ -439,7 +417,7 @@ namespace Avalonia
 
             if (!Equals(oldValue, newValue))
             {
-                RaisePropertyChanged(property, oldValue, newValue, priority);
+                RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority);
 
                 Logger.Verbose(
                     LogArea.Property,
@@ -448,14 +426,14 @@ namespace Avalonia
                     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/>
@@ -468,10 +446,7 @@ namespace Avalonia
         /// 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.
@@ -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>
         /// 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._values.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>
@@ -801,22 +726,9 @@ namespace Avalonia
                     originalValue,
                     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);
-            v.SetValue(value, (int)priority);
+            _values.AddValue(property, value, (int)priority);
         }
 
         /// <summary>

+ 5 - 4
src/Avalonia.Base/IPriorityValueOwner.cs

@@ -13,18 +13,19 @@ namespace Avalonia
         /// <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.

+ 2 - 2
src/Avalonia.Base/PriorityValue.cs

@@ -281,12 +281,12 @@ namespace Avalonia
 
                 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

+ 134 - 0
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;
+        }
+    }
+}

+ 2 - 2
tests/Avalonia.Base.UnitTests/PriorityValueTests.cs

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