Browse Source

Merge pull request #7980 from AvaloniaUI/refactor/valuestore-nongeneric

Remove virtual generic methods from ValueStore.
pull/8023/head
Dariusz Komosiński 4 years ago
committed by GitHub
parent
commit
8bd9cb17f8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      samples/SampleControls/HamburgerMenu/HamburgerMenu.cs
  2. 7
      src/Avalonia.Base/Animation/Animatable.cs
  3. 65
      src/Avalonia.Base/AvaloniaObject.cs
  4. 234
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  5. 12
      src/Avalonia.Base/AvaloniaProperty.cs
  6. 43
      src/Avalonia.Base/AvaloniaPropertyChangedExtensions.cs
  7. 12
      src/Avalonia.Base/DirectPropertyBase.cs
  8. 2
      src/Avalonia.Base/GeometryGroup.cs
  9. 104
      src/Avalonia.Base/IAvaloniaObject.cs
  10. 8
      src/Avalonia.Base/Input/InputElement.cs
  11. 2
      src/Avalonia.Base/Input/KeyboardNavigation.cs
  12. 5
      src/Avalonia.Base/Input/Navigation/TabNavigation.cs
  13. 4
      src/Avalonia.Base/Layout/StackLayout.cs
  14. 18
      src/Avalonia.Base/Layout/UniformGridLayout.cs
  15. 2
      src/Avalonia.Base/Layout/WrapLayout/WrapLayout.cs
  16. 5
      src/Avalonia.Base/Media/DashStyle.cs
  17. 2
      src/Avalonia.Base/Media/DrawingImage.cs
  18. 2
      src/Avalonia.Base/Media/Pen.cs
  19. 17
      src/Avalonia.Base/PropertyStore/BindingEntry.cs
  20. 13
      src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs
  21. 2
      src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs
  22. 3
      src/Avalonia.Base/PropertyStore/IValue.cs
  23. 17
      src/Avalonia.Base/PropertyStore/IValueSink.cs
  24. 5
      src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
  25. 67
      src/Avalonia.Base/PropertyStore/PriorityValue.cs
  26. 45
      src/Avalonia.Base/PropertyStore/ValueOwner.cs
  27. 2
      src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs
  28. 30
      src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs
  29. 7
      src/Avalonia.Base/StyledElement.cs
  30. 12
      src/Avalonia.Base/StyledPropertyBase.cs
  31. 27
      src/Avalonia.Base/ValueStore.cs
  32. 5
      src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
  33. 2
      src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
  34. 4
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  35. 2
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  36. 2
      src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
  37. 17
      src/Avalonia.Controls/ApiCompatBaseline.txt
  38. 14
      src/Avalonia.Controls/Button.cs
  39. 4
      src/Avalonia.Controls/ButtonSpinner.cs
  40. 4
      src/Avalonia.Controls/ContextMenu.cs
  41. 2
      src/Avalonia.Controls/Control.cs
  42. 2
      src/Avalonia.Controls/Documents/Inline.cs
  43. 2
      src/Avalonia.Controls/Documents/Run.cs
  44. 2
      src/Avalonia.Controls/Documents/TextElement.cs
  45. 4
      src/Avalonia.Controls/Expander.cs
  46. 4
      src/Avalonia.Controls/ItemsControl.cs
  47. 2
      src/Avalonia.Controls/MaskedTextBox.cs
  48. 4
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  49. 2
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  50. 2
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  51. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  52. 46
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  53. 4
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  54. 10
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  55. 4
      src/Avalonia.Controls/Primitives/Track.cs
  56. 6
      src/Avalonia.Controls/ProgressBar.cs
  57. 4
      src/Avalonia.Controls/RepeatButton.cs
  58. 43
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  59. 4
      src/Avalonia.Controls/Repeater/RecyclePool.cs
  60. 6
      src/Avalonia.Controls/Repeater/ViewManager.cs
  61. 4
      src/Avalonia.Controls/Slider.cs
  62. 11
      src/Avalonia.Controls/SplitButton/SplitButton.cs
  63. 2
      src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
  64. 2
      src/Avalonia.Controls/TextBlock.cs
  65. 4
      src/Avalonia.Controls/TextBox.cs
  66. 2
      src/Avalonia.Controls/TransitioningContentControl.cs
  67. 8
      src/Avalonia.Controls/TrayIcon.cs
  68. 4
      src/Avalonia.Controls/Viewbox.cs
  69. 6
      src/Avalonia.Controls/Window.cs
  70. 4
      src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs
  71. 4
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs
  72. 2
      src/Avalonia.Themes.Default/SimpleTheme.cs
  73. 2
      src/Avalonia.Themes.Fluent/FluentTheme.cs
  74. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  75. 15
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs
  76. 12
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
  77. 49
      tests/Avalonia.Base.UnitTests/PriorityValueTests.cs
  78. 49
      tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs
  79. 83
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_TemplatedParent.cs
  80. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/InitializationOrderTracker.cs

5
samples/SampleControls/HamburgerMenu/HamburgerMenu.cs

@ -43,14 +43,13 @@ namespace ControlSamples
_splitView = e.NameScope.Find<SplitView>("PART_NavigationPane"); _splitView = e.NameScope.Find<SplitView>("PART_NavigationPane");
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == BoundsProperty && _splitView is not null) if (change.Property == BoundsProperty && _splitView is not null)
{ {
var oldBounds = change.OldValue.GetValueOrDefault<Rect>(); var (oldBounds, newBounds) = change.GetOldAndNewValue<Rect>();
var newBounds = change.NewValue.GetValueOrDefault<Rect>();
EnsureSplitViewMode(oldBounds, newBounds); EnsureSplitViewMode(oldBounds, newBounds);
} }
} }

7
src/Avalonia.Base/Animation/Animatable.cs

