/// <summary>
        /// Registers a <see cref="AvaloniaProperty"/> on a type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="property">The property.</param>
        /// <remarks>
        /// You won't usually want to call this method directly, instead use the
        /// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TOwner, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
        /// method.
        /// </remarks>
        public void Register(Type type, AvaloniaProperty property)
        {
            Contract.Requires <ArgumentNullException>(type != null);
            Contract.Requires <ArgumentNullException>(property != null);

            if (!_registered.TryGetValue(type, out var inner))
            {
                inner = new Dictionary <int, AvaloniaProperty>();
                inner.Add(property.Id, property);
                _registered.Add(type, inner);
            }
            else if (!inner.ContainsKey(property.Id))
            {
                inner.Add(property.Id, property);
            }

            if (!_properties.ContainsKey(property.Id))
            {
                _properties.Add(property.Id, property);
            }

            _registeredCache.Clear();
        }
Beispiel #2
0
        internal void PriorityValueChanged(AvaloniaProperty property, int priority, object oldValue, object newValue)
        {
            oldValue = (oldValue == AvaloniaProperty.UnsetValue) ?
                       GetDefaultValue(property) :
                       oldValue;
            newValue = (newValue == AvaloniaProperty.UnsetValue) ?
                       GetDefaultValue(property) :
                       newValue;

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

                Logger.Verbose(
                    LogArea.Property,
                    this,
                    "{Property} changed from {$Old} to {$Value} with priority {Priority}",
                    property,
                    oldValue,
                    newValue,
                    (BindingPriority)priority);
            }
        }
Beispiel #3
0
        private void AddValueInternal(AvaloniaProperty property, object value)
        {
            Entry[] entries = new Entry[_entries.Length + 1];

            for (int i = 0; i < _entries.Length; ++i)
            {
                if (_entries[i].PropertyId > property.Id)
                {
                    if (i > 0)
                    {
                        Array.Copy(_entries, 0, entries, 0, i);
                    }

                    entries[i] = new Entry {
                        PropertyId = property.Id, Value = value
                    };
                    Array.Copy(_entries, i, entries, i + 1, _entries.Length - i);
                    break;
                }
            }

            _entries = entries;
        }
Beispiel #4
0
        /// <summary>
        /// Sets the value of a styled property.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <param name="value">The value.</param>
        /// <param name="priority">The priority of the value.</param>
        private void SetStyledValue(AvaloniaProperty property, object value, BindingPriority priority)
        {
            var notification = value as BindingNotification;

            // We currently accept BindingNotifications for non-direct properties but we just
            // strip them to their underlying value.
            if (notification != null)
            {
                if (!notification.HasValue)
                {
                    return;
                }
                else
                {
                    value = notification.Value;
                }
            }

            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 == null)
            {
                _values = new ValueStore(this);
            }

            LogPropertySet(property, value, priority);
            _values.AddValue(property, value, (int)priority);
        }
        /// <summary>
        /// Registers an attached <see cref="AvaloniaProperty"/> on a type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="property">The property.</param>
        /// <remarks>
        /// You won't usually want to call this method directly, instead use the
        /// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{THost, TValue, TValue})"/>
        /// method.
        /// </remarks>
        public void RegisterAttached(Type type, AvaloniaProperty property)
        {
            Contract.Requires <ArgumentNullException>(type != null);
            Contract.Requires <ArgumentNullException>(property != null);

            if (!property.IsAttached)
            {
                throw new InvalidOperationException(
                          "Cannot register a non-attached property as attached.");
            }

            if (!_attached.TryGetValue(type, out var inner))
            {
                inner = new Dictionary <int, AvaloniaProperty>();
                inner.Add(property.Id, property);
                _attached.Add(type, inner);
            }
            else
            {
                inner.Add(property.Id, property);
            }

            _attachedCache.Clear();
        }
