Browse Source

Merge branch 'master' into fixes/XML_Documentation_2

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

+ 1 - 1
native/Avalonia.Native/src/OSX/window.mm

@@ -175,7 +175,7 @@ public:
         {
             if(Window != nullptr)
             {
-                [Window makeKeyWindow];
+                [Window makeKeyAndOrderFront:nil];
                 [NSApp activateIgnoringOtherApps:YES];
             }
         }

+ 2 - 2
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

+ 2 - 2
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 - 0
src/Avalonia.Base/Data/AssignBindingAttribute.cs

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

+ 5 - 2
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 - 0
src/Avalonia.Base/Data/BindingMode.cs

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

+ 25 - 23
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;

+ 16 - 6
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;
 

+ 3 - 1
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,
     }
-}
+}

+ 21 - 25
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 - 0
src/Avalonia.Base/Data/Converters/BoolConverters.cs

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

+ 4 - 2
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);
         }

+ 7 - 5
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)
                     {

+ 7 - 5
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();
         }

+ 3 - 1
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);
     }
 }

+ 4 - 2
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);
     }
 }

+ 10 - 8
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);

+ 3 - 2
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);
     }
 }

+ 3 - 2
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));
     }
 }

+ 6 - 6
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())

+ 7 - 7
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");
         }

+ 10 - 8
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
             {

+ 24 - 24
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);
             }
         }

+ 10 - 8
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 - 0
src/Avalonia.Base/Data/Core/CommonPropertyNames.cs

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

+ 3 - 1
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
     {

+ 31 - 23
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
             {

+ 37 - 39
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);
         }
     }

+ 3 - 1
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;

+ 4 - 2
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);
     }
 }

+ 7 - 5
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?;
         } 

+ 10 - 8
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);
     }
 }

+ 8 - 3
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)

+ 4 - 2
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 - 0
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 - 0
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

+ 19 - 33
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);
             }

+ 8 - 6
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>();
 

+ 10 - 8
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);

+ 6 - 4
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
                 {

+ 4 - 2
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);
     }

+ 6 - 4
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.

+ 3 - 1
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);
     }
 }

+ 4 - 2
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);
     }
 }

+ 12 - 12
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;
             }

+ 22 - 19
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;
             }

+ 20 - 19
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()
             {

+ 10 - 7
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;
     }
 }

+ 9 - 7
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.

+ 5 - 3
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;
         }

+ 9 - 7
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");
             

+ 12 - 9
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 - 0
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

+ 8 - 6
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 - 0
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

+ 6 - 4
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)
             {

+ 6 - 4
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);

+ 4 - 2
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; }
     }
 }

+ 5 - 3
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);
     }
 }

+ 6 - 4
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);

+ 14 - 7
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);
         }
     }
 }

+ 15 - 13
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);
         }

+ 10 - 14
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;
         }
     }
 }

+ 3 - 3
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,

+ 16 - 4
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,

+ 3 - 3
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>

+ 5 - 5
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,

+ 3 - 3
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,

+ 5 - 4
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;
             }
         }
     }

+ 17 - 1
src/Avalonia.Controls/SplitView.cs

@@ -11,6 +11,7 @@ using System;
 using System.Reactive.Disposables;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Templates;
+using Avalonia.LogicalTree;
 
 namespace Avalonia.Controls
 {
@@ -168,7 +169,8 @@ namespace Avalonia.Controls
             CompactPaneLengthProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnCompactPaneLengthChanged(v));
             PanePlacementProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnPanePlacementChanged(v));
             DisplayModeProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnDisplayModeChanged(v));
-            
+
+            PaneProperty.Changed.AddClassHandler<SplitView>((x, e) => x.PaneChanged(e));
         }
 
         /// <summary>
@@ -258,6 +260,7 @@ namespace Avalonia.Controls
         /// <summary>
         /// Gets or sets the Pane for the SplitView
         /// </summary>
+        [DependsOn(nameof(PaneTemplate))]
         public object Pane
         {
             get => GetValue(PaneProperty);
@@ -460,5 +463,18 @@ namespace Avalonia.Controls
             var mode = (bool)e.NewValue;
             PseudoClasses.Set(":lightdismiss", mode);
         }
+
+        private void PaneChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            if (e.OldValue is ILogical oldChild)
+            {
+                LogicalChildren.Remove(oldChild);
+            }
+
+            if (e.NewValue is ILogical newChild)
+            {
+                LogicalChildren.Add(newChild);
+            }
+        }
     }
 }

+ 12 - 6
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
                 {

+ 2 - 2
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;

+ 1 - 1
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!);
             }
         }
 

+ 3 - 3
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))
             {

+ 1 - 1
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));
 

+ 4 - 4
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(

+ 6 - 5
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);

+ 3 - 3
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);
                 }

+ 1 - 1
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))

+ 12 - 2
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);
         }

+ 1 - 1
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);

+ 2 - 2
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);
             }

+ 13 - 7
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;
         }

+ 10 - 0
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

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

+ 11 - 6
src/Windows/Avalonia.Win32/Win32NativeControlHost.cs

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

+ 1 - 1
src/Windows/Avalonia.Win32/WindowImpl.cs

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