Browse Source

Merge branch 'master' into fixes/2398-osx-bring-window-to-front-when-activated

pull/7110/head
Max Katz 4 years ago
committed by GitHub
parent
commit
bbf61c5e60
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/Avalonia.Base/AvaloniaObject.cs
  2. 4
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  3. 2
      src/Avalonia.Base/Data/AssignBindingAttribute.cs
  4. 7
      src/Avalonia.Base/Data/BindingChainException.cs
  5. 2
      src/Avalonia.Base/Data/BindingMode.cs
  6. 48
      src/Avalonia.Base/Data/BindingNotification.cs
  7. 22
      src/Avalonia.Base/Data/BindingOperations.cs
  8. 4
      src/Avalonia.Base/Data/BindingPriority.cs
  9. 46
      src/Avalonia.Base/Data/BindingValue.cs
  10. 2
      src/Avalonia.Base/Data/Converters/BoolConverters.cs
  11. 6
      src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
  12. 12
      src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs
  13. 12
      src/Avalonia.Base/Data/Converters/FuncValueConverter.cs
  14. 4
      src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs
  15. 6
      src/Avalonia.Base/Data/Converters/IValueConverter.cs
  16. 18
      src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs
  17. 5
      src/Avalonia.Base/Data/Converters/ObjectConverters.cs
  18. 5
      src/Avalonia.Base/Data/Converters/StringConverters.cs
  19. 12
      src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs
  20. 14
      src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs
  21. 18
      src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs
  22. 48
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  23. 18
      src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs
  24. 2
      src/Avalonia.Base/Data/Core/CommonPropertyNames.cs
  25. 4
      src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs
  26. 54
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  27. 76
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs
  28. 4
      src/Avalonia.Base/Data/Core/ExpressionParseException.cs
  29. 6
      src/Avalonia.Base/Data/Core/ITransformNode.cs
  30. 12
      src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs
  31. 18
      src/Avalonia.Base/Data/Core/IndexerNodeBase.cs
  32. 11
      src/Avalonia.Base/Data/Core/LogicalNotNode.cs
  33. 6
      src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs
  34. 2
      src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs
  35. 2
      src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs
  36. 52
      src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs
  37. 14
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
  38. 18
      src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs
  39. 10
      src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
  40. 6
      src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs
  41. 10
      src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs
  42. 4
      src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs
  43. 6
      src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs
  44. 24
      src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
  45. 41
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  46. 39
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  47. 17
      src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs
  48. 16
      src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs
  49. 8
      src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs
  50. 16
      src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs
  51. 21
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  52. 2
      src/Avalonia.Base/Data/Core/PropertyPath.cs
  53. 14
      src/Avalonia.Base/Data/Core/SettableNode.cs
  54. 2
      src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs
  55. 10
      src/Avalonia.Base/Data/Core/StreamNode.cs
  56. 10
      src/Avalonia.Base/Data/Core/TypeCastNode.cs
  57. 6
      src/Avalonia.Base/Data/DataValidationException.cs
  58. 8
      src/Avalonia.Base/Data/IBinding.cs
  59. 10
      src/Avalonia.Base/Data/IndexerBinding.cs
  60. 21
      src/Avalonia.Base/Data/IndexerDescriptor.cs
  61. 28
      src/Avalonia.Base/Data/InstancedBinding.cs
  62. 24
      src/Avalonia.Base/Data/Optional.cs
  63. 6
      src/Avalonia.Base/PropertyStore/BindingEntry.cs
  64. 20
      src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs
  65. 6
      src/Avalonia.Base/PropertyStore/IValue.cs
  66. 10
      src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
  67. 6
      src/Avalonia.Base/PropertyStore/PriorityValue.cs
  68. 9
      src/Avalonia.Base/ValueStore.cs
  69. 18
      src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs
  70. 4
      src/Avalonia.Styling/Styling/PropertySetterInstance.cs
  71. 2
      src/Avalonia.Styling/Styling/Setter.cs
  72. 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  73. 2
      src/Markup/Avalonia.Markup/Data/Binding.cs
  74. 8
      src/Markup/Avalonia.Markup/Data/BindingBase.cs
  75. 11
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  76. 6
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  77. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  78. 14
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  79. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
  80. 4
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs
  81. 20
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs
  82. 10
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  83. 17
      src/Windows/Avalonia.Win32/Win32NativeControlHost.cs
  84. 2
      src/Windows/Avalonia.Win32/WindowImpl.cs

4
src/Avalonia.Base/AvaloniaObject.cs

@ -115,8 +115,8 @@ namespace Avalonia
/// <param name="binding">The binding information.</param> /// <param name="binding">The binding information.</param>
public IBinding this[IndexerDescriptor binding] public IBinding this[IndexerDescriptor binding]
{ {
get { return new IndexerBinding(this, binding.Property, binding.Mode); } get { return new IndexerBinding(this, binding.Property!, binding.Mode); }
set { this.Bind(binding.Property, value); } set { this.Bind(binding.Property!, value); }
} }
private ValueStore Values private ValueStore Values

