/// <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(); }
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); } }
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; }
/// <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(); }
/// <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); } }
/// <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); } }
/// <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)); }
/// <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()}"); }
/// <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)); }
/// <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)))); }
internal void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification) { UpdateDataValidation(property, notification); }
public void ClearValue(AvaloniaProperty property) { property = property ?? throw new ArgumentNullException(nameof(property)); property.RouteClearValue(this); }
/// <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)); } }
/// <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) { }
/// <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); }
/// <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) { }
void IValueSink.Completed(AvaloniaProperty property, IPriorityValueEntry entry) { }
/// <summary> /// Forces the specified property to be revalidated. /// </summary> /// <param name="property">The property.</param> public void Revalidate(AvaloniaProperty property) { VerifyAccess(); _values?.Revalidate(property); }
public void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue) { _owner.PriorityValueChanged(property, priority, oldValue, newValue); }
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); }
/// <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) { }
/// <inheritdoc/> void IPriorityValueOwner.BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification) { UpdateDataValidation(property, notification); }
public object this[AvaloniaProperty property] { get { return(GetValue(property)); } set { SetValue(property, value); } }
/// <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); }
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)); }