@ -87,12 +87,13 @@ namespace Avalonia.Animation
} }
} }
protected sealed override void OnPropertyChangedCore<T>(AvaloniaPropertyChangedEventArgs<T> change) protected sealed override void OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.Property == TransitionsProperty && change.IsEffectiveValueChange) if (change.Property == TransitionsProperty && change.IsEffectiveValueChange)
{ {
var oldTransitions = change.OldValue.GetValueOrDefault<Transitions>(); var e = (AvaloniaPropertyChangedEventArgs<Transitions?>)change;
var newTransitions = change.NewValue.GetValueOrDefault<Transitions>(); var oldTransitions = e.OldValue.GetValueOrDefault();
var newTransitions = e.NewValue.GetValueOrDefault();
// When transitions are replaced, we add the new transitions before removing the old // When transitions are replaced, we add the new transitions before removing the old
// transitions, so that when the old transition being disposed causes the value to // transitions, so that when the old transition being disposed causes the value to

65
src/Avalonia.Base/AvaloniaObject.cs

@ -5,6 +5,7 @@ using Avalonia.Data;
using Avalonia.Diagnostics; using Avalonia.Diagnostics;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.PropertyStore; using Avalonia.PropertyStore;
using Avalonia.Reactive;
using Avalonia.Threading; using Avalonia.Threading;
namespace Avalonia namespace Avalonia
@ -15,13 +16,13 @@ namespace Avalonia
/// <remarks> /// <remarks>
/// This class is analogous to DependencyObject in WPF. /// This class is analogous to DependencyObject in WPF.
/// </remarks> /// </remarks>
public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged, IValueSink public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
{ {
private IAvaloniaObject? _inheritanceParent; private AvaloniaObject? _inheritanceParent;
private List<IDisposable>? _directBindings; private List<IDisposable>? _directBindings;
private PropertyChangedEventHandler? _inpcChanged; private PropertyChangedEventHandler? _inpcChanged;
private EventHandler<AvaloniaPropertyChangedEventArgs>? _propertyChanged; private EventHandler<AvaloniaPropertyChangedEventArgs>? _propertyChanged;
private List<IAvaloniaObject>? _inheritanceChildren; private List<AvaloniaObject>? _inheritanceChildren;
private ValueStore? _values; private ValueStore? _values;
private bool _batchUpdate; private bool _batchUpdate;
@ -58,7 +59,7 @@ namespace Avalonia
/// <value> /// <value>
/// The inheritance parent. /// The inheritance parent.
/// </value> /// </value>
protected IAvaloniaObject? InheritanceParent protected AvaloniaObject? InheritanceParent
{ {
get get
{ {
@ -320,14 +321,14 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param> /// <param name="priority">The priority of the value.</param>
public void SetValue( public IDisposable? SetValue(
AvaloniaProperty property, AvaloniaProperty property,
object? value, object? value,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
property.RouteSetValue(this, value, priority); return property.RouteSetValue(this, value, priority);
} }
/// <summary> /// <summary>
@ -385,6 +386,26 @@ namespace Avalonia
SetDirectValueUnchecked(property, value); SetDirectValueUnchecked(property, value);
} }
/// <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)
{
property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source));
return property.RouteBind(this, source.ToBindingValue(), priority);
}
/// <summary> /// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable. /// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary> /// </summary>
@ -445,9 +466,8 @@ namespace Avalonia
/// <summary> /// <summary>
/// Coerces the specified <see cref="AvaloniaProperty"/>. /// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
public void CoerceValue<T>(StyledPropertyBase<T> property) public void CoerceValue(AvaloniaProperty property)
{ {
_values?.CoerceValue(property); _values?.CoerceValue(property);
} }
@ -475,19 +495,19 @@ namespace Avalonia
} }
/// <inheritdoc/> /// <inheritdoc/>
void IAvaloniaObject.AddInheritanceChild(IAvaloniaObject child) internal void AddInheritanceChild(AvaloniaObject child)
{ {
_inheritanceChildren ??= new List<IAvaloniaObject>(); _inheritanceChildren ??= new List<AvaloniaObject>();
_inheritanceChildren.Add(child); _inheritanceChildren.Add(child);
} }
/// <inheritdoc/> /// <inheritdoc/>
void IAvaloniaObject.RemoveInheritanceChild(IAvaloniaObject child) internal void RemoveInheritanceChild(AvaloniaObject child)
{ {
_inheritanceChildren?.Remove(child); _inheritanceChildren?.Remove(child);
} }
void IAvaloniaObject.InheritedPropertyChanged<T>( internal void InheritedPropertyChanged<T>(
AvaloniaProperty<T> property, AvaloniaProperty<T> property,
Optional<T> oldValue, Optional<T> oldValue,
Optional<T> newValue) Optional<T> newValue)
@ -504,7 +524,7 @@ namespace Avalonia
return _propertyChanged?.GetInvocationList(); return _propertyChanged?.GetInvocationList();
} }
void IValueSink.ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) internal void ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{ {
var property = (StyledPropertyBase<T>)change.Property; var property = (StyledPropertyBase<T>)change.Property;
@ -543,7 +563,7 @@ namespace Avalonia
} }
} }
void IValueSink.Completed<T>( internal void Completed<T>(
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
IPriorityValueEntry entry, IPriorityValueEntry entry,
Optional<T> oldValue) Optional<T> oldValue)
@ -554,7 +574,7 @@ namespace Avalonia
oldValue, oldValue,
default, default,
BindingPriority.Unset); BindingPriority.Unset);
((IValueSink)this).ValueChanged(change); ValueChanged(change);
} }
/// <summary> /// <summary>
@ -565,14 +585,11 @@ namespace Avalonia
/// <param name="oldParent">The old inheritance parent.</param> /// <param name="oldParent">The old inheritance parent.</param>
internal void InheritanceParentChanged<T>( internal void InheritanceParentChanged<T>(
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
IAvaloniaObject? oldParent) AvaloniaObject? oldParent)
{ {
var oldValue = oldParent switch var oldValue = oldParent is not null ?
{ oldParent.GetValueOrInheritedOrDefault(property) :
AvaloniaObject o => o.GetValueOrInheritedOrDefault(property), property.GetDefaultValue(GetType());
null => property.GetDefaultValue(GetType()),
_ => oldParent.GetValue(property)
};
var newValue = GetInheritedOrDefault(property); var newValue = GetInheritedOrDefault(property);
@ -640,7 +657,7 @@ namespace Avalonia
/// Called when a avalonia property changes on the object. /// Called when a avalonia property changes on the object.
/// </summary> /// </summary>
/// <param name="change">The property change details.</param> /// <param name="change">The property change details.</param>
protected virtual void OnPropertyChangedCore<T>(AvaloniaPropertyChangedEventArgs<T> change) protected virtual void OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.IsEffectiveValueChange) if (change.IsEffectiveValueChange)
{ {
@ -652,7 +669,7 @@ namespace Avalonia
/// Called when a avalonia property changes on the object. /// Called when a avalonia property changes on the object.
/// </summary> /// </summary>
/// <param name="change">The property change details.</param> /// <param name="change">The property change details.</param>
protected virtual void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected virtual void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
} }

234
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -25,7 +25,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets an observable for a <see cref="AvaloniaProperty"/>. /// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
@ -44,7 +44,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets an observable for a <see cref="AvaloniaProperty"/>. /// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
/// <typeparam name="T">The property type.</typeparam> /// <typeparam name="T">The property type.</typeparam>
@ -64,7 +64,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets an observable for a <see cref="AvaloniaProperty"/>. /// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
@ -85,7 +85,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets an observable for a <see cref="AvaloniaProperty"/>. /// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
/// <typeparam name="T">The property type.</typeparam> /// <typeparam name="T">The property type.</typeparam>
@ -128,7 +128,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets a subject for a <see cref="AvaloniaProperty"/>. /// Gets a subject for an <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
@ -150,7 +150,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets a subject for a <see cref="AvaloniaProperty"/>. /// Gets a subject for an <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The property type.</typeparam> /// <typeparam name="T">The property type.</typeparam>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
@ -230,30 +230,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable. /// Binds an <see cref="AvaloniaProperty"/> to an observable.
/// </summary>
/// <param name="target">The object.</param>
/// <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 static IDisposable Bind(
this IAvaloniaObject target,
AvaloniaProperty property,
IObservable<BindingValue<object?>> source,
BindingPriority priority = BindingPriority.LocalValue)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source));
return property.RouteBind(target, source, priority);
}
/// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the property.</typeparam> /// <typeparam name="T">The type of the property.</typeparam>
/// <param name="target">The object.</param> /// <param name="target">The object.</param>
@ -273,42 +250,22 @@ namespace Avalonia
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source)); source = source ?? throw new ArgumentNullException(nameof(source));
return property switch if (target is AvaloniaObject ao)
{ {
StyledPropertyBase<T> styled => target.Bind(styled, source, priority), return property switch
DirectPropertyBase<T> direct => target.Bind(direct, source), {
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."), StyledPropertyBase<T> styled => ao.Bind(styled, source, priority),
}; DirectPropertyBase<T> direct => ao.Bind(direct, source),
} _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
/// <summary> throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
/// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary>
/// <param name="target">The object.</param>
/// <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 static IDisposable Bind(
this IAvaloniaObject target,
AvaloniaProperty property,
IObservable<object?> source,
BindingPriority priority = BindingPriority.LocalValue)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source));
return target.Bind(
property,
source.ToBindingValue(),
priority);
} }
/// <summary> /// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable. /// Binds an <see cref="AvaloniaProperty"/> to an observable.
/// </summary> /// </summary>
/// <param name="target">The object.</param> /// <param name="target">The object.</param>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
@ -334,7 +291,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Binds a property on an <see cref="IAvaloniaObject"/> to an <see cref="IBinding"/>. /// Binds a property on an <see cref="AvaloniaObject"/> to an <see cref="IBinding"/>.
/// </summary> /// </summary>
/// <param name="target">The object.</param> /// <param name="target">The object.</param>
/// <param name="property">The property to bind.</param> /// <param name="property">The property to bind.</param>
@ -374,56 +331,6 @@ namespace Avalonia
} }
} }
/// <summary>
/// Clears a <see cref="AvaloniaProperty"/>'s local value.
/// </summary>
/// <param name="target">The object.</param>
/// <param name="property">The property.</param>
public static void ClearValue(this IAvaloniaObject target, AvaloniaProperty property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
property.RouteClearValue(target);
}
/// <summary>
/// Clears a <see cref="AvaloniaProperty"/>'s local value.
/// </summary>
/// <param name="target">The object.</param>
/// <param name="property">The property.</param>
public static void ClearValue<T>(this IAvaloniaObject target, AvaloniaProperty<T> property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
switch (property)
{
case StyledPropertyBase<T> styled:
target.ClearValue(styled);
break;
case DirectPropertyBase<T> direct:
target.ClearValue(direct);
break;
default:
throw new NotSupportedException("Unsupported AvaloniaProperty type.");
}
}
/// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <param name="target">The object.</param>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
public static object? GetValue(this IAvaloniaObject target, AvaloniaProperty property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
return property.RouteGetValue(target);
}
/// <summary> /// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value. /// Gets a <see cref="AvaloniaProperty"/> value.
/// </summary> /// </summary>
@ -436,12 +343,18 @@ namespace Avalonia
target = target ?? throw new ArgumentNullException(nameof(target)); target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
return property switch if (target is AvaloniaObject ao)
{ {
StyledPropertyBase<T> styled => target.GetValue(styled), return property switch
DirectPropertyBase<T> direct => target.GetValue(direct), {
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.") StyledPropertyBase<T> styled => ao.GetValue(styled),
}; DirectPropertyBase<T> direct => ao.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -456,7 +369,7 @@ namespace Avalonia
/// <see cref="AvaloniaProperty.UnsetValue"/>. Note that this method does not return /// <see cref="AvaloniaProperty.UnsetValue"/>. Note that this method does not return
/// property values that come from inherited or default values. /// property values that come from inherited or default values.
/// ///
/// For direct properties returns <see cref="GetValue(IAvaloniaObject, AvaloniaProperty)"/>. /// For direct properties returns the current value of the property.
/// </remarks> /// </remarks>
public static object? GetBaseValue( public static object? GetBaseValue(
this IAvaloniaObject target, this IAvaloniaObject target,
@ -466,7 +379,9 @@ namespace Avalonia
target = target ?? throw new ArgumentNullException(nameof(target)); target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
return property.RouteGetBaseValue(target, maxPriority); if (target is AvaloniaObject ao)
return property.RouteGetBaseValue(ao, maxPriority);
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -481,8 +396,7 @@ namespace Avalonia
/// <see cref="Optional{T}.Empty"/>. Note that this method does not return property values /// <see cref="Optional{T}.Empty"/>. Note that this method does not return property values
/// that come from inherited or default values. /// that come from inherited or default values.
/// ///
/// For direct properties returns /// For direct properties returns the current value of the property.
/// <see cref="IAvaloniaObject.GetValue{T}(DirectPropertyBase{T})"/>.
/// </remarks> /// </remarks>
public static Optional<T> GetBaseValue<T>( public static Optional<T> GetBaseValue<T>(
this IAvaloniaObject target, this IAvaloniaObject target,
@ -492,69 +406,18 @@ namespace Avalonia
target = target ?? throw new ArgumentNullException(nameof(target)); target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
target = target ?? throw new ArgumentNullException(nameof(target)); if (target is AvaloniaObject ao)
property = property ?? throw new ArgumentNullException(nameof(property));
return property switch
{ {
StyledPropertyBase<T> styled => target.GetBaseValue(styled, maxPriority), return property switch
DirectPropertyBase<T> direct => target.GetValue(direct), {
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.") StyledPropertyBase<T> styled => ao.GetBaseValue(styled, maxPriority),
}; DirectPropertyBase<T> direct => ao.GetValue(direct),
} _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
/// <summary>
/// Sets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <param name="target">The object.</param>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
public static IDisposable? SetValue(
this IAvaloniaObject target,
AvaloniaProperty property,
object? value,
BindingPriority priority = BindingPriority.LocalValue)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
return property.RouteSetValue(target, value, priority);
}
/// <summary>
/// Sets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="target">The object.</param>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
public static IDisposable? SetValue<T>(
this IAvaloniaObject target,
AvaloniaProperty<T> property,
T value,
BindingPriority priority = BindingPriority.LocalValue)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
switch (property)
{
case StyledPropertyBase<T> styled:
return target.SetValue(styled, value, priority);
case DirectPropertyBase<T> direct:
target.SetValue(direct, value);
return null;
default:
throw new NotSupportedException("Unsupported AvaloniaProperty type.");
} }
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -622,17 +485,6 @@ namespace Avalonia
return observable.Subscribe(e => SubscribeAdapter(e, handler)); return observable.Subscribe(e => SubscribeAdapter(e, handler));
} }
/// <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> /// <summary>
/// Observer method for <see cref="AddClassHandler{TTarget}(IObservable{AvaloniaPropertyChangedEventArgs}, /// Observer method for <see cref="AddClassHandler{TTarget}(IObservable{AvaloniaPropertyChangedEventArgs},
/// Func{TTarget, Action{AvaloniaPropertyChangedEventArgs}})"/>. /// Func{TTarget, Action{AvaloniaPropertyChangedEventArgs}})"/>.

12
src/Avalonia.Base/AvaloniaProperty.cs

@ -467,20 +467,20 @@ namespace Avalonia
/// Routes an untyped ClearValue call to a typed call. /// Routes an untyped ClearValue call to a typed call.
/// </summary> /// </summary>
/// <param name="o">The object instance.</param> /// <param name="o">The object instance.</param>
internal abstract void RouteClearValue(IAvaloniaObject o); internal abstract void RouteClearValue(AvaloniaObject o);
/// <summary> /// <summary>
/// Routes an untyped GetValue call to a typed call. /// Routes an untyped GetValue call to a typed call.
/// </summary> /// </summary>
/// <param name="o">The object instance.</param> /// <param name="o">The object instance.</param>
internal abstract object? RouteGetValue(IAvaloniaObject o); internal abstract object? RouteGetValue(AvaloniaObject o);
/// <summary> /// <summary>
/// Routes an untyped GetBaseValue call to a typed call. /// Routes an untyped GetBaseValue call to a typed call.
/// </summary> /// </summary>
/// <param name="o">The object instance.</param> /// <param name="o">The object instance.</param>
/// <param name="maxPriority">The maximum priority for the value.</param> /// <param name="maxPriority">The maximum priority for the value.</param>
internal abstract object? RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority); internal abstract object? RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority);
/// <summary> /// <summary>
/// Routes an untyped SetValue call to a typed call. /// Routes an untyped SetValue call to a typed call.
@ -492,7 +492,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>
internal abstract IDisposable? RouteSetValue( internal abstract IDisposable? RouteSetValue(
IAvaloniaObject o, AvaloniaObject o,
object? value, object? value,
BindingPriority priority); BindingPriority priority);
@ -503,11 +503,11 @@ namespace Avalonia
/// <param name="source">The binding source.</param> /// <param name="source">The binding source.</param>
/// <param name="priority">The priority.</param> /// <param name="priority">The priority.</param>
internal abstract IDisposable RouteBind( internal abstract IDisposable RouteBind(
IAvaloniaObject o, AvaloniaObject o,
IObservable<BindingValue<object?>> source, IObservable<BindingValue<object?>> source,
BindingPriority priority); BindingPriority priority);
internal abstract void RouteInheritanceParentChanged(AvaloniaObject o, IAvaloniaObject? oldParent); internal abstract void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject? oldParent);
/// <summary> /// <summary>
/// Overrides the metadata for the property on the specified type. /// Overrides the metadata for the property on the specified type.

43
src/Avalonia.Base/AvaloniaPropertyChangedExtensions.cs

@ -0,0 +1,43 @@
namespace Avalonia
{
/// <summary>
/// Provides extensions for <see cref="AvaloniaPropertyChangedEventArgs"/>.
/// </summary>
public static class AvaloniaPropertyChangedExtensions
{
/// <summary>
/// Gets a typed value from <see cref="AvaloniaPropertyChangedEventArgs.OldValue"/>.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="e">The event args.</param>
/// <returns>The value.</returns>
public static T GetOldValue<T>(this AvaloniaPropertyChangedEventArgs e)
{
return ((AvaloniaPropertyChangedEventArgs<T>)e).OldValue.GetValueOrDefault()!;
}
/// <summary>
/// Gets a typed value from <see cref="AvaloniaPropertyChangedEventArgs.NewValue"/>.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="e">The event args.</param>
/// <returns>The value.</returns>
public static T GetNewValue<T>(this AvaloniaPropertyChangedEventArgs e)
{
return ((AvaloniaPropertyChangedEventArgs<T>)e).NewValue.GetValueOrDefault()!;
}
/// <summary>
/// Gets a typed value from <see cref="AvaloniaPropertyChangedEventArgs.OldValue"/> and
/// <see cref="AvaloniaPropertyChangedEventArgs.NewValue"/>.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="e">The event args.</param>
/// <returns>The value.</returns>
public static (T oldValue, T newValue) GetOldAndNewValue<T>(this AvaloniaPropertyChangedEventArgs e)
{
var ev = (AvaloniaPropertyChangedEventArgs<T>)e;
return (ev.OldValue.GetValueOrDefault()!, ev.NewValue.GetValueOrDefault()!);
}
}
}

12
src/Avalonia.Base/DirectPropertyBase.cs