Beispiel #6
0
        /// <summary>
        /// Sets a property value for a direct property binding.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        private void DirectBindingSet(AvaloniaProperty property, object value)
        {
            var error = value as BindingError;

            if (error == null)
            {
                SetValue(property, value);
            }
            else
            {
                if (error.UseFallbackValue)
                {
                    SetValue(property, error.FallbackValue);
                }

                Logger.Error(
                    LogArea.Binding,
                    this,
                    "Error binding to {Target}.{Property}: {Message}",
                    this,
                    property,
                    error.Exception.Message);
            }
        }
Beispiel #7
0
        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event.
        /// </summary>
        /// <param name="property">The property that has changed.</param>
        /// <param name="oldValue">The old property value.</param>
        /// <param name="newValue">The new property value.</param>
        /// <param name="priority">The priority of the binding that produced the value.</param>
        protected void RaisePropertyChanged(
            AvaloniaProperty property,
            object oldValue,
            object newValue,
            BindingPriority priority = BindingPriority.LocalValue)
        {
            Contract.Requires <ArgumentNullException>(property != null);
            VerifyAccess();

            AvaloniaPropertyChangedEventArgs e = new AvaloniaPropertyChangedEventArgs(
                this,
                property,
                oldValue,
                newValue,
                priority);

            property.Notifying?.Invoke(this, true);

            try
            {
                OnPropertyChanged(e);
                property.NotifyChanged(e);

                _propertyChanged?.Invoke(this, e);

                if (_inpcChanged != null)
                {
                    PropertyChangedEventArgs e2 = new PropertyChangedEventArgs(property.Name);
                    _inpcChanged(this, e2);
                }
            }
            finally
            {
                property.Notifying?.Invoke(this, false);
            }
        }
Beispiel #8
0
        /// <summary>
        /// Gets a <see cref="AvaloniaProperty"/> value.
        /// </summary>
        /// <typeparam name="T">The type of the property.</typeparam>
        /// <param name="property">The property.</param>
        /// <returns>The value.</returns>
        public T GetValue <T>(AvaloniaProperty <T> property)
        {
            Contract.Requires <ArgumentNullException>(property != null);

            return((T)GetValue((AvaloniaProperty)property));
        }
Beispiel #9
0
 /// <summary>
 /// Throws an exception indicating that the specified property is not registered on this
 /// object.
 /// </summary>
 /// <param name="p">The property</param>
 private void ThrowNotRegistered(AvaloniaProperty p)
 {
     throw new ArgumentException($"Property '{p.Name} not registered on '{this.GetType()}");
 }
Beispiel #10
0
        /// <summary>
        /// Binds a <see cref="AvaloniaProperty"/> to an observable.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <param name="source">The observable.</param>
        /// <param name="priority">The priority of the binding.</param>
        /// <returns>
        /// A disposable which can be used to terminate the binding.
        /// </returns>
        public IDisposable Bind(
            AvaloniaProperty property,
            IObservable <object> source,
            BindingPriority priority = BindingPriority.LocalValue)
        {
            Contract.Requires <ArgumentNullException>(property != null);
            Contract.Requires <ArgumentNullException>(source != null);

            VerifyAccess();

            var description = GetDescription(source);

            var scheduler = AvaloniaLocator.Current.GetService <IScheduler>() ?? ImmediateScheduler.Instance;

            source = source.ObserveOn(scheduler);

            if (property.IsDirect)
            {
                if (property.IsReadOnly)
                {
                    throw new ArgumentException($"The property {property.Name} is readonly.");
                }

                Logger.Verbose(
                    LogArea.Property,
                    this,
                    "Bound {Property} to {Binding} with priority LocalValue",
                    property,
                    description);

                IDisposable subscription = null;

                if (_directBindings == null)
                {
                    _directBindings = new List <IDisposable>();
                }

                subscription = source
                               .Select(x => CastOrDefault(x, property.PropertyType))
                               .Do(_ => { }, () => _directBindings.Remove(subscription))
                               .Subscribe(x => SetDirectValue(property, x));

                _directBindings.Add(subscription);

                return(Disposable.Create(() =>
                {
                    subscription.Dispose();
                    _directBindings.Remove(subscription);
                }));
            }
            else
            {
                PriorityValue v;

                if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
                {
                    ThrowNotRegistered(property);
                }

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

                Logger.Verbose(
                    LogArea.Property,
                    this,
                    "Bound {Property} to {Binding} with priority {Priority}",
                    property,
                    description,
                    priority);

                return(v.Add(source, (int)priority));
            }
        }
 /// <summary>
 /// Gets a description of a property that van be used in observables.
 /// </summary>
 /// <param name="o">The object.</param>
 /// <param name="property">The property</param>
 /// <returns>The description.</returns>
 private static string GetDescription(IAvaloniaObject o, AvaloniaProperty property)
 {
     return($"{o.GetType().Name}.{property.Name}");
 }
 /// <summary>
 /// Checks whether a <see cref="AvaloniaProperty"/> is registered on a object.
 /// </summary>
 /// <param name="o">The object.</param>
 /// <param name="property">The property.</param>
 /// <returns>True if the property is registered, otherwise false.</returns>
 public bool IsRegistered(object o, AvaloniaProperty property)
 {
     return(IsRegistered(o.GetType(), property));
 }
