// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Diagnostics; using Avalonia.Data; using Avalonia.Reactive; namespace Avalonia { /// /// Base class for styled properties. /// public abstract class StyledPropertyBase : AvaloniaProperty, IStyledPropertyAccessor { private bool _inherits; /// /// 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) { Contract.Requires(name != null); Contract.Requires(ownerType != null); if (name.Contains(".")) { throw new ArgumentException("'name' may not contain periods."); } _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 a value indicating whether the property inherits its value. /// /// /// A value indicating whether the property inherits its value. /// public override bool Inherits => _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(IAvaloniaObject 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) { Contract.Requires(type != null); return GetMetadata(type).DefaultValue; } /// /// Gets the property metadata for the specified type. /// /// The type. /// /// The property metadata. /// public new StyledPropertyMetadata GetMetadata(Type 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 : IAvaloniaObject { 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 : IAvaloniaObject { 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); /// internal override void RouteClearValue(IAvaloniaObject o) { o.ClearValue(this); } /// internal override object RouteGetValue(IAvaloniaObject o) { return o.GetValue(this); } /// internal override void RouteSetValue( IAvaloniaObject o, object value, BindingPriority priority) { var v = TryConvert(value); if (v.HasValue) { o.SetValue(this, (TValue)v.Value, priority); } else if (v.Type == BindingValueType.UnsetValue) { o.ClearValue(this); } else if (v.HasError) { throw v.Error; } } /// internal override IDisposable RouteBind( IAvaloniaObject o, IObservable> source, BindingPriority priority) { var adapter = TypedBindingAdapter.Create(o, this, source); return o.Bind(this, adapter, priority); } /// internal override void RouteInheritanceParentChanged( AvaloniaObject o, IAvaloniaObject oldParent) { o.InheritanceParentChanged(this, oldParent); } private object GetDefaultBoxedValue(Type type) { Contract.Requires(type != null); return GetMetadata(type).DefaultValue; } [DebuggerHidden] private Func Cast(Func validate) { return (o, v) => validate((THost)o, v); } } }