4
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -662,9 +662,9 @@ namespace Avalonia
this._source = source; this._source = source;
} }
public InstancedBinding Initiate( public InstancedBinding? Initiate(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object? anchor = null, object? anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)
{ {

2
src/Avalonia.Base/Data/AssignBindingAttribute.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>

7
src/Avalonia.Base/Data/BindingChainException.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
@ -16,6 +18,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
public BindingChainException() public BindingChainException()
{ {
_message = "Binding error";
} }
/// <summary> /// <summary>
@ -45,12 +48,12 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets the expression that could not be evaluated. /// Gets the expression that could not be evaluated.
/// </summary> /// </summary>
public string Expression { get; protected set; } public string? Expression { get; protected set; }
/// <summary> /// <summary>
/// Gets the point in the expression at which the error occurred. /// Gets the point in the expression at which the error occurred.
/// </summary> /// </summary>
public string ExpressionErrorPoint { get; protected set; } public string? ExpressionErrorPoint { get; protected set; }
/// <inheritdoc/> /// <inheritdoc/>
public override string Message public override string Message

2
src/Avalonia.Base/Data/BindingMode.cs

@ -1,3 +1,5 @@
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>

48
src/Avalonia.Base/Data/BindingNotification.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
@ -47,13 +49,13 @@ namespace Avalonia.Data
public static readonly BindingNotification UnsetValue = public static readonly BindingNotification UnsetValue =
new BindingNotification(AvaloniaProperty.UnsetValue); new BindingNotification(AvaloniaProperty.UnsetValue);
private object _value; private object? _value;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BindingNotification"/> class. /// Initializes a new instance of the <see cref="BindingNotification"/> class.
/// </summary> /// </summary>
/// <param name="value">The binding value.</param> /// <param name="value">The binding value.</param>
public BindingNotification(object value) public BindingNotification(object? value)
{ {
_value = value; _value = value;
} }
@ -81,7 +83,7 @@ namespace Avalonia.Data
/// <param name="error">The binding error.</param> /// <param name="error">The binding error.</param>
/// <param name="errorType">The type of the binding error.</param> /// <param name="errorType">The type of the binding error.</param>
/// <param name="fallbackValue">The fallback value.</param> /// <param name="fallbackValue">The fallback value.</param>
public BindingNotification(Exception error, BindingErrorType errorType, object fallbackValue) public BindingNotification(Exception error, BindingErrorType errorType, object? fallbackValue)
: this(error, errorType) : this(error, errorType)
{ {
_value = fallbackValue; _value = fallbackValue;
@ -95,7 +97,7 @@ namespace Avalonia.Data
/// If this property is read when <see cref="HasValue"/> is false then it will return /// If this property is read when <see cref="HasValue"/> is false then it will return
/// <see cref="AvaloniaProperty.UnsetValue"/>. /// <see cref="AvaloniaProperty.UnsetValue"/>.
/// </remarks> /// </remarks>
public object Value => _value; public object? Value => _value;
/// <summary> /// <summary>
/// Gets a value indicating whether <see cref="Value"/> should be pushed to the target. /// Gets a value indicating whether <see cref="Value"/> should be pushed to the target.
@ -105,7 +107,7 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets the error that occurred on the source, if any. /// Gets the error that occurred on the source, if any.
/// </summary> /// </summary>
public Exception Error { get; set; } public Exception? Error { get; set; }
/// <summary> /// <summary>
/// Gets the type of error that <see cref="Error"/> represents, if any. /// Gets the type of error that <see cref="Error"/> represents, if any.
@ -118,14 +120,14 @@ namespace Avalonia.Data
/// <param name="a">The first instance.</param> /// <param name="a">The first instance.</param>
/// <param name="b">The second instance.</param> /// <param name="b">The second instance.</param>
/// <returns>true if the two instances are equal; otherwise false.</returns> /// <returns>true if the two instances are equal; otherwise false.</returns>
public static bool operator ==(BindingNotification a, BindingNotification b) public static bool operator ==(BindingNotification? a, BindingNotification? b)
{ {
if (object.ReferenceEquals(a, b)) if (object.ReferenceEquals(a, b))
{ {
return true; return true;
} }
if ((object)a == null || (object)b == null) if (a is null || b is null)
{ {
return false; return false;
} }
@ -142,7 +144,7 @@ namespace Avalonia.Data
/// <param name="a">The first instance.</param> /// <param name="a">The first instance.</param>
/// <param name="b">The second instance.</param> /// <param name="b">The second instance.</param>
/// <returns>true if the two instances are unequal; otherwise false.</returns> /// <returns>true if the two instances are unequal; otherwise false.</returns>
public static bool operator !=(BindingNotification a, BindingNotification b) public static bool operator !=(BindingNotification? a, BindingNotification? b)
{ {
return !(a == b); return !(a == b);
} }
@ -156,10 +158,10 @@ namespace Avalonia.Data
/// If <paramref name="o"/> is a <see cref="BindingNotification"/> then returns the binding /// If <paramref name="o"/> is a <see cref="BindingNotification"/> then returns the binding
/// notification's <see cref="Value"/>. If not, returns the object unchanged. /// notification's <see cref="Value"/>. If not, returns the object unchanged.
/// </remarks> /// </remarks>
public static object ExtractValue(object o) public static object? ExtractValue(object? o)
{ {
var notification = o as BindingNotification; var notification = o as BindingNotification;
return notification != null ? notification.Value : o; return notification is not null ? notification.Value : o;
} }
/// <summary> /// <summary>
@ -171,7 +173,7 @@ namespace Avalonia.Data
/// If <paramref name="o"/> is a <see cref="BindingNotification"/> then returns the binding /// If <paramref name="o"/> is a <see cref="BindingNotification"/> then returns the binding
/// notification's <see cref="Error"/>. If not, returns the object unchanged. /// notification's <see cref="Error"/>. If not, returns the object unchanged.
/// </remarks> /// </remarks>
public static object ExtractError(object o) public static object? ExtractError(object? o)
{ {
return o is BindingNotification notification ? notification.Error : o; return o is BindingNotification notification ? notification.Error : o;
} }
@ -181,7 +183,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
/// <param name="obj">The object to compare.</param> /// <param name="obj">The object to compare.</param>
/// <returns>true if the two instances are equal; otherwise false.</returns> /// <returns>true if the two instances are equal; otherwise false.</returns>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return Equals(obj as BindingNotification); return Equals(obj as BindingNotification);
} }
@ -191,7 +193,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
/// <param name="other">The value to compare.</param> /// <param name="other">The value to compare.</param>
/// <returns>true if the two instances are equal; otherwise false.</returns> /// <returns>true if the two instances are equal; otherwise false.</returns>
public bool Equals(BindingNotification other) public bool Equals(BindingNotification? other)
{ {
return this == other; return this == other;
} }
@ -234,28 +236,28 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Sets the <see cref="Value"/>. /// Sets the <see cref="Value"/>.
/// </summary> /// </summary>
public void SetValue(object value) public void SetValue(object? value)
{ {
_value = value; _value = value;
} }
public BindingValue<object> ToBindingValue() public BindingValue<object?> ToBindingValue()
{ {
if (ErrorType == BindingErrorType.None) if (ErrorType == BindingErrorType.None)
{ {
return HasValue ? new BindingValue<object>(Value) : BindingValue<object>.Unset; return HasValue ? new BindingValue<object?>(Value) : BindingValue<object?>.Unset;
} }
else if (ErrorType == BindingErrorType.Error) else if (ErrorType == BindingErrorType.Error)
{ {
return BindingValue<object>.BindingError( return BindingValue<object?>.BindingError(
Error, Error!,
HasValue ? new Optional<object>(Value) : Optional<object>.Empty); HasValue ? new Optional<object?>(Value) : Optional<object?>.Empty);
} }
else else
{ {
return BindingValue<object>.DataValidationError( return BindingValue<object?>.DataValidationError(
Error, Error!,
HasValue ? new Optional<object>(Value) : Optional<object>.Empty); HasValue ? new Optional<object?>(Value) : Optional<object?>.Empty);
} }
} }
@ -273,7 +275,7 @@ namespace Avalonia.Data
} }
} }
private static bool ExceptionEquals(Exception a, Exception b) private static bool ExceptionEquals(Exception? a, Exception? b)
{ {
return a?.GetType() == b?.GetType() && return a?.GetType() == b?.GetType() &&
a?.Message == b?.Message; a?.Message == b?.Message;

22
src/Avalonia.Base/Data/BindingOperations.cs

@ -1,7 +1,8 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Reactive;
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
@ -26,11 +27,11 @@ namespace Avalonia.Data
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty property, AvaloniaProperty property,
InstancedBinding binding, InstancedBinding binding,
object anchor) object? anchor)
{ {
Contract.Requires<ArgumentNullException>(target != null); _ = target ?? throw new ArgumentNullException(nameof(target));
Contract.Requires<ArgumentNullException>(property != null); _ = property ?? throw new ArgumentNullException(nameof(property));
Contract.Requires<ArgumentNullException>(binding != null); _ = binding ?? throw new ArgumentNullException(nameof(binding));
var mode = binding.Mode; var mode = binding.Mode;
@ -43,8 +44,12 @@ namespace Avalonia.Data
{ {
case BindingMode.Default: case BindingMode.Default:
case BindingMode.OneWay: case BindingMode.OneWay:
return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority); if (binding.Observable is null)
throw new InvalidOperationException("InstancedBinding does not contain an observable.");
return target.Bind(property, binding.Observable, binding.Priority);
case BindingMode.TwoWay: case BindingMode.TwoWay:
if (binding.Subject is null)
throw new InvalidOperationException("InstancedBinding does not contain a subject.");
return new TwoWayBindingDisposable( return new TwoWayBindingDisposable(
target.Bind(property, binding.Subject, binding.Priority), target.Bind(property, binding.Subject, binding.Priority),
target.GetObservable(property).Subscribe(binding.Subject)); target.GetObservable(property).Subscribe(binding.Subject));
@ -74,6 +79,11 @@ namespace Avalonia.Data
case BindingMode.OneWayToSource: case BindingMode.OneWayToSource:
{ {
if (binding.Observable is null)
throw new InvalidOperationException("InstancedBinding does not contain an observable.");
if (binding.Subject is null)
throw new InvalidOperationException("InstancedBinding does not contain a subject.");
// Perf: Avoid allocating closure in the outer scope. // Perf: Avoid allocating closure in the outer scope.
var bindingCopy = binding; var bindingCopy = binding;

4
src/Avalonia.Base/Data/BindingPriority.cs

@ -1,3 +1,5 @@
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
@ -41,4 +43,4 @@ namespace Avalonia.Data
/// </summary> /// </summary>
Unset = int.MaxValue, Unset = int.MaxValue,
} }
} }

46
src/Avalonia.Base/Data/BindingValue.cs

@ -1,6 +1,5 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable #nullable enable
@ -83,14 +82,14 @@ namespace Avalonia.Data
/// </remarks> /// </remarks>
public readonly struct BindingValue<T> public readonly struct BindingValue<T>
{ {
[AllowNull] private readonly T _value; private readonly T _value;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BindingValue{T}"/> struct with a type of /// Initializes a new instance of the <see cref="BindingValue{T}"/> struct with a type of
/// <see cref="BindingValueType.Value"/> /// <see cref="BindingValueType.Value"/>
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public BindingValue([AllowNull] T value) public BindingValue(T value)
{ {
ValidateValue(value); ValidateValue(value);
_value = value; _value = value;
@ -98,9 +97,9 @@ namespace Avalonia.Data
Error = null; Error = null;
} }
private BindingValue(BindingValueType type, [AllowNull] T value, Exception? error) private BindingValue(BindingValueType type, T? value, Exception? error)
{ {
_value = value; _value = value!;
Type = type; Type = type;
Error = error; Error = error;
} }
@ -127,7 +126,7 @@ namespace Avalonia.Data
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// <see cref="HasValue"/> is false. /// <see cref="HasValue"/> is false.
/// </exception> /// </exception>
public T Value => HasValue ? _value : throw new InvalidOperationException("BindingValue has no value."); public T Value => HasValue ? _value! : throw new InvalidOperationException("BindingValue has no value.");
/// <summary> /// <summary>
/// Gets the binding or data validation error. /// Gets the binding or data validation error.
@ -157,13 +156,13 @@ namespace Avalonia.Data
BindingValueType.DoNothing => BindingOperations.DoNothing, BindingValueType.DoNothing => BindingOperations.DoNothing,
BindingValueType.Value => _value, BindingValueType.Value => _value,
BindingValueType.BindingError => BindingValueType.BindingError =>
new BindingNotification(Error, BindingErrorType.Error), new BindingNotification(Error!, BindingErrorType.Error),
BindingValueType.BindingErrorWithFallback => BindingValueType.BindingErrorWithFallback =>
new BindingNotification(Error, BindingErrorType.Error, Value), new BindingNotification(Error!, BindingErrorType.Error, Value),
BindingValueType.DataValidationError => BindingValueType.DataValidationError =>
new BindingNotification(Error, BindingErrorType.DataValidationError), new BindingNotification(Error!, BindingErrorType.DataValidationError),
BindingValueType.DataValidationErrorWithFallback => BindingValueType.DataValidationErrorWithFallback =>
new BindingNotification(Error, BindingErrorType.DataValidationError, Value), new BindingNotification(Error!, BindingErrorType.DataValidationError, Value),
_ => throw new NotSupportedException("Invalid BindingValueType."), _ => throw new NotSupportedException("Invalid BindingValueType."),
}; };
} }
@ -177,7 +176,7 @@ namespace Avalonia.Data
/// The binding type is <see cref="BindingValueType.UnsetValue"/> or /// The binding type is <see cref="BindingValueType.UnsetValue"/> or
/// <see cref="BindingValueType.DoNothing"/>. /// <see cref="BindingValueType.DoNothing"/>.
/// </exception> /// </exception>
public BindingValue<T> WithValue([AllowNull] T value) public BindingValue<T> WithValue(T value)
{ {
if (Type == BindingValueType.DoNothing) if (Type == BindingValueType.DoNothing)
{ {
@ -192,15 +191,14 @@ namespace Avalonia.Data
/// Gets the value of the binding value if present, otherwise the default value. /// Gets the value of the binding value if present, otherwise the default value.
/// </summary> /// </summary>
/// <returns>The value.</returns> /// <returns>The value.</returns>
[return: MaybeNull] public T? GetValueOrDefault() => HasValue ? _value : default;
public T GetValueOrDefault() => HasValue ? _value : default;
/// <summary> /// <summary>
/// Gets the value of the binding value if present, otherwise a default value. /// Gets the value of the binding value if present, otherwise a default value.
/// </summary> /// </summary>
/// <param name="defaultValue">The default value.</param> /// <param name="defaultValue">The default value.</param>
/// <returns>The value.</returns> /// <returns>The value.</returns>
public T GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue; public T? GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue;
/// <summary> /// <summary>
/// Gets the value if present, otherwise the default value. /// Gets the value if present, otherwise the default value.
@ -209,8 +207,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is /// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type. /// not present or of an incorrect type.
/// </returns> /// </returns>
[return: MaybeNull] public TResult? GetValueOrDefault<TResult>()
public TResult GetValueOrDefault<TResult>()
{ {
return HasValue ? return HasValue ?
_value is TResult result ? result : default _value is TResult result ? result : default
@ -226,8 +223,7 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or <paramref name="defaultValue"/> if the /// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
/// value is not present. /// value is not present.
/// </returns> /// </returns>
[return: MaybeNull] public TResult? GetValueOrDefault<TResult>(TResult defaultValue)
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
{ {
return HasValue ? return HasValue ?
_value is TResult result ? result : default _value is TResult result ? result : default
@ -247,7 +243,7 @@ namespace Avalonia.Data
UnsetValueType _ => Unset, UnsetValueType _ => Unset,
DoNothingType _ => DoNothing, DoNothingType _ => DoNothing,
BindingNotification n => n.ToBindingValue().Cast<T>(), BindingNotification n => n.ToBindingValue().Cast<T>(),
_ => new BindingValue<T>((T?)value) _ => new BindingValue<T>((T)value!)
}; };
} }
@ -255,7 +251,7 @@ namespace Avalonia.Data
/// Creates a binding value from an instance of the underlying value type. /// Creates a binding value from an instance of the underlying value type.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public static implicit operator BindingValue<T>([AllowNull] T value) => new BindingValue<T>(value); public static implicit operator BindingValue<T>(T value) => new BindingValue<T>(value);
/// <summary> /// <summary>
/// Creates a binding value from an <see cref="Optional{T}"/>. /// Creates a binding value from an <see cref="Optional{T}"/>.
@ -360,7 +356,7 @@ namespace Avalonia.Data
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void ValidateValue([AllowNull] T value) private static void ValidateValue(T value)
{ {
if (value is UnsetValueType) if (value is UnsetValueType)
{ {
@ -387,21 +383,21 @@ namespace Avalonia.Data
/// <typeparam name="T">The target type.</typeparam> /// <typeparam name="T">The target type.</typeparam>
/// <param name="value">The binding value.</param> /// <param name="value">The binding value.</param>
/// <returns>The cast value.</returns> /// <returns>The cast value.</returns>
public static BindingValue<T> Cast<T>(this BindingValue<object> value) public static BindingValue<T> Cast<T>(this BindingValue<object?> value)
{ {
return value.Type switch return value.Type switch
{ {
BindingValueType.DoNothing => BindingValue<T>.DoNothing, BindingValueType.DoNothing => BindingValue<T>.DoNothing,
BindingValueType.UnsetValue => BindingValue<T>.Unset, BindingValueType.UnsetValue => BindingValue<T>.Unset,
BindingValueType.Value => new BindingValue<T>((T)value.Value), BindingValueType.Value => new BindingValue<T>((T)value.Value!),
BindingValueType.BindingError => BindingValue<T>.BindingError(value.Error!), BindingValueType.BindingError => BindingValue<T>.BindingError(value.Error!),
BindingValueType.BindingErrorWithFallback => BindingValue<T>.BindingError( BindingValueType.BindingErrorWithFallback => BindingValue<T>.BindingError(
value.Error!, value.Error!,
(T)value.Value), (T)value.Value!),
BindingValueType.DataValidationError => BindingValue<T>.DataValidationError(value.Error!), BindingValueType.DataValidationError => BindingValue<T>.DataValidationError(value.Error!),
BindingValueType.DataValidationErrorWithFallback => BindingValue<T>.DataValidationError( BindingValueType.DataValidationErrorWithFallback => BindingValue<T>.DataValidationError(
value.Error!, value.Error!,
(T)value.Value), (T)value.Value!),
_ => throw new NotSupportedException("Invalid BindingValue type."), _ => throw new NotSupportedException("Invalid BindingValue type."),
}; };
} }

2
src/Avalonia.Base/Data/Converters/BoolConverters.cs

@ -1,5 +1,7 @@
using System.Linq; using System.Linq;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>

6
src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@ -3,6 +3,8 @@ using System.Globalization;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>
@ -24,7 +26,7 @@ namespace Avalonia.Data.Converters
/// <param name="parameter">A user-defined parameter.</param> /// <param name="parameter">A user-defined parameter.</param>
/// <param name="culture">The culture to use.</param> /// <param name="culture">The culture to use.</param>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value == null) if (value == null)
{ {
@ -63,7 +65,7 @@ namespace Avalonia.Data.Converters
/// <param name="parameter">A user-defined parameter.</param> /// <param name="parameter">A user-defined parameter.</param>
/// <param name="culture">The culture to use.</param> /// <param name="culture">The culture to use.</param>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
return Convert(value, targetType, parameter, culture); return Convert(value, targetType, parameter, culture);
} }

12
src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>
@ -13,24 +15,24 @@ namespace Avalonia.Data.Converters
/// <typeparam name="TOut">The output type.</typeparam> /// <typeparam name="TOut">The output type.</typeparam>
public class FuncMultiValueConverter<TIn, TOut> : IMultiValueConverter public class FuncMultiValueConverter<TIn, TOut> : IMultiValueConverter
{ {
private readonly Func<IEnumerable<TIn>, TOut> _convert; private readonly Func<IEnumerable<TIn?>, TOut> _convert;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TOut}"/> class. /// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TOut}"/> class.
/// </summary> /// </summary>
/// <param name="convert">The convert function.</param> /// <param name="convert">The convert function.</param>
public FuncMultiValueConverter(Func<IEnumerable<TIn>, TOut> convert) public FuncMultiValueConverter(Func<IEnumerable<TIn?>, TOut> convert)
{ {
_convert = convert; _convert = convert;
} }
/// <inheritdoc/> /// <inheritdoc/>
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture) public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{ {
//standard OfType skip null values, even they are valid for the Type //standard OfType skip null values, even they are valid for the Type
static IEnumerable<TIn> OfTypeWithDefaultSupport(IList<object> list) static IEnumerable<TIn?> OfTypeWithDefaultSupport(IList<object?> list)
{ {
foreach (object obj in list) foreach (var obj in list)
{ {
if (obj is TIn result) if (obj is TIn result)
{ {

12
src/Avalonia.Base/Data/Converters/FuncValueConverter.cs

@ -2,6 +2,8 @@ using System;
using System.Globalization; using System.Globalization;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>
@ -12,23 +14,23 @@ namespace Avalonia.Data.Converters
/// <typeparam name="TOut">The output type.</typeparam> /// <typeparam name="TOut">The output type.</typeparam>
public class FuncValueConverter<TIn, TOut> : IValueConverter public class FuncValueConverter<TIn, TOut> : IValueConverter
{ {
private readonly Func<TIn, TOut> _convert; private readonly Func<TIn?, TOut> _convert;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TOut}"/> class. /// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TOut}"/> class.
/// </summary> /// </summary>
/// <param name="convert">The convert function.</param> /// <param name="convert">The convert function.</param>
public FuncValueConverter(Func<TIn, TOut> convert) public FuncValueConverter(Func<TIn?, TOut> convert)
{ {
_convert = convert; _convert = convert;
} }
/// <inheritdoc/> /// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (TypeUtilities.CanCast<TIn>(value)) if (TypeUtilities.CanCast<TIn>(value))
{ {
return _convert((TIn)value); return _convert((TIn?)value);
} }
else else
{ {
@ -37,7 +39,7 @@ namespace Avalonia.Data.Converters
} }
/// <inheritdoc/> /// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

4
src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>
@ -22,6 +24,6 @@ namespace Avalonia.Data.Converters
/// <see cref="AvaloniaProperty.UnsetValue"/>. Any exception thrown will be treated as /// <see cref="AvaloniaProperty.UnsetValue"/>. Any exception thrown will be treated as
/// an application exception. /// an application exception.
/// </remarks> /// </remarks>
object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture); object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture);
} }
} }

6
src/Avalonia.Base/Data/Converters/IValueConverter.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>
@ -21,7 +23,7 @@ namespace Avalonia.Data.Converters
/// a <see cref="BindingNotification"/> in an error state. Any exceptions thrown will be /// a <see cref="BindingNotification"/> in an error state. Any exceptions thrown will be
/// treated as an application exception. /// treated as an application exception.
/// </remarks> /// </remarks>
object Convert(object value, Type targetType, object parameter, CultureInfo culture); object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture);
/// <summary> /// <summary>
/// Converts a value. /// Converts a value.
@ -36,6 +38,6 @@ namespace Avalonia.Data.Converters
/// a <see cref="BindingNotification"/> in an error state. Any exceptions thrown will be /// a <see cref="BindingNotification"/> in an error state. Any exceptions thrown will be
/// treated as an application exception. /// treated as an application exception.
/// </remarks> /// </remarks>
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture); object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture);
} }
} }

18
src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs

@ -7,6 +7,8 @@ using System.Reflection;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
class MethodToCommandConverter : ICommand class MethodToCommandConverter : ICommand
@ -18,9 +20,9 @@ namespace Avalonia.Data.Converters
.GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static); .GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static);
readonly Func<object, bool> canExecute; readonly Func<object, bool> canExecute;
readonly Action<object> execute; readonly Action<object> execute;
readonly WeakPropertyChangedProxy weakPropertyChanged; readonly WeakPropertyChangedProxy? weakPropertyChanged;
readonly PropertyChangedEventHandler propertyChangedEventHandler; readonly PropertyChangedEventHandler? propertyChangedEventHandler;
readonly string[] dependencyProperties; readonly string[]? dependencyProperties;
public MethodToCommandConverter(Delegate action) public MethodToCommandConverter(Delegate action)
{ {
@ -74,7 +76,7 @@ namespace Avalonia.Data.Converters
} }
#pragma warning disable 0067 #pragma warning disable 0067
public event EventHandler CanExecuteChanged; public event EventHandler? CanExecuteChanged;
#pragma warning restore 0067 #pragma warning restore 0067
public bool CanExecute(object parameter) => canExecute(parameter); public bool CanExecute(object parameter) => canExecute(parameter);
@ -161,14 +163,14 @@ namespace Avalonia.Data.Converters
.Compile(); .Compile();
} }
private static Expression ConvertTarget(object target, MethodInfo method) => private static Expression? ConvertTarget(object target, MethodInfo method) =>
target is null ? null : Expression.Convert(Expression.Constant(target), method.DeclaringType); target is null ? null : Expression.Convert(Expression.Constant(target), method.DeclaringType);
internal class WeakPropertyChangedProxy internal class WeakPropertyChangedProxy
{ {
readonly WeakReference<PropertyChangedEventHandler> _listener = new WeakReference<PropertyChangedEventHandler>(null); readonly WeakReference<PropertyChangedEventHandler?> _listener = new WeakReference<PropertyChangedEventHandler?>(null);
readonly PropertyChangedEventHandler _handler; readonly PropertyChangedEventHandler _handler;
internal WeakReference<INotifyPropertyChanged> Source { get; } = new WeakReference<INotifyPropertyChanged>(null); internal WeakReference<INotifyPropertyChanged?> Source { get; } = new WeakReference<INotifyPropertyChanged?>(null);
public WeakPropertyChangedProxy() public WeakPropertyChangedProxy()
{ {
@ -190,7 +192,7 @@ namespace Avalonia.Data.Converters
public void Unsubscribe() public void Unsubscribe()
{ {
if (Source.TryGetTarget(out INotifyPropertyChanged source) && source != null) if (Source.TryGetTarget(out var source) && source != null)
source.PropertyChanged -= _handler; source.PropertyChanged -= _handler;
Source.SetTarget(null); Source.SetTarget(null);

5
src/Avalonia.Base/Data/Converters/ObjectConverters.cs

@ -1,3 +1,4 @@
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
@ -10,12 +11,12 @@ namespace Avalonia.Data.Converters
/// A value converter that returns true if the input object is a null reference. /// A value converter that returns true if the input object is a null reference.
/// </summary> /// </summary>
public static readonly IValueConverter IsNull = public static readonly IValueConverter IsNull =
new FuncValueConverter<object, bool>(x => x is null); new FuncValueConverter<object?, bool>(x => x is null);
/// <summary> /// <summary>
/// A value converter that returns true if the input object is not null. /// A value converter that returns true if the input object is not null.
/// </summary> /// </summary>
public static readonly IValueConverter IsNotNull = public static readonly IValueConverter IsNotNull =
new FuncValueConverter<object, bool>(x => !(x is null)); new FuncValueConverter<object?, bool>(x => x is not null);
} }
} }

5
src/Avalonia.Base/Data/Converters/StringConverters.cs

@ -1,3 +1,4 @@
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
@ -10,12 +11,12 @@ namespace Avalonia.Data.Converters
/// A value converter that returns true if the input string is null or an empty string. /// A value converter that returns true if the input string is null or an empty string.
/// </summary> /// </summary>
public static readonly IValueConverter IsNullOrEmpty = public static readonly IValueConverter IsNullOrEmpty =
new FuncValueConverter<string, bool>(string.IsNullOrEmpty); new FuncValueConverter<string?, bool>(string.IsNullOrEmpty);
/// <summary> /// <summary>
/// A value converter that returns true if the input string is not null or empty. /// A value converter that returns true if the input string is not null or empty.
/// </summary> /// </summary>
public static readonly IValueConverter IsNotNullOrEmpty = public static readonly IValueConverter IsNotNullOrEmpty =
new FuncValueConverter<string, bool>(x => !string.IsNullOrEmpty(x)); new FuncValueConverter<string?, bool>(x => !string.IsNullOrEmpty(x));
} }
} }