Beispiel #13
0
 /// <summary>
 /// Gets an observable for a <see cref="AvaloniaProperty"/>.
 /// </summary>
 /// <param name="o">The object.</param>
 /// <typeparam name="T">The property type.</typeparam>
 /// <param name="property">The property.</param>
 /// <returns>
 /// An observable which fires immediately with the current value of the property on the
 /// object and subsequently each time the property value changes.
 /// </returns>
 /// <remarks>
 /// The subscription to <paramref name="o"/> is created using a weak reference.
 /// </remarks>
 public static IObservable <T> GetObservable <T>(this IAvaloniaObject o, AvaloniaProperty <T> property)
 {
     return(new AvaloniaPropertyObservable <T>(
                o ?? throw new ArgumentNullException(nameof(o)),
                property ?? throw new ArgumentNullException(nameof(property))));
 }
Beispiel #14
0
 internal void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
 {
     UpdateDataValidation(property, notification);
 }
Beispiel #15
0
        public void ClearValue(AvaloniaProperty property)
        {
            property = property ?? throw new ArgumentNullException(nameof(property));

            property.RouteClearValue(this);
        }
Beispiel #16
0
        /// <summary>
        /// Binds a <see cref="AvaloniaProperty"/> to an observable.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <param name="source">The observable.</param>
        /// <param name="priority">The priority of the binding.</param>
        /// <returns>
        /// A disposable which can be used to terminate the binding.
        /// </returns>
        public IDisposable Bind(
            AvaloniaProperty property,
            IObservable <object> source,
            BindingPriority priority = BindingPriority.LocalValue)
        {
            Contract.Requires <ArgumentNullException>(property != null);
            VerifyAccess();

            if (property.IsDirect)
            {
                if (property.IsReadOnly)
                {
                    throw new ArgumentException($"The property {property.Name} is readonly.");
                }

                Logger.Verbose(
                    LogArea.Property,
                    this,
                    "Bound {Property} to {Binding} with priority LocalValue",
                    property,
                    GetDescription(source));

                IDisposable subscription          = null;
                IDisposable validationSubcription = null;

                if (_directBindings == null)
                {
                    _directBindings = new List <IDisposable>();
                }

                subscription = source
                               .Where(x => !(x is IValidationStatus))
                               .Select(x => CastOrDefault(x, property.PropertyType))
                               .Do(_ => { }, () => _directBindings.Remove(subscription))
                               .Subscribe(x => DirectBindingSet(property, x));
                validationSubcription = source
                                        .OfType <IValidationStatus>()
                                        .Subscribe(x => DataValidationChanged(property, x));

                _directBindings.Add(subscription);

                return(Disposable.Create(() =>
                {
                    validationSubcription.Dispose();
                    subscription.Dispose();
                    _directBindings.Remove(subscription);
                }));
            }
            else
            {
                PriorityValue v;

                if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
                {
                    ThrowNotRegistered(property);
                }

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

                Logger.Verbose(
                    LogArea.Property,
                    this,
                    "Bound {Property} to {Binding} with priority {Priority}",
                    property,
                    GetDescription(source),
                    priority);

                return(v.Add(source, (int)priority));
            }
        }
