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");
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == BoundsProperty && _splitView is not null)
{
var oldBounds = change.OldValue.GetValueOrDefault<Rect>();
var newBounds = change.NewValue.GetValueOrDefault<Rect>();
var (oldBounds, newBounds) = change.GetOldAndNewValue<Rect>();
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)
{
var oldTransitions = change.OldValue.GetValueOrDefault<Transitions>();
var newTransitions = change.NewValue.GetValueOrDefault<Transitions>();
var e = (AvaloniaPropertyChangedEventArgs<Transitions?>)change;
var oldTransitions = e.OldValue.GetValueOrDefault();
var newTransitions = e.NewValue.GetValueOrDefault();
// 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

65
src/Avalonia.Base/AvaloniaObject.cs

@ -5,6 +5,7 @@ using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Logging;
using Avalonia.PropertyStore;
using Avalonia.Reactive;
using Avalonia.Threading;
namespace Avalonia
@ -15,13 +16,13 @@ namespace Avalonia
/// <remarks>
/// This class is analogous to DependencyObject in WPF.
/// </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 PropertyChangedEventHandler? _inpcChanged;
private EventHandler<AvaloniaPropertyChangedEventArgs>? _propertyChanged;
private List<IAvaloniaObject>? _inheritanceChildren;
private List<AvaloniaObject>? _inheritanceChildren;
private ValueStore? _values;
private bool _batchUpdate;
@ -58,7 +59,7 @@ namespace Avalonia
/// <value>
/// The inheritance parent.
/// </value>
protected IAvaloniaObject? InheritanceParent
protected AvaloniaObject? InheritanceParent
{
get
{
@ -320,14 +321,14 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
public void SetValue(
public IDisposable? SetValue(
AvaloniaProperty property,
object? value,
BindingPriority priority = BindingPriority.LocalValue)
{
property = property ?? throw new ArgumentNullException(nameof(property));
property.RouteSetValue(this, value, priority);
return property.RouteSetValue(this, value, priority);
}
/// <summary>
@ -385,6 +386,26 @@ namespace Avalonia
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>
/// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary>
@ -445,9 +466,8 @@ namespace Avalonia
/// <summary>
/// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
public void CoerceValue<T>(StyledPropertyBase<T> property)
public void CoerceValue(AvaloniaProperty property)
{
_values?.CoerceValue(property);
}
@ -475,19 +495,19 @@ namespace Avalonia
}
/// <inheritdoc/>
void IAvaloniaObject.AddInheritanceChild(IAvaloniaObject child)
internal void AddInheritanceChild(AvaloniaObject child)
{
_inheritanceChildren ??= new List<IAvaloniaObject>();
_inheritanceChildren ??= new List<AvaloniaObject>();
_inheritanceChildren.Add(child);
}
/// <inheritdoc/>
void IAvaloniaObject.RemoveInheritanceChild(IAvaloniaObject child)
internal void RemoveInheritanceChild(AvaloniaObject child)
{
_inheritanceChildren?.Remove(child);
}
void IAvaloniaObject.InheritedPropertyChanged<T>(
internal void InheritedPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
Optional<T> newValue)
@ -504,7 +524,7 @@ namespace Avalonia
return _propertyChanged?.GetInvocationList();
}
void IValueSink.ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
internal void ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
var property = (StyledPropertyBase<T>)change.Property;
@ -543,7 +563,7 @@ namespace Avalonia
}
}
void IValueSink.Completed<T>(
internal void Completed<T>(
StyledPropertyBase<T> property,
IPriorityValueEntry entry,
Optional<T> oldValue)
@ -554,7 +574,7 @@ namespace Avalonia
oldValue,
default,
BindingPriority.Unset);
((IValueSink)this).ValueChanged(change);
ValueChanged(change);
}
/// <summary>
@ -565,14 +585,11 @@ namespace Avalonia
/// <param name="oldParent">The old inheritance parent.</param>
internal void InheritanceParentChanged<T>(
StyledPropertyBase<T> property,
IAvaloniaObject? oldParent)
AvaloniaObject? oldParent)
{
var oldValue = oldParent switch
{
AvaloniaObject o => o.GetValueOrInheritedOrDefault(property),
null => property.GetDefaultValue(GetType()),
_ => oldParent.GetValue(property)
};
var oldValue = oldParent is not null ?
oldParent.GetValueOrInheritedOrDefault(property) :
property.GetDefaultValue(GetType());
var newValue = GetInheritedOrDefault(property);
@ -640,7 +657,7 @@ namespace Avalonia
/// Called when a avalonia property changes on the object.
/// </summary>
/// <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)
{
@ -652,7 +669,7 @@ namespace Avalonia
/// Called when a avalonia property changes on the object.
/// </summary>
/// <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>
/// Gets an observable for a <see cref="AvaloniaProperty"/>.
/// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="property">The property.</param>
@ -44,7 +44,7 @@ namespace Avalonia
}
/// <summary>
/// Gets an observable for a <see cref="AvaloniaProperty"/>.
/// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <typeparam name="T">The property type.</typeparam>
@ -64,7 +64,7 @@ namespace Avalonia
}
/// <summary>
/// Gets an observable for a <see cref="AvaloniaProperty"/>.
/// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="property">The property.</param>
@ -85,7 +85,7 @@ namespace Avalonia
}
/// <summary>
/// Gets an observable for a <see cref="AvaloniaProperty"/>.
/// Gets an observable for an <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <typeparam name="T">The property type.</typeparam>
@ -128,7 +128,7 @@ namespace Avalonia
}
/// <summary>
/// Gets a subject for a <see cref="AvaloniaProperty"/>.
/// Gets a subject for an <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="property">The property.</param>
@ -150,7 +150,7 @@ namespace Avalonia
}
/// <summary>
/// Gets a subject for a <see cref="AvaloniaProperty"/>.
/// Gets a subject for an <see cref="AvaloniaProperty"/>.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="o">The object.</param>
@ -230,30 +230,7 @@ namespace Avalonia
}
/// <summary>
/// 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<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.
/// Binds an <see cref="AvaloniaProperty"/> to an observable.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="target">The object.</param>
@ -273,42 +250,22 @@ namespace Avalonia
property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source));
return property switch
if (target is AvaloniaObject ao)
{
StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
DirectPropertyBase<T> direct => target.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
return property switch
{
StyledPropertyBase<T> styled => ao.Bind(styled, source, priority),
DirectPropertyBase<T> direct => ao.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
/// <summary>
/// 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));
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
return target.Bind(
property,
source.ToBindingValue(),
priority);
}
/// <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>
@ -334,7 +291,7 @@ namespace Avalonia
}
/// <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>
/// <param name="target">The object.</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>
/// Gets a <see cref="AvaloniaProperty"/> value.
/// </summary>
@ -436,12 +343,18 @@ namespace Avalonia
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
return property switch
if (target is AvaloniaObject ao)
{
StyledPropertyBase<T> styled => target.GetValue(styled),
DirectPropertyBase<T> direct => target.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
return property switch
{
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>
@ -456,7 +369,7 @@ namespace Avalonia
/// <see cref="AvaloniaProperty.UnsetValue"/>. Note that this method does not return
/// 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>
public static object? GetBaseValue(
this IAvaloniaObject target,
@ -466,7 +379,9 @@ namespace Avalonia
target = target ?? throw new ArgumentNullException(nameof(target));
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>
@ -481,8 +396,7 @@ namespace Avalonia
/// <see cref="Optional{T}.Empty"/>. Note that this method does not return property values
/// that come from inherited or default values.
///
/// For direct properties returns
/// <see cref="IAvaloniaObject.GetValue{T}(DirectPropertyBase{T})"/>.
/// For direct properties returns the current value of the property.
/// </remarks>
public static Optional<T> GetBaseValue<T>(
this IAvaloniaObject target,
@ -492,69 +406,18 @@ namespace Avalonia
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
return property switch
if (target is AvaloniaObject ao)
{
StyledPropertyBase<T> styled => target.GetBaseValue(styled, maxPriority),
DirectPropertyBase<T> direct => target.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));
return property switch
{
StyledPropertyBase<T> styled => ao.GetBaseValue(styled, maxPriority),
DirectPropertyBase<T> direct => ao.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
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>
@ -622,17 +485,6 @@ namespace Avalonia
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>
/// Observer method for <see cref="AddClassHandler{TTarget}(IObservable{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.
/// </summary>
/// <param name="o">The object instance.</param>
internal abstract void RouteClearValue(IAvaloniaObject o);
internal abstract void RouteClearValue(AvaloniaObject o);
/// <summary>
/// Routes an untyped GetValue call to a typed call.
/// </summary>
/// <param name="o">The object instance.</param>
internal abstract object? RouteGetValue(IAvaloniaObject o);
internal abstract object? RouteGetValue(AvaloniaObject o);
/// <summary>
/// Routes an untyped GetBaseValue call to a typed call.
/// </summary>
/// <param name="o">The object instance.</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>
/// 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.
/// </returns>
internal abstract IDisposable? RouteSetValue(
IAvaloniaObject o,
AvaloniaObject o,
object? value,
BindingPriority priority);
@ -503,11 +503,11 @@ namespace Avalonia
/// <param name="source">The binding source.</param>
/// <param name="priority">The priority.</param>
internal abstract IDisposable RouteBind(
IAvaloniaObject o,
AvaloniaObject o,
IObservable<BindingValue<object?>> source,
BindingPriority priority);
internal abstract void RouteInheritanceParentChanged(AvaloniaObject o, IAvaloniaObject? oldParent);
internal abstract void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject? oldParent);
/// <summary>
/// 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/>
internal override void RouteClearValue(IAvaloniaObject o)
internal override void RouteClearValue(AvaloniaObject o)
{
o.ClearValue<TValue>(this);
}
/// <inheritdoc/>
internal override object? RouteGetValue(IAvaloniaObject o)
internal override object? RouteGetValue(AvaloniaObject o)
{
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);
}
/// <inheritdoc/>
internal override IDisposable? RouteSetValue(
IAvaloniaObject o,
AvaloniaObject o,
object? value,
BindingPriority priority)
{
@ -169,7 +169,7 @@ namespace Avalonia
/// <inheritdoc/>
internal override IDisposable RouteBind(
IAvaloniaObject o,
AvaloniaObject o,
IObservable<BindingValue<object?>> source,
BindingPriority priority)
{
@ -177,7 +177,7 @@ namespace Avalonia
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.");
}

2
src/Avalonia.Base/GeometryGroup.cs

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

104
src/Avalonia.Base/IAvaloniaObject.cs

@ -17,42 +17,14 @@ namespace Avalonia
/// Clears an <see cref="AvaloniaProperty"/>'s local value.
/// </summary>
/// <param name="property">The property.</param>
void ClearValue<T>(StyledPropertyBase<T> 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);
void ClearValue(AvaloniaProperty 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>(DirectPropertyBase<T> 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);
object? GetValue(AvaloniaProperty property);
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is animating.
@ -71,93 +43,35 @@ namespace Avalonia
/// <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>
/// <param name="priority">The priority of the value.</param>
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
IDisposable? SetValue<T>(
StyledPropertyBase<T> property,
T value,
IDisposable? SetValue(
AvaloniaProperty property,
object? value,
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>
/// 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>
/// <param name="priority">The priority of the binding.</param>
/// <returns>
/// A disposable which can be used to terminate the binding.
/// </returns>
IDisposable Bind<T>(
StyledPropertyBase<T> property,
IObservable<BindingValue<T>> source,
IDisposable Bind(
AvaloniaProperty property,
IObservable<object?> source,
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>
/// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
void CoerceValue<T>(StyledPropertyBase<T> 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);
void CoerceValue(AvaloniaProperty property);
}
}

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);
if (change.Property == IsFocusedProperty)
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>(), null);
UpdatePseudoClasses(change.GetNewValue<bool>(), null);
}
else if (change.Property == IsPointerOverProperty)
{
UpdatePseudoClasses(null, change.NewValue.GetValueOrDefault<bool>());
UpdatePseudoClasses(null, change.GetNewValue<bool>());
}
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>
public static int GetTabIndex(IInputElement element)
{
return ((IAvaloniaObject)element).GetValue(TabIndexProperty);
return ((AvaloniaObject)element).GetValue(TabIndexProperty);
}
/// <summary>

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

@ -610,7 +610,7 @@ namespace Avalonia.Input.Navigation
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);
@ -655,8 +655,9 @@ namespace Avalonia.Input.Navigation
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 IsGroup(IInputElement e) => GetKeyNavigationMode(e) != KeyboardNavigationMode.Continue;

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

@ -320,11 +320,11 @@ namespace Avalonia.Layout
InvalidateLayout();
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
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.
//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);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
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.
//i.e. the properties are the inverse of each other.
@ -484,31 +484,31 @@ namespace Avalonia.Layout
}
else if (change.Property == MinColumnSpacingProperty)
{
_minColumnSpacing = change.NewValue.GetValueOrDefault<double>();
_minColumnSpacing = change.GetNewValue<double>();
}
else if (change.Property == MinRowSpacingProperty)
{
_minRowSpacing = change.NewValue.GetValueOrDefault<double>();
_minRowSpacing = change.GetNewValue<double>();
}
else if (change.Property == ItemsJustificationProperty)
{
_itemsJustification = change.NewValue.GetValueOrDefault<UniformGridLayoutItemsJustification>();
_itemsJustification = change.GetNewValue<UniformGridLayoutItemsJustification>();
}
else if (change.Property == ItemsStretchProperty)
{
_itemsStretch = change.NewValue.GetValueOrDefault<UniformGridLayoutItemsStretch>();
_itemsStretch = change.GetNewValue<UniformGridLayoutItemsStretch>();
}
else if (change.Property == MinItemWidthProperty)
{
_minItemWidth = change.NewValue.GetValueOrDefault<double>();
_minItemWidth = change.GetNewValue<double>();
}
else if (change.Property == MinItemHeightProperty)
{
_minItemHeight = change.NewValue.GetValueOrDefault<double>();
_minItemHeight = change.GetNewValue<double>();
}
else if (change.Property == MaximumRowsOrColumnsProperty)
{
_maximumRowsOrColumns = change.NewValue.GetValueOrDefault<int>();
_maximumRowsOrColumns = change.GetNewValue<int>();
}
InvalidateLayout();

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

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

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

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

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

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

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

@ -193,7 +193,7 @@ namespace Avalonia.Media
MiterLimit);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
_invalidated?.Invoke(this, EventArgs.Empty);
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>
internal class BindingEntry<T> : IBindingEntry, IPriorityValueEntry<T>, IObserver<BindingValue<T>>
{
private readonly IAvaloniaObject _owner;
private IValueSink _sink;
private readonly AvaloniaObject _owner;
private ValueOwner<T> _sink;
private IDisposable? _subscription;
private bool _isSubscribed;
private bool _batchUpdate;
private Optional<T> _value;
public BindingEntry(
IAvaloniaObject owner,
AvaloniaObject owner,
StyledPropertyBase<T> property,
IObservable<BindingValue<T>> source,
BindingPriority priority,
IValueSink sink)
ValueOwner<T> sink)
{
_owner = owner;
Property = property;
@ -50,7 +50,7 @@ namespace Avalonia.PropertyStore
{
_batchUpdate = false;
if (_sink is ValueStore)
if (_sink.IsValueStore)
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(
IValueSink sink,
IAvaloniaObject owner,
AvaloniaObject owner,
AvaloniaProperty property,
Optional<object?> oldValue,
Optional<object?> newValue)
{
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.Cast<T>(),

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

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

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

@ -5,7 +5,6 @@
/// </summary>
internal interface IPriorityValueEntry : IValue
{
void Reparent(IValueSink sink);
}
/// <summary>
@ -14,5 +13,6 @@
/// <typeparam name="T">The property type.</typeparam>
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();
void Start();
void RaiseValueChanged(
IValueSink sink,
IAvaloniaObject owner,
AvaloniaObject owner,
AvaloniaProperty property,
Optional<object?> oldValue,
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 RaiseValueChanged(
IValueSink sink,
IAvaloniaObject owner,
AvaloniaObject owner,
AvaloniaProperty property,
Optional<object?> oldValue,
Optional<object?> newValue)
{
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.Cast<T>(),

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

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

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

@ -49,23 +49,31 @@ namespace Avalonia.Reactive
{
if (e.Property == _property)
{
T newValue;
if (e is AvaloniaPropertyChangedEventArgs<T> typed)
if (e.Sender is AvaloniaObject ao)
{
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
{
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>
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)

12
src/Avalonia.Base/StyledPropertyBase.cs

@ -177,19 +177,19 @@ namespace Avalonia
object? IStyledPropertyAccessor.GetDefaultValue(Type type) => GetDefaultBoxedValue(type);
/// <inheritdoc/>
internal override void RouteClearValue(IAvaloniaObject o)
internal override void RouteClearValue(AvaloniaObject o)
{
o.ClearValue<TValue>(this);
}
/// <inheritdoc/>
internal override object? RouteGetValue(IAvaloniaObject o)
internal override object? RouteGetValue(AvaloniaObject o)
{
return o.GetValue<TValue>(this);
}
/// <inheritdoc/>
internal override object? RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority)
internal override object? RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority)
{
var value = o.GetBaseValue<TValue>(this, maxPriority);
return value.HasValue ? value.Value : AvaloniaProperty.UnsetValue;
@ -197,7 +197,7 @@ namespace Avalonia
/// <inheritdoc/>
internal override IDisposable? RouteSetValue(
IAvaloniaObject o,
AvaloniaObject o,
object? value,
BindingPriority priority)
{
@ -221,7 +221,7 @@ namespace Avalonia
/// <inheritdoc/>
internal override IDisposable RouteBind(
IAvaloniaObject o,
AvaloniaObject o,
IObservable<BindingValue<object?>> source,
BindingPriority priority)
{
@ -232,7 +232,7 @@ namespace Avalonia
/// <inheritdoc/>
internal override void RouteInheritanceParentChanged(
AvaloniaObject o,
IAvaloniaObject? oldParent)
AvaloniaObject? 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 all other cases it will be an instance of <see cref="PriorityValue{T}"/>
/// </remarks>
internal class ValueStore : IValueSink
internal class ValueStore
{
private readonly AvaloniaObject _owner;
private readonly IValueSink _sink;
private readonly AvaloniaPropertyValueStore<IValue> _values;
private BatchUpdate? _batchUpdate;
public ValueStore(AvaloniaObject owner)
{
_sink = _owner = owner;
_owner = owner;
_values = new AvaloniaPropertyValueStore<IValue>();
}
@ -122,7 +121,7 @@ namespace Avalonia
}
else
{
var entry = new ConstantValueEntry<T>(property, value, priority, this);
var entry = new ConstantValueEntry<T>(property, value, priority, new(this));
AddValue(property, entry);
NotifyValueChanged<T>(property, default, value, priority);
result = entry;
@ -151,7 +150,7 @@ namespace Avalonia
}
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);
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
// them to a constant value entry with Unset priority in the event of a local value being
// 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);
}
@ -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 (slot is PriorityValue<T> p)
if (slot is IPriorityValue p)
{
p.UpdateEffectiveValue();
}
@ -222,7 +221,7 @@ namespace Avalonia
return null;
}
void IValueSink.ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
public void ValueChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
if (_batchUpdate is object)
{
@ -233,11 +232,11 @@ namespace Avalonia
}
else
{
_sink.ValueChanged(change);
_owner.ValueChanged(change);
}
}
void IValueSink.Completed<T>(
public void Completed<T>(
StyledPropertyBase<T> property,
IPriorityValueEntry entry,
Optional<T> oldValue)
@ -248,7 +247,7 @@ namespace Avalonia
if (_batchUpdate is null)
{
_values.Remove(property);
_sink.Completed(property, entry, oldValue);
_owner.Completed(property, entry, oldValue);
}
else
{
@ -352,7 +351,7 @@ namespace Avalonia
{
if (_batchUpdate is null)
{
_sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
_owner.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
_owner,
property,
oldValue,
@ -451,7 +450,7 @@ namespace Avalonia
};
// 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
// 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);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

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

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

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

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

17
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -1,11 +1,17 @@
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.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.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.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.
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.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.
@ -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 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 '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 '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.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.
@ -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.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 '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.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.
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.
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.
@ -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.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 '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.
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 '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 '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.
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.
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.
@ -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.
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.
Total Issues: 95
Total Issues: 110

14
src/Avalonia.Controls/Button.cs

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

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

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

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

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

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

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

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

@ -139,13 +139,13 @@ namespace Avalonia.Controls.Notifications
notificationControl.Close();
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
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);
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)
{

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

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

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

@ -69,15 +69,18 @@ namespace Avalonia.Controls.Primitives
{
foreach (var child in Children)
{
var info = child.GetValue(s_adornedElementInfoProperty);
if (info != null && info.Bounds.HasValue)
{
child.Measure(info.Bounds.Value.Bounds.Size);
}
else
if (child is AvaloniaObject ao)
{
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)
{
var info = child.GetValue(s_adornedElementInfoProperty);
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
if (child is AvaloniaObject ao)
{
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);
if (change.Property == OrientationProperty)
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<Orientation>());
UpdatePseudoClasses(change.GetNewValue<Orientation>());
}
else if (change.Property == AllowAutoHideProperty)
{

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

@ -533,9 +533,9 @@ namespace Avalonia.Controls.Primitives
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)
{
@ -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);
@ -595,7 +595,7 @@ namespace Avalonia.Controls.Primitives
}
if (change.Property == ItemsProperty && _updateState is null && _selection is object)
{
var newValue = change.NewValue.GetValueOrDefault<IEnumerable>();
var newValue = change.GetNewValue<IEnumerable>();
_selection.Source = newValue;
if (newValue is null)
@ -605,7 +605,7 @@ namespace Avalonia.Controls.Primitives
}
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);
}
else if (change.Property == WrapSelectionProperty)

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

@ -291,13 +291,13 @@ namespace Avalonia.Controls.Primitives
return arrangeSize;
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
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);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == IsIndeterminateProperty)
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>(), null);
UpdatePseudoClasses(change.GetNewValue<bool>(), null);
}
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();
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == IsPressedProperty && change.NewValue.GetValueOrDefault<bool>() == false)
if (change.Property == IsPressedProperty && change.GetNewValue<bool>() == false)
{
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 static VirtualizationInfo TryGetVirtualizationInfo(IControl element)
internal static VirtualizationInfo? TryGetVirtualizationInfo(IControl element)
{
var value = element.GetValue(VirtualizationInfoProperty);
return value;
return (element as AvaloniaObject)?.GetValue(VirtualizationInfoProperty);
}
internal static VirtualizationInfo CreateAndInitializeVirtualizationInfo(IControl element)
@ -287,15 +286,20 @@ namespace Avalonia.Controls
internal static VirtualizationInfo GetVirtualizationInfo(IControl element)
{
var result = element.GetValue(VirtualizationInfoProperty);
if (result == null)
if (element is AvaloniaObject ao)
{
result = new VirtualizationInfo();
element.SetValue(VirtualizationInfoProperty, result);
var result = ao.GetValue(VirtualizationInfoProperty);
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()
@ -426,12 +430,11 @@ namespace Avalonia.Controls
_viewportManager.ResetScrollers();
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property == ItemsProperty)
{
var oldEnumerable = change.OldValue.GetValueOrDefault<IEnumerable>();
var newEnumerable = change.NewValue.GetValueOrDefault<IEnumerable>();
var (oldEnumerable, newEnumerable) = change.GetOldAndNewValue<IEnumerable?>();
if (oldEnumerable != newEnumerable)
{
@ -446,23 +449,21 @@ namespace Avalonia.Controls
}
else if (change.Property == ItemTemplateProperty)
{
OnItemTemplateChanged(
change.OldValue.GetValueOrDefault<IDataTemplate>(),
change.NewValue.GetValueOrDefault<IDataTemplate>());
var (oldvalue, newValue) = change.GetOldAndNewValue<IDataTemplate?>();
OnItemTemplateChanged(oldvalue, newValue);
}
else if (change.Property == LayoutProperty)
{
OnLayoutChanged(
change.OldValue.GetValueOrDefault<AttachedLayout>(),
change.NewValue.GetValueOrDefault<AttachedLayout>());
var (oldvalue, newValue) = change.GetOldAndNewValue<AttachedLayout>();
OnLayoutChanged(oldvalue, newValue);
}
else if (change.Property == HorizontalCacheLengthProperty)
{
_viewportManager.HorizontalCacheLength = change.NewValue.GetValueOrDefault<double>();
_viewportManager.HorizontalCacheLength = change.GetNewValue<double>();
}
else if (change.Property == VerticalCacheLengthProperty)
{
_viewportManager.VerticalCacheLength = change.NewValue.GetValueOrDefault<double>();
_viewportManager.VerticalCacheLength = change.GetNewValue<double>();
}
base.OnPropertyChanged(change);
@ -497,7 +498,7 @@ namespace Avalonia.Controls
if (parent == this)
{
var virtInfo = TryGetVirtualizationInfo(element);
return _viewManager.GetElementIndex(virtInfo);
return _viewManager.GetElementIndex(virtInfo!);
}
return -1;

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

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

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

@ -47,7 +47,7 @@ namespace Avalonia.Controls
if (madeAnchor != null)
{
var anchorVirtInfo = ItemsRepeater.TryGetVirtualizationInfo(madeAnchor);
if (anchorVirtInfo.Index == index)
if (anchorVirtInfo!.Index == index)
{
element = madeAnchor;
}
@ -60,12 +60,12 @@ namespace Avalonia.Controls
var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);
if (suppressAutoRecycle)
{
virtInfo.AutoRecycleCandidate = false;
virtInfo!.AutoRecycleCandidate = false;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} Not AutoRecycleCandidate:", virtInfo.Index);
}
else
{
virtInfo.AutoRecycleCandidate = true;
virtInfo!.AutoRecycleCandidate = true;
virtInfo.KeepAlive = true;
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);
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/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> e)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == CommandProperty)
{
if (_isAttachedToLogicalTree)
{
// 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;
}
if (e.NewValue.GetValueOrDefault() is ICommand newCommand)
if (newValue is ICommand newCommand)
{
newCommand.CanExecuteChanged += CanExecuteChanged;
}
@ -302,8 +304,7 @@ namespace Avalonia.Controls
}
else if (e.Property == FlyoutProperty)
{
var oldFlyout = e.OldValue.GetValueOrDefault() as FlyoutBase;
var newFlyout = e.NewValue.GetValueOrDefault() as FlyoutBase;
var (oldFlyout, newFlyout) = e.GetOldAndNewValue<FlyoutBase?>();
// If flyout is changed while one is already open, make sure we
// close the old one first

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

@ -90,7 +90,7 @@ namespace Avalonia.Controls
////////////////////////////////////////////////////////////////////////
/// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> e)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
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;
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

4
src/Avalonia.Controls/TextBox.cs

@ -585,7 +585,7 @@ namespace Avalonia.Controls
_imClient.SetPresenter(null, null);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
@ -594,7 +594,7 @@ namespace Avalonia.Controls
UpdatePseudoclasses();
UpdateCommandStates();
}
else if (change.Property == IsUndoEnabledProperty && change.NewValue.GetValueOrDefault<bool>() == false)
else if (change.Property == IsUndoEnabledProperty && change.GetNewValue<bool>() == false)
{
// from docs at
// 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();
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs 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);
@ -216,15 +216,15 @@ namespace Avalonia.Controls
}
else if (change.Property == IsVisibleProperty)
{
_impl?.SetIsVisible(change.NewValue.GetValueOrDefault<bool>());
_impl?.SetIsVisible(change.GetNewValue<bool>());
}
else if (change.Property == ToolTipTextProperty)
{
_impl?.SetToolTipText(change.NewValue.GetValueOrDefault<string?>());
_impl?.SetToolTipText(change.GetNewValue<string?>());
}
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;
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ChildProperty)
{
_containerVisual.Child = change.NewValue.GetValueOrDefault<IControl>();
_containerVisual.Child = change.GetNewValue<IControl>();
InvalidateMeasure();
}
}