@ -127,25 +127,25 @@ namespace Avalonia
} }
/// <inheritdoc/> /// <inheritdoc/>
internal override void RouteClearValue(IAvaloniaObject o) internal override void RouteClearValue(AvaloniaObject o)
{ {
o.ClearValue<TValue>(this); o.ClearValue<TValue>(this);
} }
/// <inheritdoc/> /// <inheritdoc/>
internal override object? RouteGetValue(IAvaloniaObject o) internal override object? RouteGetValue(AvaloniaObject o)
{ {
return o.GetValue<TValue>(this); return o.GetValue<TValue>(this);
} }
internal override object? RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority) internal override object? RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority)
{ {
return o.GetValue<TValue>(this); return o.GetValue<TValue>(this);
} }
/// <inheritdoc/> /// <inheritdoc/>
internal override IDisposable? RouteSetValue( internal override IDisposable? RouteSetValue(
IAvaloniaObject o, AvaloniaObject o,
object? value, object? value,
BindingPriority priority) BindingPriority priority)
{ {
@ -169,7 +169,7 @@ namespace Avalonia
/// <inheritdoc/> /// <inheritdoc/>
internal override IDisposable RouteBind( internal override IDisposable RouteBind(
IAvaloniaObject o, AvaloniaObject o,
IObservable<BindingValue<object?>> source, IObservable<BindingValue<object?>> source,
BindingPriority priority) BindingPriority priority)
{ {
@ -177,7 +177,7 @@ namespace Avalonia
return o.Bind<TValue>(this, adapter); return o.Bind<TValue>(this, adapter);
} }
internal override void RouteInheritanceParentChanged(AvaloniaObject o, IAvaloniaObject? oldParent) internal override void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject? oldParent)
{ {
throw new NotSupportedException("Direct properties do not support inheritance."); throw new NotSupportedException("Direct properties do not support inheritance.");
} }

2
src/Avalonia.Base/GeometryGroup.cs

@ -68,7 +68,7 @@ namespace Avalonia.Media
return null; return null;
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

104
src/Avalonia.Base/IAvaloniaObject.cs

@ -17,42 +17,14 @@ namespace Avalonia
/// Clears an <see cref="AvaloniaProperty"/>'s local value. /// Clears an <see cref="AvaloniaProperty"/>'s local value.
/// </summary> /// </summary>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
void ClearValue<T>(StyledPropertyBase<T> property); void ClearValue(AvaloniaProperty property);
/// <summary>
/// Clears an <see cref="AvaloniaProperty"/>'s local value.
/// </summary>
/// <param name="property">The property.</param>
void ClearValue<T>(DirectPropertyBase<T> property);
/// <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>
T GetValue<T>(StyledPropertyBase<T> property);
/// <summary> /// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value. /// Gets a <see cref="AvaloniaProperty"/> value.
/// </summary> /// </summary>
/// <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>
T GetValue<T>(DirectPropertyBase<T> property); object? GetValue(AvaloniaProperty property);
/// <summary>
/// Gets an <see cref="AvaloniaProperty"/> base value.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="maxPriority">The maximum priority for the value.</param>
/// <remarks>
/// Gets the value of the property, if set on this object with a priority equal or lower to
/// <paramref name="maxPriority"/>, otherwise <see cref="Optional{T}.Empty"/>. Note that
/// this method does not return property values that come from inherited or default values.
/// </remarks>
Optional<T> GetBaseValue<T>(StyledPropertyBase<T> property, BindingPriority maxPriority);
/// <summary> /// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is animating. /// Checks whether a <see cref="AvaloniaProperty"/> is animating.
@ -71,93 +43,35 @@ namespace Avalonia
/// <summary> /// <summary>
/// Sets a <see cref="AvaloniaProperty"/> value. /// Sets a <see cref="AvaloniaProperty"/> value.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param> /// <param name="priority">The priority of the value.</param>
/// <returns> /// <returns>
/// 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>
IDisposable? SetValue<T>( IDisposable? SetValue(
StyledPropertyBase<T> property, AvaloniaProperty property,
T value, object? value,
BindingPriority priority = BindingPriority.LocalValue); BindingPriority priority = BindingPriority.LocalValue);
/// <summary>
/// Sets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
void SetValue<T>(DirectPropertyBase<T> property, T value);
/// <summary> /// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable. /// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <param name="source">The observable.</param> /// <param name="source">The observable.</param>
/// <param name="priority">The priority of the binding.</param> /// <param name="priority">The priority of the binding.</param>
/// <returns> /// <returns>
/// A disposable which can be used to terminate the binding. /// A disposable which can be used to terminate the binding.
/// </returns> /// </returns>
IDisposable Bind<T>( IDisposable Bind(
StyledPropertyBase<T> property, AvaloniaProperty property,
IObservable<BindingValue<T>> source, IObservable<object?> source,
BindingPriority priority = BindingPriority.LocalValue); BindingPriority priority = BindingPriority.LocalValue);
/// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="source">The observable.</param>
/// <returns>
/// A disposable which can be used to terminate the binding.
/// </returns>
IDisposable Bind<T>(
DirectPropertyBase<T> property,
IObservable<BindingValue<T>> source);
/// <summary> /// <summary>
/// Coerces the specified <see cref="AvaloniaProperty"/>. /// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
void CoerceValue<T>(StyledPropertyBase<T> property); void CoerceValue(AvaloniaProperty property);
/// <summary>
/// Registers an object as an inheritance child.
/// </summary>
/// <param name="child">The inheritance child.</param>
/// <remarks>
/// Inheritance children will receive a call to
/// <see cref="InheritedPropertyChanged{T}(AvaloniaProperty{T}, Optional{T}, Optional{T})"/>
/// when an inheritable property value changes on the parent.
/// </remarks>
void AddInheritanceChild(IAvaloniaObject child);
/// <summary>
/// Unregisters an object as an inheritance child.
/// </summary>
/// <param name="child">The inheritance child.</param>
/// <remarks>
/// Removes an inheritance child that was added by a call to
/// <see cref="AddInheritanceChild(IAvaloniaObject)"/>.
/// </remarks>
void RemoveInheritanceChild(IAvaloniaObject child);
/// <summary>
/// Called when an inheritable property changes on an object registered as an inheritance
/// parent.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="property">The property that has changed.</param>
/// <param name="oldValue"></param>
/// <param name="newValue"></param>
void InheritedPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
Optional<T> newValue);
} }
} }

8
src/Avalonia.Base/Input/InputElement.cs

@ -601,21 +601,21 @@ namespace Avalonia.Input
{ {
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == IsFocusedProperty) if (change.Property == IsFocusedProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>(), null); UpdatePseudoClasses(change.GetNewValue<bool>(), null);
} }
else if (change.Property == IsPointerOverProperty) else if (change.Property == IsPointerOverProperty)
{ {
UpdatePseudoClasses(null, change.NewValue.GetValueOrDefault<bool>()); UpdatePseudoClasses(null, change.GetNewValue<bool>());
} }
else if (change.Property == IsKeyboardFocusWithinProperty) else if (change.Property == IsKeyboardFocusWithinProperty)
{ {
PseudoClasses.Set(":focus-within", change.NewValue.GetValueOrDefault<bool>()); PseudoClasses.Set(":focus-within", change.GetNewValue<bool>());
} }
} }

2
src/Avalonia.Base/Input/KeyboardNavigation.cs

@ -58,7 +58,7 @@ namespace Avalonia.Input
/// <returns>The <see cref="KeyboardNavigationMode"/> for the container.</returns> /// <returns>The <see cref="KeyboardNavigationMode"/> for the container.</returns>
public static int GetTabIndex(IInputElement element) public static int GetTabIndex(IInputElement element)
{ {
return ((IAvaloniaObject)element).GetValue(TabIndexProperty); return ((AvaloniaObject)element).GetValue(TabIndexProperty);
} }
/// <summary> /// <summary>

5
src/Avalonia.Base/Input/Navigation/TabNavigation.cs

@ -610,7 +610,7 @@ namespace Avalonia.Input.Navigation
private static IInputElement? GetActiveElement(IInputElement e) private static IInputElement? GetActiveElement(IInputElement e)
{ {
return ((IAvaloniaObject)e).GetValue(KeyboardNavigation.TabOnceActiveElementProperty); return ((AvaloniaObject)e).GetValue(KeyboardNavigation.TabOnceActiveElementProperty);
} }
private static IInputElement GetGroupParent(IInputElement e) => GetGroupParent(e, false); private static IInputElement GetGroupParent(IInputElement e) => GetGroupParent(e, false);
@ -655,8 +655,9 @@ namespace Avalonia.Input.Navigation
private static KeyboardNavigationMode GetKeyNavigationMode(IInputElement e) private static KeyboardNavigationMode GetKeyNavigationMode(IInputElement e)
{ {
return ((IAvaloniaObject)e).GetValue(KeyboardNavigation.TabNavigationProperty); return ((AvaloniaObject)e).GetValue(KeyboardNavigation.TabNavigationProperty);
} }
private static bool IsFocusScope(IInputElement e) => FocusManager.GetIsFocusScope(e) || GetParent(e) == null; private static bool IsFocusScope(IInputElement e) => FocusManager.GetIsFocusScope(e) || GetParent(e) == null;
private static bool IsGroup(IInputElement e) => GetKeyNavigationMode(e) != KeyboardNavigationMode.Continue; private static bool IsGroup(IInputElement e) => GetKeyNavigationMode(e) != KeyboardNavigationMode.Continue;

4
src/Avalonia.Base/Layout/StackLayout.cs