Beispiel #17
0
 /// <summary>
 /// Called when the validation state on a tracked property is changed.
 /// </summary>
 /// <param name="property">The property whose validation state changed.</param>
 /// <param name="status">The new validation state.</param>
 protected virtual void DataValidationChanged(AvaloniaProperty property, IValidationStatus status)
 {
 }
Beispiel #18
0
        /// <summary>
        /// Clears a <see cref="AvaloniaProperty"/>'s local value.
        /// </summary>
        /// <param name="property">The property.</param>
        public void ClearValue(AvaloniaProperty property)
        {
            Contract.Requires <ArgumentNullException>(property != null);

            SetValue(property, AvaloniaProperty.UnsetValue);
        }
Beispiel #19
0
 /// <summary>
 /// Called to update the validation state for properties for which data validation is
 /// enabled.
 /// </summary>
 /// <param name="property">The property.</param>
 /// <param name="value">The new binding value for the property.</param>
 protected virtual void UpdateDataValidation <T>(
     AvaloniaProperty <T> property,
     BindingValue <T> value)
 {
 }
Beispiel #20
0
 void IValueSink.Completed(AvaloniaProperty property, IPriorityValueEntry entry)
 {
 }
Beispiel #21
0
 /// <summary>
 /// Forces the specified property to be revalidated.
 /// </summary>
 /// <param name="property">The property.</param>
 public void Revalidate(AvaloniaProperty property)
 {
     VerifyAccess();
     _values?.Revalidate(property);
 }
Beispiel #22
0
 public void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue)
 {
     _owner.PriorityValueChanged(property, priority, oldValue, newValue);
 }
Beispiel #23
0
 public void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
 {
     _owner.BindingNotificationReceived(property, notification);
 }
 /// <summary>
 /// Checks whether a <see cref="AvaloniaProperty"/> is registered on a type.
 /// </summary>
 /// <param name="type">The type.</param>
 /// <param name="property">The property.</param>
 /// <returns>True if the property is registered, otherwise false.</returns>
 public bool IsRegistered(Type type, AvaloniaProperty property)
 {
     return(FindRegistered(type, property) != null);
 }
Beispiel #25
0
 /// <summary>
 /// Called to update the validation state for properties for which data validation is
 /// enabled.
 /// </summary>
 /// <param name="property">The property.</param>
 /// <param name="status">The new validation status.</param>
 protected virtual void UpdateDataValidation(
     AvaloniaProperty property,
     BindingNotification status)
 {
 }
Beispiel #26
0
 /// <inheritdoc/>
 void IPriorityValueOwner.BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
 {
     UpdateDataValidation(property, notification);
 }
Beispiel #27
0
 public object this[AvaloniaProperty property]
 {
     get { return(GetValue(property)); }
     set { SetValue(property, value); }
 }
Beispiel #28
0
 /// <summary>
 /// Converts an unset value to the default value for a direct property.
 /// </summary>
 /// <param name="value">The value.</param>
 /// <param name="property">The property.</param>
 /// <returns>The value.</returns>
 private object DirectUnsetToDefault(object value, AvaloniaProperty property)
 {
     return(value == AvaloniaProperty.UnsetValue ?
            ((IDirectPropertyMetadata)property.GetMetadata(GetType())).UnsetValue :
            value);
 }
Beispiel #29
0
        public object GetValue(AvaloniaProperty property)
        {
            property = property ?? throw new ArgumentNullException(nameof(property));

            return(property.RouteGetValue(this));
        }
 /// <summary>
 /// Finds <see cref="AvaloniaProperty"/> registered on an object.
 /// </summary>
 /// <param name="o">The object.</param>
 /// <param name="property">The property.</param>
 /// <returns>The registered property or null if not found.</returns>
 /// <remarks>
 /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a
 /// different object but is equal according to <see cref="object.Equals(object)"/>.
 /// </remarks>
 public AvaloniaProperty FindRegistered(object o, AvaloniaProperty property)
 {
     return(FindRegistered(o.GetType(), property));
 }