12
src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>
@ -17,18 +19,16 @@ namespace Avalonia.Data.Converters
/// <param name="inner"> /// <param name="inner">
/// An optional inner converter to be called before the format takes place. /// An optional inner converter to be called before the format takes place.
/// </param> /// </param>
public StringFormatMultiValueConverter(string format, IMultiValueConverter inner) public StringFormatMultiValueConverter(string format, IMultiValueConverter? inner)
{ {
Contract.Requires<ArgumentNullException>(format != null); Format = format ?? throw new ArgumentNullException(nameof(format));
Format = format;
Inner = inner; Inner = inner;
} }
/// <summary> /// <summary>
/// Gets an inner value converter which will be called before the string format takes place. /// Gets an inner value converter which will be called before the string format takes place.
/// </summary> /// </summary>
public IMultiValueConverter Inner { get; } public IMultiValueConverter? Inner { get; }
/// <summary> /// <summary>
/// Gets the format string. /// Gets the format string.
@ -36,7 +36,7 @@ namespace Avalonia.Data.Converters
public string Format { get; } public string Format { get; }
/// <inheritdoc/> /// <inheritdoc/>
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture) public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{ {
return Inner == null return Inner == null
? string.Format(culture, Format, values.ToArray()) ? string.Format(culture, Format, values.ToArray())

14
src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
#nullable enable
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
/// <summary> /// <summary>
@ -15,18 +17,16 @@ namespace Avalonia.Data.Converters
/// <param name="inner"> /// <param name="inner">
/// An optional inner converter to be called before the format takes place. /// An optional inner converter to be called before the format takes place.
/// </param> /// </param>
public StringFormatValueConverter(string format, IValueConverter inner) public StringFormatValueConverter(string format, IValueConverter? inner)
{ {
Contract.Requires<ArgumentNullException>(format != null); Format = format ?? throw new ArgumentNullException(nameof(format));
Format = format;
Inner = inner; Inner = inner;
} }
/// <summary> /// <summary>
/// Gets an inner value converter which will be called before the string format takes place. /// Gets an inner value converter which will be called before the string format takes place.
/// </summary> /// </summary>
public IValueConverter Inner { get; } public IValueConverter? Inner { get; }
/// <summary> /// <summary>
/// Gets the format string. /// Gets the format string.
@ -34,14 +34,14 @@ namespace Avalonia.Data.Converters
public string Format { get; } public string Format { get; }
/// <inheritdoc/> /// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
value = Inner?.Convert(value, targetType, parameter, culture) ?? value; value = Inner?.Convert(value, targetType, parameter, culture) ?? value;
return string.Format(culture, Format, value); return string.Format(culture, Format, value);
} }
/// <inheritdoc/> /// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotSupportedException("Two way bindings are not supported with a string format"); throw new NotSupportedException("Two way bindings are not supported with a string format");
} }

18
src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs

