diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index dc94dfba40..1946d4ba5c 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -132,7 +132,7 @@ namespace Avalonia switch (property) { - case StyledPropertyBase styled: + case StyledProperty styled: ClearValue(styled); break; case DirectPropertyBase direct: @@ -147,7 +147,7 @@ namespace Avalonia /// Clears a 's local value. /// /// The property. - public void ClearValue(StyledPropertyBase property) + public void ClearValue(StyledProperty property) { property = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); @@ -220,7 +220,7 @@ namespace Avalonia /// The type of the property. /// The property. /// The value. - public T GetValue(StyledPropertyBase property) + public T GetValue(StyledProperty property) { _ = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); @@ -243,7 +243,7 @@ namespace Avalonia } /// - public Optional GetBaseValue(StyledPropertyBase property) + public Optional GetBaseValue(StyledProperty property) { _ = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); @@ -309,7 +309,7 @@ namespace Avalonia /// An if setting the property can be undone, otherwise null. /// public IDisposable? SetValue( - StyledPropertyBase property, + StyledProperty property, T value, BindingPriority priority = BindingPriority.LocalValue) { @@ -373,7 +373,7 @@ namespace Avalonia /// A disposable which can be used to terminate the binding. /// public IDisposable Bind( - StyledPropertyBase property, + StyledProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { @@ -396,7 +396,7 @@ namespace Avalonia /// A disposable which can be used to terminate the binding. /// public IDisposable Bind( - StyledPropertyBase property, + StyledProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { @@ -419,7 +419,7 @@ namespace Avalonia /// A disposable which can be used to terminate the binding. /// public IDisposable Bind( - StyledPropertyBase property, + StyledProperty property, IObservable> source, BindingPriority priority = BindingPriority.LocalValue) { diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 7b17b9152d..6231483ff8 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -146,7 +146,7 @@ namespace Avalonia return property switch { - StyledPropertyBase styled => target.Bind(styled, source, priority), + StyledProperty styled => target.Bind(styled, source, priority), DirectPropertyBase direct => target.Bind(direct, source), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."), }; @@ -170,7 +170,7 @@ namespace Avalonia { return property switch { - StyledPropertyBase styled => target.Bind(styled, source, priority), + StyledProperty styled => target.Bind(styled, source, priority), DirectPropertyBase direct => target.Bind(direct, source), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."), }; @@ -231,7 +231,7 @@ namespace Avalonia return property switch { - StyledPropertyBase styled => target.GetValue(styled), + StyledProperty styled => target.GetValue(styled), DirectPropertyBase direct => target.GetValue(direct), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.") }; @@ -280,7 +280,7 @@ namespace Avalonia return property switch { - StyledPropertyBase styled => target.GetBaseValue(styled), + StyledProperty styled => target.GetBaseValue(styled), DirectPropertyBase direct => target.GetValue(direct), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.") }; diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs index 93fffb3755..3e20dcce56 100644 --- a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs +++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs @@ -19,7 +19,7 @@ namespace Avalonia.PropertyStore private T? _baseValue; private UncommonFields? _uncommon; - public EffectiveValue(AvaloniaObject owner, StyledPropertyBase property) + public EffectiveValue(AvaloniaObject owner, StyledProperty property) { Priority = BindingPriority.Unset; BasePriority = BindingPriority.Unset; @@ -57,12 +57,12 @@ namespace Avalonia.PropertyStore Debug.Assert(priority != BindingPriority.LocalValue); UpdateValueEntry(value, priority); - SetAndRaiseCore(owner, (StyledPropertyBase)value.Property, GetValue(value), priority); + SetAndRaiseCore(owner, (StyledProperty)value.Property, GetValue(value), priority); } public void SetLocalValueAndRaise( ValueStore owner, - StyledPropertyBase property, + StyledProperty property, T value) { SetAndRaiseCore(owner, property, value, BindingPriority.LocalValue); @@ -82,7 +82,7 @@ namespace Avalonia.PropertyStore { Debug.Assert(oldValue is not null || newValue is not null); - var p = (StyledPropertyBase)property; + var p = (StyledProperty)property; var o = oldValue is not null ? ((EffectiveValue)oldValue).Value : _metadata.DefaultValue; var n = newValue is not null ? ((EffectiveValue)newValue).Value : _metadata.DefaultValue; var priority = newValue is not null ? BindingPriority.Inherited : BindingPriority.Unset; @@ -98,7 +98,7 @@ namespace Avalonia.PropertyStore Debug.Assert(Priority != BindingPriority.Animation); Debug.Assert(BasePriority != BindingPriority.Unset); UpdateValueEntry(null, BindingPriority.Animation); - SetAndRaiseCore(owner, (StyledPropertyBase)property, _baseValue!, BasePriority); + SetAndRaiseCore(owner, (StyledProperty)property, _baseValue!, BasePriority); } public override void CoerceValue(ValueStore owner, AvaloniaProperty property) @@ -107,7 +107,7 @@ namespace Avalonia.PropertyStore return; SetAndRaiseCore( owner, - (StyledPropertyBase)property, + (StyledProperty)property, _uncommon._uncoercedValue!, Priority, _uncommon._uncoercedBaseValue!, @@ -117,10 +117,10 @@ namespace Avalonia.PropertyStore public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property) { UnsubscribeValueEntries(); - DisposeAndRaiseUnset(owner, (StyledPropertyBase)property); + DisposeAndRaiseUnset(owner, (StyledProperty)property); } - public void DisposeAndRaiseUnset(ValueStore owner, StyledPropertyBase property) + public void DisposeAndRaiseUnset(ValueStore owner, StyledProperty property) { BindingPriority priority; T oldValue; @@ -156,7 +156,7 @@ namespace Avalonia.PropertyStore private void SetAndRaiseCore( ValueStore owner, - StyledPropertyBase property, + StyledProperty property, T value, BindingPriority priority) { @@ -203,7 +203,7 @@ namespace Avalonia.PropertyStore private void SetAndRaiseCore( ValueStore owner, - StyledPropertyBase property, + StyledProperty property, T value, BindingPriority priority, T baseValue, diff --git a/src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs b/src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs index 364b4e1225..d8a353dc70 100644 --- a/src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs @@ -9,7 +9,7 @@ namespace Avalonia.PropertyStore public ImmediateValueEntry( ImmediateValueFrame owner, - StyledPropertyBase property, + StyledProperty property, T value) { _owner = owner; @@ -17,7 +17,7 @@ namespace Avalonia.PropertyStore Property = property; } - public StyledPropertyBase Property { get; } + public StyledProperty Property { get; } public bool HasValue => true; AvaloniaProperty IValueEntry.Property => Property; diff --git a/src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs b/src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs index 50d5333b9f..7e9f3ab312 100644 --- a/src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs +++ b/src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs @@ -15,7 +15,7 @@ namespace Avalonia.PropertyStore } public TypedBindingEntry AddBinding( - StyledPropertyBase property, + StyledProperty property, IObservable> source) { var e = new TypedBindingEntry(this, property, source); @@ -24,7 +24,7 @@ namespace Avalonia.PropertyStore } public TypedBindingEntry AddBinding( - StyledPropertyBase property, + StyledProperty property, IObservable source) { var e = new TypedBindingEntry(this, property, source); @@ -33,7 +33,7 @@ namespace Avalonia.PropertyStore } public SourceUntypedBindingEntry AddBinding( - StyledPropertyBase property, + StyledProperty property, IObservable source) { var e = new SourceUntypedBindingEntry(this, property, source); @@ -41,7 +41,7 @@ namespace Avalonia.PropertyStore return e; } - public ImmediateValueEntry AddValue(StyledPropertyBase property, T value) + public ImmediateValueEntry AddValue(StyledProperty property, T value) { var e = new ImmediateValueEntry(this, property, value); Add(e); diff --git a/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs b/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs index 8acb885604..f89cb029b6 100644 --- a/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs +++ b/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs @@ -11,13 +11,13 @@ namespace Avalonia.PropertyStore private readonly ValueStore _owner; private IDisposable? _subscription; - public LocalValueBindingObserver(ValueStore owner, StyledPropertyBase property) + public LocalValueBindingObserver(ValueStore owner, StyledProperty property) { _owner = owner; Property = property; } - public StyledPropertyBase Property { get;} + public StyledProperty Property { get;} public void Start(IObservable source) { @@ -41,7 +41,7 @@ namespace Avalonia.PropertyStore public void OnNext(T value) { - static void Execute(ValueStore owner, StyledPropertyBase property, T value) + static void Execute(ValueStore owner, StyledProperty property, T value) { if (property.ValidateValue?.Invoke(value) != false) owner.SetValue(property, value, BindingPriority.LocalValue); diff --git a/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs b/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs index 7c529591b6..2d157b2519 100644 --- a/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs +++ b/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs @@ -11,13 +11,13 @@ namespace Avalonia.PropertyStore private readonly ValueStore _owner; private IDisposable? _subscription; - public LocalValueUntypedBindingObserver(ValueStore owner, StyledPropertyBase property) + public LocalValueUntypedBindingObserver(ValueStore owner, StyledProperty property) { _owner = owner; Property = property; } - public StyledPropertyBase Property { get; } + public StyledProperty Property { get; } public void Start(IObservable source) { diff --git a/src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs b/src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs index b4ac06d2bf..b56d0d4529 100644 --- a/src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs @@ -13,14 +13,14 @@ namespace Avalonia.PropertyStore public SourceUntypedBindingEntry( ValueFrame frame, - StyledPropertyBase property, + StyledProperty property, IObservable source) : base(frame, property, source) { _validate = property.ValidateValue; } - public new StyledPropertyBase Property => (StyledPropertyBase)base.Property; + public new StyledProperty Property => (StyledProperty)base.Property; protected override BindingValue ConvertAndValidate(object? value) { diff --git a/src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs b/src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs index 2276991a18..697725c87b 100644 --- a/src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs @@ -11,7 +11,7 @@ namespace Avalonia.PropertyStore { public TypedBindingEntry( ValueFrame frame, - StyledPropertyBase property, + StyledProperty property, IObservable source) : base(frame, property, source) { @@ -19,13 +19,13 @@ namespace Avalonia.PropertyStore public TypedBindingEntry( ValueFrame frame, - StyledPropertyBase property, + StyledProperty property, IObservable> source) : base(frame, property, source) { } - public new StyledPropertyBase Property => (StyledPropertyBase)base.Property; + public new StyledProperty Property => (StyledProperty)base.Property; protected override BindingValue ConvertAndValidate(T value) { diff --git a/src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs b/src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs index 5c5591dcb5..372a808fb2 100644 --- a/src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs +++ b/src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs @@ -26,7 +26,7 @@ namespace Avalonia.PropertyStore [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] public static bool TryConvertAndValidate( - StyledPropertyBase property, + StyledProperty property, object? value, [MaybeNullWhen(false)] out T result) { diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index 92e5288255..f36a96992b 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -43,7 +43,7 @@ namespace Avalonia.PropertyStore } public IDisposable AddBinding( - StyledPropertyBase property, + StyledProperty property, IObservable> source, BindingPriority priority) { @@ -71,7 +71,7 @@ namespace Avalonia.PropertyStore } public IDisposable AddBinding( - StyledPropertyBase property, + StyledProperty property, IObservable source, BindingPriority priority) { @@ -99,7 +99,7 @@ namespace Avalonia.PropertyStore } public IDisposable AddBinding( - StyledPropertyBase property, + StyledProperty property, IObservable source, BindingPriority priority) { @@ -165,7 +165,7 @@ namespace Avalonia.PropertyStore } } - public IDisposable? SetValue(StyledPropertyBase property, T value, BindingPriority priority) + public IDisposable? SetValue(StyledProperty property, T value, BindingPriority priority) { if (property.ValidateValue?.Invoke(value) == false) { @@ -219,7 +219,7 @@ namespace Avalonia.PropertyStore return GetDefaultValue(property); } - public T GetValue(StyledPropertyBase property) + public T GetValue(StyledProperty property) { if (_effectiveValues.TryGetValue(property, out var v)) return ((EffectiveValue)v).Value; @@ -248,7 +248,7 @@ namespace Avalonia.PropertyStore v.CoerceValue(this, property); } - public Optional GetBaseValue(StyledPropertyBase property) + public Optional GetBaseValue(StyledProperty property) { if (TryGetEffectiveValue(property, out var v) && ((EffectiveValue)v).TryGetBaseValue(out var baseValue)) @@ -450,7 +450,7 @@ namespace Avalonia.PropertyStore /// The old value of the property. /// The effective value instance. public void OnInheritedEffectiveValueChanged( - StyledPropertyBase property, + StyledProperty property, T oldValue, EffectiveValue value) { @@ -475,7 +475,7 @@ namespace Avalonia.PropertyStore /// /// The property whose value changed. /// The old value of the property. - public void OnInheritedEffectiveValueDisposed(StyledPropertyBase property, T oldValue) + public void OnInheritedEffectiveValueDisposed(StyledProperty property, T oldValue) { Debug.Assert(property.Inherits); @@ -520,7 +520,7 @@ namespace Avalonia.PropertyStore /// The old value of the property. /// The new value of the property. public void OnAncestorInheritedValueChanged( - StyledPropertyBase property, + StyledProperty property, T oldValue, T newValue) { diff --git a/src/Avalonia.Base/StyledProperty.cs b/src/Avalonia.Base/StyledProperty.cs index 019ed09c20..79d1b9202d 100644 --- a/src/Avalonia.Base/StyledProperty.cs +++ b/src/Avalonia.Base/StyledProperty.cs @@ -1,14 +1,18 @@ using System; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Data; +using Avalonia.PropertyStore; +using Avalonia.Utilities; namespace Avalonia { /// /// A styled avalonia property. /// - public class StyledProperty : StyledPropertyBase + public class StyledProperty : AvaloniaProperty, IStyledPropertyAccessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The name of the property. /// The type of the class that registers the property. @@ -23,20 +27,30 @@ namespace Avalonia bool inherits = false, Func? validate = null, Action? notifying = null) - : base(name, ownerType, metadata, inherits, validate, notifying) + : base(name, ownerType, metadata, notifying) { + Inherits = inherits; + ValidateValue = validate; + HasCoercion |= metadata.CoerceValue != null; + + if (validate?.Invoke(metadata.DefaultValue) == false) + { + throw new ArgumentException( + $"'{metadata.DefaultValue}' is not a valid default value for '{name}'."); + } } /// - /// Initializes a new instance of the class. + /// Gets the value validation callback for the property. /// - /// The property to add the owner to. - /// The type of the class that registers the property. - internal StyledProperty(StyledPropertyBase source, Type ownerType) - : base(source, ownerType) - { - } - + public Func? ValidateValue { get; } + + /// + /// Gets a value indicating whether this property has any value coercion callbacks defined + /// in its metadata. + /// + internal bool HasCoercion { get; private set; } + /// /// Registers the property on another type. /// @@ -47,5 +61,177 @@ namespace Avalonia AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this); return this; } + + public TValue CoerceValue(AvaloniaObject instance, TValue baseValue) + { + var metadata = GetMetadata(instance.GetType()); + + if (metadata.CoerceValue != null) + { + return metadata.CoerceValue.Invoke(instance, baseValue); + } + + return baseValue; + } + + /// + /// Gets the default value for the property on the specified type. + /// + /// The type. + /// The default value. + public TValue GetDefaultValue(Type type) + { + return GetMetadata(type).DefaultValue; + } + + /// + /// Gets the property metadata for the specified type. + /// + /// The type. + /// + /// The property metadata. + /// + public new StyledPropertyMetadata GetMetadata(Type type) + { + _ = type ?? throw new ArgumentNullException(nameof(type)); + return (StyledPropertyMetadata)base.GetMetadata(type); + } + + /// + /// Overrides the default value for the property on the specified type. + /// + /// The type. + /// The default value. + public void OverrideDefaultValue(TValue defaultValue) where T : AvaloniaObject + { + OverrideDefaultValue(typeof(T), defaultValue); + } + + /// + /// Overrides the default value for the property on the specified type. + /// + /// The type. + /// The default value. + public void OverrideDefaultValue(Type type, TValue defaultValue) + { + OverrideMetadata(type, new StyledPropertyMetadata(defaultValue)); + } + + /// + /// Overrides the metadata for the property on the specified type. + /// + /// The type. + /// The metadata. + public void OverrideMetadata(StyledPropertyMetadata metadata) where T : AvaloniaObject + { + base.OverrideMetadata(typeof(T), metadata); + } + + /// + /// Overrides the metadata for the property on the specified type. + /// + /// The type. + /// The metadata. + public void OverrideMetadata(Type type, StyledPropertyMetadata metadata) + { + if (ValidateValue != null) + { + if (!ValidateValue(metadata.DefaultValue)) + { + throw new ArgumentException( + $"'{metadata.DefaultValue}' is not a valid default value for '{Name}'."); + } + } + + HasCoercion |= metadata.CoerceValue != null; + + base.OverrideMetadata(type, metadata); + } + + /// + /// Gets the string representation of the property. + /// + /// The property's string representation. + public override string ToString() + { + return Name; + } + + /// + object? IStyledPropertyAccessor.GetDefaultValue(Type type) => GetDefaultBoxedValue(type); + + bool IStyledPropertyAccessor.ValidateValue(object? value) + { + if (value is null && !typeof(TValue).IsValueType) + return ValidateValue?.Invoke(default!) ?? true; + if (value is TValue typed) + return ValidateValue?.Invoke(typed) ?? true; + return false; + } + + internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) + { + return new EffectiveValue(o, this); + } + + /// + internal override void RouteClearValue(AvaloniaObject o) + { + o.ClearValue(this); + } + + /// + internal override object? RouteGetValue(AvaloniaObject o) + { + return o.GetValue(this); + } + + /// + internal override object? RouteGetBaseValue(AvaloniaObject o) + { + var value = o.GetBaseValue(this); + return value.HasValue ? value.Value : AvaloniaProperty.UnsetValue; + } + + /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] + internal override IDisposable? RouteSetValue( + AvaloniaObject target, + object? value, + BindingPriority priority) + { + if (value == BindingOperations.DoNothing) + { + return null; + } + else if (value == UnsetValue) + { + target.ClearValue(this); + return null; + } + else if (TypeUtilities.TryConvertImplicit(PropertyType, value, out var converted)) + { + return target.SetValue(this, (TValue)converted!, priority); + } + else + { + var type = value?.GetType().FullName ?? "(null)"; + throw new ArgumentException($"Invalid value for Property '{Name}': '{value}' ({type})"); + } + } + + internal override IDisposable RouteBind( + AvaloniaObject target, + IObservable source, + BindingPriority priority) + { + return target.Bind(this, source, priority); + } + + private object? GetDefaultBoxedValue(Type type) + { + _ = type ?? throw new ArgumentNullException(nameof(type)); + return GetMetadata(type).DefaultValue; + } } } diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs deleted file mode 100644 index b39f45189c..0000000000 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Avalonia.Data; -using Avalonia.PropertyStore; -using Avalonia.Utilities; - -namespace Avalonia -{ - /// - /// Base class for styled properties. - /// - public abstract class StyledPropertyBase : AvaloniaProperty, IStyledPropertyAccessor - { - /// - /// Initializes a new instance of the class. - /// - /// The name of the property. - /// The type of the class that registers the property. - /// The property metadata. - /// Whether the property inherits its value. - /// A value validation callback. - /// A callback. - protected StyledPropertyBase( - string name, - Type ownerType, - StyledPropertyMetadata metadata, - bool inherits = false, - Func? validate = null, - Action? notifying = null) - : base(name, ownerType, metadata, notifying) - { - Inherits = inherits; - ValidateValue = validate; - HasCoercion |= metadata.CoerceValue != null; - - if (validate?.Invoke(metadata.DefaultValue) == false) - { - throw new ArgumentException( - $"'{metadata.DefaultValue}' is not a valid default value for '{name}'."); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The property to add the owner to. - /// The type of the class that registers the property. - protected StyledPropertyBase(StyledPropertyBase source, Type ownerType) - : base(source, ownerType, null) - { - Inherits = source.Inherits; - } - - /// - /// Gets the value validation callback for the property. - /// - public Func? ValidateValue { get; } - - /// - /// Gets a value indicating whether this property has any value coercion callbacks defined - /// in its metadata. - /// - internal bool HasCoercion { get; private set; } - - public TValue CoerceValue(AvaloniaObject instance, TValue baseValue) - { - var metadata = GetMetadata(instance.GetType()); - - if (metadata.CoerceValue != null) - { - return metadata.CoerceValue.Invoke(instance, baseValue); - } - - return baseValue; - } - - /// - /// Gets the default value for the property on the specified type. - /// - /// The type. - /// The default value. - public TValue GetDefaultValue(Type type) - { - return GetMetadata(type).DefaultValue; - } - - /// - /// Gets the property metadata for the specified type. - /// - /// The type. - /// - /// The property metadata. - /// - public new StyledPropertyMetadata GetMetadata(Type type) - { - _ = type ?? throw new ArgumentNullException(nameof(type)); - return (StyledPropertyMetadata)base.GetMetadata(type); - } - - /// - /// Overrides the default value for the property on the specified type. - /// - /// The type. - /// The default value. - public void OverrideDefaultValue(TValue defaultValue) where T : AvaloniaObject - { - OverrideDefaultValue(typeof(T), defaultValue); - } - - /// - /// Overrides the default value for the property on the specified type. - /// - /// The type. - /// The default value. - public void OverrideDefaultValue(Type type, TValue defaultValue) - { - OverrideMetadata(type, new StyledPropertyMetadata(defaultValue)); - } - - /// - /// Overrides the metadata for the property on the specified type. - /// - /// The type. - /// The metadata. - public void OverrideMetadata(StyledPropertyMetadata metadata) where T : AvaloniaObject - { - base.OverrideMetadata(typeof(T), metadata); - } - - /// - /// Overrides the metadata for the property on the specified type. - /// - /// The type. - /// The metadata. - public void OverrideMetadata(Type type, StyledPropertyMetadata metadata) - { - if (ValidateValue != null) - { - if (!ValidateValue(metadata.DefaultValue)) - { - throw new ArgumentException( - $"'{metadata.DefaultValue}' is not a valid default value for '{Name}'."); - } - } - - HasCoercion |= metadata.CoerceValue != null; - - base.OverrideMetadata(type, metadata); - } - - /// - /// Gets the string representation of the property. - /// - /// The property's string representation. - public override string ToString() - { - return Name; - } - - /// - object? IStyledPropertyAccessor.GetDefaultValue(Type type) => GetDefaultBoxedValue(type); - - bool IStyledPropertyAccessor.ValidateValue(object? value) - { - if (value is null && !typeof(TValue).IsValueType) - return ValidateValue?.Invoke(default!) ?? true; - if (value is TValue typed) - return ValidateValue?.Invoke(typed) ?? true; - return false; - } - - internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) - { - return new EffectiveValue(o, this); - } - - /// - internal override void RouteClearValue(AvaloniaObject o) - { - o.ClearValue(this); - } - - /// - internal override object? RouteGetValue(AvaloniaObject o) - { - return o.GetValue(this); - } - - /// - internal override object? RouteGetBaseValue(AvaloniaObject o) - { - var value = o.GetBaseValue(this); - return value.HasValue ? value.Value : AvaloniaProperty.UnsetValue; - } - - /// - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] - internal override IDisposable? RouteSetValue( - AvaloniaObject target, - object? value, - BindingPriority priority) - { - if (value == BindingOperations.DoNothing) - { - return null; - } - else if (value == UnsetValue) - { - target.ClearValue(this); - return null; - } - else if (TypeUtilities.TryConvertImplicit(PropertyType, value, out var converted)) - { - return target.SetValue(this, (TValue)converted!, priority); - } - else - { - var type = value?.GetType().FullName ?? "(null)"; - throw new ArgumentException($"Invalid value for Property '{Name}': '{value}' ({type})"); - } - } - - internal override IDisposable RouteBind( - AvaloniaObject target, - IObservable source, - BindingPriority priority) - { - return target.Bind(this, source, priority); - } - - private object? GetDefaultBoxedValue(Type type) - { - _ = type ?? throw new ArgumentNullException(nameof(type)); - return GetMetadata(type).DefaultValue; - } - } -} diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs index 68a9b8aafe..af5540ecf0 100644 --- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs @@ -14,7 +14,7 @@ namespace Avalonia.Styling ISetterInstance { private readonly StyledElement _target; - private readonly StyledPropertyBase? _styledProperty; + private readonly StyledProperty? _styledProperty; private readonly DirectPropertyBase? _directProperty; private readonly T _value; private IDisposable? _subscription; @@ -22,7 +22,7 @@ namespace Avalonia.Styling public PropertySetterInstance( StyledElement target, - StyledPropertyBase property, + StyledProperty property, T value) { _target = target; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index aab6239a35..0b61316603 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -126,7 +126,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers AvaloniaObjectSetStyledPropertyValue = AvaloniaObject .FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "SetValue" && m.Parameters.Count == 3 - && m.Parameters[0].Name == "StyledPropertyBase`1" + && m.Parameters[0].Name == "StyledProperty`1" && m.Parameters[2].Equals(BindingPriority)); IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding"); IDisposable = cfg.TypeSystem.GetType("System.IDisposable");