Browse Source

Added nullable annotations to Avalonia.Data. (#7105)

* Added nullable annotations to Avalonia.Data.

* ExpressionObserver root can be null.
pull/7132/head
Steven Kirk 4 years ago
committed by GitHub
parent
commit
0bda763518
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

4
src/Avalonia.Base/AvaloniaObject.cs

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

4
src/Avalonia.Base/AvaloniaObjectExtensions.cs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -3,6 +3,8 @@ using System.Globalization;
using System.Windows.Input;
using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Converters
{
/// <summary>
@ -24,7 +26,7 @@ namespace Avalonia.Data.Converters
/// <param name="parameter">A user-defined parameter.</param>
/// <param name="culture">The culture to use.</param>
/// <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)
{
@ -63,7 +65,7 @@ namespace Avalonia.Data.Converters
/// <param name="parameter">A user-defined parameter.</param>
/// <param name="culture">The culture to use.</param>
/// <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);
}

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

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
#nullable enable
namespace Avalonia.Data.Converters
{
/// <summary>
@ -13,24 +15,24 @@ namespace Avalonia.Data.Converters
/// <typeparam name="TOut">The output type.</typeparam>
public class FuncMultiValueConverter<TIn, TOut> : IMultiValueConverter
{
private readonly Func<IEnumerable<TIn>, TOut> _convert;
private readonly Func<IEnumerable<TIn?>, TOut> _convert;
/// <summary>
/// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TOut}"/> class.
/// </summary>
/// <param name="convert">The convert function.</param>
public FuncMultiValueConverter(Func<IEnumerable<TIn>, TOut> convert)
public FuncMultiValueConverter(Func<IEnumerable<TIn?>, TOut> convert)
{
_convert = convert;
}
/// <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
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)
{

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

@ -2,6 +2,8 @@ using System;
using System.Globalization;
using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Converters
{
/// <summary>
@ -12,23 +14,23 @@ namespace Avalonia.Data.Converters
/// <typeparam name="TOut">The output type.</typeparam>
public class FuncValueConverter<TIn, TOut> : IValueConverter
{
private readonly Func<TIn, TOut> _convert;
private readonly Func<TIn?, TOut> _convert;
/// <summary>
/// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TOut}"/> class.
/// </summary>
/// <param name="convert">The convert function.</param>
public FuncValueConverter(Func<TIn, TOut> convert)
public FuncValueConverter(Func<TIn?, TOut> convert)
{
_convert = convert;
}
/// <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))
{
return _convert((TIn)value);
return _convert((TIn?)value);
}
else
{
@ -37,7 +39,7 @@ namespace Avalonia.Data.Converters
}
/// <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();
}

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

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using System.Globalization;
#nullable enable
namespace Avalonia.Data.Converters
{
/// <summary>
@ -22,6 +24,6 @@ namespace Avalonia.Data.Converters
/// <see cref="AvaloniaProperty.UnsetValue"/>. Any exception thrown will be treated as
/// an application exception.
/// </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.Globalization;
#nullable enable
namespace Avalonia.Data.Converters
{
/// <summary>
@ -21,7 +23,7 @@ namespace Avalonia.Data.Converters
/// a <see cref="BindingNotification"/> in an error state. Any exceptions thrown will be
/// treated as an application exception.
/// </remarks>
object Convert(object value, Type targetType, object parameter, CultureInfo culture);
object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture);
/// <summary>
/// Converts a value.
@ -36,6 +38,6 @@ namespace Avalonia.Data.Converters
/// a <see cref="BindingNotification"/> in an error state. Any exceptions thrown will be
/// treated as an application exception.
/// </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 Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Converters
{
class MethodToCommandConverter : ICommand
@ -18,9 +20,9 @@ namespace Avalonia.Data.Converters
.GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static);
readonly Func<object, bool> canExecute;
readonly Action<object> execute;
readonly WeakPropertyChangedProxy weakPropertyChanged;
readonly PropertyChangedEventHandler propertyChangedEventHandler;
readonly string[] dependencyProperties;
readonly WeakPropertyChangedProxy? weakPropertyChanged;
readonly PropertyChangedEventHandler? propertyChangedEventHandler;
readonly string[]? dependencyProperties;
public MethodToCommandConverter(Delegate action)
{
@ -74,7 +76,7 @@ namespace Avalonia.Data.Converters
}
#pragma warning disable 0067
public event EventHandler CanExecuteChanged;
public event EventHandler? CanExecuteChanged;
#pragma warning restore 0067
public bool CanExecute(object parameter) => canExecute(parameter);
@ -161,14 +163,14 @@ namespace Avalonia.Data.Converters
.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);
internal class WeakPropertyChangedProxy
{
readonly WeakReference<PropertyChangedEventHandler> _listener = new WeakReference<PropertyChangedEventHandler>(null);
readonly WeakReference<PropertyChangedEventHandler?> _listener = new WeakReference<PropertyChangedEventHandler?>(null);
readonly PropertyChangedEventHandler _handler;
internal WeakReference<INotifyPropertyChanged> Source { get; } = new WeakReference<INotifyPropertyChanged>(null);
internal WeakReference<INotifyPropertyChanged?> Source { get; } = new WeakReference<INotifyPropertyChanged?>(null);
public WeakPropertyChangedProxy()
{
@ -190,7 +192,7 @@ namespace Avalonia.Data.Converters
public void Unsubscribe()
{
if (Source.TryGetTarget(out INotifyPropertyChanged source) && source != null)
if (Source.TryGetTarget(out var source) && source != null)
source.PropertyChanged -= _handler;
Source.SetTarget(null);

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

@ -1,3 +1,4 @@
#nullable enable
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.
/// </summary>
public static readonly IValueConverter IsNull =
new FuncValueConverter<object, bool>(x => x is null);
new FuncValueConverter<object?, bool>(x => x is null);
/// <summary>
/// A value converter that returns true if the input object is not null.
/// </summary>
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
{
@ -10,12 +11,12 @@ namespace Avalonia.Data.Converters
/// A value converter that returns true if the input string is null or an empty string.
/// </summary>
public static readonly IValueConverter IsNullOrEmpty =
new FuncValueConverter<string, bool>(string.IsNullOrEmpty);
new FuncValueConverter<string?, bool>(string.IsNullOrEmpty);
/// <summary>
/// A value converter that returns true if the input string is not null or empty.
/// </summary>
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.Linq;
#nullable enable
namespace Avalonia.Data.Converters
{
/// <summary>
@ -17,18 +19,16 @@ namespace Avalonia.Data.Converters
/// <param name="inner">
/// An optional inner converter to be called before the format takes place.
/// </param>
public StringFormatMultiValueConverter(string format, IMultiValueConverter inner)
public StringFormatMultiValueConverter(string format, IMultiValueConverter? inner)
{
Contract.Requires<ArgumentNullException>(format != null);
Format = format;
Format = format ?? throw new ArgumentNullException(nameof(format));
Inner = inner;
}
/// <summary>
/// Gets an inner value converter which will be called before the string format takes place.
/// </summary>
public IMultiValueConverter Inner { get; }
public IMultiValueConverter? Inner { get; }
/// <summary>
/// Gets the format string.
@ -36,7 +36,7 @@ namespace Avalonia.Data.Converters
public string Format { get; }
/// <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
? string.Format(culture, Format, values.ToArray())

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

@ -1,6 +1,8 @@
using System;
using System.Globalization;
#nullable enable
namespace Avalonia.Data.Converters
{
/// <summary>
@ -15,18 +17,16 @@ namespace Avalonia.Data.Converters
/// <param name="inner">
/// An optional inner converter to be called before the format takes place.
/// </param>
public StringFormatValueConverter(string format, IValueConverter inner)
public StringFormatValueConverter(string format, IValueConverter? inner)
{
Contract.Requires<ArgumentNullException>(format != null);
Format = format;
Format = format ?? throw new ArgumentNullException(nameof(format));
Inner = inner;
}
/// <summary>
/// Gets an inner value converter which will be called before the string format takes place.
/// </summary>
public IValueConverter Inner { get; }
public IValueConverter? Inner { get; }
/// <summary>
/// Gets the format string.
@ -34,14 +34,14 @@ namespace Avalonia.Data.Converters
public string Format { get; }
/// <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;
return string.Format(culture, Format, value);
}
/// <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");
}

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

@ -2,11 +2,13 @@
using System.Reactive.Linq;
using Avalonia.Reactive;
#nullable enable
namespace Avalonia.Data.Core
{
public class AvaloniaPropertyAccessorNode : SettableNode
{
private IDisposable _subscription;
private IDisposable? _subscription;
private readonly bool _enableValidation;
private readonly AvaloniaProperty _property;
@ -16,15 +18,15 @@ namespace Avalonia.Data.Core
_enableValidation = enableValidation;
}
public override string Description => PropertyName;
public string PropertyName { get; }
public override string? Description => PropertyName;
public string? PropertyName { get; }
public override Type PropertyType => _property.PropertyType;
protected override bool SetTargetValueCore(object value, BindingPriority priority)
protected override bool SetTargetValueCore(object? value, BindingPriority priority)
{
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);
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
{

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

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

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

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

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

@ -1,3 +1,5 @@
#nullable enable
namespace Avalonia.Data.Core
{
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
{

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

@ -1,35 +1,37 @@
using System;
#nullable enable
namespace Avalonia.Data.Core
{
public abstract class ExpressionNode
{
private static readonly object CacheInvalid = new object();
protected static readonly WeakReference<object> UnsetReference =
new WeakReference<object>(AvaloniaProperty.UnsetValue);
protected static readonly WeakReference<object?> UnsetReference =
new WeakReference<object?>(AvaloniaProperty.UnsetValue);
protected static readonly WeakReference<object> NullReference =
new WeakReference<object>(null);
protected static readonly WeakReference<object?> NullReference =
new WeakReference<object?>(null);
private WeakReference<object> _target = UnsetReference;
private Action<object> _subscriber;
private WeakReference<object?> _target = UnsetReference;
private Action<object?>? _subscriber;
private bool _listening;
protected WeakReference<object> LastValue { get; private set; }
protected WeakReference<object?>? LastValue { get; private set; }
public abstract string Description { get; }
public ExpressionNode Next { get; set; }
public abstract string? Description { get; }
public ExpressionNode? Next { get; set; }
public WeakReference<object> Target
public WeakReference<object?> Target
{
get { return _target; }
set
{
Contract.Requires<ArgumentNullException>(value != null);
_ = value ?? throw new ArgumentNullException(nameof(value));
_target.TryGetTarget(out var oldTarget);
value.TryGetTarget(out object newTarget);
value.TryGetTarget(out var 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)
{
@ -73,9 +75,9 @@ namespace Avalonia.Data.Core
_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);
}
@ -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;
bindingBroken?.AddNode(Description);
bindingBroken?.AddNode(Description ?? "{empty}");
_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;
if (notification == null)
{
LastValue = value != null ? new WeakReference<object>(value) : NullReference;
LastValue = value != null ? new WeakReference<object?>(value) : NullReference;
if (Next != null)
{
@ -112,7 +120,7 @@ namespace Avalonia.Data.Core
}
else
{
LastValue = notification.Value != null ? new WeakReference<object>(notification.Value) : NullReference;
LastValue = notification.Value != null ? new WeakReference<object?>(notification.Value) : NullReference;
if (Next != null)
{
@ -128,7 +136,7 @@ namespace Avalonia.Data.Core
private void StartListening()
{
_target.TryGetTarget(out object target);
_target.TryGetTarget(out var target);
if (target == null)
{
@ -138,7 +146,7 @@ namespace Avalonia.Data.Core
else if (target != AvaloniaProperty.UnsetValue)
{
_listening = true;
StartListeningCore(_target);
StartListeningCore(_target!);
}
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.Reactive;
#nullable enable
namespace Avalonia.Data.Core
{
/// <summary>
/// Observes and sets the value of an expression on an object.
/// </summary>
public class ExpressionObserver : LightweightObservableBase<object>, IDescription
public class ExpressionObserver : LightweightObservableBase<object?>, IDescription
{
/// <summary>
/// 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 readonly ExpressionNode _node;
private object _root;
private IDisposable _rootSubscription;
private WeakReference<object> _value;
private IReadOnlyList<ITransformNode> _transformNodes;
private object? _root;
private IDisposable? _rootSubscription;
private WeakReference<object?>? _value;
private IReadOnlyList<ITransformNode>? _transformNodes;
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
@ -65,18 +67,13 @@ namespace Avalonia.Data.Core
/// A description of the expression.
/// </param>
public ExpressionObserver(
object root,
object? root,
ExpressionNode node,
string description = null)
string? description = null)
{
if (root == AvaloniaProperty.UnsetValue)
{
root = null;
}
_node = node;
Description = description;
_root = new WeakReference<object>(root);
_root = new WeakReference<object?>(root == AvaloniaProperty.UnsetValue ? null : root);
}
/// <summary>
@ -88,11 +85,11 @@ namespace Avalonia.Data.Core
/// A description of the expression.
/// </param>
public ExpressionObserver(
IObservable<object> rootObservable,
IObservable<object?> rootObservable,
ExpressionNode node,
string description)
string? description)
{
Contract.Requires<ArgumentNullException>(rootObservable != null);
_ = rootObservable ??throw new ArgumentNullException(nameof(rootObservable));
_node = node;
Description = description;
@ -109,16 +106,16 @@ namespace Avalonia.Data.Core
/// A description of the expression.
/// </param>
public ExpressionObserver(
Func<object> rootGetter,
Func<object?> rootGetter,
ExpressionNode node,
IObservable<Unit> update,
string description)
string? description)
{
Contract.Requires<ArgumentNullException>(rootGetter != null);
Contract.Requires<ArgumentNullException>(update != null);
_ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
Description = description;
_node = node;
_node.Target = new WeakReference<object>(rootGetter());
_node = node ?? throw new ArgumentNullException(nameof(rootGetter));
_node.Target = new WeakReference<object?>(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.
/// </param>
public static ExpressionObserver Create<T, U>(
T root,
T? root,
Expression<Func<T, U>> expression,
bool enableDataValidation = false,
string description = null)
string? description = null)
{
return new ExpressionObserver(root, Parse(expression, enableDataValidation), description ?? expression.ToString());
}
@ -154,11 +151,12 @@ namespace Avalonia.Data.Core
IObservable<T> rootObservable,
Expression<Func<T, U>> expression,
bool enableDataValidation = false,
string description = null)
string? description = null)
{
Contract.Requires<ArgumentNullException>(rootObservable != null);
_ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
return new ExpressionObserver(
rootObservable.Select(o => (object)o),
rootObservable.Select(o => (object?)o),
Parse(expression, enableDataValidation),
description ?? expression.ToString());
}
@ -178,9 +176,9 @@ namespace Avalonia.Data.Core
Expression<Func<T, U>> expression,
IObservable<Unit> update,
bool enableDataValidation = false,
string description = null)
string? description = null)
{
Contract.Requires<ArgumentNullException>(rootGetter != null);
_ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
return new ExpressionObserver(
() => rootGetter(),
@ -218,7 +216,7 @@ namespace Avalonia.Data.Core
/// before setting the target value can work, as setting the value requires the
/// expression to be evaluated.
/// </returns>
public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue)
public bool SetValue(object? value, BindingPriority priority = BindingPriority.LocalValue)
{
if (Leaf is SettableNode settable)
{
@ -238,18 +236,18 @@ namespace Avalonia.Data.Core
/// <summary>
/// Gets a description of the expression being observed.
/// </summary>
public string Description { get; }
public string? Description { get; }
/// <summary>
/// Gets the expression being observed.
/// </summary>
public string Expression { get; }
public string? Expression { get; }
/// <summary>
/// Gets the type of the expression result or null if the expression could not be
/// evaluated.
/// </summary>
public Type ResultType => (Leaf as SettableNode)?.PropertyType;
public Type? ResultType => (Leaf as SettableNode)?.PropertyType;
/// <summary>
/// Gets the leaf node.
@ -278,7 +276,7 @@ namespace Avalonia.Data.Core
_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))
{
@ -296,21 +294,21 @@ namespace Avalonia.Data.Core
if (_root is IObservable<object> observable)
{
_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(),
() => PublishCompleted());
}
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;
broken?.Commit(Description);
_value = new WeakReference<object>(value);
broken?.Commit(Description ?? "{empty}");
_value = new WeakReference<object?>(value);
PublishNext(value);
}
}

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

@ -1,5 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Data.Core
{
/// <summary>
@ -17,7 +19,7 @@ namespace Avalonia.Data.Core
/// <param name="column">The column position of the error.</param>
/// <param name="message">The exception message.</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)
{
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
{
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.Reflection;
#nullable enable
namespace Avalonia.Data.Core
{
class IndexerExpressionNode : IndexerNodeBase
@ -32,11 +34,11 @@ namespace Avalonia.Data.Core
public override string Description => _expression.ToString();
protected override bool SetTargetValueCore(object value, BindingPriority priority)
protected override bool SetTargetValueCore(object? value, BindingPriority priority)
{
try
{
Target.TryGetTarget(out object target);
Target.TryGetTarget(out var target);
_setDelegate.DynamicInvoke(target, value);
return true;
@ -47,7 +49,7 @@ namespace Avalonia.Data.Core
}
}
protected override object GetValue(object target)
protected override object? GetValue(object? target)
{
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;
}
protected override int? TryGetFirstArgumentAsInt()
{
Target.TryGetTarget(out object target);
Target.TryGetTarget(out var target);
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 Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Core
{
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 inpc = target as INotifyPropertyChanged;
var inputs = new List<IObservable<object>>();
var inputs = new List<IObservable<object?>>();
if (incc != null)
{
@ -44,14 +46,14 @@ namespace Avalonia.Data.Core
protected override void StopListeningCore()
{
_subscription.Dispose();
_subscription?.Dispose();
}
protected abstract object GetValue(object target);
protected abstract object? GetValue(object? target);
protected abstract int? TryGetFirstArgumentAsInt();
private bool ShouldUpdate(object sender, NotifyCollectionChangedEventArgs e)
private bool ShouldUpdate(object? sender, NotifyCollectionChangedEventArgs e)
{
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
}
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.Globalization;
#nullable enable
namespace Avalonia.Data.Core
{
public class LogicalNotNode : ExpressionNode, ITransformNode
{
public override string Description => "!";
protected override void NextValueChanged(object value)
protected override void NextValueChanged(object? value)
{
base.NextValueChanged(Negate(value));
}
private static object Negate(object value)
private static object Negate(object? value)
{
var notification = value as BindingNotification;
var v = BindingNotification.ExtractValue(value);
@ -74,8 +76,11 @@ namespace Avalonia.Data.Core
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 negated = Negate(value);
if (negated is BindingNotification)

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

@ -2,11 +2,13 @@
using System.Collections.Generic;
using System.Linq;
#nullable enable
namespace Avalonia.Data.Core
{
internal class MarkupBindingChainException : BindingChainException
{
private IList<string> _nodes = new List<string>();
private IList<string>? _nodes = new List<string>();
public MarkupBindingChainException(string message)
: base(message)
@ -26,7 +28,7 @@ namespace Avalonia.Data.Core
}
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)
{

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

@ -1,6 +1,8 @@
using System.Linq;
using System.Linq.Expressions;
#nullable enable
namespace Avalonia.Data.Core.Parsers
{
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.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Parsers
{
class ExpressionVisitorNodeBuilder : ExpressionVisitor

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

@ -1,6 +1,8 @@
using System;
using System.Runtime.ExceptionServices;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -28,12 +30,14 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made.
/// </returns>
public IPropertyAccessor Start(WeakReference<object> reference, string propertyName)
public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName)
{
Contract.Requires<ArgumentNullException>(reference != null);
Contract.Requires<ArgumentNullException>(propertyName != null);
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = 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 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);
}
private static bool IsOfType(Type type, string typeName)
{
while (type != null)
{
if (type.Name == typeName)
{
return true;
}
type = type.BaseType;
}
return false;
}
private class Accessor : PropertyAccessorBase, IObserver<object>
private class Accessor : PropertyAccessorBase, IObserver<object?>
{
private readonly WeakReference<AvaloniaObject> _reference;
private readonly AvaloniaProperty _property;
private IDisposable _subscription;
private IDisposable? _subscription;
public Accessor(WeakReference<AvaloniaObject> reference, AvaloniaProperty property)
{
Contract.Requires<ArgumentNullException>(reference != null);
Contract.Requires<ArgumentNullException>(property != null);
_reference = reference;
_property = property;
_reference = reference ?? throw new ArgumentNullException(nameof(reference));
_property = property ?? throw new ArgumentNullException(nameof(property));
}
public AvaloniaObject Instance
@ -98,10 +84,10 @@ namespace Avalonia.Data.Core.Plugins
}
}
public override Type PropertyType => _property.PropertyType;
public override object Value => Instance?.GetValue(_property);
public override Type? PropertyType => _property?.PropertyType;
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)
{
@ -123,16 +109,16 @@ namespace Avalonia.Data.Core.Plugins
_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();
}
void IObserver<object>.OnNext(object value)
void IObserver<object?>.OnNext(object? 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.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -12,9 +14,9 @@ namespace Avalonia.Data.Core.Plugins
public class DataAnnotationsValidationPlugin : IDataValidationPlugin
{
/// <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?
.GetType()
@ -24,7 +26,7 @@ namespace Avalonia.Data.Core.Plugins
}
/// <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);
}
@ -33,16 +35,16 @@ namespace Avalonia.Data.Core.Plugins
{
private readonly ValidationContext _context;
public Accessor(WeakReference<object> reference, string name, IPropertyAccessor inner)
public Accessor(WeakReference<object?> reference, string name, IPropertyAccessor inner)
: base(inner)
{
reference.TryGetTarget(out object target);
reference.TryGetTarget(out var target);
_context = new ValidationContext(target);
_context.MemberName = name;
}
protected override void InnerValueChanged(object value)
protected override void InnerValueChanged(object? value)
{
var errors = new List<ValidationResult>();

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

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

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

@ -1,6 +1,8 @@
using System;
using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -9,22 +11,22 @@ namespace Avalonia.Data.Core.Plugins
public class ExceptionValidationPlugin : IDataValidationPlugin
{
/// <inheritdoc/>
public bool Match(WeakReference<object> reference, string memberName) => true;
public bool Match(WeakReference<object?> reference, string memberName) => true;
/// <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);
}
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)
{
}
public override bool SetValue(object value, BindingPriority priority)
public override bool SetValue(object? value, BindingPriority priority)
{
try
{

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

@ -1,5 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -13,7 +15,7 @@ namespace Avalonia.Data.Core.Plugins
/// <param name="reference">A weak reference to the object.</param>
/// <param name="memberName">The name of the member to validate.</param>
/// <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>
/// 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
/// property will be made.
/// </returns>
IPropertyAccessor Start(WeakReference<object> reference,
IPropertyAccessor Start(WeakReference<object?> reference,
string propertyName,
IPropertyAccessor inner);
}

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

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

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

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

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

@ -1,5 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -12,7 +14,7 @@ namespace Avalonia.Data.Core.Plugins
/// </summary>
/// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns>
bool Match(WeakReference<object> reference);
bool Match(WeakReference<object?> reference);
/// <summary>
/// Starts producing output based on the specified value.
@ -21,6 +23,6 @@ namespace Avalonia.Data.Core.Plugins
/// <returns>
/// An observable that produces the output for the value.
/// </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 Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -12,25 +14,25 @@ namespace Avalonia.Data.Core.Plugins
public class IndeiValidationPlugin : IDataValidationPlugin
{
/// <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;
}
/// <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);
}
private class Validator : DataValidationBase, IWeakSubscriber<DataErrorsChangedEventArgs>
{
private readonly WeakReference<object> _reference;
private readonly WeakReference<object?> _reference;
private readonly string _name;
public Validator(WeakReference<object> reference, string name, IPropertyAccessor inner)
public Validator(WeakReference<object?> reference, string name, IPropertyAccessor inner)
: base(inner)
{
_reference = reference;
@ -75,16 +77,14 @@ namespace Avalonia.Data.Core.Plugins
base.UnsubscribeCore();
}
protected override void InnerValueChanged(object value)
protected override void InnerValueChanged(object? value)
{
PublishValue(CreateBindingNotification(value));
}
private BindingNotification CreateBindingNotification(object value)
private BindingNotification CreateBindingNotification(object? value)
{
var target = (INotifyDataErrorInfo)GetReferenceTarget();
if (target != null)
if (GetReferenceTarget() is INotifyDataErrorInfo target)
{
var errors = target.GetErrors(_name)?
.Cast<object>()
@ -103,9 +103,9 @@ namespace Avalonia.Data.Core.Plugins
return new BindingNotification(value);
}
private object GetReferenceTarget()
private object? GetReferenceTarget()
{
_reference.TryGetTarget(out object target);
_reference.TryGetTarget(out var target);
return target;
}

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

@ -4,6 +4,8 @@ using System.ComponentModel;
using System.Reflection;
using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -12,8 +14,8 @@ namespace Avalonia.Data.Core.Plugins
/// </summary>
public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
{
private readonly Dictionary<(Type, string), PropertyInfo> _propertyLookup =
new Dictionary<(Type, string), PropertyInfo>();
private readonly Dictionary<(Type, string), PropertyInfo?> _propertyLookup =
new Dictionary<(Type, string), PropertyInfo?>();
/// <inheritdoc/>
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
/// property will be made.
/// </returns>
public IPropertyAccessor Start(WeakReference<object> reference, string propertyName)
public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName)
{
Contract.Requires<ArgumentNullException>(reference != null);
Contract.Requires<ArgumentNullException>(propertyName != null);
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = 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);
@ -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);
if (!_propertyLookup.TryGetValue(key, out PropertyInfo propertyInfo))
if (!_propertyLookup.TryGetValue(key, out var propertyInfo))
{
propertyInfo = TryFindAndCacheProperty(type, propertyName);
}
@ -60,9 +63,9 @@ namespace Avalonia.Data.Core.Plugins
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 =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
@ -86,22 +89,22 @@ namespace Avalonia.Data.Core.Plugins
private class Accessor : PropertyAccessorBase, IWeakSubscriber<PropertyChangedEventArgs>
{
private readonly WeakReference<object> _reference;
private readonly WeakReference<object?> _reference;
private readonly PropertyInfo _property;
private bool _eventRaised;
public Accessor(WeakReference<object> reference, PropertyInfo property)
public Accessor(WeakReference<object?> reference, PropertyInfo property)
{
Contract.Requires<ArgumentNullException>(reference != null);
Contract.Requires<ArgumentNullException>(property != null);
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = property ?? throw new ArgumentNullException(nameof(property));
_reference = reference;
_property = property;
}
public override Type PropertyType => _property.PropertyType;
public override Type? PropertyType => _property.PropertyType;
public override object Value
public override object? Value
{
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)
{
@ -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;
}

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

@ -3,21 +3,24 @@ using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
public class MethodAccessorPlugin : IPropertyAccessorPlugin
{
private readonly Dictionary<(Type, string), MethodInfo> _methodLookup =
new Dictionary<(Type, string), MethodInfo>();
private readonly Dictionary<(Type, string), MethodInfo?> _methodLookup =
new Dictionary<(Type, string), MethodInfo?>();
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);
Contract.Requires<ArgumentNullException>(methodName != null);
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = 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);
@ -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);
if (!_methodLookup.TryGetValue(key, out MethodInfo methodInfo))
if (!_methodLookup.TryGetValue(key, out var methodInfo))
{
methodInfo = TryFindAndCacheMethod(type, methodName);
}
@ -55,9 +58,9 @@ namespace Avalonia.Data.Core.Plugins
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 =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
@ -81,10 +84,10 @@ namespace Avalonia.Data.Core.Plugins
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);
Contract.Requires<ArgumentNullException>(method != null);
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = method ?? throw new ArgumentNullException(nameof(method));
var returnType = method.ReturnType;
bool hasReturn = returnType != typeof(void);
@ -115,19 +118,17 @@ namespace Avalonia.Data.Core.Plugins
{
Value = method.CreateDelegate(PropertyType);
}
else
else if (reference.TryGetTarget(out var target))
{
reference.TryGetTarget(out object 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()
{

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

@ -3,6 +3,8 @@ using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -10,16 +12,16 @@ namespace Avalonia.Data.Core.Plugins
/// </summary>
public class ObservableStreamPlugin : IStreamPlugin
{
static MethodInfo observableSelect;
static MethodInfo? observableSelect;
/// <summary>
/// Checks whether this plugin handles the specified value.
/// </summary>
/// <param name="reference">A weak reference to the value.</param>
/// <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 =>
x.IsGenericType &&
@ -33,12 +35,13 @@ namespace Avalonia.Data.Core.Plugins
/// <returns>
/// An observable that produces the output for the value.
/// </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 (target is IObservable<object> result)
if (target is IObservable<object?> result)
{
return result;
};
@ -98,6 +101,6 @@ namespace Avalonia.Data.Core.Plugins
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;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -7,13 +9,13 @@ namespace Avalonia.Data.Core.Plugins
/// </summary>
public abstract class PropertyAccessorBase : IPropertyAccessor
{
private Action<object> _listener;
private Action<object?>? _listener;
/// <inheritdoc/>
public abstract Type PropertyType { get; }
public abstract Type? PropertyType { get; }
/// <inheritdoc/>
public abstract object Value { get; }
public abstract object? Value { get; }
/// <inheritdoc/>
public void Dispose()
@ -25,12 +27,12 @@ namespace Avalonia.Data.Core.Plugins
}
/// <inheritdoc/>
public abstract bool SetValue(object value, BindingPriority priority);
public abstract bool SetValue(object? value, BindingPriority priority);
/// <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)
{
@ -58,7 +60,7 @@ namespace Avalonia.Data.Core.Plugins
/// Publishes a value to the listener.
/// </summary>
/// <param name="value">The value.</param>
protected void PublishValue(object value) => _listener?.Invoke(value);
protected void PublishValue(object? value) => _listener?.Invoke(value);
/// <summary>
/// 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;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -19,10 +21,10 @@ namespace Avalonia.Data.Core.Plugins
}
/// <inheritdoc/>
public Type PropertyType => null;
public Type? PropertyType => null;
/// <inheritdoc/>
public object Value => _error;
public object? Value => _error;
/// <inheritdoc/>
public void Dispose()
@ -30,7 +32,7 @@ namespace Avalonia.Data.Core.Plugins
}
/// <inheritdoc/>
public bool SetValue(object value, BindingPriority priority)
public bool SetValue(object? value, BindingPriority priority)
{
return false;
}

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

@ -4,6 +4,8 @@ using System.Reactive.Subjects;
using System.Reflection;
using System.Threading.Tasks;
#nullable enable
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
@ -16,9 +18,9 @@ namespace Avalonia.Data.Core.Plugins
/// </summary>
/// <param name="reference">A weak reference to the value.</param>
/// <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;
}
@ -30,9 +32,9 @@ namespace Avalonia.Data.Core.Plugins
/// <returns>
/// An observable that produces the output for the value.
/// </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)
{
@ -46,7 +48,7 @@ namespace Avalonia.Data.Core.Plugins
case TaskStatus.Faulted:
return HandleCompleted(task);
default:
var subject = new Subject<object>();
var subject = new Subject<object?>();
task.ContinueWith(
x => HandleCompleted(task).Subscribe(subject),
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");

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

@ -2,13 +2,15 @@ using System;
using System.Diagnostics;
using Avalonia.Data.Core.Plugins;
#nullable enable
namespace Avalonia.Data.Core
{
public class PropertyAccessorNode : SettableNode
{
private readonly bool _enableValidation;
private IPropertyAccessorPlugin _customPlugin;
private IPropertyAccessor _accessor;
private IPropertyAccessorPlugin? _customPlugin;
private IPropertyAccessor? _accessor;
public PropertyAccessorNode(string propertyName, bool enableValidation)
{
@ -25,9 +27,9 @@ namespace Avalonia.Data.Core
public override string Description => PropertyName;
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)
{
@ -41,9 +43,10 @@ namespace Avalonia.Data.Core
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 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.
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}'";
@ -80,7 +83,7 @@ namespace Avalonia.Data.Core
accessor.Subscribe(ValueChanged);
}
private IPropertyAccessorPlugin GetPropertyAccessorPluginForObject(object target)
private IPropertyAccessorPlugin? GetPropertyAccessorPluginForObject(object target)
{
foreach (IPropertyAccessorPlugin x in ExpressionObserver.PropertyAccessors)
{
@ -94,7 +97,7 @@ namespace Avalonia.Data.Core
protected override void StopListeningCore()
{
_accessor.Dispose();
_accessor?.Dispose();
_accessor = null;
}
}

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

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

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

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

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

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

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

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Text;
#nullable enable
namespace Avalonia.Data.Core
{
public class TypeCastNode : ExpressionNode
@ -15,17 +17,17 @@ namespace Avalonia.Data.Core
TargetType = type;
}
protected virtual object Cast(object value)
protected virtual object? Cast(object? value)
{
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);
reference = target == null ? NullReference : new WeakReference<object>(target);
reference = target == null ? NullReference : new WeakReference<object?>(target);
}
base.StartListeningCore(reference);

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

@ -1,5 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Data
{
/// <summary>
@ -11,11 +13,11 @@ namespace Avalonia.Data
/// Initializes a new instance of the <see cref="DataValidationException"/> class.
/// </summary>
/// <param name="errorData">Data of validation error.</param>
public DataValidationException(object errorData) : base(errorData?.ToString())
public DataValidationException(object? errorData) : base(errorData?.ToString())
{
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
{
/// <summary>
@ -20,10 +22,10 @@ namespace Avalonia.Data
/// <returns>
/// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
/// </returns>
InstancedBinding Initiate(
InstancedBinding? Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
AvaloniaProperty? targetProperty,
object? anchor = null,
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
{
@ -16,10 +18,10 @@
public AvaloniaProperty Property { get; }
private BindingMode Mode { get; }
public InstancedBinding Initiate(
public InstancedBinding? Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
AvaloniaProperty? targetProperty,
object? anchor = null,
bool enableDataValidation = false)
{
return new InstancedBinding(Source.GetSubject(Property), Mode, BindingPriority.LocalValue);

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

@ -1,12 +1,14 @@
using System;
using System.Reactive;
#nullable enable
namespace Avalonia.Data
{
/// <summary>
/// Holds a description of a binding for <see cref="AvaloniaObject"/>'s [] operator.
/// </summary>
public class IndexerDescriptor : ObservableBase<object>, IDescription
public class IndexerDescriptor : ObservableBase<object?>, IDescription
{
/// <summary>
/// Gets or sets the binding mode.
@ -29,7 +31,7 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the source property.
/// </summary>
public AvaloniaProperty Property
public AvaloniaProperty? Property
{
get;
set;
@ -38,7 +40,7 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the source object.
/// </summary>
public AvaloniaObject Source
public AvaloniaObject? Source
{
get;
set;
@ -50,7 +52,7 @@ namespace Avalonia.Data
/// <remarks>
/// If null, then <see cref="Source"/>.<see cref="Property"/> will be used.
/// </remarks>
public IObservable<object> SourceObservable
public IObservable<object>? SourceObservable
{
get;
set;
@ -59,7 +61,7 @@ namespace Avalonia.Data
/// <summary>
/// Gets a description of the binding.
/// </summary>
public string Description => $"{Source?.GetType().Name}.{Property.Name}";
public string Description => $"{Source?.GetType().Name}.{Property?.Name}";
/// <summary>
/// Makes a two-way binding.
@ -104,9 +106,14 @@ namespace Avalonia.Data
}
/// <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.Reactive.Subjects;
#nullable enable
namespace Avalonia.Data
{
/// <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
/// something other than a subject, use one of the static creation methods on this class.
/// </remarks>
public InstancedBinding(ISubject<object> subject, BindingMode mode, BindingPriority priority)
public InstancedBinding(ISubject<object?> subject, BindingMode mode, BindingPriority priority)
{
Contract.Requires<ArgumentNullException>(subject != null);
@ -35,7 +37,7 @@ namespace Avalonia.Data
Value = subject;
}
private InstancedBinding(object value, BindingMode mode, BindingPriority priority)
private InstancedBinding(object? value, BindingMode mode, BindingPriority priority)
{
Mode = mode;
Priority = priority;
@ -55,17 +57,17 @@ namespace Avalonia.Data
/// <summary>
/// Gets the value or source of the binding.
/// </summary>
public object Value { get; }
public object? Value { get; }
/// <summary>
/// Gets the <see cref="Value"/> as an observable.
/// </summary>
public IObservable<object> Observable => Value as IObservable<object>;
public IObservable<object?>? Observable => Value as IObservable<object?>;
/// <summary>
/// Gets the <see cref="Value"/> as a subject.
/// </summary>
public ISubject<object> Subject => Value as ISubject<object>;
public ISubject<object?>? Subject => Value as ISubject<object?>;
/// <summary>
/// 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>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneTime(
IObservable<object> observable,
IObservable<object?> observable,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(observable != null);
_ = observable ?? throw new ArgumentNullException(nameof(observable));
return new InstancedBinding(observable, BindingMode.OneTime, priority);
}
@ -102,10 +104,10 @@ namespace Avalonia.Data
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneWay(
IObservable<object> observable,
IObservable<object?> observable,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(observable != null);
_ = observable ?? throw new ArgumentNullException(nameof(observable));
return new InstancedBinding(observable, BindingMode.OneWay, priority);
}
@ -117,10 +119,10 @@ namespace Avalonia.Data
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneWayToSource(
ISubject<object> subject,
ISubject<object?> subject,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(subject != null);
_ = subject ?? throw new ArgumentNullException(nameof(subject));
return new InstancedBinding(subject, BindingMode.OneWayToSource, priority);
}
@ -132,10 +134,10 @@ namespace Avalonia.Data
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding TwoWay(
ISubject<object> subject,
ISubject<object?> subject,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(subject != null);
_ = subject ?? throw new ArgumentNullException(nameof(subject));
return new InstancedBinding(subject, BindingMode.TwoWay, priority);
}

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

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#nullable enable
@ -23,13 +22,13 @@ namespace Avalonia.Data
/// </remarks>
public readonly struct Optional<T> : IEquatable<Optional<T>>
{
[AllowNull] private readonly T _value;
private readonly T _value;
/// <summary>
/// Initializes a new instance of the <see cref="Optional{T}"/> struct with value.
/// </summary>
/// <param name="value">The value.</param>
public Optional([AllowNull] T value)
public Optional(T value)
{
_value = value;
HasValue = true;
@ -61,7 +60,7 @@ namespace Avalonia.Data
/// Casts the value (if any) to an <see cref="object"/>.
/// </summary>
/// <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/>
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.
/// </summary>
/// <returns>The value.</returns>
[return: MaybeNull]
public T GetValueOrDefault() => HasValue ? _value : default;
public T? GetValueOrDefault() => HasValue ? _value : default;
/// <summary>
/// Gets the value if present, otherwise a default value.
/// </summary>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value.</returns>
public T GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue;
public T? GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue;
/// <summary>
/// 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
/// not present or of an incorrect type.
/// </returns>
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>()
public TResult? GetValueOrDefault<TResult>()
{
return HasValue ?
_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
/// value is not present.
/// </returns>
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
public TResult? GetValueOrDefault<TResult>(TResult defaultValue)
{
return HasValue ?
_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.
/// </summary>
/// <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>
/// Compares two <see cref="Optional{T}"/>s for inequality.
@ -162,9 +158,9 @@ namespace Avalonia.Data
/// <typeparam name="T">The target type.</typeparam>
/// <param name="value">The binding value.</param>
/// <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 BindingPriority Priority { get; private set; }
public IObservable<BindingValue<T>> Source { get; }
Optional<object> IValue.GetValue() => _value.ToObject();
Optional<object?> IValue.GetValue() => _value.ToObject();
public void BeginBatchUpdate() => _batchUpdate = true;
@ -121,8 +121,8 @@ namespace Avalonia.PropertyStore
IValueSink sink,
IAvaloniaObject owner,
AvaloniaProperty property,
Optional<object> oldValue,
Optional<object> newValue)
Optional<object?> oldValue,
Optional<object?> newValue)
{
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,

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

@ -25,7 +25,19 @@ namespace Avalonia.PropertyStore
public ConstantValueEntry(
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,
IValueSink sink)
{
@ -37,7 +49,7 @@ namespace Avalonia.PropertyStore
public StyledPropertyBase<T> Property { get; }
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)
{
@ -59,8 +71,8 @@ namespace Avalonia.PropertyStore
IValueSink sink,
IAvaloniaObject owner,
AvaloniaProperty property,
Optional<object> oldValue,
Optional<object> newValue)
Optional<object?> oldValue,
Optional<object?> newValue)
{
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,

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

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

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

@ -12,11 +12,11 @@ namespace Avalonia.PropertyStore
/// <typeparam name="T">The property type.</typeparam>
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;
Optional<object> IValue.GetValue() => new Optional<object>(_value);
Optional<object?> IValue.GetValue() => new Optional<object?>(_value);
public Optional<T> GetValue(BindingPriority maxPriority)
{
@ -30,8 +30,8 @@ namespace Avalonia.PropertyStore
IValueSink sink,
IAvaloniaObject owner,
AvaloniaProperty property,
Optional<object> oldValue,
Optional<object> newValue)
Optional<object?> oldValue,
Optional<object?> newValue)
{
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,

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

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

9
src/Avalonia.Base/ValueStore.cs

@ -169,7 +169,8 @@ namespace Avalonia
}
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
// 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
// them to a constant value entry with Unset priority in the event of a local value being
// cleared during a batch update.
var sentinel = new ConstantValueEntry<T>(property, default, BindingPriority.Unset, _sink);
var sentinel = new ConstantValueEntry<T>(property, Optional<T>.Empty, BindingPriority.Unset, _sink);
_values.SetValue(property, sentinel);
}
@ -479,7 +480,7 @@ namespace Avalonia
return true;
}
public void ValueChanged(AvaloniaProperty property, Optional<object> oldValue)
public void ValueChanged(AvaloniaProperty property, Optional<object?> oldValue)
{
_notifications ??= new List<Notification>();
@ -506,7 +507,7 @@ namespace Avalonia
private struct Notification
{
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 StyledPropertyBase<T>? _styledProperty;
private readonly DirectPropertyBase<T>? _directProperty;
private readonly InstancedBinding _binding;
private readonly InstancedBinding? _binding;
private readonly Inner _inner;
private BindingValue<T> _value;
private IDisposable? _subscription;
@ -36,7 +36,7 @@ namespace Avalonia.Styling
_styledProperty = 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
// sure what the semantics should be in the case of activation/deactivation.
@ -59,6 +59,9 @@ namespace Avalonia.Styling
public void Start(bool hasActivator)
{
if (_binding is null)
return;
_isActive = !hasActivator;
if (_styledProperty is object)
@ -90,9 +93,12 @@ namespace Avalonia.Styling
public void Activate()
{
if (_binding is null)
return;
if (!_isActive)
{
_innerSubscription ??= _binding.Observable.Subscribe(_inner);
_innerSubscription ??= _binding.Observable!.Subscribe(_inner);
_isActive = true;
PublishNext();
}
@ -140,7 +146,7 @@ namespace Avalonia.Styling
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);
}
@ -148,11 +154,11 @@ namespace Avalonia.Styling
protected override void Subscribed()
{
if (_isActive)
if (_isActive && _binding?.Observable is not null)
{
if (_innerSubscription is null)
{
_innerSubscription ??= _binding.Observable.Subscribe(_inner);
_innerSubscription ??= _binding.Observable!.Subscribe(_inner);
}
else
{

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

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

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

@ -101,7 +101,7 @@ namespace Avalonia.Styling
data.result = new PropertySetterInstance<T>(
data.target,
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(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor,
AvaloniaProperty? targetProperty,
object? anchor,
bool enableDataValidation)
{
if (ResourceKey is null)
@ -64,7 +64,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
return null;
}
private Func<object?, object?>? GetConverter(AvaloniaProperty targetProperty)
private Func<object?, object?>? GetConverter(AvaloniaProperty? targetProperty)
{
if (targetProperty?.PropertyType == typeof(IBrush))
{

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

@ -61,7 +61,7 @@ namespace Avalonia.Data
/// </summary>
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));

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

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

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

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

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

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

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

@ -21,7 +21,7 @@ namespace Avalonia.Markup.Parsers
_enableValidation = enableValidation;
}
public (ExpressionNode? Node, SourceMode Mode) Parse(ref CharacterReader r)
public (ExpressionNode Node, SourceMode Mode) Parse(ref CharacterReader r)
{
ExpressionNode? rootNode = 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);
}
@ -108,6 +111,9 @@ namespace Avalonia.Markup.Parsers
castType = _typeResolver(node.Namespace, node.TypeName);
}
if (castType is null)
throw new InvalidOperationException("Unable to determine type for cast.");
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?");
}
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);
}

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

@ -19,7 +19,7 @@ namespace Avalonia.Markup.Parsers.Nodes
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))
_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);
}

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) + "]";
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 list = target as IList;
@ -103,7 +104,7 @@ namespace Avalonia.Markup.Parsers.Nodes
return false;
}
private bool SetValueInArray(Array array, object value)
private bool SetValueInArray(Array array, object? value)
{
int[] 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))
{
@ -129,7 +130,7 @@ namespace Avalonia.Markup.Parsers.Nodes
{
get
{
if (!Target.TryGetTarget(out object target))
if (!Target.TryGetTarget(out var target) || target is 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 list = target as IList;
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();
return typeInfo.GetDeclaredProperty(e.PropertyName)?.GetIndexParameters().Any() ?? false;
}

Loading…
Cancel
Save