Browse Source

Merge branch 'master' into fixes/revertChanges

pull/10063/head
Benedikt Stebner 4 years ago
committed by GitHub
parent
commit
7bd86d95a5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/Avalonia.Base/AttachedProperty.cs
  2. 16
      src/Avalonia.Base/AvaloniaObject.cs
  3. 8
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  4. 8
      src/Avalonia.Base/AvaloniaProperty.cs
  5. 13
      src/Avalonia.Base/DirectProperty.cs
  6. 6
      src/Avalonia.Base/DirectPropertyBase.cs
  7. 2
      src/Avalonia.Base/Logging/TraceLogSink.cs
  8. 20
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  9. 4
      src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs
  10. 8
      src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs
  11. 6
      src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs
  12. 4
      src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs
  13. 4
      src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs
  14. 6
      src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs
  15. 2
      src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs
  16. 18
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  17. 208
      src/Avalonia.Base/StyledProperty.cs
  18. 250
      src/Avalonia.Base/StyledPropertyBase.cs
  19. 4
      src/Avalonia.Base/Styling/PropertySetterInstance.cs
  20. 2
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  21. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  22. 7
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

4
src/Avalonia.Base/AttachedProperty.cs

@ -24,11 +24,9 @@ namespace Avalonia
Func<TValue, bool>? validate = null) Func<TValue, bool>? validate = null)
: base(name, ownerType, metadata, inherits, validate) : base(name, ownerType, metadata, inherits, validate)
{ {
IsAttached = true;
} }
/// <inheritdoc/>
public override bool IsAttached => true;
/// <summary> /// <summary>
/// Attaches the property as a non-attached property on the specified type. /// Attaches the property as a non-attached property on the specified type.
/// </summary> /// </summary>

16
src/Avalonia.Base/AvaloniaObject.cs