6
src/Avalonia.Controls/Window.cs

@ -1019,16 +1019,16 @@ namespace Avalonia.Controls
/// </remarks>
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);
if (change.Property == SystemDecorationsProperty)
{
var typedNewValue = change.NewValue.GetValueOrDefault<SystemDecorations>();
var (typedOldValue, typedNewValue) = change.GetOldAndNewValue<SystemDecorations>();
PlatformImpl?.SetSystemDecorations(typedNewValue);
var o = change.OldValue.GetValueOrDefault<SystemDecorations>() == SystemDecorations.Full;
var o = typedOldValue == SystemDecorations.Full;
var n = typedNewValue == SystemDecorations.Full;
if (o != n)

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

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

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

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

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

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

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

@ -78,7 +78,7 @@ namespace Avalonia.Themes.Fluent
set => SetValue(DensityStyleProperty, value);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
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",
XamlIlTypes.Void, StyledElement, INameScope)
{ IsStatic = true });
AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", XamlIlTypes.Void,
AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", IDisposable,
false, AvaloniaProperty, XamlIlTypes.Object, BindingPriority);
IPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.IPropertyInfo");
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> CoreChanges { get; }
protected override void OnPropertyChangedCore<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
{
CoreChanges.Add(Clone(change));
base.OnPropertyChangedCore(change);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
Changes.Add(Clone(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.Property,
change.OldValue,
change.NewValue,
e.Property,
e.OldValue,
e.NewValue,
change.Priority);
if (!change.IsEffectiveValueChange)

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

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

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

@ -10,8 +10,8 @@ namespace Avalonia.Base.UnitTests
{
public class PriorityValueTests
{
private static readonly IValueSink NullSink = new MockSink();
private static readonly IAvaloniaObject Owner = Mock.Of<IAvaloniaObject>();
private static readonly AvaloniaObject Owner = new AvaloniaObject();
private static readonly ValueStore ValueStore = new ValueStore(Owner);
private static readonly StyledProperty<string> TestProperty = new StyledProperty<string>(
"Test",
typeof(PriorityValueTests),
@ -23,12 +23,12 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>(
Owner,
TestProperty,
NullSink,
ValueStore,
new ConstantValueEntry<string>(
TestProperty,
"1",
BindingPriority.StyleTrigger,
NullSink));
new(ValueStore)));
Assert.Equal("1", target.GetValue().Value);
Assert.Equal(BindingPriority.StyleTrigger, target.Priority);
@ -40,7 +40,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>(
Owner,
TestProperty,
NullSink);
ValueStore);
target.SetValue("animation", BindingPriority.Animation);
target.SetValue("local", BindingPriority.LocalValue);
@ -60,7 +60,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>(
Owner,
TestProperty,
NullSink);
ValueStore);
target.SetValue("1", BindingPriority.LocalValue);
target.SetValue("2", BindingPriority.LocalValue);
@ -74,7 +74,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>(
Owner,
TestProperty,
NullSink);
ValueStore);
target.SetValue("1", BindingPriority.Style);
target.SetValue("2", BindingPriority.Animation);
@ -93,7 +93,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue<string>(
Owner,
TestProperty,
NullSink);
ValueStore);
Assert.Equal(BindingPriority.Unset, target.Priority);
target.SetValue("style", BindingPriority.Style);
@ -109,7 +109,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
@ -129,7 +129,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
@ -149,7 +149,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
@ -169,7 +169,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
var source3 = new Source("3");
@ -191,7 +191,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
var source3 = new Source("3");
@ -214,7 +214,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
var source3 = new Source("3");
@ -229,7 +229,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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");
target.AddBinding(source1, BindingPriority.LocalValue).Start();
@ -241,7 +241,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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");
target.AddBinding(source1, BindingPriority.Style).Start();
@ -253,7 +253,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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");
target.AddBinding(source1, BindingPriority.Animation).Start();
@ -265,7 +265,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
var source3 = new Source("3");
@ -281,7 +281,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
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 source2 = new Source("2");
var source3 = new Source("3");
@ -310,16 +310,5 @@ namespace Avalonia.Base.UnitTests
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.Data;
using Avalonia.Data.Converters;
using Avalonia.Diagnostics;
using Avalonia.Styling;
using Moq;
using Xunit;
@ -105,51 +106,51 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Setter_Should_Apply_Value_With_Activator_As_Binding_With_StyleTrigger_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var control = new Canvas();
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.Activate();
control.Verify(x => x.Bind(
TextBlock.TagProperty,
It.IsAny<IObservable<BindingValue<object>>>(),
BindingPriority.StyleTrigger));
Assert.Equal("foo", control.Tag);
Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority);
}
[Fact]
public void Setter_Should_Apply_Binding_Without_Activator_With_Style_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TagProperty, CreateMockBinding(TextBlock.TagProperty));
var control = new Canvas();
var source = new { Foo = "foo" };
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(
TextBlock.TagProperty,
It.IsAny<PropertySetterBindingInstance<object>>(),
BindingPriority.Style));
Assert.Equal("foo", control.Tag);
Assert.Equal(BindingPriority.Style, control.GetDiagnostic(TextBlock.TagProperty).Priority);
}
[Fact]
public void Setter_Should_Apply_Binding_With_Activator_With_StyleTrigger_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TagProperty, CreateMockBinding(TextBlock.TagProperty));
var control = new Canvas();
var source = new { Foo = "foo" };
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.Activate();
control.Verify(x => x.Bind(
TextBlock.TagProperty,
It.IsAny<IObservable<BindingValue<object>>>(),
BindingPriority.StyleTrigger));
Assert.Equal("foo", control.Tag);
Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority);
}
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.Subjects;
using Moq;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Styling;
using Xunit;
using System.Reactive.Disposables;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using System.Linq;
using Avalonia.Markup.Data;
using Xunit;
namespace Avalonia.Markup.UnitTests.Data
{
@ -19,53 +14,57 @@ namespace Avalonia.Markup.UnitTests.Data
[Fact]
public void OneWay_Binding_Should_Be_Set_Up()
{
var target = CreateTarget();
var binding = new Binding
var source = new Button
{
Mode = BindingMode.OneWay,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
Priority = BindingPriority.TemplatedParent,
Path = "Foo",
Template = new FuncControlTemplate<Button>((parent, _) =>
new ContentPresenter
{
[~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(
TextBox.TextProperty,
It.IsAny<IObservable<BindingValue<string>>>()));
var target = (ContentPresenter)source.GetVisualChildren().Single();
Assert.Null(target.Content);
source.Content = "foo";
Assert.Equal("foo", target.Content);
source.Content = "bar";
Assert.Equal("bar", target.Content);
}
[Fact]
public void TwoWay_Binding_Should_Be_Set_Up()
{
var target = CreateTarget();
var binding = new Binding
var source = new Button
{
Mode = BindingMode.TwoWay,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
Priority = BindingPriority.TemplatedParent,
Path = "Foo",
Template = new FuncControlTemplate<Button>((parent, _) =>
new ContentPresenter
{
[~ContentPresenter.ContentProperty] = new Binding
{
Mode = BindingMode.TwoWay,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
Path = "Content",
}
}),
};
target.Object.Bind(TextBox.TextProperty, binding);
target.Verify(x => x.Bind(
TextBox.TextProperty,
It.IsAny<IObservable<BindingValue<string>>>()));
}
source.ApplyTemplate();
private Mock<IControl> CreateTarget(
ITemplatedControl templatedParent = null,
string text = null)
{
var result = new Mock<IControl>();
var target = (ContentPresenter)source.GetVisualChildren().Single();
result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent);
result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent);
result.Setup(x => x.GetValue(TextBox.TextProperty)).Returns(text);
result.Setup(x => x.Bind(It.IsAny<DirectPropertyBase<string>>(), It.IsAny<IObservable<BindingValue<string>>>()))
.Returns(Disposable.Empty);
return result;
Assert.Null(target.Content);
source.Content = "foo";
Assert.Equal("foo", target.Content);
target.Content = "bar";
Assert.Equal("bar", source.Content);
}
}
}

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

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

Loading…
Cancel
Save