@ -2,11 +2,13 @@
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Reactive; using Avalonia.Reactive;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class AvaloniaPropertyAccessorNode : SettableNode public class AvaloniaPropertyAccessorNode : SettableNode
{ {
private IDisposable _subscription; private IDisposable? _subscription;
private readonly bool _enableValidation; private readonly bool _enableValidation;
private readonly AvaloniaProperty _property; private readonly AvaloniaProperty _property;
@ -16,15 +18,15 @@ namespace Avalonia.Data.Core
_enableValidation = enableValidation; _enableValidation = enableValidation;
} }
public override string Description => PropertyName; public override string? Description => PropertyName;
public string PropertyName { get; } public string? PropertyName { get; }
public override Type PropertyType => _property.PropertyType; public override Type PropertyType => _property.PropertyType;
protected override bool SetTargetValueCore(object value, BindingPriority priority) protected override bool SetTargetValueCore(object? value, BindingPriority priority)
{ {
try try
{ {
if (Target.TryGetTarget(out object target) && target is IAvaloniaObject obj) if (Target.TryGetTarget(out var target) && target is IAvaloniaObject obj)
{ {
obj.SetValue(_property, value, priority); obj.SetValue(_property, value, priority);
return true; return true;
@ -37,11 +39,11 @@ namespace Avalonia.Data.Core
} }
} }
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
if (reference.TryGetTarget(out object target) && target is IAvaloniaObject obj) if (reference.TryGetTarget(out var target) && target is IAvaloniaObject obj)
{ {
_subscription = new AvaloniaPropertyObservable<object>(obj, _property).Subscribe(ValueChanged); _subscription = new AvaloniaPropertyObservable<object?>(obj, _property).Subscribe(ValueChanged);
} }
else else
{ {

48
src/Avalonia.Base/Data/Core/BindingExpression.cs

@ -7,21 +7,23 @@ using Avalonia.Logging;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
/// <summary> /// <summary>
/// Binds to an expression on an object using a type value converter to convert the values /// Binds to an expression on an object using a type value converter to convert the values
/// that are sent and received. /// that are sent and received.
/// </summary> /// </summary>
public class BindingExpression : LightweightObservableBase<object>, ISubject<object>, IDescription public class BindingExpression : LightweightObservableBase<object?>, ISubject<object?>, IDescription
{ {
private readonly ExpressionObserver _inner; private readonly ExpressionObserver _inner;
private readonly Type _targetType; private readonly Type _targetType;
private readonly object _fallbackValue; private readonly object? _fallbackValue;
private readonly object _targetNullValue; private readonly object? _targetNullValue;
private readonly BindingPriority _priority; private readonly BindingPriority _priority;
InnerListener _innerListener; InnerListener? _innerListener;
WeakReference<object> _value; WeakReference<object>? _value;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class. /// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
@ -47,7 +49,7 @@ namespace Avalonia.Data.Core
ExpressionObserver inner, ExpressionObserver inner,
Type targetType, Type targetType,
IValueConverter converter, IValueConverter converter,
object converterParameter = null, object? converterParameter = null,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
: this(inner, targetType, AvaloniaProperty.UnsetValue, AvaloniaProperty.UnsetValue, converter, converterParameter, priority) : this(inner, targetType, AvaloniaProperty.UnsetValue, AvaloniaProperty.UnsetValue, converter, converterParameter, priority)
{ {
@ -72,15 +74,15 @@ namespace Avalonia.Data.Core
public BindingExpression( public BindingExpression(
ExpressionObserver inner, ExpressionObserver inner,
Type targetType, Type targetType,
object fallbackValue, object? fallbackValue,
object targetNullValue, object? targetNullValue,
IValueConverter converter, IValueConverter converter,
object converterParameter = null, object? converterParameter = null,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
Contract.Requires<ArgumentNullException>(inner != null); _ = inner ?? throw new ArgumentNullException(nameof(inner));
Contract.Requires<ArgumentNullException>(targetType != null); _ = targetType ?? throw new ArgumentNullException(nameof(targetType));
Contract.Requires<ArgumentNullException>(converter != null); _ = converter ?? throw new ArgumentNullException(nameof(converter));
_inner = inner; _inner = inner;
_targetType = targetType; _targetType = targetType;
@ -99,10 +101,10 @@ namespace Avalonia.Data.Core
/// <summary> /// <summary>
/// Gets a parameter to pass to <see cref="Converter"/>. /// Gets a parameter to pass to <see cref="Converter"/>.
/// </summary> /// </summary>
public object ConverterParameter { get; } public object? ConverterParameter { get; }
/// <inheritdoc/> /// <inheritdoc/>
string IDescription.Description => _inner.Expression; string? IDescription.Description => _inner.Expression;
/// <inheritdoc/> /// <inheritdoc/>
public void OnCompleted() public void OnCompleted()
@ -115,7 +117,7 @@ namespace Avalonia.Data.Core
} }
/// <inheritdoc/> /// <inheritdoc/>
public void OnNext(object value) public void OnNext(object? value)
{ {
if (value == BindingOperations.DoNothing) if (value == BindingOperations.DoNothing)
{ {
@ -144,10 +146,8 @@ namespace Avalonia.Data.Core
converted = TypeUtilities.Default(type); converted = TypeUtilities.Default(type);
_inner.SetValue(converted, _priority); _inner.SetValue(converted, _priority);
} }
else if (converted is BindingNotification) else if (converted is BindingNotification notification)
{ {
var notification = converted as BindingNotification;
if (notification.ErrorType == BindingErrorType.None) if (notification.ErrorType == BindingErrorType.None)
{ {
throw new AvaloniaInternalException( throw new AvaloniaInternalException(
@ -185,7 +185,7 @@ namespace Avalonia.Data.Core
} }
protected override void Initialize() => _innerListener = new InnerListener(this); protected override void Initialize() => _innerListener = new InnerListener(this);
protected override void Deinitialize() => _innerListener.Dispose(); protected override void Deinitialize() => _innerListener?.Dispose();
protected override void Subscribed(IObserver<object> observer, bool first) protected override void Subscribed(IObserver<object> observer, bool first)
{ {
@ -196,7 +196,7 @@ namespace Avalonia.Data.Core
} }
/// <inheritdoc/> /// <inheritdoc/>
private object ConvertValue(object value) private object? ConvertValue(object? value)
{ {
if (value == null && _targetNullValue != AvaloniaProperty.UnsetValue) if (value == null && _targetNullValue != AvaloniaProperty.UnsetValue)
{ {
@ -302,7 +302,7 @@ namespace Avalonia.Data.Core
} }
} }
private static BindingNotification Merge(BindingNotification a, object b) private static BindingNotification Merge(BindingNotification a, object? b)
{ {
var bn = b as BindingNotification; var bn = b as BindingNotification;
@ -337,7 +337,7 @@ namespace Avalonia.Data.Core
return a; return a;
} }
public class InnerListener : IObserver<object>, IDisposable public class InnerListener : IObserver<object?>, IDisposable
{ {
private readonly BindingExpression _owner; private readonly BindingExpression _owner;
private readonly IDisposable _dispose; private readonly IDisposable _dispose;
@ -352,7 +352,7 @@ namespace Avalonia.Data.Core
public void OnCompleted() => _owner.PublishCompleted(); public void OnCompleted() => _owner.PublishCompleted();
public void OnError(Exception error) => _owner.PublishError(error); public void OnError(Exception error) => _owner.PublishError(error);
public void OnNext(object value) public void OnNext(object? value)
{ {
if (value == BindingOperations.DoNothing) if (value == BindingOperations.DoNothing)
{ {
@ -366,7 +366,7 @@ namespace Avalonia.Data.Core
return; return;
} }
_owner._value = new WeakReference<object>(converted); _owner._value = converted is not null ? new WeakReference<object>(converted) : null;
_owner.PublishNext(converted); _owner.PublishNext(converted);
} }
} }

18
src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs

@ -2,14 +2,16 @@ using System;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class ClrPropertyInfo : IPropertyInfo public class ClrPropertyInfo : IPropertyInfo
{ {
private readonly Func<object, object> _getter; private readonly Func<object, object?>? _getter;
private readonly Action<object, object> _setter; private readonly Action<object, object?>? _setter;
public ClrPropertyInfo(string name, Func<object, object> getter, Action<object, object> setter, Type propertyType) public ClrPropertyInfo(string name, Func<object, object?>? getter, Action<object, object?>? setter, Type propertyType)
{ {
_getter = getter; _getter = getter;
_setter = setter; _setter = setter;
@ -20,14 +22,14 @@ namespace Avalonia.Data.Core
public string Name { get; } public string Name { get; }
public Type PropertyType { get; } public Type PropertyType { get; }
public object Get(object target) public object? Get(object target)
{ {
if (_getter == null) if (_getter == null)
throw new NotSupportedException("Property " + Name + " doesn't have a getter"); throw new NotSupportedException("Property " + Name + " doesn't have a getter");
return _getter(target); return _getter(target);
} }
public void Set(object target, object value) public void Set(object target, object? value)
{ {
if (_setter == null) if (_setter == null)
throw new NotSupportedException("Property " + Name + " doesn't have a setter"); throw new NotSupportedException("Property " + Name + " doesn't have a setter");
@ -40,20 +42,20 @@ namespace Avalonia.Data.Core
public class ReflectionClrPropertyInfo : ClrPropertyInfo public class ReflectionClrPropertyInfo : ClrPropertyInfo
{ {
static Action<object, object> CreateSetter(PropertyInfo info) static Action<object, object?>? CreateSetter(PropertyInfo info)
{ {
if (info.SetMethod == null) if (info.SetMethod == null)
return null; return null;
var target = Expression.Parameter(typeof(object), "target"); var target = Expression.Parameter(typeof(object), "target");
var value = Expression.Parameter(typeof(object), "value"); var value = Expression.Parameter(typeof(object), "value");
return Expression.Lambda<Action<object, object>>( return Expression.Lambda<Action<object, object?>>(
Expression.Call(Expression.Convert(target, info.DeclaringType), info.SetMethod, Expression.Call(Expression.Convert(target, info.DeclaringType), info.SetMethod,
Expression.Convert(value, info.SetMethod.GetParameters()[0].ParameterType)), Expression.Convert(value, info.SetMethod.GetParameters()[0].ParameterType)),
target, value) target, value)
.Compile(); .Compile();
} }
static Func<object, object> CreateGetter(PropertyInfo info) static Func<object, object>? CreateGetter(PropertyInfo info)
{ {
if (info.GetMethod == null) if (info.GetMethod == null)
return null; return null;

2
src/Avalonia.Base/Data/Core/CommonPropertyNames.cs

@ -1,3 +1,5 @@
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public static class CommonPropertyNames public static class CommonPropertyNames

4
src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs

@ -1,4 +1,6 @@
namespace Avalonia.Data.Core #nullable enable
namespace Avalonia.Data.Core
{ {
public class EmptyExpressionNode : ExpressionNode public class EmptyExpressionNode : ExpressionNode
{ {

54
src/Avalonia.Base/Data/Core/ExpressionNode.cs

@ -1,35 +1,37 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public abstract class ExpressionNode public abstract class ExpressionNode
{ {
private static readonly object CacheInvalid = new object(); private static readonly object CacheInvalid = new object();
protected static readonly WeakReference<object> UnsetReference = protected static readonly WeakReference<object?> UnsetReference =
new WeakReference<object>(AvaloniaProperty.UnsetValue); new WeakReference<object?>(AvaloniaProperty.UnsetValue);
protected static readonly WeakReference<object> NullReference = protected static readonly WeakReference<object?> NullReference =
new WeakReference<object>(null); new WeakReference<object?>(null);
private WeakReference<object> _target = UnsetReference; private WeakReference<object?> _target = UnsetReference;
private Action<object> _subscriber; private Action<object?>? _subscriber;
private bool _listening; private bool _listening;
protected WeakReference<object> LastValue { get; private set; } protected WeakReference<object?>? LastValue { get; private set; }
public abstract string Description { get; } public abstract string? Description { get; }
public ExpressionNode Next { get; set; } public ExpressionNode? Next { get; set; }
public WeakReference<object> Target public WeakReference<object?> Target
{ {
get { return _target; } get { return _target; }
set set
{ {
Contract.Requires<ArgumentNullException>(value != null); _ = value ?? throw new ArgumentNullException(nameof(value));
_target.TryGetTarget(out var oldTarget); _target.TryGetTarget(out var oldTarget);
value.TryGetTarget(out object newTarget); value.TryGetTarget(out var newTarget);
if (!ReferenceEquals(oldTarget, newTarget)) if (!ReferenceEquals(oldTarget, newTarget))
{ {
@ -48,7 +50,7 @@ namespace Avalonia.Data.Core
} }
} }
public void Subscribe(Action<object> subscriber) public void Subscribe(Action<object?> subscriber)
{ {
if (_subscriber != null) if (_subscriber != null)
{ {
@ -73,9 +75,9 @@ namespace Avalonia.Data.Core
_subscriber = null; _subscriber = null;
} }
protected virtual void StartListeningCore(WeakReference<object> reference) protected virtual void StartListeningCore(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
ValueChanged(target); ValueChanged(target);
} }
@ -84,22 +86,28 @@ namespace Avalonia.Data.Core
{ {
} }
protected virtual void NextValueChanged(object value) protected virtual void NextValueChanged(object? value)
{ {
if (_subscriber is null)
return;
var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainException; var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainException;
bindingBroken?.AddNode(Description); bindingBroken?.AddNode(Description ?? "{empty}");
_subscriber(value); _subscriber(value);
} }
protected void ValueChanged(object value) => ValueChanged(value, true); protected void ValueChanged(object? value) => ValueChanged(value, true);
private void ValueChanged(object value, bool notify) private void ValueChanged(object? value, bool notify)
{ {
if (_subscriber is null)
return;
var notification = value as BindingNotification; var notification = value as BindingNotification;
if (notification == null) if (notification == null)
{ {
LastValue = value != null ? new WeakReference<object>(value) : NullReference; LastValue = value != null ? new WeakReference<object?>(value) : NullReference;
if (Next != null) if (Next != null)
{ {
@ -112,7 +120,7 @@ namespace Avalonia.Data.Core
} }
else else
{ {
LastValue = notification.Value != null ? new WeakReference<object>(notification.Value) : NullReference; LastValue = notification.Value != null ? new WeakReference<object?>(notification.Value) : NullReference;
if (Next != null) if (Next != null)
{ {
@ -128,7 +136,7 @@ namespace Avalonia.Data.Core
private void StartListening() private void StartListening()
{ {
_target.TryGetTarget(out object target); _target.TryGetTarget(out var target);
if (target == null) if (target == null)
{ {
@ -138,7 +146,7 @@ namespace Avalonia.Data.Core
else if (target != AvaloniaProperty.UnsetValue) else if (target != AvaloniaProperty.UnsetValue)
{ {
_listening = true; _listening = true;
StartListeningCore(_target); StartListeningCore(_target!);
} }
else else
{ {

76
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@ -7,12 +7,14 @@ using Avalonia.Data.Core.Parsers;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
using Avalonia.Reactive; using Avalonia.Reactive;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
/// <summary> /// <summary>
/// Observes and sets the value of an expression on an object. /// Observes and sets the value of an expression on an object.
/// </summary> /// </summary>
public class ExpressionObserver : LightweightObservableBase<object>, IDescription public class ExpressionObserver : LightweightObservableBase<object?>, IDescription
{ {
/// <summary> /// <summary>
/// An ordered collection of property accessor plugins that can be used to customize /// An ordered collection of property accessor plugins that can be used to customize
@ -51,10 +53,10 @@ namespace Avalonia.Data.Core
private static readonly object UninitializedValue = new object(); private static readonly object UninitializedValue = new object();
private readonly ExpressionNode _node; private readonly ExpressionNode _node;
private object _root; private object? _root;
private IDisposable _rootSubscription; private IDisposable? _rootSubscription;
private WeakReference<object> _value; private WeakReference<object?>? _value;
private IReadOnlyList<ITransformNode> _transformNodes; private IReadOnlyList<ITransformNode>? _transformNodes;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class. /// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
@ -65,18 +67,13 @@ namespace Avalonia.Data.Core
/// A description of the expression. /// A description of the expression.
/// </param> /// </param>
public ExpressionObserver( public ExpressionObserver(
object root, object? root,
ExpressionNode node, ExpressionNode node,
string description = null) string? description = null)
{ {
if (root == AvaloniaProperty.UnsetValue)
{
root = null;
}
_node = node; _node = node;
Description = description; Description = description;
_root = new WeakReference<object>(root); _root = new WeakReference<object?>(root == AvaloniaProperty.UnsetValue ? null : root);
} }
/// <summary> /// <summary>
@ -88,11 +85,11 @@ namespace Avalonia.Data.Core
/// A description of the expression. /// A description of the expression.
/// </param> /// </param>
public ExpressionObserver( public ExpressionObserver(
IObservable<object> rootObservable, IObservable<object?> rootObservable,
ExpressionNode node, ExpressionNode node,
string description) string? description)
{ {
Contract.Requires<ArgumentNullException>(rootObservable != null); _ = rootObservable ??throw new ArgumentNullException(nameof(rootObservable));
_node = node; _node = node;
Description = description; Description = description;
@ -109,16 +106,16 @@ namespace Avalonia.Data.Core
/// A description of the expression. /// A description of the expression.
/// </param> /// </param>
public ExpressionObserver( public ExpressionObserver(
Func<object> rootGetter, Func<object?> rootGetter,
ExpressionNode node, ExpressionNode node,
IObservable<Unit> update, IObservable<Unit> update,
string description) string? description)
{ {
Contract.Requires<ArgumentNullException>(rootGetter != null); _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
Contract.Requires<ArgumentNullException>(update != null);
Description = description; Description = description;
_node = node; _node = node ?? throw new ArgumentNullException(nameof(rootGetter));
_node.Target = new WeakReference<object>(rootGetter()); _node.Target = new WeakReference<object?>(rootGetter());
_root = update.Select(x => rootGetter()); _root = update.Select(x => rootGetter());
} }
@ -133,10 +130,10 @@ namespace Avalonia.Data.Core
/// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used. /// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used.
/// </param> /// </param>
public static ExpressionObserver Create<T, U>( public static ExpressionObserver Create<T, U>(
T root, T? root,
Expression<Func<T, U>> expression, Expression<Func<T, U>> expression,
bool enableDataValidation = false, bool enableDataValidation = false,
string description = null) string? description = null)
{ {
return new ExpressionObserver(root, Parse(expression, enableDataValidation), description ?? expression.ToString()); return new ExpressionObserver(root, Parse(expression, enableDataValidation), description ?? expression.ToString());
} }
@ -154,11 +151,12 @@ namespace Avalonia.Data.Core
IObservable<T> rootObservable, IObservable<T> rootObservable,
Expression<Func<T, U>> expression, Expression<Func<T, U>> expression,
bool enableDataValidation = false, bool enableDataValidation = false,
string description = null) string? description = null)
{ {
Contract.Requires<ArgumentNullException>(rootObservable != null); _ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
return new ExpressionObserver( return new ExpressionObserver(
rootObservable.Select(o => (object)o), rootObservable.Select(o => (object?)o),
Parse(expression, enableDataValidation), Parse(expression, enableDataValidation),
description ?? expression.ToString()); description ?? expression.ToString());
} }
@ -178,9 +176,9 @@ namespace Avalonia.Data.Core
Expression<Func<T, U>> expression, Expression<Func<T, U>> expression,
IObservable<Unit> update, IObservable<Unit> update,
bool enableDataValidation = false, bool enableDataValidation = false,
string description = null) string? description = null)
{ {
Contract.Requires<ArgumentNullException>(rootGetter != null); _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
return new ExpressionObserver( return new ExpressionObserver(
() => rootGetter(), () => rootGetter(),
@ -218,7 +216,7 @@ namespace Avalonia.Data.Core
/// before setting the target value can work, as setting the value requires the /// before setting the target value can work, as setting the value requires the
/// expression to be evaluated. /// expression to be evaluated.
/// </returns> /// </returns>
public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue) public bool SetValue(object? value, BindingPriority priority = BindingPriority.LocalValue)
{ {
if (Leaf is SettableNode settable) if (Leaf is SettableNode settable)
{ {
@ -238,18 +236,18 @@ namespace Avalonia.Data.Core
/// <summary> /// <summary>
/// Gets a description of the expression being observed. /// Gets a description of the expression being observed.
/// </summary> /// </summary>
public string Description { get; } public string? Description { get; }
/// <summary> /// <summary>
/// Gets the expression being observed. /// Gets the expression being observed.
/// </summary> /// </summary>
public string Expression { get; } public string? Expression { get; }
/// <summary> /// <summary>
/// Gets the type of the expression result or null if the expression could not be /// Gets the type of the expression result or null if the expression could not be
/// evaluated. /// evaluated.
/// </summary> /// </summary>
public Type ResultType => (Leaf as SettableNode)?.PropertyType; public Type? ResultType => (Leaf as SettableNode)?.PropertyType;
/// <summary> /// <summary>
/// Gets the leaf node. /// Gets the leaf node.
@ -278,7 +276,7 @@ namespace Avalonia.Data.Core
_node.Unsubscribe(); _node.Unsubscribe();
} }
protected override void Subscribed(IObserver<object> observer, bool first) protected override void Subscribed(IObserver<object?> observer, bool first)
{ {
if (!first && _value != null && _value.TryGetTarget(out var value)) if (!first && _value != null && _value.TryGetTarget(out var value))
{ {
@ -296,21 +294,21 @@ namespace Avalonia.Data.Core
if (_root is IObservable<object> observable) if (_root is IObservable<object> observable)
{ {
_rootSubscription = observable.Subscribe( _rootSubscription = observable.Subscribe(
x => _node.Target = new WeakReference<object>(x != AvaloniaProperty.UnsetValue ? x : null), x => _node.Target = new WeakReference<object?>(x != AvaloniaProperty.UnsetValue ? x : null),
x => PublishCompleted(), x => PublishCompleted(),
() => PublishCompleted()); () => PublishCompleted());
} }
else else
{ {
_node.Target = (WeakReference<object>)_root; _node.Target = (WeakReference<object?>)_root!;
} }
} }
private void ValueChanged(object value) private void ValueChanged(object? value)
{ {
var broken = BindingNotification.ExtractError(value) as MarkupBindingChainException; var broken = BindingNotification.ExtractError(value) as MarkupBindingChainException;
broken?.Commit(Description); broken?.Commit(Description ?? "{empty}");
_value = new WeakReference<object>(value); _value = new WeakReference<object?>(value);
PublishNext(value); PublishNext(value);
} }
} }

4
src/Avalonia.Base/Data/Core/ExpressionParseException.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
/// <summary> /// <summary>
@ -17,7 +19,7 @@ namespace Avalonia.Data.Core
/// <param name="column">The column position of the error.</param> /// <param name="column">The column position of the error.</param>
/// <param name="message">The exception message.</param> /// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the parsing failure.</param> /// <param name="innerException">The exception that caused the parsing failure.</param>
public ExpressionParseException(int column, string message, Exception innerException = null) public ExpressionParseException(int column, string message, Exception? innerException = null)
: base(message, innerException) : base(message, innerException)
{ {
Column = column; Column = column;

6
src/Avalonia.Base/Data/Core/ITransformNode.cs

@ -1,7 +1,9 @@
namespace Avalonia.Data.Core #nullable enable
namespace Avalonia.Data.Core
{ {
interface ITransformNode interface ITransformNode
{ {
object Transform(object value); object? Transform(object? value);
} }
} }

12
src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs

@ -4,6 +4,8 @@ using System.ComponentModel;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
class IndexerExpressionNode : IndexerNodeBase class IndexerExpressionNode : IndexerNodeBase
@ -32,11 +34,11 @@ namespace Avalonia.Data.Core
public override string Description => _expression.ToString(); public override string Description => _expression.ToString();
protected override bool SetTargetValueCore(object value, BindingPriority priority) protected override bool SetTargetValueCore(object? value, BindingPriority priority)
{ {
try try
{ {
Target.TryGetTarget(out object target); Target.TryGetTarget(out var target);
_setDelegate.DynamicInvoke(target, value); _setDelegate.DynamicInvoke(target, value);
return true; return true;
@ -47,7 +49,7 @@ namespace Avalonia.Data.Core
} }
} }
protected override object GetValue(object target) protected override object? GetValue(object? target)
{ {
try try
{ {
@ -61,14 +63,14 @@ namespace Avalonia.Data.Core
} }
} }
protected override bool ShouldUpdate(object sender, PropertyChangedEventArgs e) protected override bool ShouldUpdate(object? sender, PropertyChangedEventArgs e)
{ {
return _expression.Indexer == null || _expression.Indexer.Name == e.PropertyName; return _expression.Indexer == null || _expression.Indexer.Name == e.PropertyName;
} }
protected override int? TryGetFirstArgumentAsInt() protected override int? TryGetFirstArgumentAsInt()
{ {
Target.TryGetTarget(out object target); Target.TryGetTarget(out var target);
return _firstArgumentDelegate.DynamicInvoke(target) as int?; return _firstArgumentDelegate.DynamicInvoke(target) as int?;
} }

18
src/Avalonia.Base/Data/Core/IndexerNodeBase.cs

@ -7,19 +7,21 @@ using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public abstract class IndexerNodeBase : SettableNode public abstract class IndexerNodeBase : SettableNode
{ {
private IDisposable _subscription; private IDisposable? _subscription;
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
var incc = target as INotifyCollectionChanged; var incc = target as INotifyCollectionChanged;
var inpc = target as INotifyPropertyChanged; var inpc = target as INotifyPropertyChanged;
var inputs = new List<IObservable<object>>(); var inputs = new List<IObservable<object?>>();
if (incc != null) if (incc != null)
{ {
@ -44,14 +46,14 @@ namespace Avalonia.Data.Core
protected override void StopListeningCore() protected override void StopListeningCore()
{ {
_subscription.Dispose(); _subscription?.Dispose();
} }
protected abstract object GetValue(object target); protected abstract object? GetValue(object? target);
protected abstract int? TryGetFirstArgumentAsInt(); protected abstract int? TryGetFirstArgumentAsInt();
private bool ShouldUpdate(object sender, NotifyCollectionChangedEventArgs e) private bool ShouldUpdate(object? sender, NotifyCollectionChangedEventArgs e)
{ {
if (sender is IList) if (sender is IList)
{ {
@ -84,6 +86,6 @@ namespace Avalonia.Data.Core
return true; // Implementation defined meaning for the index, so just try to update anyway return true; // Implementation defined meaning for the index, so just try to update anyway
} }
protected abstract bool ShouldUpdate(object sender, PropertyChangedEventArgs e); protected abstract bool ShouldUpdate(object? sender, PropertyChangedEventArgs e);
} }
} }

11
src/Avalonia.Base/Data/Core/LogicalNotNode.cs

@ -1,18 +1,20 @@
using System; using System;
using System.Globalization; using System.Globalization;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class LogicalNotNode : ExpressionNode, ITransformNode public class LogicalNotNode : ExpressionNode, ITransformNode
{ {
public override string Description => "!"; public override string Description => "!";
protected override void NextValueChanged(object value) protected override void NextValueChanged(object? value)
{ {
base.NextValueChanged(Negate(value)); base.NextValueChanged(Negate(value));
} }
private static object Negate(object value) private static object Negate(object? value)
{ {
var notification = value as BindingNotification; var notification = value as BindingNotification;
var v = BindingNotification.ExtractValue(value); var v = BindingNotification.ExtractValue(value);
@ -74,8 +76,11 @@ namespace Avalonia.Data.Core
return notification ?? AvaloniaProperty.UnsetValue; return notification ?? AvaloniaProperty.UnsetValue;
} }
public object Transform(object value) public object? Transform(object? value)
{ {
if (value is null)
return null;
var originalType = value.GetType(); var originalType = value.GetType();
var negated = Negate(value); var negated = Negate(value);
if (negated is BindingNotification) if (negated is BindingNotification)

6
src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs

@ -2,11 +2,13 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
internal class MarkupBindingChainException : BindingChainException internal class MarkupBindingChainException : BindingChainException
{ {
private IList<string> _nodes = new List<string>(); private IList<string>? _nodes = new List<string>();
public MarkupBindingChainException(string message) public MarkupBindingChainException(string message)
: base(message) : base(message)
@ -26,7 +28,7 @@ namespace Avalonia.Data.Core
} }
public bool HasNodes => _nodes?.Count > 0; public bool HasNodes => _nodes?.Count > 0;
public void AddNode(string node) => _nodes.Add(node); public void AddNode(string node) => _nodes?.Add(node);
public void Commit(string expression) public void Commit(string expression)
{ {

2
src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs

@ -1,6 +1,8 @@
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
#nullable enable
namespace Avalonia.Data.Core.Parsers namespace Avalonia.Data.Core.Parsers
{ {
static class ExpressionTreeParser static class ExpressionTreeParser

2
src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs

@ -4,6 +4,8 @@ using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Parsers namespace Avalonia.Data.Core.Parsers
{ {
class ExpressionVisitorNodeBuilder : ExpressionVisitor class ExpressionVisitorNodeBuilder : ExpressionVisitor

52
src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -28,12 +30,14 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
public IPropertyAccessor Start(WeakReference<object> reference, string propertyName) public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName)
{ {
Contract.Requires<ArgumentNullException>(reference != null); _ = reference ?? throw new ArgumentNullException(nameof(reference));
Contract.Requires<ArgumentNullException>(propertyName != null); _ = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
if (!reference.TryGetTarget(out var instance) || instance is null)
return null;
reference.TryGetTarget(out object instance);
var o = (AvaloniaObject)instance; var o = (AvaloniaObject)instance;
var p = LookupProperty(o, propertyName); var p = LookupProperty(o, propertyName);
@ -53,39 +57,21 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
private static AvaloniaProperty LookupProperty(AvaloniaObject o, string propertyName) private static AvaloniaProperty? LookupProperty(AvaloniaObject o, string propertyName)
{ {
return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName); return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName);
} }
private static bool IsOfType(Type type, string typeName) private class Accessor : PropertyAccessorBase, IObserver<object?>
{
while (type != null)
{
if (type.Name == typeName)
{
return true;
}
type = type.BaseType;
}
return false;
}
private class Accessor : PropertyAccessorBase, IObserver<object>
{ {
private readonly WeakReference<AvaloniaObject> _reference; private readonly WeakReference<AvaloniaObject> _reference;
private readonly AvaloniaProperty _property; private readonly AvaloniaProperty _property;
private IDisposable _subscription; private IDisposable? _subscription;
public Accessor(WeakReference<AvaloniaObject> reference, AvaloniaProperty property) public Accessor(WeakReference<AvaloniaObject> reference, AvaloniaProperty property)
{ {
Contract.Requires<ArgumentNullException>(reference != null); _reference = reference ?? throw new ArgumentNullException(nameof(reference));
Contract.Requires<ArgumentNullException>(property != null); _property = property ?? throw new ArgumentNullException(nameof(property));
_reference = reference;
_property = property;
} }
public AvaloniaObject Instance public AvaloniaObject Instance
@ -98,10 +84,10 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
public override Type PropertyType => _property.PropertyType; public override Type? PropertyType => _property?.PropertyType;
public override object Value => Instance?.GetValue(_property); public override object? Value => Instance?.GetValue(_property);
public override bool SetValue(object value, BindingPriority priority) public override bool SetValue(object? value, BindingPriority priority)
{ {
if (!_property.IsReadOnly) if (!_property.IsReadOnly)
{ {
@ -123,16 +109,16 @@ namespace Avalonia.Data.Core.Plugins
_subscription = null; _subscription = null;
} }
void IObserver<object>.OnCompleted() void IObserver<object?>.OnCompleted()
{ {
} }
void IObserver<object>.OnError(Exception error) void IObserver<object?>.OnError(Exception error)
{ {
ExceptionDispatchInfo.Capture(error).Throw(); ExceptionDispatchInfo.Capture(error).Throw();
} }
void IObserver<object>.OnNext(object value) void IObserver<object?>.OnNext(object? value)
{ {
PublishValue(value); PublishValue(value);
} }

14
src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

@ -4,6 +4,8 @@ using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -12,9 +14,9 @@ namespace Avalonia.Data.Core.Plugins
public class DataAnnotationsValidationPlugin : IDataValidationPlugin public class DataAnnotationsValidationPlugin : IDataValidationPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool Match(WeakReference<object> reference, string memberName) public bool Match(WeakReference<object?> reference, string memberName)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
return target? return target?
.GetType() .GetType()
@ -24,7 +26,7 @@ namespace Avalonia.Data.Core.Plugins
} }
/// <inheritdoc/> /// <inheritdoc/>
public IPropertyAccessor Start(WeakReference<object> reference, string name, IPropertyAccessor inner) public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor inner)
{ {
return new Accessor(reference, name, inner); return new Accessor(reference, name, inner);
} }
@ -33,16 +35,16 @@ namespace Avalonia.Data.Core.Plugins
{ {
private readonly ValidationContext _context; private readonly ValidationContext _context;
public Accessor(WeakReference<object> reference, string name, IPropertyAccessor inner) public Accessor(WeakReference<object?> reference, string name, IPropertyAccessor inner)
: base(inner) : base(inner)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
_context = new ValidationContext(target); _context = new ValidationContext(target);
_context.MemberName = name; _context.MemberName = name;
} }
protected override void InnerValueChanged(object value) protected override void InnerValueChanged(object? value)
{ {
var errors = new List<ValidationResult>(); var errors = new List<ValidationResult>();

18
src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -11,7 +13,7 @@ namespace Avalonia.Data.Core.Plugins
/// and convert any values received from the inner property accessor into /// and convert any values received from the inner property accessor into
/// <see cref="BindingNotification"/>s. /// <see cref="BindingNotification"/>s.
/// </remarks> /// </remarks>
public abstract class DataValidationBase : PropertyAccessorBase, IObserver<object> public abstract class DataValidationBase : PropertyAccessorBase, IObserver<object?>
{ {
private readonly IPropertyAccessor _inner; private readonly IPropertyAccessor _inner;
@ -25,31 +27,31 @@ namespace Avalonia.Data.Core.Plugins
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Type PropertyType => _inner.PropertyType; public override Type? PropertyType => _inner.PropertyType;
/// <inheritdoc/> /// <inheritdoc/>
public override object Value => _inner.Value; public override object? Value => _inner.Value;
/// <inheritdoc/> /// <inheritdoc/>
public override bool SetValue(object value, BindingPriority priority) => _inner.SetValue(value, priority); public override bool SetValue(object? value, BindingPriority priority) => _inner.SetValue(value, priority);
/// <summary> /// <summary>
/// Should never be called: the inner <see cref="IPropertyAccessor"/> should never notify /// Should never be called: the inner <see cref="IPropertyAccessor"/> should never notify
/// completion. /// completion.
/// </summary> /// </summary>
void IObserver<object>.OnCompleted() { } void IObserver<object?>.OnCompleted() { }
/// <summary> /// <summary>
/// Should never be called: the inner <see cref="IPropertyAccessor"/> should never notify /// Should never be called: the inner <see cref="IPropertyAccessor"/> should never notify
/// an error. /// an error.
/// </summary> /// </summary>
void IObserver<object>.OnError(Exception error) { } void IObserver<object?>.OnError(Exception error) { }
/// <summary> /// <summary>
/// Called when the inner <see cref="IPropertyAccessor"/> notifies with a new value. /// Called when the inner <see cref="IPropertyAccessor"/> notifies with a new value.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
void IObserver<object>.OnNext(object value) => InnerValueChanged(value); void IObserver<object?>.OnNext(object? value) => InnerValueChanged(value);
/// <summary> /// <summary>
/// Begins listening to the inner <see cref="IPropertyAccessor"/>. /// Begins listening to the inner <see cref="IPropertyAccessor"/>.
@ -67,7 +69,7 @@ namespace Avalonia.Data.Core.Plugins
/// Notifies the observer that the value has changed. The value will be wrapped in a /// Notifies the observer that the value has changed. The value will be wrapped in a
/// <see cref="BindingNotification"/> if it is not already a binding notification. /// <see cref="BindingNotification"/> if it is not already a binding notification.
/// </remarks> /// </remarks>
protected virtual void InnerValueChanged(object value) protected virtual void InnerValueChanged(object? value)
{ {
var notification = value as BindingNotification ?? new BindingNotification(value); var notification = value as BindingNotification ?? new BindingNotification(value);
PublishValue(notification); PublishValue(notification);

10
src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Reflection; using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -9,22 +11,22 @@ namespace Avalonia.Data.Core.Plugins
public class ExceptionValidationPlugin : IDataValidationPlugin public class ExceptionValidationPlugin : IDataValidationPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool Match(WeakReference<object> reference, string memberName) => true; public bool Match(WeakReference<object?> reference, string memberName) => true;
/// <inheritdoc/> /// <inheritdoc/>
public IPropertyAccessor Start(WeakReference<object> reference, string name, IPropertyAccessor inner) public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor inner)
{ {
return new Validator(reference, name, inner); return new Validator(reference, name, inner);
} }
private sealed class Validator : DataValidationBase private sealed class Validator : DataValidationBase
{ {
public Validator(WeakReference<object> reference, string name, IPropertyAccessor inner) public Validator(WeakReference<object?> reference, string name, IPropertyAccessor inner)
: base(inner) : base(inner)
{ {
} }
public override bool SetValue(object value, BindingPriority priority) public override bool SetValue(object? value, BindingPriority priority)
{ {
try try
{ {

6
src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -13,7 +15,7 @@ namespace Avalonia.Data.Core.Plugins
/// <param name="reference">A weak reference to the object.</param> /// <param name="reference">A weak reference to the object.</param>
/// <param name="memberName">The name of the member to validate.</param> /// <param name="memberName">The name of the member to validate.</param>
/// <returns>True if the plugin can handle the object; otherwise false.</returns> /// <returns>True if the plugin can handle the object; otherwise false.</returns>
bool Match(WeakReference<object> reference, string memberName); bool Match(WeakReference<object?> reference, string memberName);
/// <summary> /// <summary>
/// Starts monitoring the data validation state of a property on an object. /// Starts monitoring the data validation state of a property on an object.
@ -25,7 +27,7 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
IPropertyAccessor Start(WeakReference<object> reference, IPropertyAccessor Start(WeakReference<object?> reference,
string propertyName, string propertyName,
IPropertyAccessor inner); IPropertyAccessor inner);
} }

10
src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -14,12 +16,12 @@ namespace Avalonia.Data.Core.Plugins
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The accessor has not been subscribed to yet. /// The accessor has not been subscribed to yet.
/// </exception> /// </exception>
Type PropertyType { get; } Type? PropertyType { get; }
/// <summary> /// <summary>
/// Gets the current value of the property. /// Gets the current value of the property.
/// </summary> /// </summary>
object Value { get; } object? Value { get; }
/// <summary> /// <summary>
/// Sets the property value. /// Sets the property value.
@ -33,13 +35,13 @@ namespace Avalonia.Data.Core.Plugins
/// <returns> /// <returns>
/// True if the property was set; false if the property could not be set. /// True if the property was set; false if the property could not be set.
/// </returns> /// </returns>
bool SetValue(object value, BindingPriority priority); bool SetValue(object? value, BindingPriority priority);
/// <summary> /// <summary>
/// Subscribes to the value of the member. /// Subscribes to the value of the member.
/// </summary> /// </summary>
/// <param name="listener">A method that receives the values.</param> /// <param name="listener">A method that receives the values.</param>
void Subscribe(Action<object> listener); void Subscribe(Action<object?> listener);
/// <summary> /// <summary>
/// Unsubscribes to the value of the member. /// Unsubscribes to the value of the member.

4
src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -25,7 +27,7 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
IPropertyAccessor Start(WeakReference<object> reference, IPropertyAccessor? Start(WeakReference<object?> reference,
string propertyName); string propertyName);
} }
} }

6
src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -12,7 +14,7 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
/// <param name="reference">A weak reference to the value.</param> /// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns> /// <returns>True if the plugin can handle the value; otherwise false.</returns>
bool Match(WeakReference<object> reference); bool Match(WeakReference<object?> reference);
/// <summary> /// <summary>
/// Starts producing output based on the specified value. /// Starts producing output based on the specified value.
@ -21,6 +23,6 @@ namespace Avalonia.Data.Core.Plugins
/// <returns> /// <returns>
/// An observable that produces the output for the value. /// An observable that produces the output for the value.
/// </returns> /// </returns>
IObservable<object> Start(WeakReference<object> reference); IObservable<object?> Start(WeakReference<object?> reference);
} }
} }

24
src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs

@ -4,6 +4,8 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -12,25 +14,25 @@ namespace Avalonia.Data.Core.Plugins
public class IndeiValidationPlugin : IDataValidationPlugin public class IndeiValidationPlugin : IDataValidationPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool Match(WeakReference<object> reference, string memberName) public bool Match(WeakReference<object?> reference, string memberName)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
return target is INotifyDataErrorInfo; return target is INotifyDataErrorInfo;
} }
/// <inheritdoc/> /// <inheritdoc/>
public IPropertyAccessor Start(WeakReference<object> reference, string name, IPropertyAccessor accessor) public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor accessor)
{ {
return new Validator(reference, name, accessor); return new Validator(reference, name, accessor);
} }
private class Validator : DataValidationBase, IWeakSubscriber<DataErrorsChangedEventArgs> private class Validator : DataValidationBase, IWeakSubscriber<DataErrorsChangedEventArgs>
{ {
private readonly WeakReference<object> _reference; private readonly WeakReference<object?> _reference;
private readonly string _name; private readonly string _name;
public Validator(WeakReference<object> reference, string name, IPropertyAccessor inner) public Validator(WeakReference<object?> reference, string name, IPropertyAccessor inner)
: base(inner) : base(inner)
{ {
_reference = reference; _reference = reference;
@ -75,16 +77,14 @@ namespace Avalonia.Data.Core.Plugins
base.UnsubscribeCore(); base.UnsubscribeCore();
} }
protected override void InnerValueChanged(object value) protected override void InnerValueChanged(object? value)
{ {
PublishValue(CreateBindingNotification(value)); PublishValue(CreateBindingNotification(value));
} }
private BindingNotification CreateBindingNotification(object value) private BindingNotification CreateBindingNotification(object? value)
{ {
var target = (INotifyDataErrorInfo)GetReferenceTarget(); if (GetReferenceTarget() is INotifyDataErrorInfo target)
if (target != null)
{ {
var errors = target.GetErrors(_name)? var errors = target.GetErrors(_name)?
.Cast<object>() .Cast<object>()
@ -103,9 +103,9 @@ namespace Avalonia.Data.Core.Plugins
return new BindingNotification(value); return new BindingNotification(value);
} }
private object GetReferenceTarget() private object? GetReferenceTarget()
{ {
_reference.TryGetTarget(out object target); _reference.TryGetTarget(out var target);
return target; return target;
} }

41
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -4,6 +4,8 @@ using System.ComponentModel;
using System.Reflection; using System.Reflection;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -12,8 +14,8 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
{ {
private readonly Dictionary<(Type, string), PropertyInfo> _propertyLookup = private readonly Dictionary<(Type, string), PropertyInfo?> _propertyLookup =
new Dictionary<(Type, string), PropertyInfo>(); new Dictionary<(Type, string), PropertyInfo?>();
/// <inheritdoc/> /// <inheritdoc/>
public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null; public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null;
@ -27,12 +29,13 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
public IPropertyAccessor Start(WeakReference<object> reference, string propertyName) public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName)
{ {
Contract.Requires<ArgumentNullException>(reference != null); _ = reference ?? throw new ArgumentNullException(nameof(reference));
Contract.Requires<ArgumentNullException>(propertyName != null); _ = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
reference.TryGetTarget(out object instance); if (!reference.TryGetTarget(out var instance) || instance is null)
return null;
var p = GetFirstPropertyWithName(instance.GetType(), propertyName); var p = GetFirstPropertyWithName(instance.GetType(), propertyName);
@ -48,11 +51,11 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
private PropertyInfo GetFirstPropertyWithName(Type type, string propertyName) private PropertyInfo? GetFirstPropertyWithName(Type type, string propertyName)
{ {
var key = (type, propertyName); var key = (type, propertyName);
if (!_propertyLookup.TryGetValue(key, out PropertyInfo propertyInfo)) if (!_propertyLookup.TryGetValue(key, out var propertyInfo))
{ {
propertyInfo = TryFindAndCacheProperty(type, propertyName); propertyInfo = TryFindAndCacheProperty(type, propertyName);
} }
@ -60,9 +63,9 @@ namespace Avalonia.Data.Core.Plugins
return propertyInfo; return propertyInfo;
} }
private PropertyInfo TryFindAndCacheProperty(Type type, string propertyName) private PropertyInfo? TryFindAndCacheProperty(Type type, string propertyName)
{ {
PropertyInfo found = null; PropertyInfo? found = null;
const BindingFlags bindingFlags = const BindingFlags bindingFlags =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
@ -86,22 +89,22 @@ namespace Avalonia.Data.Core.Plugins
private class Accessor : PropertyAccessorBase, IWeakSubscriber<PropertyChangedEventArgs> private class Accessor : PropertyAccessorBase, IWeakSubscriber<PropertyChangedEventArgs>
{ {
private readonly WeakReference<object> _reference; private readonly WeakReference<object?> _reference;
private readonly PropertyInfo _property; private readonly PropertyInfo _property;
private bool _eventRaised; private bool _eventRaised;
public Accessor(WeakReference<object> reference, PropertyInfo property) public Accessor(WeakReference<object?> reference, PropertyInfo property)
{ {
Contract.Requires<ArgumentNullException>(reference != null); _ = reference ?? throw new ArgumentNullException(nameof(reference));
Contract.Requires<ArgumentNullException>(property != null); _ = property ?? throw new ArgumentNullException(nameof(property));
_reference = reference; _reference = reference;
_property = property; _property = property;
} }
public override Type PropertyType => _property.PropertyType; public override Type? PropertyType => _property.PropertyType;
public override object Value public override object? Value
{ {
get get
{ {
@ -110,7 +113,7 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
public override bool SetValue(object value, BindingPriority priority) public override bool SetValue(object? value, BindingPriority priority)
{ {
if (_property.CanWrite) if (_property.CanWrite)
{ {
@ -156,9 +159,9 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
private object GetReferenceTarget() private object? GetReferenceTarget()
{ {
_reference.TryGetTarget(out object target); _reference.TryGetTarget(out var target);
return target; return target;
} }

39
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -3,21 +3,24 @@ using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
public class MethodAccessorPlugin : IPropertyAccessorPlugin public class MethodAccessorPlugin : IPropertyAccessorPlugin
{ {
private readonly Dictionary<(Type, string), MethodInfo> _methodLookup = private readonly Dictionary<(Type, string), MethodInfo?> _methodLookup =
new Dictionary<(Type, string), MethodInfo>(); new Dictionary<(Type, string), MethodInfo?>();
public bool Match(object obj, string methodName) => GetFirstMethodWithName(obj.GetType(), methodName) != null; public bool Match(object obj, string methodName) => GetFirstMethodWithName(obj.GetType(), methodName) != null;
public IPropertyAccessor Start(WeakReference<object> reference, string methodName) public IPropertyAccessor? Start(WeakReference<object?> reference, string methodName)
{ {
Contract.Requires<ArgumentNullException>(reference != null); _ = reference ?? throw new ArgumentNullException(nameof(reference));
Contract.Requires<ArgumentNullException>(methodName != null); _ = methodName ?? throw new ArgumentNullException(nameof(methodName));
reference.TryGetTarget(out object instance); if (!reference.TryGetTarget(out var instance) || instance is null)
return null;
var method = GetFirstMethodWithName(instance.GetType(), methodName); var method = GetFirstMethodWithName(instance.GetType(), methodName);
@ -43,11 +46,11 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
private MethodInfo GetFirstMethodWithName(Type type, string methodName) private MethodInfo? GetFirstMethodWithName(Type type, string methodName)
{ {
var key = (type, methodName); var key = (type, methodName);
if (!_methodLookup.TryGetValue(key, out MethodInfo methodInfo)) if (!_methodLookup.TryGetValue(key, out var methodInfo))
{ {
methodInfo = TryFindAndCacheMethod(type, methodName); methodInfo = TryFindAndCacheMethod(type, methodName);
} }
@ -55,9 +58,9 @@ namespace Avalonia.Data.Core.Plugins
return methodInfo; return methodInfo;
} }
private MethodInfo TryFindAndCacheMethod(Type type, string methodName) private MethodInfo? TryFindAndCacheMethod(Type type, string methodName)
{ {
MethodInfo found = null; MethodInfo? found = null;
const BindingFlags bindingFlags = const BindingFlags bindingFlags =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
@ -81,10 +84,10 @@ namespace Avalonia.Data.Core.Plugins
private sealed class Accessor : PropertyAccessorBase private sealed class Accessor : PropertyAccessorBase
{ {
public Accessor(WeakReference<object> reference, MethodInfo method, ParameterInfo[] parameters) public Accessor(WeakReference<object?> reference, MethodInfo method, ParameterInfo[] parameters)
{ {
Contract.Requires<ArgumentNullException>(reference != null); _ = reference ?? throw new ArgumentNullException(nameof(reference));
Contract.Requires<ArgumentNullException>(method != null); _ = method ?? throw new ArgumentNullException(nameof(method));
var returnType = method.ReturnType; var returnType = method.ReturnType;
bool hasReturn = returnType != typeof(void); bool hasReturn = returnType != typeof(void);
@ -115,19 +118,17 @@ namespace Avalonia.Data.Core.Plugins
{ {
Value = method.CreateDelegate(PropertyType); Value = method.CreateDelegate(PropertyType);
} }
else else if (reference.TryGetTarget(out var target))
{ {
reference.TryGetTarget(out object target);
Value = method.CreateDelegate(PropertyType, target); Value = method.CreateDelegate(PropertyType, target);
} }
} }
public override Type PropertyType { get; } public override Type? PropertyType { get; }
public override object Value { get; } public override object? Value { get; }
public override bool SetValue(object value, BindingPriority priority) => false; public override bool SetValue(object? value, BindingPriority priority) => false;
protected override void SubscribeCore() protected override void SubscribeCore()
{ {

17
src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs

@ -3,6 +3,8 @@ using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reflection; using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -10,16 +12,16 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
public class ObservableStreamPlugin : IStreamPlugin public class ObservableStreamPlugin : IStreamPlugin
{ {
static MethodInfo observableSelect; static MethodInfo? observableSelect;
/// <summary> /// <summary>
/// Checks whether this plugin handles the specified value. /// Checks whether this plugin handles the specified value.
/// </summary> /// </summary>
/// <param name="reference">A weak reference to the value.</param> /// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns> /// <returns>True if the plugin can handle the value; otherwise false.</returns>
public virtual bool Match(WeakReference<object> reference) public virtual bool Match(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
return target != null && target.GetType().GetInterfaces().Any(x => return target != null && target.GetType().GetInterfaces().Any(x =>
x.IsGenericType && x.IsGenericType &&
@ -33,12 +35,13 @@ namespace Avalonia.Data.Core.Plugins
/// <returns> /// <returns>
/// An observable that produces the output for the value. /// An observable that produces the output for the value.
/// </returns> /// </returns>
public virtual IObservable<object> Start(WeakReference<object> reference) public virtual IObservable<object?> Start(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out object target); if (!reference.TryGetTarget(out var target) || target is null)
return Observable.Empty<object?>();
// If the observable returns a reference type then we can cast it. // If the observable returns a reference type then we can cast it.
if (target is IObservable<object> result) if (target is IObservable<object?> result)
{ {
return result; return result;
}; };
@ -98,6 +101,6 @@ namespace Avalonia.Data.Core.Plugins
return observableSelect; return observableSelect;
} }
private static object Box<T>(T value) => (object)value; private static object? Box<T>(T value) => (object?)value;
} }
} }

16
src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -7,13 +9,13 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
public abstract class PropertyAccessorBase : IPropertyAccessor public abstract class PropertyAccessorBase : IPropertyAccessor
{ {
private Action<object> _listener; private Action<object?>? _listener;
/// <inheritdoc/> /// <inheritdoc/>
public abstract Type PropertyType { get; } public abstract Type? PropertyType { get; }
/// <inheritdoc/> /// <inheritdoc/>
public abstract object Value { get; } public abstract object? Value { get; }
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose()
@ -25,12 +27,12 @@ namespace Avalonia.Data.Core.Plugins
} }
/// <inheritdoc/> /// <inheritdoc/>
public abstract bool SetValue(object value, BindingPriority priority); public abstract bool SetValue(object? value, BindingPriority priority);
/// <inheritdoc/> /// <inheritdoc/>
public void Subscribe(Action<object> listener) public void Subscribe(Action<object?> listener)
{ {
Contract.Requires<ArgumentNullException>(listener != null); _ = listener ?? throw new ArgumentNullException(nameof(listener));
if (_listener != null) if (_listener != null)
{ {
@ -58,7 +60,7 @@ namespace Avalonia.Data.Core.Plugins
/// Publishes a value to the listener. /// Publishes a value to the listener.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
protected void PublishValue(object value) => _listener?.Invoke(value); protected void PublishValue(object? value) => _listener?.Invoke(value);
/// <summary> /// <summary>
/// When overridden in a derived class, begins listening to the member. /// When overridden in a derived class, begins listening to the member.

8
src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -19,10 +21,10 @@ namespace Avalonia.Data.Core.Plugins
} }
/// <inheritdoc/> /// <inheritdoc/>
public Type PropertyType => null; public Type? PropertyType => null;
/// <inheritdoc/> /// <inheritdoc/>
public object Value => _error; public object? Value => _error;
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose()
@ -30,7 +32,7 @@ namespace Avalonia.Data.Core.Plugins
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool SetValue(object value, BindingPriority priority) public bool SetValue(object? value, BindingPriority priority)
{ {
return false; return false;
} }

16
src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs

@ -4,6 +4,8 @@ using System.Reactive.Subjects;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
#nullable enable
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
/// <summary> /// <summary>
@ -16,9 +18,9 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
/// <param name="reference">A weak reference to the value.</param> /// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns> /// <returns>True if the plugin can handle the value; otherwise false.</returns>
public virtual bool Match(WeakReference<object> reference) public virtual bool Match(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
return target is Task; return target is Task;
} }
@ -30,9 +32,9 @@ namespace Avalonia.Data.Core.Plugins
/// <returns> /// <returns>
/// An observable that produces the output for the value. /// An observable that produces the output for the value.
/// </returns> /// </returns>
public virtual IObservable<object> Start(WeakReference<object> reference) public virtual IObservable<object?> Start(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out var target);
if (target is Task task) if (target is Task task)
{ {
@ -46,7 +48,7 @@ namespace Avalonia.Data.Core.Plugins
case TaskStatus.Faulted: case TaskStatus.Faulted:
return HandleCompleted(task); return HandleCompleted(task);
default: default:
var subject = new Subject<object>(); var subject = new Subject<object?>();
task.ContinueWith( task.ContinueWith(
x => HandleCompleted(task).Subscribe(subject), x => HandleCompleted(task).Subscribe(subject),
TaskScheduler.FromCurrentSynchronizationContext()) TaskScheduler.FromCurrentSynchronizationContext())
@ -56,10 +58,10 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
return Observable.Empty<object>(); return Observable.Empty<object?>();
} }
private IObservable<object> HandleCompleted(Task task) private IObservable<object?> HandleCompleted(Task task)
{ {
var resultProperty = task.GetType().GetRuntimeProperty("Result"); var resultProperty = task.GetType().GetRuntimeProperty("Result");

21
src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -2,13 +2,15 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class PropertyAccessorNode : SettableNode public class PropertyAccessorNode : SettableNode
{ {
private readonly bool _enableValidation; private readonly bool _enableValidation;
private IPropertyAccessorPlugin _customPlugin; private IPropertyAccessorPlugin? _customPlugin;
private IPropertyAccessor _accessor; private IPropertyAccessor? _accessor;
public PropertyAccessorNode(string propertyName, bool enableValidation) public PropertyAccessorNode(string propertyName, bool enableValidation)
{ {
@ -25,9 +27,9 @@ namespace Avalonia.Data.Core
public override string Description => PropertyName; public override string Description => PropertyName;
public string PropertyName { get; } public string PropertyName { get; }
public override Type PropertyType => _accessor?.PropertyType; public override Type? PropertyType => _accessor?.PropertyType;
protected override bool SetTargetValueCore(object value, BindingPriority priority) protected override bool SetTargetValueCore(object? value, BindingPriority priority)
{ {
if (_accessor != null) if (_accessor != null)
{ {
@ -41,9 +43,10 @@ namespace Avalonia.Data.Core
return false; return false;
} }
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out object target); if (!reference.TryGetTarget(out var target) || target is null)
return;
var plugin = _customPlugin ?? GetPropertyAccessorPluginForObject(target); var plugin = _customPlugin ?? GetPropertyAccessorPluginForObject(target);
var accessor = plugin?.Start(reference, PropertyName); var accessor = plugin?.Start(reference, PropertyName);
@ -51,7 +54,7 @@ namespace Avalonia.Data.Core
// We need to handle accessor fallback before handling validation. Validators do not support null accessors. // We need to handle accessor fallback before handling validation. Validators do not support null accessors.
if (accessor == null) if (accessor == null)
{ {
reference.TryGetTarget(out object instance); reference.TryGetTarget(out var instance);
var message = $"Could not find a matching property accessor for '{PropertyName}' on '{instance}'"; var message = $"Could not find a matching property accessor for '{PropertyName}' on '{instance}'";
@ -80,7 +83,7 @@ namespace Avalonia.Data.Core
accessor.Subscribe(ValueChanged); accessor.Subscribe(ValueChanged);
} }
private IPropertyAccessorPlugin GetPropertyAccessorPluginForObject(object target) private IPropertyAccessorPlugin? GetPropertyAccessorPluginForObject(object target)
{ {
foreach (IPropertyAccessorPlugin x in ExpressionObserver.PropertyAccessors) foreach (IPropertyAccessorPlugin x in ExpressionObserver.PropertyAccessors)
{ {
@ -94,7 +97,7 @@ namespace Avalonia.Data.Core
protected override void StopListeningCore() protected override void StopListeningCore()
{ {
_accessor.Dispose(); _accessor?.Dispose();
_accessor = null; _accessor = null;
} }
} }

2
src/Avalonia.Base/Data/Core/PropertyPath.cs

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class PropertyPath public class PropertyPath

14
src/Avalonia.Base/Data/Core/SettableNode.cs

@ -1,10 +1,12 @@
using System; using System;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public abstract class SettableNode : ExpressionNode public abstract class SettableNode : ExpressionNode
{ {
public bool SetTargetValue(object value, BindingPriority priority) public bool SetTargetValue(object? value, BindingPriority priority)
{ {
if (ShouldNotSet(value)) if (ShouldNotSet(value))
{ {
@ -13,7 +15,7 @@ namespace Avalonia.Data.Core
return SetTargetValueCore(value, priority); return SetTargetValueCore(value, priority);
} }
private bool ShouldNotSet(object value) private bool ShouldNotSet(object? value)
{ {
var propertyType = PropertyType; var propertyType = PropertyType;
if (propertyType == null) if (propertyType == null)
@ -26,7 +28,7 @@ namespace Avalonia.Data.Core
return false; return false;
} }
bool isLastValueAlive = LastValue.TryGetTarget(out object lastValue); bool isLastValueAlive = LastValue.TryGetTarget(out var lastValue);
if (!isLastValueAlive) if (!isLastValueAlive)
{ {
@ -40,14 +42,14 @@ namespace Avalonia.Data.Core
if (propertyType.IsValueType) if (propertyType.IsValueType)
{ {
return lastValue.Equals(value); return Equals(lastValue, value);
} }
return ReferenceEquals(lastValue, value); return ReferenceEquals(lastValue, value);
} }
protected abstract bool SetTargetValueCore(object value, BindingPriority priority); protected abstract bool SetTargetValueCore(object? value, BindingPriority priority);
public abstract Type PropertyType { get; } public abstract Type? PropertyType { get; }
} }
} }

2
src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
#nullable enable
namespace Avalonia namespace Avalonia
{ {
public static class StreamBindingExtensions public static class StreamBindingExtensions

10
src/Avalonia.Base/Data/Core/StreamNode.cs

@ -2,12 +2,14 @@ using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class StreamNode : ExpressionNode public class StreamNode : ExpressionNode
{ {
private IStreamPlugin _customPlugin = null; private IStreamPlugin? _customPlugin = null;
private IDisposable _subscription; private IDisposable? _subscription;
public override string Description => "^"; public override string Description => "^";
@ -18,7 +20,7 @@ namespace Avalonia.Data.Core
_customPlugin = customPlugin; _customPlugin = customPlugin;
} }
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
_subscription = GetPlugin(reference)?.Start(reference).Subscribe(ValueChanged); _subscription = GetPlugin(reference)?.Start(reference).Subscribe(ValueChanged);
} }
@ -29,7 +31,7 @@ namespace Avalonia.Data.Core
_subscription = null; _subscription = null;
} }
private IStreamPlugin GetPlugin(WeakReference<object> reference) private IStreamPlugin? GetPlugin(WeakReference<object?> reference)
{ {
if (_customPlugin != null) if (_customPlugin != null)
{ {

10
src/Avalonia.Base/Data/Core/TypeCastNode.cs

@ -2,6 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
#nullable enable
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class TypeCastNode : ExpressionNode public class TypeCastNode : ExpressionNode
@ -15,17 +17,17 @@ namespace Avalonia.Data.Core
TargetType = type; TargetType = type;
} }
protected virtual object Cast(object value) protected virtual object? Cast(object? value)
{ {
return TargetType.IsInstanceOfType(value) ? value : null; return TargetType.IsInstanceOfType(value) ? value : null;
} }
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
if (reference.TryGetTarget(out object target)) if (reference.TryGetTarget(out var target))
{ {
target = Cast(target); target = Cast(target);
reference = target == null ? NullReference : new WeakReference<object>(target); reference = target == null ? NullReference : new WeakReference<object?>(target);
} }
base.StartListeningCore(reference); base.StartListeningCore(reference);

6
src/Avalonia.Base/Data/DataValidationException.cs

@ -1,5 +1,7 @@
using System; using System;
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
@ -11,11 +13,11 @@ namespace Avalonia.Data
/// Initializes a new instance of the <see cref="DataValidationException"/> class. /// Initializes a new instance of the <see cref="DataValidationException"/> class.
/// </summary> /// </summary>
/// <param name="errorData">Data of validation error.</param> /// <param name="errorData">Data of validation error.</param>
public DataValidationException(object errorData) : base(errorData?.ToString()) public DataValidationException(object? errorData) : base(errorData?.ToString())
{ {
ErrorData = errorData; ErrorData = errorData;
} }
public object ErrorData { get; } public object? ErrorData { get; }
} }
} }

8
src/Avalonia.Base/Data/IBinding.cs

@ -1,3 +1,5 @@
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
@ -20,10 +22,10 @@ namespace Avalonia.Data
/// <returns> /// <returns>
/// A <see cref="InstancedBinding"/> or null if the binding could not be resolved. /// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
/// </returns> /// </returns>
InstancedBinding Initiate( InstancedBinding? Initiate(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object anchor = null, object? anchor = null,
bool enableDataValidation = false); bool enableDataValidation = false);
} }
} }

10
src/Avalonia.Base/Data/IndexerBinding.cs

@ -1,4 +1,6 @@
namespace Avalonia.Data #nullable enable
namespace Avalonia.Data
{ {
public class IndexerBinding : IBinding public class IndexerBinding : IBinding
{ {
@ -16,10 +18,10 @@
public AvaloniaProperty Property { get; } public AvaloniaProperty Property { get; }
private BindingMode Mode { get; } private BindingMode Mode { get; }
public InstancedBinding Initiate( public InstancedBinding? Initiate(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object anchor = null, object? anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)
{ {
return new InstancedBinding(Source.GetSubject(Property), Mode, BindingPriority.LocalValue); return new InstancedBinding(Source.GetSubject(Property), Mode, BindingPriority.LocalValue);

21
src/Avalonia.Base/Data/IndexerDescriptor.cs

@ -1,12 +1,14 @@
using System; using System;
using System.Reactive; using System.Reactive;
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
/// Holds a description of a binding for <see cref="AvaloniaObject"/>'s [] operator. /// Holds a description of a binding for <see cref="AvaloniaObject"/>'s [] operator.
/// </summary> /// </summary>
public class IndexerDescriptor : ObservableBase<object>, IDescription public class IndexerDescriptor : ObservableBase<object?>, IDescription
{ {
/// <summary> /// <summary>
/// Gets or sets the binding mode. /// Gets or sets the binding mode.
@ -29,7 +31,7 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets or sets the source property. /// Gets or sets the source property.
/// </summary> /// </summary>
public AvaloniaProperty Property public AvaloniaProperty? Property
{ {
get; get;
set; set;
@ -38,7 +40,7 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets or sets the source object. /// Gets or sets the source object.
/// </summary> /// </summary>
public AvaloniaObject Source public AvaloniaObject? Source
{ {
get; get;
set; set;
@ -50,7 +52,7 @@ namespace Avalonia.Data
/// <remarks> /// <remarks>
/// If null, then <see cref="Source"/>.<see cref="Property"/> will be used. /// If null, then <see cref="Source"/>.<see cref="Property"/> will be used.
/// </remarks> /// </remarks>
public IObservable<object> SourceObservable public IObservable<object>? SourceObservable
{ {
get; get;
set; set;
@ -59,7 +61,7 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets a description of the binding. /// Gets a description of the binding.
/// </summary> /// </summary>
public string Description => $"{Source?.GetType().Name}.{Property.Name}"; public string Description => $"{Source?.GetType().Name}.{Property?.Name}";
/// <summary> /// <summary>
/// Makes a two-way binding. /// Makes a two-way binding.
@ -104,9 +106,14 @@ namespace Avalonia.Data
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override IDisposable SubscribeCore(IObserver<object> observer) protected override IDisposable SubscribeCore(IObserver<object?> observer)
{ {
return (SourceObservable ?? Source.GetObservable(Property)).Subscribe(observer); if (SourceObservable is null && Source is null)
throw new InvalidOperationException("Cannot subscribe to IndexerDescriptor.");
if (Property is null)
throw new InvalidOperationException("Cannot subscribe to IndexerDescriptor.");
return (SourceObservable ?? Source!.GetObservable(Property)).Subscribe(observer);
} }
} }
} }

28
src/Avalonia.Base/Data/InstancedBinding.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Reactive.Subjects; using System.Reactive.Subjects;
#nullable enable
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
@ -26,7 +28,7 @@ namespace Avalonia.Data
/// source which can be used for all binding modes. If you wish to create an instance with /// source which can be used for all binding modes. If you wish to create an instance with
/// something other than a subject, use one of the static creation methods on this class. /// something other than a subject, use one of the static creation methods on this class.
/// </remarks> /// </remarks>
public InstancedBinding(ISubject<object> subject, BindingMode mode, BindingPriority priority) public InstancedBinding(ISubject<object?> subject, BindingMode mode, BindingPriority priority)
{ {
Contract.Requires<ArgumentNullException>(subject != null); Contract.Requires<ArgumentNullException>(subject != null);
@ -35,7 +37,7 @@ namespace Avalonia.Data
Value = subject; Value = subject;
} }
private InstancedBinding(object value, BindingMode mode, BindingPriority priority) private InstancedBinding(object? value, BindingMode mode, BindingPriority priority)
{ {
Mode = mode; Mode = mode;
Priority = priority; Priority = priority;
@ -55,17 +57,17 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets the value or source of the binding. /// Gets the value or source of the binding.
/// </summary> /// </summary>
public object Value { get; } public object? Value { get; }
/// <summary> /// <summary>
/// Gets the <see cref="Value"/> as an observable. /// Gets the <see cref="Value"/> as an observable.
/// </summary> /// </summary>
public IObservable<object> Observable => Value as IObservable<object>; public IObservable<object?>? Observable => Value as IObservable<object?>;
/// <summary> /// <summary>
/// Gets the <see cref="Value"/> as a subject. /// Gets the <see cref="Value"/> as a subject.
/// </summary> /// </summary>
public ISubject<object> Subject => Value as ISubject<object>; public ISubject<object?>? Subject => Value as ISubject<object?>;
/// <summary> /// <summary>
/// Creates a new one-time binding with a fixed value. /// Creates a new one-time binding with a fixed value.
@ -87,10 +89,10 @@ namespace Avalonia.Data
/// <param name="priority">The priority of the binding.</param> /// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns> /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneTime( public static InstancedBinding OneTime(
IObservable<object> observable, IObservable<object?> observable,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
Contract.Requires<ArgumentNullException>(observable != null); _ = observable ?? throw new ArgumentNullException(nameof(observable));
return new InstancedBinding(observable, BindingMode.OneTime, priority); return new InstancedBinding(observable, BindingMode.OneTime, priority);
} }
@ -102,10 +104,10 @@ namespace Avalonia.Data
/// <param name="priority">The priority of the binding.</param> /// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns> /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneWay( public static InstancedBinding OneWay(
IObservable<object> observable, IObservable<object?> observable,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
Contract.Requires<ArgumentNullException>(observable != null); _ = observable ?? throw new ArgumentNullException(nameof(observable));
return new InstancedBinding(observable, BindingMode.OneWay, priority); return new InstancedBinding(observable, BindingMode.OneWay, priority);
} }
@ -117,10 +119,10 @@ namespace Avalonia.Data
/// <param name="priority">The priority of the binding.</param> /// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns> /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneWayToSource( public static InstancedBinding OneWayToSource(
ISubject<object> subject, ISubject<object?> subject,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
Contract.Requires<ArgumentNullException>(subject != null); _ = subject ?? throw new ArgumentNullException(nameof(subject));
return new InstancedBinding(subject, BindingMode.OneWayToSource, priority); return new InstancedBinding(subject, BindingMode.OneWayToSource, priority);
} }
@ -132,10 +134,10 @@ namespace Avalonia.Data
/// <param name="priority">The priority of the binding.</param> /// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns> /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding TwoWay( public static InstancedBinding TwoWay(
ISubject<object> subject, ISubject<object?> subject,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
Contract.Requires<ArgumentNullException>(subject != null); _ = subject ?? throw new ArgumentNullException(nameof(subject));
return new InstancedBinding(subject, BindingMode.TwoWay, priority); return new InstancedBinding(subject, BindingMode.TwoWay, priority);
} }

24
src/Avalonia.Base/Data/Optional.cs

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#nullable enable #nullable enable
@ -23,13 +22,13 @@ namespace Avalonia.Data
/// </remarks> /// </remarks>
public readonly struct Optional<T> : IEquatable<Optional<T>> public readonly struct Optional<T> : IEquatable<Optional<T>>
{ {
[AllowNull] private readonly T _value; private readonly T _value;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Optional{T}"/> struct with value. /// Initializes a new instance of the <see cref="Optional{T}"/> struct with value.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public Optional([AllowNull] T value) public Optional(T value)
{ {
_value = value; _value = value;
HasValue = true; HasValue = true;
@ -61,7 +60,7 @@ namespace Avalonia.Data
/// Casts the value (if any) to an <see cref="object"/>. /// Casts the value (if any) to an <see cref="object"/>.
/// </summary> /// </summary>
/// <returns>The cast optional value.</returns> /// <returns>The cast optional value.</returns>
public Optional<object> ToObject() => HasValue ? new Optional<object>(_value) : default; public Optional<object?> ToObject() => HasValue ? new Optional<object?>(_value) : default;
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() => HasValue ? _value?.ToString() ?? "(null)" : "(empty)"; public override string ToString() => HasValue ? _value?.ToString() ?? "(null)" : "(empty)";
@ -70,15 +69,14 @@ namespace Avalonia.Data
/// Gets the value if present, otherwise the default value. /// Gets the value if present, otherwise the default value.
/// </summary> /// </summary>
/// <returns>The value.</returns> /// <returns>The value.</returns>
[return: MaybeNull] public T? GetValueOrDefault() => HasValue ? _value : default;
public T GetValueOrDefault() => HasValue ? _value : default;
/// <summary> /// <summary>
/// Gets the value if present, otherwise a default value. /// Gets the value if present, otherwise a default value.
/// </summary> /// </summary>
/// <param name="defaultValue">The default value.</param> /// <param name="defaultValue">The default value.</param>
/// <returns>The value.</returns> /// <returns>The value.</returns>
public T GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue; public T? GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue;
/// <summary> /// <summary>
/// Gets the value if present, otherwise the default value. /// Gets the value if present, otherwise the default value.
@ -87,8 +85,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is /// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type. /// not present or of an incorrect type.
/// </returns> /// </returns>
[return: MaybeNull] public TResult? GetValueOrDefault<TResult>()
public TResult GetValueOrDefault<TResult>()
{ {
return HasValue ? return HasValue ?
_value is TResult result ? result : default _value is TResult result ? result : default
@ -104,8 +101,7 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or <paramref name="defaultValue"/> if the /// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
/// value is not present. /// value is not present.
/// </returns> /// </returns>
[return: MaybeNull] public TResult? GetValueOrDefault<TResult>(TResult defaultValue)
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
{ {
return HasValue ? return HasValue ?
_value is TResult result ? result : default _value is TResult result ? result : default
@ -116,7 +112,7 @@ namespace Avalonia.Data
/// Creates an <see cref="Optional{T}"/> from an instance of the underlying value type. /// Creates an <see cref="Optional{T}"/> from an instance of the underlying value type.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public static implicit operator Optional<T>([AllowNull] T value) => new Optional<T>(value); public static implicit operator Optional<T>(T value) => new Optional<T>(value);
/// <summary> /// <summary>
/// Compares two <see cref="Optional{T}"/>s for inequality. /// Compares two <see cref="Optional{T}"/>s for inequality.
@ -162,9 +158,9 @@ namespace Avalonia.Data
/// <typeparam name="T">The target type.</typeparam> /// <typeparam name="T">The target type.</typeparam>
/// <param name="value">The binding value.</param> /// <param name="value">The binding value.</param>
/// <returns>The cast value.</returns> /// <returns>The cast value.</returns>
public static Optional<T> Cast<T>(this Optional<object> value) public static Optional<T> Cast<T>(this Optional<object?> value)
{ {
return value.HasValue ? new Optional<T>((T)value.Value) : Optional<T>.Empty; return value.HasValue ? new Optional<T>((T)value.Value!) : Optional<T>.Empty;
} }
} }
} }

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

@ -44,7 +44,7 @@ namespace Avalonia.PropertyStore
public StyledPropertyBase<T> Property { get; } public StyledPropertyBase<T> Property { get; }
public BindingPriority Priority { get; private set; } public BindingPriority Priority { get; private set; }
public IObservable<BindingValue<T>> Source { get; } public IObservable<BindingValue<T>> Source { get; }
Optional<object> IValue.GetValue() => _value.ToObject(); Optional<object?> IValue.GetValue() => _value.ToObject();
public void BeginBatchUpdate() => _batchUpdate = true; public void BeginBatchUpdate() => _batchUpdate = true;
@ -121,8 +121,8 @@ namespace Avalonia.PropertyStore
IValueSink sink, IValueSink sink,
IAvaloniaObject owner, IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object> oldValue, Optional<object?> oldValue,
Optional<object> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,

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

@ -25,7 +25,19 @@ namespace Avalonia.PropertyStore
public ConstantValueEntry( public ConstantValueEntry(
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
[AllowNull] T value, T value,
BindingPriority priority,
IValueSink sink)
{
Property = property;
_value = value;
Priority = priority;
_sink = sink;
}
public ConstantValueEntry(
StyledPropertyBase<T> property,
Optional<T> value,
BindingPriority priority, BindingPriority priority,
IValueSink sink) IValueSink sink)
{ {
@ -37,7 +49,7 @@ namespace Avalonia.PropertyStore
public StyledPropertyBase<T> Property { get; } public StyledPropertyBase<T> Property { get; }
public BindingPriority Priority { get; private set; } public BindingPriority Priority { get; private set; }
Optional<object> IValue.GetValue() => _value.ToObject(); Optional<object?> IValue.GetValue() => _value.ToObject();
public Optional<T> GetValue(BindingPriority maxPriority = BindingPriority.Animation) public Optional<T> GetValue(BindingPriority maxPriority = BindingPriority.Animation)
{ {
@ -59,8 +71,8 @@ namespace Avalonia.PropertyStore
IValueSink sink, IValueSink sink,
IAvaloniaObject owner, IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object> oldValue, Optional<object?> oldValue,
Optional<object> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,

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

@ -10,14 +10,14 @@ namespace Avalonia.PropertyStore
internal interface IValue internal interface IValue
{ {
BindingPriority Priority { get; } BindingPriority Priority { get; }
Optional<object> GetValue(); Optional<object?> GetValue();
void Start(); void Start();
void RaiseValueChanged( void RaiseValueChanged(
IValueSink sink, IValueSink sink,
IAvaloniaObject owner, IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object> oldValue, Optional<object?> oldValue,
Optional<object> newValue); Optional<object?> newValue);
} }
/// <summary> /// <summary>

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

@ -12,11 +12,11 @@ namespace Avalonia.PropertyStore
/// <typeparam name="T">The property type.</typeparam> /// <typeparam name="T">The property type.</typeparam>
internal class LocalValueEntry<T> : IValue<T> internal class LocalValueEntry<T> : IValue<T>
{ {
[AllowNull] private T _value; private T _value;
public LocalValueEntry([AllowNull] T value) => _value = value; public LocalValueEntry(T value) => _value = value;
public BindingPriority Priority => BindingPriority.LocalValue; public BindingPriority Priority => BindingPriority.LocalValue;
Optional<object> IValue.GetValue() => new Optional<object>(_value); Optional<object?> IValue.GetValue() => new Optional<object?>(_value);
public Optional<T> GetValue(BindingPriority maxPriority) public Optional<T> GetValue(BindingPriority maxPriority)
{ {
@ -30,8 +30,8 @@ namespace Avalonia.PropertyStore
IValueSink sink, IValueSink sink,
IAvaloniaObject owner, IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object> oldValue, Optional<object?> oldValue,
Optional<object> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,

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

@ -90,7 +90,7 @@ namespace Avalonia.PropertyStore
public StyledPropertyBase<T> Property { get; } public StyledPropertyBase<T> Property { get; }
public BindingPriority Priority { get; private set; } = BindingPriority.Unset; public BindingPriority Priority { get; private set; } = BindingPriority.Unset;
public IReadOnlyList<IPriorityValueEntry<T>> Entries => _entries; public IReadOnlyList<IPriorityValueEntry<T>> Entries => _entries;
Optional<object> IValue.GetValue() => _value.ToObject(); Optional<object?> IValue.GetValue() => _value.ToObject();
public void BeginBatchUpdate() public void BeginBatchUpdate()
{ {
@ -191,8 +191,8 @@ namespace Avalonia.PropertyStore
IValueSink sink, IValueSink sink,
IAvaloniaObject owner, IAvaloniaObject owner,
AvaloniaProperty property, AvaloniaProperty property,
Optional<object> oldValue, Optional<object?> oldValue,
Optional<object> newValue) Optional<object?> newValue)
{ {
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>( sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner, owner,

9
src/Avalonia.Base/ValueStore.cs

@ -169,7 +169,8 @@ namespace Avalonia
} }
else if (slot.Priority == BindingPriority.LocalValue) else if (slot.Priority == BindingPriority.LocalValue)
{ {
var old = TryGetValue(property, BindingPriority.LocalValue, out var value) ? value : default; var old = TryGetValue(property, BindingPriority.LocalValue, out var value) ?
new Optional<T>(value) : default;
// During batch update values can't be removed immediately because they're needed to raise // During batch update values can't be removed immediately because they're needed to raise
// a correctly-typed _sink.ValueChanged notification. They instead mark themselves for removal // a correctly-typed _sink.ValueChanged notification. They instead mark themselves for removal
@ -188,7 +189,7 @@ namespace Avalonia
// so there's no way to mark them for removal at the end of a batch update. Instead convert // so there's no way to mark them for removal at the end of a batch update. Instead convert
// them to a constant value entry with Unset priority in the event of a local value being // them to a constant value entry with Unset priority in the event of a local value being
// cleared during a batch update. // cleared during a batch update.
var sentinel = new ConstantValueEntry<T>(property, default, BindingPriority.Unset, _sink); var sentinel = new ConstantValueEntry<T>(property, Optional<T>.Empty, BindingPriority.Unset, _sink);
_values.SetValue(property, sentinel); _values.SetValue(property, sentinel);
} }
@ -479,7 +480,7 @@ namespace Avalonia
return true; return true;
} }
public void ValueChanged(AvaloniaProperty property, Optional<object> oldValue) public void ValueChanged(AvaloniaProperty property, Optional<object?> oldValue)
{ {
_notifications ??= new List<Notification>(); _notifications ??= new List<Notification>();
@ -506,7 +507,7 @@ namespace Avalonia
private struct Notification private struct Notification
{ {
public AvaloniaProperty property; public AvaloniaProperty property;
public Optional<object> oldValue; public Optional<object?> oldValue;
} }
} }
} }

18
src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs

@ -19,7 +19,7 @@ namespace Avalonia.Styling
private readonly IStyleable _target; private readonly IStyleable _target;
private readonly StyledPropertyBase<T>? _styledProperty; private readonly StyledPropertyBase<T>? _styledProperty;
private readonly DirectPropertyBase<T>? _directProperty; private readonly DirectPropertyBase<T>? _directProperty;
private readonly InstancedBinding _binding; private readonly InstancedBinding? _binding;
private readonly Inner _inner; private readonly Inner _inner;
private BindingValue<T> _value; private BindingValue<T> _value;
private IDisposable? _subscription; private IDisposable? _subscription;
@ -36,7 +36,7 @@ namespace Avalonia.Styling
_styledProperty = property; _styledProperty = property;
_binding = binding.Initiate(_target, property); _binding = binding.Initiate(_target, property);
if (_binding.Mode == BindingMode.OneTime) if (_binding?.Mode == BindingMode.OneTime)
{ {
// For the moment, we don't support OneTime bindings in setters, because I'm not // For the moment, we don't support OneTime bindings in setters, because I'm not
// sure what the semantics should be in the case of activation/deactivation. // sure what the semantics should be in the case of activation/deactivation.
@ -59,6 +59,9 @@ namespace Avalonia.Styling
public void Start(bool hasActivator) public void Start(bool hasActivator)
{ {
if (_binding is null)
return;
_isActive = !hasActivator; _isActive = !hasActivator;
if (_styledProperty is object) if (_styledProperty is object)
@ -90,9 +93,12 @@ namespace Avalonia.Styling
public void Activate() public void Activate()
{ {
if (_binding is null)
return;
if (!_isActive) if (!_isActive)
{ {
_innerSubscription ??= _binding.Observable.Subscribe(_inner); _innerSubscription ??= _binding.Observable!.Subscribe(_inner);
_isActive = true; _isActive = true;
PublishNext(); PublishNext();
} }
@ -140,7 +146,7 @@ namespace Avalonia.Styling
void IObserver<BindingValue<T>>.OnNext(BindingValue<T> value) void IObserver<BindingValue<T>>.OnNext(BindingValue<T> value)
{ {
if (value.HasValue && _isActive) if (value.HasValue && _isActive && _binding?.Subject is not null)
{ {
_binding.Subject.OnNext(value.Value); _binding.Subject.OnNext(value.Value);
} }
@ -148,11 +154,11 @@ namespace Avalonia.Styling
protected override void Subscribed() protected override void Subscribed()
{ {
if (_isActive) if (_isActive && _binding?.Observable is not null)
{ {
if (_innerSubscription is null) if (_innerSubscription is null)
{ {
_innerSubscription ??= _binding.Observable.Subscribe(_inner); _innerSubscription ??= _binding.Observable!.Subscribe(_inner);
} }
else else
{ {

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

@ -16,14 +16,14 @@ namespace Avalonia.Styling
private readonly IStyleable _target; private readonly IStyleable _target;
private readonly StyledPropertyBase<T>? _styledProperty; private readonly StyledPropertyBase<T>? _styledProperty;
private readonly DirectPropertyBase<T>? _directProperty; private readonly DirectPropertyBase<T>? _directProperty;
private readonly T? _value; private readonly T _value;
private IDisposable? _subscription; private IDisposable? _subscription;
private bool _isActive; private bool _isActive;
public PropertySetterInstance( public PropertySetterInstance(
IStyleable target, IStyleable target,
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
T? value) T value)
{ {
_target = target; _target = target;
_styledProperty = property; _styledProperty = property;

2
src/Avalonia.Styling/Styling/Setter.cs

@ -101,7 +101,7 @@ namespace Avalonia.Styling
data.result = new PropertySetterInstance<T>( data.result = new PropertySetterInstance<T>(
data.target, data.target,
property, property,
(T?)data.value); (T)data.value!);
} }
} }

6
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@ -39,8 +39,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
InstancedBinding? IBinding.Initiate( InstancedBinding? IBinding.Initiate(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object anchor, object? anchor,
bool enableDataValidation) bool enableDataValidation)
{ {
if (ResourceKey is null) if (ResourceKey is null)
@ -64,7 +64,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
return null; return null;
} }
private Func<object?, object?>? GetConverter(AvaloniaProperty targetProperty) private Func<object?, object?>? GetConverter(AvaloniaProperty? targetProperty)
{ {
if (targetProperty?.PropertyType == typeof(IBrush)) if (targetProperty?.PropertyType == typeof(IBrush))
{ {

2
src/Markup/Avalonia.Markup/Data/Binding.cs

@ -61,7 +61,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
public Func<string, string, Type>? TypeResolver { get; set; } public Func<string, string, Type>? TypeResolver { get; set; }
protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object? anchor, bool enableDataValidation) protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor, bool enableDataValidation)
{ {
_ = target ?? throw new ArgumentNullException(nameof(target)); _ = target ?? throw new ArgumentNullException(nameof(target));

8
src/Markup/Avalonia.Markup/Data/BindingBase.cs

@ -76,14 +76,14 @@ namespace Avalonia.Data
protected abstract ExpressionObserver CreateExpressionObserver( protected abstract ExpressionObserver CreateExpressionObserver(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object? anchor, object? anchor,
bool enableDataValidation); bool enableDataValidation);
/// <inheritdoc/> /// <inheritdoc/>
public InstancedBinding Initiate( public InstancedBinding? Initiate(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object? anchor = null, object? anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)
{ {
@ -115,7 +115,7 @@ namespace Avalonia.Data
if (!string.IsNullOrWhiteSpace(StringFormat) && if (!string.IsNullOrWhiteSpace(StringFormat) &&
(targetType == typeof(string) || targetType == typeof(object))) (targetType == typeof(string) || targetType == typeof(object)))
{ {
converter = new StringFormatValueConverter(StringFormat, converter); converter = new StringFormatValueConverter(StringFormat!, converter);
} }
var subject = new BindingExpression( var subject = new BindingExpression(

11
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -66,9 +66,9 @@ namespace Avalonia.Data
} }
/// <inheritdoc/> /// <inheritdoc/>
public InstancedBinding Initiate( public InstancedBinding? Initiate(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object? anchor = null, object? anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)
{ {
@ -80,12 +80,13 @@ namespace Avalonia.Data
if (!string.IsNullOrWhiteSpace(StringFormat) && if (!string.IsNullOrWhiteSpace(StringFormat) &&
(targetType == typeof(string) || targetType == typeof(object))) (targetType == typeof(string) || targetType == typeof(object)))
{ {
converter = new StringFormatMultiValueConverter(StringFormat, converter); converter = new StringFormatMultiValueConverter(StringFormat!, converter);
} }
var children = Bindings.Select(x => x.Initiate(target, null)); var children = Bindings.Select(x => x.Initiate(target, null));
var input = children.Select(x => x.Observable) var input = children.Select(x => x?.Observable!)
.Where(x => x is not null)
.CombineLatest() .CombineLatest()
.Select(x => ConvertValue(x, targetType, converter)) .Select(x => ConvertValue(x, targetType, converter))
.Where(x => x != BindingOperations.DoNothing); .Where(x => x != BindingOperations.DoNothing);
@ -117,7 +118,7 @@ namespace Avalonia.Data
var culture = CultureInfo.CurrentCulture; var culture = CultureInfo.CurrentCulture;
values = new System.Collections.ObjectModel.ReadOnlyCollection<object?>(values); values = new System.Collections.ObjectModel.ReadOnlyCollection<object?>(values);
object converted; object? converted;
if (converter != null) if (converter != null)
{ {
converted = converter.Convert(values, targetType, ConverterParameter, culture); converted = converter.Convert(values, targetType, ConverterParameter, culture);

6
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@ -30,9 +30,9 @@ namespace Avalonia.Data
} }
/// <inheritdoc/> /// <inheritdoc/>
public InstancedBinding Initiate( public InstancedBinding? Initiate(
IAvaloniaObject target, IAvaloniaObject target,
AvaloniaProperty targetProperty, AvaloniaProperty? targetProperty,
object? anchor = null, object? anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)
{ {
@ -137,7 +137,7 @@ namespace Avalonia.Data
_target.TemplatedParent.GetValue(Property) : _target.TemplatedParent.GetValue(Property) :
_target.TemplatedParent; _target.TemplatedParent;
if (Converter != null) if (Converter is not null && _targetType is not null)
{ {
value = Converter.Convert(value, _targetType, ConverterParameter, CultureInfo.CurrentCulture); value = Converter.Convert(value, _targetType, ConverterParameter, CultureInfo.CurrentCulture);
} }

2
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

@ -8,7 +8,7 @@ namespace Avalonia.Markup.Parsers
{ {
public static class ExpressionObserverBuilder public static class ExpressionObserverBuilder
{ {
internal static (ExpressionNode? Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null, internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null,
INameScope? nameScope = null) INameScope? nameScope = null)
{ {
if (string.IsNullOrWhiteSpace(expression)) if (string.IsNullOrWhiteSpace(expression))

14
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

@ -21,7 +21,7 @@ namespace Avalonia.Markup.Parsers
_enableValidation = enableValidation; _enableValidation = enableValidation;
} }
public (ExpressionNode? Node, SourceMode Mode) Parse(ref CharacterReader r) public (ExpressionNode Node, SourceMode Mode) Parse(ref CharacterReader r)
{ {
ExpressionNode? rootNode = null; ExpressionNode? rootNode = null;
ExpressionNode? node = null; ExpressionNode? node = null;
@ -74,6 +74,9 @@ namespace Avalonia.Markup.Parsers
} }
} }
if (rootNode is null)
throw new ExpressionParseException(r.Position, "Unexpected end of expression.");
return (rootNode, mode); return (rootNode, mode);
} }
@ -108,6 +111,9 @@ namespace Avalonia.Markup.Parsers
castType = _typeResolver(node.Namespace, node.TypeName); castType = _typeResolver(node.Namespace, node.TypeName);
} }
if (castType is null)
throw new InvalidOperationException("Unable to determine type for cast.");
return new TypeCastNode(castType); return new TypeCastNode(castType);
} }
@ -118,7 +124,11 @@ namespace Avalonia.Markup.Parsers
throw new InvalidOperationException("Cannot parse a binding path with an attached property without a type resolver. Maybe you can use a LINQ Expression binding path instead?"); throw new InvalidOperationException("Cannot parse a binding path with an attached property without a type resolver. Maybe you can use a LINQ Expression binding path instead?");
} }
var property = AvaloniaPropertyRegistry.Instance.FindRegistered(_typeResolver(node.Namespace, node.TypeName), node.PropertyName); var type = _typeResolver(node.Namespace, node.TypeName);
var property = AvaloniaPropertyRegistry.Instance.FindRegistered(type, node.PropertyName);
if (property is null)
throw new InvalidOperationException($"Cannot find property {type}.{node.PropertyName}.");
return new AvaloniaPropertyAccessorNode(property, _enableValidation); return new AvaloniaPropertyAccessorNode(property, _enableValidation);
} }

2
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs

@ -19,7 +19,7 @@ namespace Avalonia.Markup.Parsers.Nodes
public override string Description => $"#{_name}"; public override string Description => $"#{_name}";
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
if (_nameScope.TryGetTarget(out var scope)) if (_nameScope.TryGetTarget(out var scope))
_subscription = NameScopeLocator.Track(scope, _name).Subscribe(ValueChanged); _subscription = NameScopeLocator.Track(scope, _name).Subscribe(ValueChanged);

4
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs

@ -31,9 +31,9 @@ namespace Avalonia.Markup.Parsers.Nodes
} }
} }
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
if (reference.TryGetTarget(out object target) && target is ILogical logical) if (reference.TryGetTarget(out var target) && target is ILogical logical)
{ {
_subscription = ControlLocator.Track(logical, _level, _ancestorType).Subscribe(ValueChanged); _subscription = ControlLocator.Track(logical, _level, _ancestorType).Subscribe(ValueChanged);
} }

20
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs

@ -21,9 +21,10 @@ namespace Avalonia.Markup.Parsers.Nodes
public override string Description => "[" + string.Join(",", Arguments) + "]"; public override string Description => "[" + string.Join(",", Arguments) + "]";
protected override bool SetTargetValueCore(object value, BindingPriority priority) protected override bool SetTargetValueCore(object? value, BindingPriority priority)
{ {
Target.TryGetTarget(out object target); if (!Target.TryGetTarget(out var target) || target is null)
return false;
var typeInfo = target.GetType().GetTypeInfo(); var typeInfo = target.GetType().GetTypeInfo();
var list = target as IList; var list = target as IList;
@ -103,7 +104,7 @@ namespace Avalonia.Markup.Parsers.Nodes
return false; return false;
} }
private bool SetValueInArray(Array array, object value) private bool SetValueInArray(Array array, object? value)
{ {
int[] intArgs; int[] intArgs;
if (!ConvertArgumentsToInts(out intArgs)) if (!ConvertArgumentsToInts(out intArgs))
@ -112,7 +113,7 @@ namespace Avalonia.Markup.Parsers.Nodes
} }
private bool SetValueInArray(Array array, int[] indices, object value) private bool SetValueInArray(Array array, int[] indices, object? value)
{ {
if (ValidBounds(indices, array)) if (ValidBounds(indices, array))
{ {
@ -129,7 +130,7 @@ namespace Avalonia.Markup.Parsers.Nodes
{ {
get get
{ {
if (!Target.TryGetTarget(out object target)) if (!Target.TryGetTarget(out var target) || target is null)
{ {
return null; return null;
} }
@ -138,8 +139,11 @@ namespace Avalonia.Markup.Parsers.Nodes
} }
} }
protected override object GetValue(object target) protected override object? GetValue(object? target)
{ {
if (target is null)
return null;
var typeInfo = target.GetType().GetTypeInfo(); var typeInfo = target.GetType().GetTypeInfo();
var list = target as IList; var list = target as IList;
var dictionary = target as IDictionary; var dictionary = target as IDictionary;
@ -291,8 +295,10 @@ namespace Avalonia.Markup.Parsers.Nodes
} }
} }
protected override bool ShouldUpdate(object sender, PropertyChangedEventArgs e) protected override bool ShouldUpdate(object? sender, PropertyChangedEventArgs e)
{ {
if (sender is null)
return false;
var typeInfo = sender.GetType().GetTypeInfo(); var typeInfo = sender.GetType().GetTypeInfo();
return typeInfo.GetDeclaredProperty(e.PropertyName)?.GetIndexParameters().Any() ?? false; return typeInfo.GetDeclaredProperty(e.PropertyName)?.GetIndexParameters().Any() ?? false;
} }

10
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1467,6 +1467,16 @@ namespace Avalonia.Win32.Interop
[DllImport("dwmapi.dll")] [DllImport("dwmapi.dll")]
public static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind); public static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);
[Flags]
public enum LayeredWindowFlags
{
LWA_ALPHA = 0x00000002,
LWA_COLORKEY = 0x00000001,
}
[DllImport("user32.dll")]
public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, LayeredWindowFlags dwFlags);
[Flags] [Flags]
public enum DWM_BB public enum DWM_BB

17
src/Windows/Avalonia.Win32/Win32NativeControlHost.cs

@ -9,10 +9,12 @@ namespace Avalonia.Win32
{ {
class Win32NativeControlHost : INativeControlHostImpl class Win32NativeControlHost : INativeControlHostImpl
{ {
private readonly bool _useLayeredWindow;
public WindowImpl Window { get; } public WindowImpl Window { get; }
public Win32NativeControlHost(WindowImpl window) public Win32NativeControlHost(WindowImpl window, bool useLayeredWindow)
{ {
_useLayeredWindow = useLayeredWindow;
Window = window; Window = window;
} }
@ -25,12 +27,12 @@ namespace Avalonia.Win32
public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent)
{ {
AssertCompatible(parent); AssertCompatible(parent);
return new DumbWindow(parent.Handle); return new DumbWindow(false, parent.Handle);
} }
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create) public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create)
{ {
var holder = new DumbWindow(Window.Handle.Handle); var holder = new DumbWindow(_useLayeredWindow, Window.Handle.Handle);
Win32NativeControlAttachment attachment = null; Win32NativeControlAttachment attachment = null;
try try
{ {
@ -52,7 +54,7 @@ namespace Avalonia.Win32
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle)
{ {
AssertCompatible(handle); AssertCompatible(handle);
return new Win32NativeControlAttachment(new DumbWindow(Window.Handle.Handle), return new Win32NativeControlAttachment(new DumbWindow(_useLayeredWindow, Window.Handle.Handle),
handle) { AttachedTo = this }; handle) { AttachedTo = this };
} }
@ -67,7 +69,7 @@ namespace Avalonia.Win32
UnmanagedMethods.WndProc _wndProcDelegate; UnmanagedMethods.WndProc _wndProcDelegate;
private readonly string _className; private readonly string _className;
public DumbWindow(IntPtr? parent = null) public DumbWindow(bool layered = false, IntPtr? parent = null)
{ {
_wndProcDelegate = WndProc; _wndProcDelegate = WndProc;
var wndClassEx = new UnmanagedMethods.WNDCLASSEX var wndClassEx = new UnmanagedMethods.WNDCLASSEX
@ -80,7 +82,7 @@ namespace Avalonia.Win32
var atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx); var atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx);
Handle = UnmanagedMethods.CreateWindowEx( Handle = UnmanagedMethods.CreateWindowEx(
0, layered ? (int)UnmanagedMethods.WindowStyles.WS_EX_LAYERED : 0,
atom, atom,
null, null,
(int)UnmanagedMethods.WindowStyles.WS_CHILD, (int)UnmanagedMethods.WindowStyles.WS_CHILD,
@ -92,6 +94,9 @@ namespace Avalonia.Win32
IntPtr.Zero, IntPtr.Zero,
IntPtr.Zero, IntPtr.Zero,
IntPtr.Zero); IntPtr.Zero);
if (layered)
UnmanagedMethods.SetLayeredWindowAttributes(Handle, 0, 255,
UnmanagedMethods.LayeredWindowFlags.LWA_ALPHA);
} }

2
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -145,7 +145,7 @@ namespace Avalonia.Win32
Screen = new ScreenImpl(); Screen = new ScreenImpl();
_nativeControlHost = new Win32NativeControlHost(this); _nativeControlHost = new Win32NativeControlHost(this, _isUsingComposition);
s_instances.Add(this); s_instances.Add(this);
} }

Loading…
Cancel
Save