@ -132,7 +132,7 @@ namespace Avalonia
switch (property) switch (property)
{ {
case StyledPropertyBase<T> styled: case StyledProperty<T> styled:
ClearValue(styled); ClearValue(styled);
break; break;
case DirectPropertyBase<T> direct: case DirectPropertyBase<T> direct:
@ -147,7 +147,7 @@ namespace Avalonia
/// Clears a <see cref="AvaloniaProperty"/>'s local value. /// Clears a <see cref="AvaloniaProperty"/>'s local value.
/// </summary> /// </summary>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
public void ClearValue<T>(StyledPropertyBase<T> property) public void ClearValue<T>(StyledProperty<T> property)
{ {
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess(); VerifyAccess();
@ -220,7 +220,7 @@ namespace Avalonia
/// <typeparam name="T">The type of the property.</typeparam> /// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <returns>The value.</returns> /// <returns>The value.</returns>
public T GetValue<T>(StyledPropertyBase<T> property) public T GetValue<T>(StyledProperty<T> property)
{ {
_ = property ?? throw new ArgumentNullException(nameof(property)); _ = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess(); VerifyAccess();
@ -243,7 +243,7 @@ namespace Avalonia
} }
/// <inheritdoc/> /// <inheritdoc/>
public Optional<T> GetBaseValue<T>(StyledPropertyBase<T> property) public Optional<T> GetBaseValue<T>(StyledProperty<T> property)
{ {
_ = property ?? throw new ArgumentNullException(nameof(property)); _ = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess(); VerifyAccess();
@ -309,7 +309,7 @@ namespace Avalonia
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null. /// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns> /// </returns>
public IDisposable? SetValue<T>( public IDisposable? SetValue<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
T value, T value,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
@ -373,7 +373,7 @@ namespace Avalonia
/// A disposable which can be used to terminate the binding. /// A disposable which can be used to terminate the binding.
/// </returns> /// </returns>
public IDisposable Bind<T>( public IDisposable Bind<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<object?> source, IObservable<object?> source,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
@ -396,7 +396,7 @@ namespace Avalonia
/// A disposable which can be used to terminate the binding. /// A disposable which can be used to terminate the binding.
/// </returns> /// </returns>
public IDisposable Bind<T>( public IDisposable Bind<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<T> source, IObservable<T> source,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
@ -419,7 +419,7 @@ namespace Avalonia
/// A disposable which can be used to terminate the binding. /// A disposable which can be used to terminate the binding.
/// </returns> /// </returns>
public IDisposable Bind<T>( public IDisposable Bind<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<BindingValue<T>> source, IObservable<BindingValue<T>> source,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {

8
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -146,7 +146,7 @@ namespace Avalonia
return property switch return property switch
{ {
StyledPropertyBase<T> styled => target.Bind(styled, source, priority), StyledProperty<T> styled => target.Bind(styled, source, priority),
DirectPropertyBase<T> direct => target.Bind(direct, source), DirectPropertyBase<T> direct => target.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
}; };
@ -170,7 +170,7 @@ namespace Avalonia
{ {
return property switch return property switch
{ {
StyledPropertyBase<T> styled => target.Bind(styled, source, priority), StyledProperty<T> styled => target.Bind(styled, source, priority),
DirectPropertyBase<T> direct => target.Bind(direct, source), DirectPropertyBase<T> direct => target.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
}; };
@ -231,7 +231,7 @@ namespace Avalonia
return property switch return property switch
{ {
StyledPropertyBase<T> styled => target.GetValue(styled), StyledProperty<T> styled => target.GetValue(styled),
DirectPropertyBase<T> direct => target.GetValue(direct), DirectPropertyBase<T> direct => target.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.") _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
}; };
@ -280,7 +280,7 @@ namespace Avalonia
return property switch return property switch
{ {
StyledPropertyBase<T> styled => target.GetBaseValue(styled), StyledProperty<T> styled => target.GetBaseValue(styled),
DirectPropertyBase<T> direct => target.GetValue(direct), DirectPropertyBase<T> direct => target.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.") _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
}; };

8
src/Avalonia.Base/AvaloniaProperty.cs

@ -107,22 +107,22 @@ namespace Avalonia
/// <summary> /// <summary>
/// Gets a value indicating whether the property inherits its value. /// Gets a value indicating whether the property inherits its value.
/// </summary> /// </summary>
public virtual bool Inherits => false; public bool Inherits { get; private protected set; }
/// <summary> /// <summary>
/// Gets a value indicating whether this is an attached property. /// Gets a value indicating whether this is an attached property.
/// </summary> /// </summary>
public virtual bool IsAttached => false; public bool IsAttached { get; private protected set; }
/// <summary> /// <summary>
/// Gets a value indicating whether this is a direct property. /// Gets a value indicating whether this is a direct property.
/// </summary> /// </summary>
public virtual bool IsDirect => false; public bool IsDirect { get; private protected set; }
/// <summary> /// <summary>
/// Gets a value indicating whether this is a readonly property. /// Gets a value indicating whether this is a readonly property.
/// </summary> /// </summary>
public virtual bool IsReadOnly => false; public bool IsReadOnly { get; private protected set; }
/// <summary> /// <summary>
/// Gets an observable that is fired when this property changes on any /// Gets an observable that is fired when this property changes on any

13
src/Avalonia.Base/DirectProperty.cs

@ -33,6 +33,8 @@ namespace Avalonia
{ {
Getter = getter ?? throw new ArgumentNullException(nameof(getter)); Getter = getter ?? throw new ArgumentNullException(nameof(getter));
Setter = setter; Setter = setter;
IsDirect = true;
IsReadOnly = setter is null;
} }
/// <summary> /// <summary>
@ -51,17 +53,10 @@ namespace Avalonia
{ {
Getter = getter ?? throw new ArgumentNullException(nameof(getter)); Getter = getter ?? throw new ArgumentNullException(nameof(getter));
Setter = setter; Setter = setter;
IsDirect = true;
IsReadOnly = setter is null;
} }
/// <inheritdoc/>
public override bool IsDirect => true;
/// <inheritdoc/>
public override bool IsReadOnly => Setter == null;
/// <inheritdoc/>
public override Type Owner => typeof(TOwner);
/// <summary> /// <summary>
/// Gets the getter function. /// Gets the getter function.
/// </summary> /// </summary>

6
src/Avalonia.Base/DirectPropertyBase.cs

@ -1,8 +1,6 @@
using System; using System;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.PropertyStore; using Avalonia.PropertyStore;
using Avalonia.Reactive;
using Avalonia.Styling;
namespace Avalonia namespace Avalonia
{ {
@ -28,6 +26,7 @@ namespace Avalonia
AvaloniaPropertyMetadata metadata) AvaloniaPropertyMetadata metadata)
: base(name, ownerType, metadata) : base(name, ownerType, metadata)
{ {
Owner = ownerType;
} }
/// <summary> /// <summary>
@ -42,12 +41,13 @@ namespace Avalonia
AvaloniaPropertyMetadata metadata) AvaloniaPropertyMetadata metadata)
: base(source, ownerType, metadata) : base(source, ownerType, metadata)
{ {
Owner = ownerType;
} }
/// <summary> /// <summary>
/// Gets the type that registered the property. /// Gets the type that registered the property.
/// </summary> /// </summary>
public abstract Type Owner { get; } public Type Owner { get; }
/// <summary> /// <summary>
/// Gets the value of the property on the instance. /// Gets the value of the property on the instance.

2
src/Avalonia.Base/Logging/TraceLogSink.cs

@ -141,7 +141,7 @@ namespace Avalonia.Logging
result.Append(')'); result.Append(')');
} }
return result.ToString(); return StringBuilderCache.GetStringAndRelease(result);
} }
} }
} }

20
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -19,7 +19,7 @@ namespace Avalonia.PropertyStore
private T? _baseValue; private T? _baseValue;
private UncommonFields? _uncommon; private UncommonFields? _uncommon;
public EffectiveValue(AvaloniaObject owner, StyledPropertyBase<T> property) public EffectiveValue(AvaloniaObject owner, StyledProperty<T> property)
{ {
Priority = BindingPriority.Unset; Priority = BindingPriority.Unset;
BasePriority = BindingPriority.Unset; BasePriority = BindingPriority.Unset;
@ -57,12 +57,12 @@ namespace Avalonia.PropertyStore
Debug.Assert(priority != BindingPriority.LocalValue); Debug.Assert(priority != BindingPriority.LocalValue);
UpdateValueEntry(value, priority); UpdateValueEntry(value, priority);
SetAndRaiseCore(owner, (StyledPropertyBase<T>)value.Property, GetValue(value), priority); SetAndRaiseCore(owner, (StyledProperty<T>)value.Property, GetValue(value), priority);
} }
public void SetLocalValueAndRaise( public void SetLocalValueAndRaise(
ValueStore owner, ValueStore owner,
StyledPropertyBase<T> property, StyledProperty<T> property,
T value) T value)
{ {
SetAndRaiseCore(owner, property, value, BindingPriority.LocalValue); SetAndRaiseCore(owner, property, value, BindingPriority.LocalValue);
@ -82,7 +82,7 @@ namespace Avalonia.PropertyStore
{ {
Debug.Assert(oldValue is not null || newValue is not null); Debug.Assert(oldValue is not null || newValue is not null);
var p = (StyledPropertyBase<T>)property; var p = (StyledProperty<T>)property;
var o = oldValue is not null ? ((EffectiveValue<T>)oldValue).Value : _metadata.DefaultValue; var o = oldValue is not null ? ((EffectiveValue<T>)oldValue).Value : _metadata.DefaultValue;
var n = newValue is not null ? ((EffectiveValue<T>)newValue).Value : _metadata.DefaultValue; var n = newValue is not null ? ((EffectiveValue<T>)newValue).Value : _metadata.DefaultValue;
var priority = newValue is not null ? BindingPriority.Inherited : BindingPriority.Unset; var priority = newValue is not null ? BindingPriority.Inherited : BindingPriority.Unset;
@ -98,7 +98,7 @@ namespace Avalonia.PropertyStore
Debug.Assert(Priority != BindingPriority.Animation); Debug.Assert(Priority != BindingPriority.Animation);
Debug.Assert(BasePriority != BindingPriority.Unset); Debug.Assert(BasePriority != BindingPriority.Unset);
UpdateValueEntry(null, BindingPriority.Animation); UpdateValueEntry(null, BindingPriority.Animation);
SetAndRaiseCore(owner, (StyledPropertyBase<T>)property, _baseValue!, BasePriority); SetAndRaiseCore(owner, (StyledProperty<T>)property, _baseValue!, BasePriority);
} }
public override void CoerceValue(ValueStore owner, AvaloniaProperty property) public override void CoerceValue(ValueStore owner, AvaloniaProperty property)
@ -107,7 +107,7 @@ namespace Avalonia.PropertyStore
return; return;
SetAndRaiseCore( SetAndRaiseCore(
owner, owner,
(StyledPropertyBase<T>)property, (StyledProperty<T>)property,
_uncommon._uncoercedValue!, _uncommon._uncoercedValue!,
Priority, Priority,
_uncommon._uncoercedBaseValue!, _uncommon._uncoercedBaseValue!,
@ -117,10 +117,10 @@ namespace Avalonia.PropertyStore
public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property) public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property)
{ {
UnsubscribeValueEntries(); UnsubscribeValueEntries();
DisposeAndRaiseUnset(owner, (StyledPropertyBase<T>)property); DisposeAndRaiseUnset(owner, (StyledProperty<T>)property);
} }
public void DisposeAndRaiseUnset(ValueStore owner, StyledPropertyBase<T> property) public void DisposeAndRaiseUnset(ValueStore owner, StyledProperty<T> property)
{ {
BindingPriority priority; BindingPriority priority;
T oldValue; T oldValue;
@ -156,7 +156,7 @@ namespace Avalonia.PropertyStore
private void SetAndRaiseCore( private void SetAndRaiseCore(
ValueStore owner, ValueStore owner,
StyledPropertyBase<T> property, StyledProperty<T> property,
T value, T value,
BindingPriority priority) BindingPriority priority)
{ {
@ -203,7 +203,7 @@ namespace Avalonia.PropertyStore
private void SetAndRaiseCore( private void SetAndRaiseCore(
ValueStore owner, ValueStore owner,
StyledPropertyBase<T> property, StyledProperty<T> property,
T value, T value,
BindingPriority priority, BindingPriority priority,
T baseValue, T baseValue,

4
src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs

@ -9,7 +9,7 @@ namespace Avalonia.PropertyStore
public ImmediateValueEntry( public ImmediateValueEntry(
ImmediateValueFrame owner, ImmediateValueFrame owner,
StyledPropertyBase<T> property, StyledProperty<T> property,
T value) T value)
{ {
_owner = owner; _owner = owner;
@ -17,7 +17,7 @@ namespace Avalonia.PropertyStore
Property = property; Property = property;
} }
public StyledPropertyBase<T> Property { get; } public StyledProperty<T> Property { get; }
public bool HasValue => true; public bool HasValue => true;
AvaloniaProperty IValueEntry.Property => Property; AvaloniaProperty IValueEntry.Property => Property;

8
src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs

@ -15,7 +15,7 @@ namespace Avalonia.PropertyStore
} }
public TypedBindingEntry<T> AddBinding<T>( public TypedBindingEntry<T> AddBinding<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<BindingValue<T>> source) IObservable<BindingValue<T>> source)
{ {
var e = new TypedBindingEntry<T>(this, property, source); var e = new TypedBindingEntry<T>(this, property, source);
@ -24,7 +24,7 @@ namespace Avalonia.PropertyStore
} }
public TypedBindingEntry<T> AddBinding<T>( public TypedBindingEntry<T> AddBinding<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<T> source) IObservable<T> source)
{ {
var e = new TypedBindingEntry<T>(this, property, source); var e = new TypedBindingEntry<T>(this, property, source);
@ -33,7 +33,7 @@ namespace Avalonia.PropertyStore
} }
public SourceUntypedBindingEntry<T> AddBinding<T>( public SourceUntypedBindingEntry<T> AddBinding<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<object?> source) IObservable<object?> source)
{ {
var e = new SourceUntypedBindingEntry<T>(this, property, source); var e = new SourceUntypedBindingEntry<T>(this, property, source);
@ -41,7 +41,7 @@ namespace Avalonia.PropertyStore
return e; return e;
} }
public ImmediateValueEntry<T> AddValue<T>(StyledPropertyBase<T> property, T value) public ImmediateValueEntry<T> AddValue<T>(StyledProperty<T> property, T value)
{ {
var e = new ImmediateValueEntry<T>(this, property, value); var e = new ImmediateValueEntry<T>(this, property, value);
Add(e); Add(e);

6
src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs

@ -11,13 +11,13 @@ namespace Avalonia.PropertyStore
private readonly ValueStore _owner; private readonly ValueStore _owner;
private IDisposable? _subscription; private IDisposable? _subscription;
public LocalValueBindingObserver(ValueStore owner, StyledPropertyBase<T> property) public LocalValueBindingObserver(ValueStore owner, StyledProperty<T> property)
{ {
_owner = owner; _owner = owner;
Property = property; Property = property;
} }
public StyledPropertyBase<T> Property { get;} public StyledProperty<T> Property { get;}
public void Start(IObservable<T> source) public void Start(IObservable<T> source)
{ {
@ -41,7 +41,7 @@ namespace Avalonia.PropertyStore
public void OnNext(T value) public void OnNext(T value)
{ {
static void Execute(ValueStore owner, StyledPropertyBase<T> property, T value) static void Execute(ValueStore owner, StyledProperty<T> property, T value)
{ {
if (property.ValidateValue?.Invoke(value) != false) if (property.ValidateValue?.Invoke(value) != false)
owner.SetValue(property, value, BindingPriority.LocalValue); owner.SetValue(property, value, BindingPriority.LocalValue);

4
src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs

@ -11,13 +11,13 @@ namespace Avalonia.PropertyStore
private readonly ValueStore _owner; private readonly ValueStore _owner;
private IDisposable? _subscription; private IDisposable? _subscription;
public LocalValueUntypedBindingObserver(ValueStore owner, StyledPropertyBase<T> property) public LocalValueUntypedBindingObserver(ValueStore owner, StyledProperty<T> property)
{ {
_owner = owner; _owner = owner;
Property = property; Property = property;
} }
public StyledPropertyBase<T> Property { get; } public StyledProperty<T> Property { get; }
public void Start(IObservable<object?> source) public void Start(IObservable<object?> source)
{ {

4
src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs

@ -13,14 +13,14 @@ namespace Avalonia.PropertyStore
public SourceUntypedBindingEntry( public SourceUntypedBindingEntry(
ValueFrame frame, ValueFrame frame,
StyledPropertyBase<TTarget> property, StyledProperty<TTarget> property,
IObservable<object?> source) IObservable<object?> source)
: base(frame, property, source) : base(frame, property, source)
{ {
_validate = property.ValidateValue; _validate = property.ValidateValue;
} }
public new StyledPropertyBase<TTarget> Property => (StyledPropertyBase<TTarget>)base.Property; public new StyledProperty<TTarget> Property => (StyledProperty<TTarget>)base.Property;
protected override BindingValue<TTarget> ConvertAndValidate(object? value) protected override BindingValue<TTarget> ConvertAndValidate(object? value)
{ {

6
src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs

@ -11,7 +11,7 @@ namespace Avalonia.PropertyStore
{ {
public TypedBindingEntry( public TypedBindingEntry(
ValueFrame frame, ValueFrame frame,
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<T> source) IObservable<T> source)
: base(frame, property, source) : base(frame, property, source)
{ {
@ -19,13 +19,13 @@ namespace Avalonia.PropertyStore
public TypedBindingEntry( public TypedBindingEntry(
ValueFrame frame, ValueFrame frame,
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<BindingValue<T>> source) IObservable<BindingValue<T>> source)
: base(frame, property, source) : base(frame, property, source)
{ {
} }
public new StyledPropertyBase<T> Property => (StyledPropertyBase<T>)base.Property; public new StyledProperty<T> Property => (StyledProperty<T>)base.Property;
protected override BindingValue<T> ConvertAndValidate(T value) protected override BindingValue<T> ConvertAndValidate(T value)
{ {

2
src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs

@ -26,7 +26,7 @@ namespace Avalonia.PropertyStore
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
public static bool TryConvertAndValidate<T>( public static bool TryConvertAndValidate<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
object? value, object? value,
[MaybeNullWhen(false)] out T result) [MaybeNullWhen(false)] out T result)
{ {

18
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -43,7 +43,7 @@ namespace Avalonia.PropertyStore
} }
public IDisposable AddBinding<T>( public IDisposable AddBinding<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<BindingValue<T>> source, IObservable<BindingValue<T>> source,
BindingPriority priority) BindingPriority priority)
{ {
@ -71,7 +71,7 @@ namespace Avalonia.PropertyStore
} }
public IDisposable AddBinding<T>( public IDisposable AddBinding<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<T> source, IObservable<T> source,
BindingPriority priority) BindingPriority priority)
{ {
@ -99,7 +99,7 @@ namespace Avalonia.PropertyStore
} }
public IDisposable AddBinding<T>( public IDisposable AddBinding<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
IObservable<object?> source, IObservable<object?> source,
BindingPriority priority) BindingPriority priority)
{ {
@ -165,7 +165,7 @@ namespace Avalonia.PropertyStore
} }
} }
public IDisposable? SetValue<T>(StyledPropertyBase<T> property, T value, BindingPriority priority) public IDisposable? SetValue<T>(StyledProperty<T> property, T value, BindingPriority priority)
{ {
if (property.ValidateValue?.Invoke(value) == false) if (property.ValidateValue?.Invoke(value) == false)
{ {
@ -219,7 +219,7 @@ namespace Avalonia.PropertyStore
return GetDefaultValue(property); return GetDefaultValue(property);
} }
public T GetValue<T>(StyledPropertyBase<T> property) public T GetValue<T>(StyledProperty<T> property)
{ {
if (_effectiveValues.TryGetValue(property, out var v)) if (_effectiveValues.TryGetValue(property, out var v))
return ((EffectiveValue<T>)v).Value; return ((EffectiveValue<T>)v).Value;
@ -248,7 +248,7 @@ namespace Avalonia.PropertyStore
v.CoerceValue(this, property); v.CoerceValue(this, property);
} }
public Optional<T> GetBaseValue<T>(StyledPropertyBase<T> property) public Optional<T> GetBaseValue<T>(StyledProperty<T> property)
{ {
if (TryGetEffectiveValue(property, out var v) && if (TryGetEffectiveValue(property, out var v) &&
((EffectiveValue<T>)v).TryGetBaseValue(out var baseValue)) ((EffectiveValue<T>)v).TryGetBaseValue(out var baseValue))
@ -450,7 +450,7 @@ namespace Avalonia.PropertyStore
/// <param name="oldValue">The old value of the property.</param> /// <param name="oldValue">The old value of the property.</param>
/// <param name="value">The effective value instance.</param> /// <param name="value">The effective value instance.</param>
public void OnInheritedEffectiveValueChanged<T>( public void OnInheritedEffectiveValueChanged<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
T oldValue, T oldValue,
EffectiveValue<T> value) EffectiveValue<T> value)
{ {
@ -475,7 +475,7 @@ namespace Avalonia.PropertyStore
/// </summary> /// </summary>
/// <param name="property">The property whose value changed.</param> /// <param name="property">The property whose value changed.</param>
/// <param name="oldValue">The old value of the property.</param> /// <param name="oldValue">The old value of the property.</param>
public void OnInheritedEffectiveValueDisposed<T>(StyledPropertyBase<T> property, T oldValue) public void OnInheritedEffectiveValueDisposed<T>(StyledProperty<T> property, T oldValue)
{ {
Debug.Assert(property.Inherits); Debug.Assert(property.Inherits);
@ -520,7 +520,7 @@ namespace Avalonia.PropertyStore
/// <param name="oldValue">The old value of the property.</param> /// <param name="oldValue">The old value of the property.</param>
/// <param name="newValue">The new value of the property.</param> /// <param name="newValue">The new value of the property.</param>
public void OnAncestorInheritedValueChanged<T>( public void OnAncestorInheritedValueChanged<T>(
StyledPropertyBase<T> property, StyledProperty<T> property,
T oldValue, T oldValue,
T newValue) T newValue)
{ {

208
src/Avalonia.Base/StyledProperty.cs

@ -1,14 +1,18 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Data;
using Avalonia.PropertyStore;
using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
{ {
/// <summary> /// <summary>
/// A styled avalonia property. /// A styled avalonia property.
/// </summary> /// </summary>
public class StyledProperty<TValue> : StyledPropertyBase<TValue> public class StyledProperty<TValue> : AvaloniaProperty<TValue>, IStyledPropertyAccessor
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StyledPropertyBase{T}"/> class. /// Initializes a new instance of the <see cref="StyledProperty{T}"/> class.
/// </summary> /// </summary>
/// <param name="name">The name of the property.</param> /// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param> /// <param name="ownerType">The type of the class that registers the property.</param>
@ -23,20 +27,30 @@ namespace Avalonia
bool inherits = false, bool inherits = false,
Func<TValue, bool>? validate = null, Func<TValue, bool>? validate = null,
Action<AvaloniaObject, bool>? notifying = null) Action<AvaloniaObject, bool>? 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}'.");
}
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StyledPropertyBase{T}"/> class. /// Gets the value validation callback for the property.
/// </summary> /// </summary>
/// <param name="source">The property to add the owner to.</param> public Func<TValue, bool>? ValidateValue { get; }
/// <param name="ownerType">The type of the class that registers the property.</param>
internal StyledProperty(StyledPropertyBase<TValue> source, Type ownerType) /// <summary>
: base(source, ownerType) /// Gets a value indicating whether this property has any value coercion callbacks defined
{ /// in its metadata.
} /// </summary>
internal bool HasCoercion { get; private set; }
/// <summary> /// <summary>
/// Registers the property on another type. /// Registers the property on another type.
/// </summary> /// </summary>
@ -47,5 +61,177 @@ namespace Avalonia
AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this); AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this);
return 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;
}
/// <summary>
/// Gets the default value for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The default value.</returns>
public TValue GetDefaultValue(Type type)
{
return GetMetadata(type).DefaultValue;
}
/// <summary>
/// Gets the property metadata for the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>
/// The property metadata.
/// </returns>
public new StyledPropertyMetadata<TValue> GetMetadata(Type type)
{
_ = type ?? throw new ArgumentNullException(nameof(type));
return (StyledPropertyMetadata<TValue>)base.GetMetadata(type);
}
/// <summary>
/// Overrides the default value for the property on the specified type.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="defaultValue">The default value.</param>
public void OverrideDefaultValue<T>(TValue defaultValue) where T : AvaloniaObject
{
OverrideDefaultValue(typeof(T), defaultValue);
}
/// <summary>
/// Overrides the default value for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="defaultValue">The default value.</param>
public void OverrideDefaultValue(Type type, TValue defaultValue)
{
OverrideMetadata(type, new StyledPropertyMetadata<TValue>(defaultValue));
}
/// <summary>
/// Overrides the metadata for the property on the specified type.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="metadata">The metadata.</param>
public void OverrideMetadata<T>(StyledPropertyMetadata<TValue> metadata) where T : AvaloniaObject
{
base.OverrideMetadata(typeof(T), metadata);
}
/// <summary>
/// Overrides the metadata for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="metadata">The metadata.</param>
public void OverrideMetadata(Type type, StyledPropertyMetadata<TValue> 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);
}
/// <summary>
/// Gets the string representation of the property.
/// </summary>
/// <returns>The property's string representation.</returns>
public override string ToString()
{
return Name;
}
/// <inheritdoc/>
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<TValue>(o, this);
}
/// <inheritdoc/>
internal override void RouteClearValue(AvaloniaObject o)
{
o.ClearValue<TValue>(this);
}
/// <inheritdoc/>
internal override object? RouteGetValue(AvaloniaObject o)
{
return o.GetValue<TValue>(this);
}
/// <inheritdoc/>
internal override object? RouteGetBaseValue(AvaloniaObject o)
{
var value = o.GetBaseValue<TValue>(this);
return value.HasValue ? value.Value : AvaloniaProperty.UnsetValue;
}
/// <inheritdoc/>
[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<TValue>(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<object?> source,
BindingPriority priority)
{
return target.Bind<TValue>(this, source, priority);
}
private object? GetDefaultBoxedValue(Type type)
{
_ = type ?? throw new ArgumentNullException(nameof(type));
return GetMetadata(type).DefaultValue;
}
} }
} }

250
src/Avalonia.Base/StyledPropertyBase.cs

@ -1,250 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Avalonia.Data;
using Avalonia.PropertyStore;
using Avalonia.Reactive;
using Avalonia.Styling;
using Avalonia.Utilities;
namespace Avalonia
{
/// <summary>
/// Base class for styled properties.
/// </summary>
public abstract class StyledPropertyBase<TValue> : AvaloniaProperty<TValue>, IStyledPropertyAccessor
{
private readonly bool _inherits;
/// <summary>
/// Initializes a new instance of the <see cref="StyledPropertyBase{T}"/> class.
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="validate">A value validation callback.</param>
/// <param name="notifying">A <see cref="AvaloniaProperty.Notifying"/> callback.</param>
protected StyledPropertyBase(
string name,
Type ownerType,
StyledPropertyMetadata<TValue> metadata,
bool inherits = false,
Func<TValue, bool>? validate = null,
Action<AvaloniaObject, bool>? 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}'.");
}
}
/// <summary>
/// Initializes a new instance of the <see cref="StyledPropertyBase{T}"/> class.
/// </summary>
/// <param name="source">The property to add the owner to.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
protected StyledPropertyBase(StyledPropertyBase<TValue> source, Type ownerType)
: base(source, ownerType, null)
{
_inherits = source.Inherits;
}
/// <summary>
/// Gets a value indicating whether the property inherits its value.
/// </summary>
/// <value>
/// A value indicating whether the property inherits its value.
/// </value>
public override bool Inherits => _inherits;
/// <summary>
/// Gets the value validation callback for the property.
/// </summary>
public Func<TValue, bool>? ValidateValue { get; }
/// <summary>
/// Gets a value indicating whether this property has any value coercion callbacks defined
/// in its metadata.
/// </summary>
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;
}
/// <summary>
/// Gets the default value for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The default value.</returns>
public TValue GetDefaultValue(Type type)
{
return GetMetadata(type).DefaultValue;
}
/// <summary>
/// Gets the property metadata for the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>
/// The property metadata.
/// </returns>
public new StyledPropertyMetadata<TValue> GetMetadata(Type type)
{
_ = type ?? throw new ArgumentNullException(nameof(type));
return (StyledPropertyMetadata<TValue>)base.GetMetadata(type);
}
/// <summary>
/// Overrides the default value for the property on the specified type.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="defaultValue">The default value.</param>
public void OverrideDefaultValue<T>(TValue defaultValue) where T : AvaloniaObject
{
OverrideDefaultValue(typeof(T), defaultValue);
}
/// <summary>
/// Overrides the default value for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="defaultValue">The default value.</param>
public void OverrideDefaultValue(Type type, TValue defaultValue)
{
OverrideMetadata(type, new StyledPropertyMetadata<TValue>(defaultValue));
}
/// <summary>
/// Overrides the metadata for the property on the specified type.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="metadata">The metadata.</param>
public void OverrideMetadata<T>(StyledPropertyMetadata<TValue> metadata) where T : AvaloniaObject
{
base.OverrideMetadata(typeof(T), metadata);
}
/// <summary>
/// Overrides the metadata for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="metadata">The metadata.</param>
public void OverrideMetadata(Type type, StyledPropertyMetadata<TValue> 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);
}
/// <summary>
/// Gets the string representation of the property.
/// </summary>
/// <returns>The property's string representation.</returns>
public override string ToString()
{
return Name;
}
/// <inheritdoc/>
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<TValue>(o, this);
}
/// <inheritdoc/>
internal override void RouteClearValue(AvaloniaObject o)
{
o.ClearValue<TValue>(this);
}
/// <inheritdoc/>
internal override object? RouteGetValue(AvaloniaObject o)
{
return o.GetValue<TValue>(this);
}
/// <inheritdoc/>
internal override object? RouteGetBaseValue(AvaloniaObject o)
{
var value = o.GetBaseValue<TValue>(this);
return value.HasValue ? value.Value : AvaloniaProperty.UnsetValue;
}
/// <inheritdoc/>
[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<TValue>(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<object?> source,
BindingPriority priority)
{
return target.Bind<TValue>(this, source, priority);
}
private object? GetDefaultBoxedValue(Type type)
{
_ = type ?? throw new ArgumentNullException(nameof(type));
return GetMetadata(type).DefaultValue;
}
}
}

4
src/Avalonia.Base/Styling/PropertySetterInstance.cs

@ -14,7 +14,7 @@ namespace Avalonia.Styling
ISetterInstance ISetterInstance
{ {
private readonly StyledElement _target; private readonly StyledElement _target;
private readonly StyledPropertyBase<T>? _styledProperty; private readonly StyledProperty<T>? _styledProperty;
private readonly DirectPropertyBase<T>? _directProperty; private readonly DirectPropertyBase<T>? _directProperty;
private readonly T _value; private readonly T _value;
private IDisposable? _subscription; private IDisposable? _subscription;
@ -22,7 +22,7 @@ namespace Avalonia.Styling
public PropertySetterInstance( public PropertySetterInstance(
StyledElement target, StyledElement target,
StyledPropertyBase<T> property, StyledProperty<T> property,
T value) T value)
{ {
_target = target; _target = target;

2
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -226,7 +226,7 @@ namespace Avalonia.Controls
{ {
if (toIndex < 0) if (toIndex < 0)
toIndex = count - 1; toIndex = count - 1;
else if (toIndex >= count - 1) else if (toIndex >= count)
toIndex = 0; toIndex = 0;
} }

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -126,7 +126,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
AvaloniaObjectSetStyledPropertyValue = AvaloniaObject AvaloniaObjectSetStyledPropertyValue = AvaloniaObject
.FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "SetValue" .FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "SetValue"
&& m.Parameters.Count == 3 && m.Parameters.Count == 3
&& m.Parameters[0].Name == "StyledPropertyBase`1" && m.Parameters[0].Name == "StyledProperty`1"
&& m.Parameters[2].Equals(BindingPriority)); && m.Parameters[2].Equals(BindingPriority));
IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding"); IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding");
IDisposable = cfg.TypeSystem.GetType("System.IDisposable"); IDisposable = cfg.TypeSystem.GetType("System.IDisposable");

7
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -759,6 +759,7 @@ namespace Avalonia.Controls.UnitTests
var lbItems = target.GetLogicalChildren().OfType<ListBoxItem>().ToArray(); var lbItems = target.GetLogicalChildren().OfType<ListBoxItem>().ToArray();
var first = lbItems.First(); var first = lbItems.First();
var beforeLast = lbItems[^2];
var last = lbItems.Last(); var last = lbItems.Last();
first.Focus(); first.Focus();
@ -769,6 +770,12 @@ namespace Avalonia.Controls.UnitTests
RaiseKeyEvent(target, Key.Up); RaiseKeyEvent(target, Key.Up);
Assert.Equal(true, last.IsSelected); Assert.Equal(true, last.IsSelected);
RaiseKeyEvent(target, Key.Up);
Assert.Equal(true, beforeLast.IsSelected);
RaiseKeyEvent(target, Key.Down);
Assert.Equal(true, last.IsSelected);
RaiseKeyEvent(target, Key.Down); RaiseKeyEvent(target, Key.Down);
Assert.Equal(true, first.IsSelected); Assert.Equal(true, first.IsSelected);

Loading…
Cancel
Save