@ -320,11 +320,11 @@ namespace Avalonia.Layout
InvalidateLayout(); InvalidateLayout();
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.Property == OrientationProperty) if (change.Property == OrientationProperty)
{ {
var orientation = change.NewValue.GetValueOrDefault<Orientation>(); var orientation = change.GetNewValue<Orientation>();
//Note: For StackLayout Vertical Orientation means we have a Vertical ScrollOrientation. //Note: For StackLayout Vertical Orientation means we have a Vertical ScrollOrientation.
//Horizontal Orientation means we have a Horizontal ScrollOrientation. //Horizontal Orientation means we have a Horizontal ScrollOrientation.

18
src/Avalonia.Base/Layout/UniformGridLayout.cs

@ -471,11 +471,11 @@ namespace Avalonia.Layout
gridState.ClearElementOnDataSourceChange(context, args); gridState.ClearElementOnDataSourceChange(context, args);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.Property == OrientationProperty) if (change.Property == OrientationProperty)
{ {
var orientation = change.NewValue.GetValueOrDefault<Orientation>(); var orientation = change.GetNewValue<Orientation>();
//Note: For UniformGridLayout Vertical Orientation means we have a Horizontal ScrollOrientation. Horizontal Orientation means we have a Vertical ScrollOrientation. //Note: For UniformGridLayout Vertical Orientation means we have a Horizontal ScrollOrientation. Horizontal Orientation means we have a Vertical ScrollOrientation.
//i.e. the properties are the inverse of each other. //i.e. the properties are the inverse of each other.
@ -484,31 +484,31 @@ namespace Avalonia.Layout
} }
else if (change.Property == MinColumnSpacingProperty) else if (change.Property == MinColumnSpacingProperty)
{ {
_minColumnSpacing = change.NewValue.GetValueOrDefault<double>(); _minColumnSpacing = change.GetNewValue<double>();
} }
else if (change.Property == MinRowSpacingProperty) else if (change.Property == MinRowSpacingProperty)
{ {
_minRowSpacing = change.NewValue.GetValueOrDefault<double>(); _minRowSpacing = change.GetNewValue<double>();
} }
else if (change.Property == ItemsJustificationProperty) else if (change.Property == ItemsJustificationProperty)
{ {
_itemsJustification = change.NewValue.GetValueOrDefault<UniformGridLayoutItemsJustification>(); _itemsJustification = change.GetNewValue<UniformGridLayoutItemsJustification>();
} }
else if (change.Property == ItemsStretchProperty) else if (change.Property == ItemsStretchProperty)
{ {
_itemsStretch = change.NewValue.GetValueOrDefault<UniformGridLayoutItemsStretch>(); _itemsStretch = change.GetNewValue<UniformGridLayoutItemsStretch>();
} }
else if (change.Property == MinItemWidthProperty) else if (change.Property == MinItemWidthProperty)
{ {
_minItemWidth = change.NewValue.GetValueOrDefault<double>(); _minItemWidth = change.GetNewValue<double>();
} }
else if (change.Property == MinItemHeightProperty) else if (change.Property == MinItemHeightProperty)
{ {
_minItemHeight = change.NewValue.GetValueOrDefault<double>(); _minItemHeight = change.GetNewValue<double>();
} }
else if (change.Property == MaximumRowsOrColumnsProperty) else if (change.Property == MaximumRowsOrColumnsProperty)
{ {
_maximumRowsOrColumns = change.NewValue.GetValueOrDefault<int>(); _maximumRowsOrColumns = change.GetNewValue<int>();
} }
InvalidateLayout(); InvalidateLayout();

2
src/Avalonia.Base/Layout/WrapLayout/WrapLayout.cs

@ -322,7 +322,7 @@ namespace Avalonia.Layout
return finalSize; return finalSize;
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

5
src/Avalonia.Base/Media/DashStyle.cs

@ -112,14 +112,13 @@ namespace Avalonia.Media
/// <returns></returns> /// <returns></returns>
public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset); public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset);
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == DashesProperty) if (change.Property == DashesProperty)
{ {
var oldValue = change.OldValue.GetValueOrDefault<AvaloniaList<double>>(); var (oldValue, newValue) = change.GetOldAndNewValue<AvaloniaList<double>>();
var newValue = change.NewValue.GetValueOrDefault<AvaloniaList<double>>();
if (oldValue is object) if (oldValue is object)
{ {

2
src/Avalonia.Base/Media/DrawingImage.cs

@ -69,7 +69,7 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

2
src/Avalonia.Base/Media/Pen.cs

@ -193,7 +193,7 @@ namespace Avalonia.Media
MiterLimit); MiterLimit);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
_invalidated?.Invoke(this, EventArgs.Empty); _invalidated?.Invoke(this, EventArgs.Empty);
if(change.Property == BrushProperty) if(change.Property == BrushProperty)

17
src/Avalonia.Base/PropertyStore/BindingEntry.cs

@ -18,19 +18,19 @@ namespace Avalonia.PropertyStore
/// <typeparam name="T">The property type.</typeparam> /// <typeparam name="T">The property type.</typeparam>
internal class BindingEntry<T> : IBindingEntry, IPriorityValueEntry<T>, IObserver<BindingValue<T>> internal class BindingEntry<T> : IBindingEntry, IPriorityValueEntry<T>, IObserver<BindingValue<T>>
{ {
private readonly IAvaloniaObject _owner; private readonly AvaloniaObject _owner;
private IValueSink _sink; private ValueOwner<T> _sink;
private IDisposable? _subscription; private IDisposable? _subscription;
private bool _isSubscribed; private bool _isSubscribed;
private bool _batchUpdate; private bool _batchUpdate;
private Optional<T> _value; private Optional<T> _value;
public BindingEntry( public BindingEntry(
IAvaloniaObject owner, AvaloniaObject owner,
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
IObservable<BindingValue<T>> source, IObservable<BindingValue<T>> source,
BindingPriority priority, BindingPriority priority,
IValueSink sink) ValueOwner<T> sink)
{ {
_owner = owner; _owner = owner;
Property = property; Property = property;
@ -50,7 +50,7 @@ namespace Avalonia.PropertyStore
{ {
_batchUpdate = false; _batchUpdate = false;
if (_sink is ValueStore) if (_sink.IsValueStore)
Start(); Start();
} }
@ -113,16 +113,15 @@ namespace Avalonia.PropertyStore
} }
} }
public void Reparent(IValueSink sink) => _sink = sink; public void Reparent(PriorityValue<T> parent) => _sink = new(parent);
public void RaiseValueChanged( public void RaiseValueChanged(
IValueSink sink, AvaloniaObject owner,
IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object?> oldValue, Optional<object?> oldValue,
Optional<object?> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,
(AvaloniaProperty<T>)property, (AvaloniaProperty<T>)property,
oldValue.Cast<T>(), oldValue.Cast<T>(),

13
src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs

@ -18,14 +18,14 @@ namespace Avalonia.PropertyStore
/// <typeparam name="T">The property type.</typeparam> /// <typeparam name="T">The property type.</typeparam>
internal class ConstantValueEntry<T> : IPriorityValueEntry<T>, IConstantValueEntry internal class ConstantValueEntry<T> : IPriorityValueEntry<T>, IConstantValueEntry
{ {
private IValueSink _sink; private ValueOwner<T> _sink;
private Optional<T> _value; private Optional<T> _value;
public ConstantValueEntry( public ConstantValueEntry(
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
T value, T value,
BindingPriority priority, BindingPriority priority,
IValueSink sink) ValueOwner<T> sink)
{ {
Property = property; Property = property;
_value = value; _value = value;
@ -37,7 +37,7 @@ namespace Avalonia.PropertyStore
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
Optional<T> value, Optional<T> value,
BindingPriority priority, BindingPriority priority,
IValueSink sink) ValueOwner<T> sink)
{ {
Property = property; Property = property;
_value = value; _value = value;
@ -62,17 +62,16 @@ namespace Avalonia.PropertyStore
_sink.Completed(Property, this, oldValue); _sink.Completed(Property, this, oldValue);
} }
public void Reparent(IValueSink sink) => _sink = sink; public void Reparent(PriorityValue<T> sink) => _sink = new(sink);
public void Start() { } public void Start() { }
public void RaiseValueChanged( public void RaiseValueChanged(
IValueSink sink, AvaloniaObject owner,
IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object?> oldValue, Optional<object?> oldValue,
Optional<object?> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,
(AvaloniaProperty<T>)property, (AvaloniaProperty<T>)property,
oldValue.Cast<T>(), oldValue.Cast<T>(),

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

@ -5,7 +5,6 @@
/// </summary> /// </summary>
internal interface IPriorityValueEntry : IValue internal interface IPriorityValueEntry : IValue
{ {
void Reparent(IValueSink sink);
} }
/// <summary> /// <summary>
@ -14,5 +13,6 @@
/// <typeparam name="T">The property type.</typeparam> /// <typeparam name="T">The property type.</typeparam>
internal interface IPriorityValueEntry<T> : IPriorityValueEntry, IValue<T> internal interface IPriorityValueEntry<T> : IPriorityValueEntry, IValue<T>
{ {
void Reparent(PriorityValue<T> parent);
} }
} }

3
src/Avalonia.Base/PropertyStore/IValue.cs

@ -11,8 +11,7 @@ namespace Avalonia.PropertyStore
Optional<object?> GetValue(); Optional<object?> GetValue();
void Start(); void Start();
void RaiseValueChanged( void RaiseValueChanged(
IValueSink sink, AvaloniaObject owner,
IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object?> oldValue, Optional<object?> oldValue,
Optional<object?> newValue); Optional<object?> newValue);

17
src/Avalonia.Base/PropertyStore/IValueSink.cs

@ -1,17 +0,0 @@
using Avalonia.Data;
namespace Avalonia.PropertyStore
{
/// <summary>
/// Represents an entity that can receive change notifications in a <see cref="ValueStore"/>.
/// </summary>
internal interface IValueSink
{
void ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change);
void Completed<T>(
StyledPropertyBase<T> property,
IPriorityValueEntry entry,
Optional<T> oldValue);
}
}

5
src/Avalonia.Base/PropertyStore/LocalValueEntry.cs

@ -25,13 +25,12 @@ namespace Avalonia.PropertyStore
public void Start() { } public void Start() { }
public void RaiseValueChanged( public void RaiseValueChanged(
IValueSink sink, AvaloniaObject owner,
IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object?> oldValue, Optional<object?> oldValue,
Optional<object?> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,
(AvaloniaProperty<T>)property, (AvaloniaProperty<T>)property,
oldValue.Cast<T>(), oldValue.Cast<T>(),

67
src/Avalonia.Base/PropertyStore/PriorityValue.cs

@ -1,10 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using Avalonia.Data; using Avalonia.Data;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
/// <summary>
/// Represents an untyped interface to <see cref="PriorityValue{T}"/>.
/// </summary>
interface IPriorityValue : IValue
{
void UpdateEffectiveValue();
}
/// <summary> /// <summary>
/// Stores a set of prioritized values and bindings in a <see cref="ValueStore"/>. /// Stores a set of prioritized values and bindings in a <see cref="ValueStore"/>.
/// </summary> /// </summary>
@ -16,10 +23,10 @@ namespace Avalonia.PropertyStore
/// <see cref="IPriorityValueEntry{T}"/> entries (sorted first by priority and then in the order /// <see cref="IPriorityValueEntry{T}"/> entries (sorted first by priority and then in the order
/// they were added) plus a local value. /// they were added) plus a local value.
/// </remarks> /// </remarks>
internal class PriorityValue<T> : IValue<T>, IValueSink, IBatchUpdate internal class PriorityValue<T> : IPriorityValue, IValue<T>, IBatchUpdate
{ {
private readonly IAvaloniaObject _owner; private readonly AvaloniaObject _owner;
private readonly IValueSink _sink; private readonly ValueStore _store;
private readonly List<IPriorityValueEntry<T>> _entries = new List<IPriorityValueEntry<T>>(); private readonly List<IPriorityValueEntry<T>> _entries = new List<IPriorityValueEntry<T>>();
private readonly Func<IAvaloniaObject, T, T>? _coerceValue; private readonly Func<IAvaloniaObject, T, T>? _coerceValue;
private Optional<T> _localValue; private Optional<T> _localValue;
@ -28,13 +35,13 @@ namespace Avalonia.PropertyStore
private bool _batchUpdate; private bool _batchUpdate;
public PriorityValue( public PriorityValue(
IAvaloniaObject owner, AvaloniaObject owner,
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
IValueSink sink) ValueStore store)
{ {
_owner = owner; _owner = owner;
Property = property; Property = property;
_sink = sink; _store = store;
if (property.HasCoercion) if (property.HasCoercion)
{ {
@ -44,11 +51,11 @@ namespace Avalonia.PropertyStore
} }
public PriorityValue( public PriorityValue(
IAvaloniaObject owner, AvaloniaObject owner,
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
IValueSink sink, ValueStore store,
IPriorityValueEntry<T> existing) IPriorityValueEntry<T> existing)
: this(owner, property, sink) : this(owner, property, store)
{ {
existing.Reparent(this); existing.Reparent(this);
_entries.Add(existing); _entries.Add(existing);
@ -75,9 +82,9 @@ namespace Avalonia.PropertyStore
} }
public PriorityValue( public PriorityValue(
IAvaloniaObject owner, AvaloniaObject owner,
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
IValueSink sink, ValueStore sink,
LocalValueEntry<T> existing) LocalValueEntry<T> existing)
: this(owner, property, sink) : this(owner, property, sink)
{ {
@ -148,7 +155,7 @@ namespace Avalonia.PropertyStore
else else
{ {
var insert = FindInsertPoint(priority); var insert = FindInsertPoint(priority);
var entry = new ConstantValueEntry<T>(Property, value, priority, this); var entry = new ConstantValueEntry<T>(Property, value, priority, new ValueOwner<T>(this));
_entries.Insert(insert, entry); _entries.Insert(insert, entry);
result = entry; result = entry;
} }
@ -165,7 +172,7 @@ namespace Avalonia.PropertyStore
public BindingEntry<T> AddBinding(IObservable<BindingValue<T>> source, BindingPriority priority) public BindingEntry<T> AddBinding(IObservable<BindingValue<T>> source, BindingPriority priority)
{ {
var binding = new BindingEntry<T>(_owner, Property, source, priority, this); var binding = new BindingEntry<T>(_owner, Property, source, priority, new(this));
var insert = FindInsertPoint(binding.Priority); var insert = FindInsertPoint(binding.Priority);
_entries.Insert(insert, binding); _entries.Insert(insert, binding);
@ -186,13 +193,12 @@ namespace Avalonia.PropertyStore
public void Start() => UpdateEffectiveValue(null); public void Start() => UpdateEffectiveValue(null);
public void RaiseValueChanged( public void RaiseValueChanged(
IValueSink sink, AvaloniaObject owner,
IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object?> oldValue, Optional<object?> oldValue,
Optional<object?> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,
(AvaloniaProperty<T>)property, (AvaloniaProperty<T>)property,
oldValue.Cast<T>(), oldValue.Cast<T>(),
@ -200,7 +206,7 @@ namespace Avalonia.PropertyStore
Priority)); Priority));
} }
void IValueSink.ValueChanged<TValue>(AvaloniaPropertyChangedEventArgs<TValue> change) public void ValueChanged<TValue>(AvaloniaPropertyChangedEventArgs<TValue> change)
{ {
if (change.Priority == BindingPriority.LocalValue) if (change.Priority == BindingPriority.LocalValue)
{ {
@ -213,22 +219,15 @@ namespace Avalonia.PropertyStore
} }
} }
void IValueSink.Completed<TValue>( public void Completed(IPriorityValueEntry entry, Optional<T> oldValue)
StyledPropertyBase<TValue> property,
IPriorityValueEntry entry,
Optional<TValue> oldValue)
{ {
_entries.Remove((IPriorityValueEntry<T>)entry); _entries.Remove((IPriorityValueEntry<T>)entry);
UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs<T>(
if (oldValue is Optional<T> o) _owner,
{ Property,
UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs<T>( oldValue,
_owner, default,
Property, entry.Priority));
o,
default,
entry.Priority));
}
} }
private int FindInsertPoint(BindingPriority priority) private int FindInsertPoint(BindingPriority priority)
@ -308,7 +307,7 @@ namespace Avalonia.PropertyStore
var old = _value; var old = _value;
_value = value; _value = value;
_sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( _store.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
_owner, _owner,
Property, Property,
old, old,
@ -319,7 +318,7 @@ namespace Avalonia.PropertyStore
{ {
change.MarkNonEffectiveValue(); change.MarkNonEffectiveValue();
change.SetOldValue(default); change.SetOldValue(default);
_sink.ValueChanged(change); _store.ValueChanged(change);
} }
} }
} }

45
src/Avalonia.Base/PropertyStore/ValueOwner.cs

@ -0,0 +1,45 @@
using Avalonia.Data;
namespace Avalonia.PropertyStore
{
/// <summary>
/// Represents a union type of <see cref="ValueStore"/> and <see cref="PriorityValue{T}"/>,
/// which are the valid owners of a value store <see cref="IValue"/>.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal readonly struct ValueOwner<T>
{
private readonly ValueStore? _store;
private readonly PriorityValue<T>? _priorityValue;
public ValueOwner(ValueStore o)
{
_store = o;
_priorityValue = null;
}
public ValueOwner(PriorityValue<T> v)
{
_store = null;
_priorityValue = v;
}
public bool IsValueStore => _store is not null;
public void Completed(StyledPropertyBase<T> property, IPriorityValueEntry entry, Optional<T> oldValue)
{
if (_store is not null)
_store?.Completed(property, entry, oldValue);
else
_priorityValue!.Completed(entry, oldValue);
}
public void ValueChanged(AvaloniaPropertyChangedEventArgs<T> e)
{
if (_store is not null)
_store?.ValueChanged(e);
else
_priorityValue!.ValueChanged(e);
}
}
}

2
src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs

@ -51,7 +51,7 @@ namespace Avalonia.Reactive
{ {
if (e is AvaloniaPropertyChangedEventArgs<T> typedArgs) if (e is AvaloniaPropertyChangedEventArgs<T> typedArgs)
{ {
var newValue = e.Sender.GetValue(typedArgs.Property); var newValue = e.Sender.GetValue<T>(typedArgs.Property);
if (!_value.HasValue || !EqualityComparer<T>.Default.Equals(newValue, _value.Value)) if (!_value.HasValue || !EqualityComparer<T>.Default.Equals(newValue, _value.Value))
{ {

30
src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs

@ -49,23 +49,31 @@ namespace Avalonia.Reactive
{ {
if (e.Property == _property) if (e.Property == _property)
{ {
T newValue; if (e.Sender is AvaloniaObject ao)
if (e is AvaloniaPropertyChangedEventArgs<T> typed)
{ {
newValue = typed.Sender.GetValue(typed.Property); T newValue;
if (e is AvaloniaPropertyChangedEventArgs<T> typed)
{
newValue = AvaloniaObjectExtensions.GetValue(ao, typed.Property);
}
else
{
newValue = (T)e.Sender.GetValue(e.Property)!;
}
if (!_value.HasValue ||
!EqualityComparer<T>.Default.Equals(newValue, _value.Value))
{
_value = newValue;
PublishNext(_value.Value!);
}
} }
else else
{ {
newValue = (T)e.Sender.GetValue(e.Property)!; throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
if (!_value.HasValue ||
!EqualityComparer<T>.Default.Equals(newValue, _value.Value))
{
_value = newValue;
PublishNext(_value.Value!);
}
} }
} }
} }

7
src/Avalonia.Base/StyledElement.cs

@ -467,7 +467,12 @@ namespace Avalonia
/// <param name="parent">The parent.</param> /// <param name="parent">The parent.</param>
void ISetInheritanceParent.SetParent(IAvaloniaObject? parent) void ISetInheritanceParent.SetParent(IAvaloniaObject? parent)
{ {
InheritanceParent = parent; InheritanceParent = parent switch
{
AvaloniaObject ao => ao,
null => null,
_ => throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.")
};
} }
void IStyleable.StyleApplied(IStyleInstance instance) void IStyleable.StyleApplied(IStyleInstance instance)

12
src/Avalonia.Base/StyledPropertyBase.cs

@ -177,19 +177,19 @@ namespace Avalonia
object? IStyledPropertyAccessor.GetDefaultValue(Type type) => GetDefaultBoxedValue(type); object? IStyledPropertyAccessor.GetDefaultValue(Type type) => GetDefaultBoxedValue(type);
/// <inheritdoc/> /// <inheritdoc/>
internal override void RouteClearValue(IAvaloniaObject o) internal override void RouteClearValue(AvaloniaObject o)
{ {
o.ClearValue<TValue>(this); o.ClearValue<TValue>(this);
} }
/// <inheritdoc/> /// <inheritdoc/>
internal override object? RouteGetValue(IAvaloniaObject o) internal override object? RouteGetValue(AvaloniaObject o)
{ {
return o.GetValue<TValue>(this); return o.GetValue<TValue>(this);
} }
/// <inheritdoc/> /// <inheritdoc/>
internal override object? RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority) internal override object? RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority)
{ {
var value = o.GetBaseValue<TValue>(this, maxPriority); var value = o.GetBaseValue<TValue>(this, maxPriority);
return value.HasValue ? value.Value : AvaloniaProperty.UnsetValue; return value.HasValue ? value.Value : AvaloniaProperty.UnsetValue;
@ -197,7 +197,7 @@ namespace Avalonia
/// <inheritdoc/> /// <inheritdoc/>
internal override IDisposable? RouteSetValue( internal override IDisposable? RouteSetValue(
IAvaloniaObject o, AvaloniaObject o,
object? value, object? value,
BindingPriority priority) BindingPriority priority)
{ {
@ -221,7 +221,7 @@ namespace Avalonia
/// <inheritdoc/> /// <inheritdoc/>
internal override IDisposable RouteBind( internal override IDisposable RouteBind(
IAvaloniaObject o, AvaloniaObject o,
IObservable<BindingValue<object?>> source, IObservable<BindingValue<object?>> source,
BindingPriority priority) BindingPriority priority)
{ {
@ -232,7 +232,7 @@ namespace Avalonia
/// <inheritdoc/> /// <inheritdoc/>
internal override void RouteInheritanceParentChanged( internal override void RouteInheritanceParentChanged(
AvaloniaObject o, AvaloniaObject o,
IAvaloniaObject? oldParent) AvaloniaObject? oldParent)
{ {
o.InheritanceParentChanged(this, oldParent); o.InheritanceParentChanged(this, oldParent);
} }

27
src/Avalonia.Base/ValueStore.cs

@ -21,16 +21,15 @@ namespace Avalonia
/// - For a single binding it will be an instance of <see cref="BindingEntry{T}"/> /// - For a single binding it will be an instance of <see cref="BindingEntry{T}"/>
/// - For all other cases it will be an instance of <see cref="PriorityValue{T}"/> /// - For all other cases it will be an instance of <see cref="PriorityValue{T}"/>
/// </remarks> /// </remarks>
internal class ValueStore : IValueSink internal class ValueStore
{ {
private readonly AvaloniaObject _owner; private readonly AvaloniaObject _owner;
private readonly IValueSink _sink;
private readonly AvaloniaPropertyValueStore<IValue> _values; private readonly AvaloniaPropertyValueStore<IValue> _values;
private BatchUpdate? _batchUpdate; private BatchUpdate? _batchUpdate;
public ValueStore(AvaloniaObject owner) public ValueStore(AvaloniaObject owner)
{ {
_sink = _owner = owner; _owner = owner;
_values = new AvaloniaPropertyValueStore<IValue>(); _values = new AvaloniaPropertyValueStore<IValue>();
} }
@ -122,7 +121,7 @@ namespace Avalonia
} }
else else
{ {
var entry = new ConstantValueEntry<T>(property, value, priority, this); var entry = new ConstantValueEntry<T>(property, value, priority, new(this));
AddValue(property, entry); AddValue(property, entry);
NotifyValueChanged<T>(property, default, value, priority); NotifyValueChanged<T>(property, default, value, priority);
result = entry; result = entry;
@ -151,7 +150,7 @@ namespace Avalonia
} }
else else
{ {
var entry = new BindingEntry<T>(_owner, property, source, priority, this); var entry = new BindingEntry<T>(_owner, property, source, priority, new(this));
AddValue(property, entry); AddValue(property, entry);
return entry; return entry;
} }
@ -187,7 +186,7 @@ namespace Avalonia
// so there's no way to mark them for removal at the end of a batch update. Instead convert // so there's no way to mark them for removal at the end of a batch update. Instead convert
// them to a constant value entry with Unset priority in the event of a local value being // them to a constant value entry with Unset priority in the event of a local value being
// cleared during a batch update. // cleared during a batch update.
var sentinel = new ConstantValueEntry<T>(property, Optional<T>.Empty, BindingPriority.Unset, _sink); var sentinel = new ConstantValueEntry<T>(property, Optional<T>.Empty, BindingPriority.Unset, new(this));
_values.SetValue(property, sentinel); _values.SetValue(property, sentinel);
} }
@ -196,11 +195,11 @@ namespace Avalonia
} }
} }
public void CoerceValue<T>(StyledPropertyBase<T> property) public void CoerceValue(AvaloniaProperty property)
{ {
if (TryGetValue(property, out var slot)) if (TryGetValue(property, out var slot))
{ {
if (slot is PriorityValue<T> p) if (slot is IPriorityValue p)
{ {
p.UpdateEffectiveValue(); p.UpdateEffectiveValue();
} }
@ -222,7 +221,7 @@ namespace Avalonia
return null; return null;
} }
void IValueSink.ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) public void ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{ {
if (_batchUpdate is object) if (_batchUpdate is object)
{ {
@ -233,11 +232,11 @@ namespace Avalonia
} }
else else
{ {
_sink.ValueChanged(change); _owner.ValueChanged(change);
} }
} }
void IValueSink.Completed<T>( public void Completed<T>(
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
IPriorityValueEntry entry, IPriorityValueEntry entry,
Optional<T> oldValue) Optional<T> oldValue)
@ -248,7 +247,7 @@ namespace Avalonia
if (_batchUpdate is null) if (_batchUpdate is null)
{ {
_values.Remove(property); _values.Remove(property);
_sink.Completed(property, entry, oldValue); _owner.Completed(property, entry, oldValue);
} }
else else
{ {
@ -352,7 +351,7 @@ namespace Avalonia
{ {
if (_batchUpdate is null) if (_batchUpdate is null)
{ {
_sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( _owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
_owner, _owner,
property, property,
oldValue, oldValue,
@ -451,7 +450,7 @@ namespace Avalonia
}; };
// Call _sink.ValueChanged with an appropriately typed AvaloniaPropertyChangedEventArgs<T>. // Call _sink.ValueChanged with an appropriately typed AvaloniaPropertyChangedEventArgs<T>.
slot.RaiseValueChanged(_owner._sink, _owner._owner, entry.property, oldValue, newValue); slot.RaiseValueChanged(_owner._owner, entry.property, oldValue, newValue);
// During batch update values can't be removed immediately because they're needed to raise // During batch update values can't be removed immediately because they're needed to raise
// the _sink.ValueChanged notification. They instead mark themselves for removal by setting // the _sink.ValueChanged notification. They instead mark themselves for removal by setting

5
src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt

@ -1 +1,4 @@
Total Issues: 0 Compat issues with assembly Avalonia.Controls.DataGrid:
MembersMustExist : Member 'protected void Avalonia.Controls.DataGridCheckBoxColumn.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.DataGridTextColumn.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
Total Issues: 2

2
src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs

@ -46,7 +46,7 @@ namespace Avalonia.Controls
set => SetValue(IsThreeStateProperty, value); set => SetValue(IsThreeStateProperty, value);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

4
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -192,14 +192,14 @@ namespace Avalonia.Controls
set => SetValue(IsVisibleProperty, value); set => SetValue(IsVisibleProperty, value);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == IsVisibleProperty) if (change.Property == IsVisibleProperty)
{ {
OwningGrid?.OnColumnVisibleStateChanging(this); OwningGrid?.OnColumnVisibleStateChanging(this);
var isVisible = change.NewValue.GetValueOrDefault<bool>(); var isVisible = change.GetNewValue<bool>();
if (_headerCell != null) if (_headerCell != null)
{ {

2
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -1062,7 +1062,7 @@ namespace Avalonia.Controls
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.Property == DataContextProperty) if (change.Property == DataContextProperty)
{ {

2
src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs

@ -119,7 +119,7 @@ namespace Avalonia.Controls
set => SetValue(ForegroundProperty, value); set => SetValue(ForegroundProperty, value);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

17
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -1,11 +1,17 @@
Compat issues with assembly Avalonia.Controls: Compat issues with assembly Avalonia.Controls:
MembersMustExist : Member 'protected void Avalonia.Controls.Button.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.ButtonSpinner.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.CalendarDatePicker.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.ContextMenu.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Controls.DropDown' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Controls.DropDown' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Controls.DropDownItem' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Controls.DropDownItem' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Expander.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick.get()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.ItemsRepeater.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.NumericUpDown, System.Double> Avalonia.DirectProperty<Avalonia.Controls.NumericUpDown, System.Double> Avalonia.Controls.NumericUpDown.ValueProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.NumericUpDown, System.Double> Avalonia.DirectProperty<Avalonia.Controls.NumericUpDown, System.Double> Avalonia.Controls.NumericUpDown.ValueProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.NumericUpDown.IncrementProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.NumericUpDown.IncrementProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.NumericUpDown.MaximumProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.NumericUpDown.MaximumProperty' does not exist in the implementation but it does exist in the contract.
@ -30,7 +36,9 @@ MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDown.Value.set
MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDownValueChangedEventArgs..ctor(Avalonia.Interactivity.RoutedEvent, System.Double, System.Double)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDownValueChangedEventArgs..ctor(Avalonia.Interactivity.RoutedEvent, System.Double, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.NewValue.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.NewValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.ProgressBar.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Slider.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontFamily> Avalonia.AttachedProperty<Avalonia.Media.FontFamily> Avalonia.Controls.TextBlock.FontFamilyProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontFamily> Avalonia.AttachedProperty<Avalonia.Media.FontFamily> Avalonia.Controls.TextBlock.FontFamilyProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontStyle> Avalonia.AttachedProperty<Avalonia.Media.FontStyle> Avalonia.Controls.TextBlock.FontStyleProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontStyle> Avalonia.AttachedProperty<Avalonia.Media.FontStyle> Avalonia.Controls.TextBlock.FontStyleProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontWeight> Avalonia.AttachedProperty<Avalonia.Media.FontWeight> Avalonia.Controls.TextBlock.FontWeightProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontWeight> Avalonia.AttachedProperty<Avalonia.Media.FontWeight> Avalonia.Controls.TextBlock.FontWeightProperty' does not exist in the implementation but it does exist in the contract.
@ -51,10 +59,12 @@ MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontSize(A
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontStyle(Avalonia.Controls.Control, Avalonia.Media.FontStyle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontStyle(Avalonia.Controls.Control, Avalonia.Media.FontStyle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontWeight(Avalonia.Controls.Control, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontWeight(Avalonia.Controls.Control, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetForeground(Avalonia.Controls.Control, Avalonia.Media.IBrush)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetForeground(Avalonia.Controls.Control, Avalonia.Media.IBrush)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.TextBox.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.TopLevel' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.TopLevel' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Viewbox' does not inherit from base type 'Avalonia.Controls.Decorator' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Viewbox' does not inherit from base type 'Avalonia.Controls.Decorator' in the implementation but it does in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Window' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Window' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Window.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.WindowBase' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.WindowBase' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.EventHandler<Avalonia.Controls.ApplicationLifetimes.ShutdownRequestedEventArgs> Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.ShutdownRequested' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.EventHandler<Avalonia.Controls.ApplicationLifetimes.ShutdownRequestedEventArgs> Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.ShutdownRequested' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.add_ShutdownRequested(System.EventHandler<Avalonia.Controls.ApplicationLifetimes.ShutdownRequestedEventArgs>)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.add_ShutdownRequested(System.EventHandler<Avalonia.Controls.ApplicationLifetimes.ShutdownRequestedEventArgs>)' is present in the implementation but not in the contract.
@ -65,12 +75,17 @@ MembersMustExist : Member 'public System.Action<Avalonia.Size> Avalonia.Controls
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.Resized.set(System.Action<Avalonia.Size>)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.Resized.set(System.Action<Avalonia.Size>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Notifications.WindowNotificationManager.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation.
MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.ScrollContentPresenter.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.CreateFormattedText()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.CreateFormattedText()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.FormattedText.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.FormattedText.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Int32 Avalonia.Controls.Presenters.TextPresenter.GetCaretIndex(Avalonia.Point)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Int32 Avalonia.Controls.Presenters.TextPresenter.GetCaretIndex(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.TextPresenter.InvalidateFormattedText()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.TextPresenter.InvalidateFormattedText()' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.ScrollBar.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.Track.OnPropertyChanged<T>(Avalonia.AvaloniaPropertyChangedEventArgs<T>)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Platform.ExportWindowingSubsystemAttribute' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Platform.ExportWindowingSubsystemAttribute' does not exist in the implementation but it does exist in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromPoint(Avalonia.PixelPoint)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromPoint(Avalonia.PixelPoint)' is present in the implementation but not in the contract.
@ -94,4 +109,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor
MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract.
Total Issues: 95 Total Issues: 110

14
src/Avalonia.Controls/Button.cs

@ -413,7 +413,7 @@ namespace Avalonia.Controls
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -421,12 +421,13 @@ namespace Avalonia.Controls
{ {
if (((ILogical)this).IsAttachedToLogicalTree) if (((ILogical)this).IsAttachedToLogicalTree)
{ {
if (change.OldValue.GetValueOrDefault() is ICommand oldCommand) var (oldValue, newValue) = change.GetOldAndNewValue<ICommand?>();
if (oldValue is ICommand oldCommand)
{ {
oldCommand.CanExecuteChanged -= CanExecuteChanged; oldCommand.CanExecuteChanged -= CanExecuteChanged;
} }
if (change.NewValue.GetValueOrDefault() is ICommand newCommand) if (newValue is ICommand newCommand)
{ {
newCommand.CanExecuteChanged += CanExecuteChanged; newCommand.CanExecuteChanged += CanExecuteChanged;
} }
@ -440,7 +441,7 @@ namespace Avalonia.Controls
} }
else if (change.Property == IsCancelProperty) else if (change.Property == IsCancelProperty)
{ {
var isCancel = change.NewValue.GetValueOrDefault<bool>(); var isCancel = change.GetNewValue<bool>();
if (VisualRoot is IInputElement inputRoot) if (VisualRoot is IInputElement inputRoot)
{ {
@ -456,7 +457,7 @@ namespace Avalonia.Controls
} }
else if (change.Property == IsDefaultProperty) else if (change.Property == IsDefaultProperty)
{ {
var isDefault = change.NewValue.GetValueOrDefault<bool>(); var isDefault = change.GetNewValue<bool>();
if (VisualRoot is IInputElement inputRoot) if (VisualRoot is IInputElement inputRoot)
{ {
@ -476,8 +477,7 @@ namespace Avalonia.Controls
} }
else if (change.Property == FlyoutProperty) else if (change.Property == FlyoutProperty)
{ {
var oldFlyout = change.OldValue.GetValueOrDefault() as FlyoutBase; var (oldFlyout, newFlyout) = change.GetOldAndNewValue<FlyoutBase?>();
var newFlyout = change.NewValue.GetValueOrDefault() as FlyoutBase;
// If flyout is changed while one is already open, make sure we // If flyout is changed while one is already open, make sure we
// close the old one first // close the old one first

4
src/Avalonia.Controls/ButtonSpinner.cs

@ -210,13 +210,13 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ButtonSpinnerLocationProperty) if (change.Property == ButtonSpinnerLocationProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<Location>()); UpdatePseudoClasses(change.GetNewValue<Location>());
} }
} }

4
src/Avalonia.Controls/ContextMenu.cs

@ -241,13 +241,13 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == WindowManagerAddShadowHintProperty && _popup != null) if (change.Property == WindowManagerAddShadowHintProperty && _popup != null)
{ {
_popup.WindowManagerAddShadowHint = change.NewValue.GetValueOrDefault<bool>(); _popup.WindowManagerAddShadowHint = change.GetNewValue<bool>();
} }
} }

2
src/Avalonia.Controls/Control.cs

@ -348,7 +348,7 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

2
src/Avalonia.Controls/Documents/Inline.cs

@ -55,7 +55,7 @@ namespace Avalonia.Controls.Documents
TextDecorations, Foreground, Background, BaselineAlignment); TextDecorations, Foreground, Background, BaselineAlignment);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

2
src/Avalonia.Controls/Documents/Run.cs

@ -71,7 +71,7 @@ namespace Avalonia.Controls.Documents
return text.Length; return text.Length;
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

2
src/Avalonia.Controls/Documents/TextElement.cs

@ -256,7 +256,7 @@ namespace Avalonia.Controls.Documents
/// </summary> /// </summary>
public event EventHandler? Invalidated; public event EventHandler? Invalidated;
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

4
src/Avalonia.Controls/Expander.cs

@ -106,13 +106,13 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ExpandDirectionProperty) if (change.Property == ExpandDirectionProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<ExpandDirection>()); UpdatePseudoClasses(change.GetNewValue<ExpandDirection>());
} }
} }

4
src/Avalonia.Controls/ItemsControl.cs

@ -341,13 +341,13 @@ namespace Avalonia.Controls
return new ItemsControlAutomationPeer(this); return new ItemsControlAutomationPeer(this);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ItemCountProperty) if (change.Property == ItemCountProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<int>()); UpdatePseudoClasses(change.GetNewValue<int>());
} }
} }

2
src/Avalonia.Controls/MaskedTextBox.cs

@ -280,7 +280,7 @@ namespace Avalonia.Controls
base.OnLostFocus(e); base.OnLostFocus(e);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
void UpdateMaskProvider() void UpdateMaskProvider()
{ {

4
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -139,13 +139,13 @@ namespace Avalonia.Controls.Notifications
notificationControl.Close(); notificationControl.Close();
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == PositionProperty) if (change.Property == PositionProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<NotificationPosition>()); UpdatePseudoClasses(change.GetNewValue<NotificationPosition>());
} }
} }

2
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -403,7 +403,7 @@ namespace Avalonia.Controls.Presenters
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
switch (change.Property.Name) switch (change.Property.Name)

2
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -469,7 +469,7 @@ namespace Avalonia.Controls.Presenters
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.Property == OffsetProperty && !_arranging) if (change.Property == OffsetProperty && !_arranging)
{ {

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -776,7 +776,7 @@ namespace Avalonia.Controls.Presenters
_caretTimer.Tick -= CaretTimerTick; _caretTimer.Tick -= CaretTimerTick;
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

46
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -69,15 +69,18 @@ namespace Avalonia.Controls.Primitives
{ {
foreach (var child in Children) foreach (var child in Children)
{ {
var info = child.GetValue(s_adornedElementInfoProperty); if (child is AvaloniaObject ao)
if (info != null && info.Bounds.HasValue)
{
child.Measure(info.Bounds.Value.Bounds.Size);
}
else
{ {
child.Measure(availableSize); var info = ao.GetValue(s_adornedElementInfoProperty);
if (info != null && info.Bounds.HasValue)
{
child.Measure(info.Bounds.Value.Bounds.Size);
}
else
{
child.Measure(availableSize);
}
} }
} }
@ -88,19 +91,22 @@ namespace Avalonia.Controls.Primitives
{ {
foreach (var child in Children) foreach (var child in Children)
{ {
var info = child.GetValue(s_adornedElementInfoProperty); if (child is AvaloniaObject ao)
var isClipEnabled = child.GetValue(IsClipEnabledProperty);
if (info != null && info.Bounds.HasValue)
{
child.RenderTransform = new MatrixTransform(info.Bounds.Value.Transform);
child.RenderTransformOrigin = new RelativePoint(new Point(0,0), RelativeUnit.Absolute);
UpdateClip(child, info.Bounds.Value, isClipEnabled);
child.Arrange(info.Bounds.Value.Bounds);
}
else
{ {
child.Arrange(new Rect(finalSize)); var info = ao.GetValue(s_adornedElementInfoProperty);
var isClipEnabled = ao.GetValue(IsClipEnabledProperty);
if (info != null && info.Bounds.HasValue)
{
child.RenderTransform = new MatrixTransform(info.Bounds.Value.Transform);
child.RenderTransformOrigin = new RelativePoint(new Point(0, 0), RelativeUnit.Absolute);
UpdateClip(child, info.Bounds.Value, isClipEnabled);
child.Arrange(info.Bounds.Value.Bounds);
}
else
{
child.Arrange(new Rect(finalSize));
}
} }
} }

4
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -194,13 +194,13 @@ namespace Avalonia.Controls.Primitives
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == OrientationProperty) if (change.Property == OrientationProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<Orientation>()); UpdatePseudoClasses(change.GetNewValue<Orientation>());
} }
else if (change.Property == AllowAutoHideProperty) else if (change.Property == AllowAutoHideProperty)
{ {

10
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -533,9 +533,9 @@ namespace Avalonia.Controls.Primitives
bool Match(ItemContainerInfo info) bool Match(ItemContainerInfo info)
{ {
if (info.ContainerControl.IsSet(TextSearch.TextProperty)) if (info.ContainerControl is AvaloniaObject ao && ao.IsSet(TextSearch.TextProperty))
{ {
var searchText = info.ContainerControl.GetValue(TextSearch.TextProperty); var searchText = ao.GetValue(TextSearch.TextProperty);
if (searchText?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true) if (searchText?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true)
{ {
@ -585,7 +585,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -595,7 +595,7 @@ namespace Avalonia.Controls.Primitives
} }
if (change.Property == ItemsProperty && _updateState is null && _selection is object) if (change.Property == ItemsProperty && _updateState is null && _selection is object)
{ {
var newValue = change.NewValue.GetValueOrDefault<IEnumerable>(); var newValue = change.GetNewValue<IEnumerable>();
_selection.Source = newValue; _selection.Source = newValue;
if (newValue is null) if (newValue is null)
@ -605,7 +605,7 @@ namespace Avalonia.Controls.Primitives
} }
else if (change.Property == SelectionModeProperty && _selection is object) else if (change.Property == SelectionModeProperty && _selection is object)
{ {
var newValue = change.NewValue.GetValueOrDefault<SelectionMode>(); var newValue = change.GetNewValue<SelectionMode>();
_selection.SingleSelect = !newValue.HasAllFlags(SelectionMode.Multiple); _selection.SingleSelect = !newValue.HasAllFlags(SelectionMode.Multiple);
} }
else if (change.Property == WrapSelectionProperty) else if (change.Property == WrapSelectionProperty)

4
src/Avalonia.Controls/Primitives/Track.cs

@ -291,13 +291,13 @@ namespace Avalonia.Controls.Primitives
return arrangeSize; return arrangeSize;
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == OrientationProperty) if (change.Property == OrientationProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<Orientation>()); UpdatePseudoClasses(change.GetNewValue<Orientation>());
} }
} }

6
src/Avalonia.Controls/ProgressBar.cs

@ -178,17 +178,17 @@ namespace Avalonia.Controls
return base.ArrangeOverride(finalSize); return base.ArrangeOverride(finalSize);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == IsIndeterminateProperty) if (change.Property == IsIndeterminateProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>(), null); UpdatePseudoClasses(change.GetNewValue<bool>(), null);
} }
else if (change.Property == OrientationProperty) else if (change.Property == OrientationProperty)
{ {
UpdatePseudoClasses(null, change.NewValue.GetValueOrDefault<Orientation>()); UpdatePseudoClasses(null, change.GetNewValue<Orientation>());
} }
} }

4
src/Avalonia.Controls/RepeatButton.cs

@ -70,11 +70,11 @@ namespace Avalonia.Controls
_repeatTimer?.Stop(); _repeatTimer?.Stop();
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == IsPressedProperty && change.NewValue.GetValueOrDefault<bool>() == false) if (change.Property == IsPressedProperty && change.GetNewValue<bool>() == false)
{ {
StopTimer(); StopTimer();
} }

43
src/Avalonia.Controls/Repeater/ItemsRepeater.cs

@ -267,10 +267,9 @@ namespace Avalonia.Controls
internal void UnpinElement(IControl element) => _viewManager.UpdatePin(element, false); internal void UnpinElement(IControl element) => _viewManager.UpdatePin(element, false);
internal static VirtualizationInfo TryGetVirtualizationInfo(IControl element) internal static VirtualizationInfo? TryGetVirtualizationInfo(IControl element)
{ {
var value = element.GetValue(VirtualizationInfoProperty); return (element as AvaloniaObject)?.GetValue(VirtualizationInfoProperty);
return value;
} }
internal static VirtualizationInfo CreateAndInitializeVirtualizationInfo(IControl element) internal static VirtualizationInfo CreateAndInitializeVirtualizationInfo(IControl element)
@ -287,15 +286,20 @@ namespace Avalonia.Controls
internal static VirtualizationInfo GetVirtualizationInfo(IControl element) internal static VirtualizationInfo GetVirtualizationInfo(IControl element)
{ {
var result = element.GetValue(VirtualizationInfoProperty); if (element is AvaloniaObject ao)
if (result == null)
{ {
result = new VirtualizationInfo(); var result = ao.GetValue(VirtualizationInfoProperty);
element.SetValue(VirtualizationInfoProperty, result);
if (result == null)
{
result = new VirtualizationInfo();
ao.SetValue(VirtualizationInfoProperty, result);
}
return result;
} }
return result; throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
private protected override void InvalidateMeasureOnChildrenChanged() private protected override void InvalidateMeasureOnChildrenChanged()
@ -426,12 +430,11 @@ namespace Avalonia.Controls
_viewportManager.ResetScrollers(); _viewportManager.ResetScrollers();
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.Property == ItemsProperty) if (change.Property == ItemsProperty)
{ {
var oldEnumerable = change.OldValue.GetValueOrDefault<IEnumerable>(); var (oldEnumerable, newEnumerable) = change.GetOldAndNewValue<IEnumerable?>();
var newEnumerable = change.NewValue.GetValueOrDefault<IEnumerable>();
if (oldEnumerable != newEnumerable) if (oldEnumerable != newEnumerable)
{ {
@ -446,23 +449,21 @@ namespace Avalonia.Controls
} }
else if (change.Property == ItemTemplateProperty) else if (change.Property == ItemTemplateProperty)
{ {
OnItemTemplateChanged( var (oldvalue, newValue) = change.GetOldAndNewValue<IDataTemplate?>();
change.OldValue.GetValueOrDefault<IDataTemplate>(), OnItemTemplateChanged(oldvalue, newValue);
change.NewValue.GetValueOrDefault<IDataTemplate>());
} }
else if (change.Property == LayoutProperty) else if (change.Property == LayoutProperty)
{ {
OnLayoutChanged( var (oldvalue, newValue) = change.GetOldAndNewValue<AttachedLayout>();
change.OldValue.GetValueOrDefault<AttachedLayout>(), OnLayoutChanged(oldvalue, newValue);
change.NewValue.GetValueOrDefault<AttachedLayout>());
} }
else if (change.Property == HorizontalCacheLengthProperty) else if (change.Property == HorizontalCacheLengthProperty)
{ {
_viewportManager.HorizontalCacheLength = change.NewValue.GetValueOrDefault<double>(); _viewportManager.HorizontalCacheLength = change.GetNewValue<double>();
} }
else if (change.Property == VerticalCacheLengthProperty) else if (change.Property == VerticalCacheLengthProperty)
{ {
_viewportManager.VerticalCacheLength = change.NewValue.GetValueOrDefault<double>(); _viewportManager.VerticalCacheLength = change.GetNewValue<double>();
} }
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -497,7 +498,7 @@ namespace Avalonia.Controls
if (parent == this) if (parent == this)
{ {
var virtInfo = TryGetVirtualizationInfo(element); var virtInfo = TryGetVirtualizationInfo(element);
return _viewManager.GetElementIndex(virtInfo); return _viewManager.GetElementIndex(virtInfo!);
} }
return -1; return -1;

4
src/Avalonia.Controls/Repeater/RecyclePool.cs

@ -80,8 +80,8 @@ namespace Avalonia.Controls
return null; return null;
} }
internal string GetReuseKey(IControl element) => element.GetValue(ReuseKeyProperty); internal string GetReuseKey(IControl element) => ((Control)element).GetValue(ReuseKeyProperty);
internal void SetReuseKey(IControl element, string value) => element.SetValue(ReuseKeyProperty, value); internal void SetReuseKey(IControl element, string value) => ((Control)element).SetValue(ReuseKeyProperty, value);
private IPanel? EnsureOwnerIsPanelOrNull(IControl? owner) private IPanel? EnsureOwnerIsPanelOrNull(IControl? owner)
{ {

6
src/Avalonia.Controls/Repeater/ViewManager.cs

@ -47,7 +47,7 @@ namespace Avalonia.Controls
if (madeAnchor != null) if (madeAnchor != null)
{ {
var anchorVirtInfo = ItemsRepeater.TryGetVirtualizationInfo(madeAnchor); var anchorVirtInfo = ItemsRepeater.TryGetVirtualizationInfo(madeAnchor);
if (anchorVirtInfo.Index == index) if (anchorVirtInfo!.Index == index)
{ {
element = madeAnchor; element = madeAnchor;
} }
@ -60,12 +60,12 @@ namespace Avalonia.Controls
var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element); var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);
if (suppressAutoRecycle) if (suppressAutoRecycle)
{ {
virtInfo.AutoRecycleCandidate = false; virtInfo!.AutoRecycleCandidate = false;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} Not AutoRecycleCandidate:", virtInfo.Index); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} Not AutoRecycleCandidate:", virtInfo.Index);
} }
else else
{ {
virtInfo.AutoRecycleCandidate = true; virtInfo!.AutoRecycleCandidate = true;
virtInfo.KeepAlive = true; virtInfo.KeepAlive = true;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} AutoRecycleCandidate:", virtInfo.Index); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} AutoRecycleCandidate:", virtInfo.Index);
} }

4
src/Avalonia.Controls/Slider.cs

@ -369,13 +369,13 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == OrientationProperty) if (change.Property == OrientationProperty)
{ {
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<Orientation>()); UpdatePseudoClasses(change.GetNewValue<Orientation>());
} }
} }

11
src/Avalonia.Controls/SplitButton/SplitButton.cs

@ -276,19 +276,21 @@ namespace Avalonia.Controls
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> e) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
if (e.Property == CommandProperty) if (e.Property == CommandProperty)
{ {
if (_isAttachedToLogicalTree) if (_isAttachedToLogicalTree)
{ {
// Must unregister events here while a reference to the old command still exists // Must unregister events here while a reference to the old command still exists
if (e.OldValue.GetValueOrDefault() is ICommand oldCommand) var (oldValue, newValue) = e.GetOldAndNewValue<ICommand?>();
if (oldValue is ICommand oldCommand)
{ {
oldCommand.CanExecuteChanged -= CanExecuteChanged; oldCommand.CanExecuteChanged -= CanExecuteChanged;
} }
if (e.NewValue.GetValueOrDefault() is ICommand newCommand) if (newValue is ICommand newCommand)
{ {
newCommand.CanExecuteChanged += CanExecuteChanged; newCommand.CanExecuteChanged += CanExecuteChanged;
} }
@ -302,8 +304,7 @@ namespace Avalonia.Controls
} }
else if (e.Property == FlyoutProperty) else if (e.Property == FlyoutProperty)
{ {
var oldFlyout = e.OldValue.GetValueOrDefault() as FlyoutBase; var (oldFlyout, newFlyout) = e.GetOldAndNewValue<FlyoutBase?>();
var newFlyout = e.NewValue.GetValueOrDefault() as FlyoutBase;
// If flyout is changed while one is already open, make sure we // If flyout is changed while one is already open, make sure we
// close the old one first // close the old one first

2
src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs

@ -90,7 +90,7 @@ namespace Avalonia.Controls
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> e) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
if (e.Property == IsCheckedProperty) if (e.Property == IsCheckedProperty)
{ {

2
src/Avalonia.Controls/TextBlock.cs

@ -639,7 +639,7 @@ namespace Avalonia.Controls
private static bool IsValidLineHeight(double lineHeight) => double.IsNaN(lineHeight) || lineHeight > 0; private static bool IsValidLineHeight(double lineHeight) => double.IsNaN(lineHeight) || lineHeight > 0;
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

4
src/Avalonia.Controls/TextBox.cs

@ -585,7 +585,7 @@ namespace Avalonia.Controls
_imClient.SetPresenter(null, null); _imClient.SetPresenter(null, null);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -594,7 +594,7 @@ namespace Avalonia.Controls
UpdatePseudoclasses(); UpdatePseudoclasses();
UpdateCommandStates(); UpdateCommandStates();
} }
else if (change.Property == IsUndoEnabledProperty && change.NewValue.GetValueOrDefault<bool>() == false) else if (change.Property == IsUndoEnabledProperty && change.GetNewValue<bool>() == false)
{ {
// from docs at // from docs at
// https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.textboxbase.isundoenabled: // https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.textboxbase.isundoenabled:

2
src/Avalonia.Controls/TransitioningContentControl.cs

@ -61,7 +61,7 @@ public class TransitioningContentControl : ContentControl
_lastTransitionCts?.Cancel(); _lastTransitionCts?.Cancel();
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

8
src/Avalonia.Controls/TrayIcon.cs

@ -206,7 +206,7 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -216,15 +216,15 @@ namespace Avalonia.Controls
} }
else if (change.Property == IsVisibleProperty) else if (change.Property == IsVisibleProperty)
{ {
_impl?.SetIsVisible(change.NewValue.GetValueOrDefault<bool>()); _impl?.SetIsVisible(change.GetNewValue<bool>());
} }
else if (change.Property == ToolTipTextProperty) else if (change.Property == ToolTipTextProperty)
{ {
_impl?.SetToolTipText(change.NewValue.GetValueOrDefault<string?>()); _impl?.SetToolTipText(change.GetNewValue<string?>());
} }
else if (change.Property == MenuProperty) else if (change.Property == MenuProperty)
{ {
_impl?.MenuExporter?.SetNativeMenu(change.NewValue.GetValueOrDefault<NativeMenu>()); _impl?.MenuExporter?.SetNativeMenu(change.GetNewValue<NativeMenu?>());
} }
} }

4
src/Avalonia.Controls/Viewbox.cs

@ -82,13 +82,13 @@ namespace Avalonia.Controls
set => _containerVisual.RenderTransform = value; set => _containerVisual.RenderTransform = value;
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ChildProperty) if (change.Property == ChildProperty)
{ {
_containerVisual.Child = change.NewValue.GetValueOrDefault<IControl>(); _containerVisual.Child = change.GetNewValue<IControl>();
InvalidateMeasure(); InvalidateMeasure();
} }
} }

6
src/Avalonia.Controls/Window.cs

@ -1019,16 +1019,16 @@ namespace Avalonia.Controls
/// </remarks> /// </remarks>
protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e); protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e);
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == SystemDecorationsProperty) if (change.Property == SystemDecorationsProperty)
{ {
var typedNewValue = change.NewValue.GetValueOrDefault<SystemDecorations>(); var (typedOldValue, typedNewValue) = change.GetOldAndNewValue<SystemDecorations>();
PlatformImpl?.SetSystemDecorations(typedNewValue); PlatformImpl?.SetSystemDecorations(typedNewValue);
var o = change.OldValue.GetValueOrDefault<SystemDecorations>() == SystemDecorations.Full; var o = typedOldValue == SystemDecorations.Full;
var n = typedNewValue == SystemDecorations.Full; var n = typedNewValue == SystemDecorations.Full;
if (o != n) if (o != n)

4
src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs

@ -92,7 +92,7 @@ namespace Avalonia.Diagnostics.Controls
set => SetValue(HighlightProperty, value); set => SetValue(HighlightProperty, value);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -102,7 +102,7 @@ namespace Avalonia.Diagnostics.Controls
{ {
_isUpdatingThickness = true; _isUpdatingThickness = true;
var value = change.NewValue.GetValueOrDefault<Thickness>(); var value = change.GetNewValue<Thickness>();
Left = value.Left; Left = value.Left;
Top = value.Top; Top = value.Top;

4
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs

@ -110,7 +110,7 @@ namespace Avalonia.Diagnostics.ViewModels
private void UpdateSizeConstraints() private void UpdateSizeConstraints()
{ {
if (_control is IAvaloniaObject ao) if (_control is AvaloniaObject ao)
{ {
string? CreateConstraintInfo(StyledProperty<double> minProperty, StyledProperty<double> maxProperty) string? CreateConstraintInfo(StyledProperty<double> minProperty, StyledProperty<double> maxProperty)
{ {
@ -191,7 +191,7 @@ namespace Avalonia.Diagnostics.ViewModels
} }
else else
{ {
if (_control is IAvaloniaObject ao) if (_control is AvaloniaObject ao)
{ {
if (e.Property == Layoutable.MarginProperty) if (e.Property == Layoutable.MarginProperty)
{ {

2
src/Avalonia.Themes.Default/SimpleTheme.cs

@ -116,7 +116,7 @@ namespace Avalonia.Themes.Default
return false; return false;
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ModeProperty) if (change.Property == ModeProperty)

2
src/Avalonia.Themes.Fluent/FluentTheme.cs

@ -78,7 +78,7 @@ namespace Avalonia.Themes.Fluent
set => SetValue(DensityStyleProperty, value); set => SetValue(DensityStyleProperty, value);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ModeProperty) if (change.Property == ModeProperty)

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

@ -138,7 +138,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
NameScopeSetNameScope = NameScope.GetMethod(new FindMethodMethodSignature("SetNameScope", NameScopeSetNameScope = NameScope.GetMethod(new FindMethodMethodSignature("SetNameScope",
XamlIlTypes.Void, StyledElement, INameScope) XamlIlTypes.Void, StyledElement, INameScope)
{ IsStatic = true }); { IsStatic = true });
AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", XamlIlTypes.Void, AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", IDisposable,
false, AvaloniaProperty, XamlIlTypes.Object, BindingPriority); false, AvaloniaProperty, XamlIlTypes.Object, BindingPriority);
IPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.IPropertyInfo"); IPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.IPropertyInfo");
ClrPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.ClrPropertyInfo"); ClrPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.ClrPropertyInfo");

15
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs

@ -109,25 +109,26 @@ namespace Avalonia.Base.UnitTests
public List<AvaloniaPropertyChangedEventArgs> Changes { get; } public List<AvaloniaPropertyChangedEventArgs> Changes { get; }
public List<AvaloniaPropertyChangedEventArgs> CoreChanges { get; } public List<AvaloniaPropertyChangedEventArgs> CoreChanges { get; }
protected override void OnPropertyChangedCore<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
{ {
CoreChanges.Add(Clone(change)); CoreChanges.Add(Clone(change));
base.OnPropertyChangedCore(change); base.OnPropertyChangedCore(change);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
Changes.Add(Clone(change)); Changes.Add(Clone(change));
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
} }
private static AvaloniaPropertyChangedEventArgs<T> Clone<T>(AvaloniaPropertyChangedEventArgs<T> change) private static AvaloniaPropertyChangedEventArgs Clone(AvaloniaPropertyChangedEventArgs change)
{ {
var result = new AvaloniaPropertyChangedEventArgs<T>( var e = (AvaloniaPropertyChangedEventArgs<string>)change;
var result = new AvaloniaPropertyChangedEventArgs<string>(
change.Sender, change.Sender,
change.Property, e.Property,
change.OldValue, e.OldValue,
change.NewValue, e.NewValue,
change.Priority); change.Priority);
if (!change.IsEffectiveValueChange) if (!change.IsEffectiveValueChange)

12
tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs

@ -152,35 +152,35 @@ namespace Avalonia.Base.UnitTests
} }
internal override IDisposable RouteBind( internal override IDisposable RouteBind(
IAvaloniaObject o, AvaloniaObject o,
IObservable<BindingValue<object>> source, IObservable<BindingValue<object>> source,
BindingPriority priority) BindingPriority priority)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal override void RouteClearValue(IAvaloniaObject o) internal override void RouteClearValue(AvaloniaObject o)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal override object RouteGetValue(IAvaloniaObject o) internal override object RouteGetValue(AvaloniaObject o)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal override object RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority) internal override object RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal override void RouteInheritanceParentChanged(AvaloniaObject o, IAvaloniaObject oldParent) internal override void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject oldParent)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal override IDisposable RouteSetValue( internal override IDisposable RouteSetValue(
IAvaloniaObject o, AvaloniaObject o,
object value, object value,
BindingPriority priority) BindingPriority priority)
{ {

49
tests/Avalonia.Base.UnitTests/PriorityValueTests.cs

@ -10,8 +10,8 @@ namespace Avalonia.Base.UnitTests
{ {
public class PriorityValueTests public class PriorityValueTests
{ {
private static readonly IValueSink NullSink = new MockSink(); private static readonly AvaloniaObject Owner = new AvaloniaObject();
private static readonly IAvaloniaObject Owner = Mock.Of<IAvaloniaObject>(); private static readonly ValueStore ValueStore = new ValueStore(Owner);
private static readonly StyledProperty<string> TestProperty = new StyledProperty<string>( private static readonly StyledProperty<string> TestProperty = new StyledProperty<string>(
"Test", "Test",
typeof(PriorityValueTests), typeof(PriorityValueTests),
@ -23,12 +23,12 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>( var target = new PriorityValue<string>(
Owner, Owner,
TestProperty, TestProperty,
NullSink, ValueStore,
new ConstantValueEntry<string>( new ConstantValueEntry<string>(
TestProperty, TestProperty,
"1", "1",
BindingPriority.StyleTrigger, BindingPriority.StyleTrigger,
NullSink)); new(ValueStore)));
Assert.Equal("1", target.GetValue().Value); Assert.Equal("1", target.GetValue().Value);
Assert.Equal(BindingPriority.StyleTrigger, target.Priority); Assert.Equal(BindingPriority.StyleTrigger, target.Priority);
@ -40,7 +40,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>( var target = new PriorityValue<string>(
Owner, Owner,
TestProperty, TestProperty,
NullSink); ValueStore);
target.SetValue("animation", BindingPriority.Animation); target.SetValue("animation", BindingPriority.Animation);
target.SetValue("local", BindingPriority.LocalValue); target.SetValue("local", BindingPriority.LocalValue);
@ -60,7 +60,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>( var target = new PriorityValue<string>(
Owner, Owner,
TestProperty, TestProperty,
NullSink); ValueStore);
target.SetValue("1", BindingPriority.LocalValue); target.SetValue("1", BindingPriority.LocalValue);
target.SetValue("2", BindingPriority.LocalValue); target.SetValue("2", BindingPriority.LocalValue);
@ -74,7 +74,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>( var target = new PriorityValue<string>(
Owner, Owner,
TestProperty, TestProperty,
NullSink); ValueStore);
target.SetValue("1", BindingPriority.Style); target.SetValue("1", BindingPriority.Style);
target.SetValue("2", BindingPriority.Animation); target.SetValue("2", BindingPriority.Animation);
@ -93,7 +93,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>( var target = new PriorityValue<string>(
Owner, Owner,
TestProperty, TestProperty,
NullSink); ValueStore);
Assert.Equal(BindingPriority.Unset, target.Priority); Assert.Equal(BindingPriority.Unset, target.Priority);
target.SetValue("style", BindingPriority.Style); target.SetValue("style", BindingPriority.Style);
@ -109,7 +109,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void Binding_With_Same_Priority_Should_Be_Appended() public void Binding_With_Same_Priority_Should_Be_Appended()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
@ -129,7 +129,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void Binding_With_Higher_Priority_Should_Be_Appended() public void Binding_With_Higher_Priority_Should_Be_Appended()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
@ -149,7 +149,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void Binding_With_Lower_Priority_Should_Be_Prepended() public void Binding_With_Lower_Priority_Should_Be_Prepended()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
@ -169,7 +169,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void Second_Binding_With_Lower_Priority_Should_Be_Inserted_In_Middle() public void Second_Binding_With_Lower_Priority_Should_Be_Inserted_In_Middle()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
var source3 = new Source("3"); var source3 = new Source("3");
@ -191,7 +191,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void Competed_Binding_Should_Be_Removed() public void Competed_Binding_Should_Be_Removed()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
var source3 = new Source("3"); var source3 = new Source("3");
@ -214,7 +214,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void Value_Should_Come_From_Last_Entry() public void Value_Should_Come_From_Last_Entry()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
var source3 = new Source("3"); var source3 = new Source("3");
@ -229,7 +229,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void LocalValue_Should_Override_LocalValue_Binding() public void LocalValue_Should_Override_LocalValue_Binding()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
target.AddBinding(source1, BindingPriority.LocalValue).Start(); target.AddBinding(source1, BindingPriority.LocalValue).Start();
@ -241,7 +241,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void LocalValue_Should_Override_Style_Binding() public void LocalValue_Should_Override_Style_Binding()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
target.AddBinding(source1, BindingPriority.Style).Start(); target.AddBinding(source1, BindingPriority.Style).Start();
@ -253,7 +253,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void LocalValue_Should_Not_Override_Animation_Binding() public void LocalValue_Should_Not_Override_Animation_Binding()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
target.AddBinding(source1, BindingPriority.Animation).Start(); target.AddBinding(source1, BindingPriority.Animation).Start();
@ -265,7 +265,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void NonAnimated_Value_Should_Be_Correct_1() public void NonAnimated_Value_Should_Be_Correct_1()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
var source3 = new Source("3"); var source3 = new Source("3");
@ -281,7 +281,7 @@ namespace Avalonia.Base.UnitTests
[Fact] [Fact]
public void NonAnimated_Value_Should_Be_Correct_2() public void NonAnimated_Value_Should_Be_Correct_2()
{ {
var target = new PriorityValue<string>(Owner, TestProperty, NullSink); var target = new PriorityValue<string>(Owner, TestProperty, ValueStore);
var source1 = new Source("1"); var source1 = new Source("1");
var source2 = new Source("2"); var source2 = new Source("2");
var source3 = new Source("3"); var source3 = new Source("3");
@ -310,16 +310,5 @@ namespace Avalonia.Base.UnitTests
public void OnCompleted() => _observer.OnCompleted(); public void OnCompleted() => _observer.OnCompleted();
} }
private class MockSink : IValueSink
{
public void Completed<T>(StyledPropertyBase<T> property, IPriorityValueEntry entry, Optional<T> oldValue)
{
}
public void ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
}
}
} }
} }

49
tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs

@ -5,6 +5,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Diagnostics;
using Avalonia.Styling; using Avalonia.Styling;
using Moq; using Moq;
using Xunit; using Xunit;
@ -105,51 +106,51 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact] [Fact]
public void Setter_Should_Apply_Value_With_Activator_As_Binding_With_StyleTrigger_Priority() public void Setter_Should_Apply_Value_With_Activator_As_Binding_With_StyleTrigger_Priority()
{ {
var control = new Mock<IStyleable>(); var control = new Canvas();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TagProperty, "foo"); var setter = new Setter(TextBlock.TagProperty, "foo");
var activator = new Subject<bool>();
var instance = setter.Instance(control.Object); var instance = setter.Instance(control);
instance.Start(true); instance.Start(true);
instance.Activate(); instance.Activate();
control.Verify(x => x.Bind( Assert.Equal("foo", control.Tag);
TextBlock.TagProperty, Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority);
It.IsAny<IObservable<BindingValue<object>>>(),
BindingPriority.StyleTrigger));
} }
[Fact] [Fact]
public void Setter_Should_Apply_Binding_Without_Activator_With_Style_Priority() public void Setter_Should_Apply_Binding_Without_Activator_With_Style_Priority()
{ {
var control = new Mock<IStyleable>(); var control = new Canvas();
var style = Mock.Of<Style>(); var source = new { Foo = "foo" };
var setter = new Setter(TextBlock.TagProperty, CreateMockBinding(TextBlock.TagProperty)); var setter = new Setter(TextBlock.TagProperty, new Binding
{
Source = source,
Path = nameof(source.Foo),
});
setter.Instance(control.Object).Start(false); setter.Instance(control).Start(false);
control.Verify(x => x.Bind( Assert.Equal("foo", control.Tag);
TextBlock.TagProperty, Assert.Equal(BindingPriority.Style, control.GetDiagnostic(TextBlock.TagProperty).Priority);
It.IsAny<PropertySetterBindingInstance<object>>(),
BindingPriority.Style));
} }
[Fact] [Fact]
public void Setter_Should_Apply_Binding_With_Activator_With_StyleTrigger_Priority() public void Setter_Should_Apply_Binding_With_Activator_With_StyleTrigger_Priority()
{ {
var control = new Mock<IStyleable>(); var control = new Canvas();
var style = Mock.Of<Style>(); var source = new { Foo = "foo" };
var setter = new Setter(TextBlock.TagProperty, CreateMockBinding(TextBlock.TagProperty)); var setter = new Setter(TextBlock.TagProperty, new Binding
{
Source = source,
Path = nameof(source.Foo),
});
var instance = setter.Instance(control.Object); var instance = setter.Instance(control);
instance.Start(true); instance.Start(true);
instance.Activate(); instance.Activate();
control.Verify(x => x.Bind( Assert.Equal("foo", control.Tag);
TextBlock.TagProperty, Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority);
It.IsAny<IObservable<BindingValue<object>>>(),
BindingPriority.StyleTrigger));
} }
private IBinding CreateMockBinding(AvaloniaProperty property) private IBinding CreateMockBinding(AvaloniaProperty property)

83
tests/Avalonia.Markup.UnitTests/Data/BindingTests_TemplatedParent.cs

@ -1,16 +1,11 @@
using System; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Subjects;
using Moq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Styling;
using Xunit;
using System.Reactive.Disposables;
using Avalonia.UnitTests;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using System.Linq; using Xunit;
using Avalonia.Markup.Data;
namespace Avalonia.Markup.UnitTests.Data namespace Avalonia.Markup.UnitTests.Data
{ {
@ -19,53 +14,57 @@ namespace Avalonia.Markup.UnitTests.Data
[Fact] [Fact]
public void OneWay_Binding_Should_Be_Set_Up() public void OneWay_Binding_Should_Be_Set_Up()
{ {
var target = CreateTarget(); var source = new Button
var binding = new Binding
{ {
Mode = BindingMode.OneWay, Template = new FuncControlTemplate<Button>((parent, _) =>
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent), new ContentPresenter
Priority = BindingPriority.TemplatedParent, {
Path = "Foo", [~ContentPresenter.ContentProperty] = new Binding
{
Mode = BindingMode.OneWay,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
Path = "Content",
}
}),
}; };
target.Object.Bind(TextBox.TextProperty, binding); source.ApplyTemplate();
target.Verify(x => x.Bind( var target = (ContentPresenter)source.GetVisualChildren().Single();
TextBox.TextProperty,
It.IsAny<IObservable<BindingValue<string>>>())); Assert.Null(target.Content);
source.Content = "foo";
Assert.Equal("foo", target.Content);
source.Content = "bar";
Assert.Equal("bar", target.Content);
} }
[Fact] [Fact]
public void TwoWay_Binding_Should_Be_Set_Up() public void TwoWay_Binding_Should_Be_Set_Up()
{ {
var target = CreateTarget(); var source = new Button
var binding = new Binding
{ {
Mode = BindingMode.TwoWay, Template = new FuncControlTemplate<Button>((parent, _) =>
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent), new ContentPresenter
Priority = BindingPriority.TemplatedParent, {
Path = "Foo", [~ContentPresenter.ContentProperty] = new Binding
{
Mode = BindingMode.TwoWay,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
Path = "Content",
}
}),
}; };
target.Object.Bind(TextBox.TextProperty, binding); source.ApplyTemplate();
target.Verify(x => x.Bind(
TextBox.TextProperty,
It.IsAny<IObservable<BindingValue<string>>>()));
}
private Mock<IControl> CreateTarget( var target = (ContentPresenter)source.GetVisualChildren().Single();
ITemplatedControl templatedParent = null,
string text = null)
{
var result = new Mock<IControl>();
result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent); Assert.Null(target.Content);
result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent); source.Content = "foo";
result.Setup(x => x.GetValue(TextBox.TextProperty)).Returns(text); Assert.Equal("foo", target.Content);
result.Setup(x => x.Bind(It.IsAny<DirectPropertyBase<string>>(), It.IsAny<IObservable<BindingValue<string>>>())) target.Content = "bar";
.Returns(Disposable.Empty); Assert.Equal("bar", source.Content);
return result;
} }
} }
} }

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/InitializationOrderTracker.cs

@ -18,7 +18,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
base.OnAttachedToLogicalTree(e); base.OnAttachedToLogicalTree(e);
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
Order.Add($"Property {change.Property.Name} Changed"); Order.Add($"Property {change.Property.Name} Changed");
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

Loading…
Cancel
Save