diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 9208848b4c..16f49b8e26 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -175,7 +175,7 @@ public: { if(Window != nullptr) { - [Window makeKeyWindow]; + [Window makeKeyAndOrderFront:nil]; [NSApp activateIgnoringOtherApps:YES]; } } diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index dddfa4bbb5..9a1101e637 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -115,8 +115,8 @@ namespace Avalonia /// The binding information. 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 diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 4e04a9045e..16f0ca38a7 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/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) { diff --git a/src/Avalonia.Base/Data/AssignBindingAttribute.cs b/src/Avalonia.Base/Data/AssignBindingAttribute.cs index 15bd32c7c2..f88055a1e1 100644 --- a/src/Avalonia.Base/Data/AssignBindingAttribute.cs +++ b/src/Avalonia.Base/Data/AssignBindingAttribute.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/BindingChainException.cs b/src/Avalonia.Base/Data/BindingChainException.cs index 86112ce12d..09acf459cc 100644 --- a/src/Avalonia.Base/Data/BindingChainException.cs +++ b/src/Avalonia.Base/Data/BindingChainException.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data { /// @@ -16,6 +18,7 @@ namespace Avalonia.Data /// public BindingChainException() { + _message = "Binding error"; } /// @@ -45,12 +48,12 @@ namespace Avalonia.Data /// /// Gets the expression that could not be evaluated. /// - public string Expression { get; protected set; } + public string? Expression { get; protected set; } /// /// Gets the point in the expression at which the error occurred. /// - public string ExpressionErrorPoint { get; protected set; } + public string? ExpressionErrorPoint { get; protected set; } /// public override string Message diff --git a/src/Avalonia.Base/Data/BindingMode.cs b/src/Avalonia.Base/Data/BindingMode.cs index 0d04c2eb10..a57487fa7a 100644 --- a/src/Avalonia.Base/Data/BindingMode.cs +++ b/src/Avalonia.Base/Data/BindingMode.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/BindingNotification.cs b/src/Avalonia.Base/Data/BindingNotification.cs index 516c26d1d3..867a7f637a 100644 --- a/src/Avalonia.Base/Data/BindingNotification.cs +++ b/src/Avalonia.Base/Data/BindingNotification.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data { /// @@ -47,13 +49,13 @@ namespace Avalonia.Data public static readonly BindingNotification UnsetValue = new BindingNotification(AvaloniaProperty.UnsetValue); - private object _value; + private object? _value; /// /// Initializes a new instance of the class. /// /// The binding value. - public BindingNotification(object value) + public BindingNotification(object? value) { _value = value; } @@ -81,7 +83,7 @@ namespace Avalonia.Data /// The binding error. /// The type of the binding error. /// The fallback value. - 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 is false then it will return /// . /// - public object Value => _value; + public object? Value => _value; /// /// Gets a value indicating whether should be pushed to the target. @@ -105,7 +107,7 @@ namespace Avalonia.Data /// /// Gets the error that occurred on the source, if any. /// - public Exception Error { get; set; } + public Exception? Error { get; set; } /// /// Gets the type of error that represents, if any. @@ -118,14 +120,14 @@ namespace Avalonia.Data /// The first instance. /// The second instance. /// true if the two instances are equal; otherwise false. - 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 /// The first instance. /// The second instance. /// true if the two instances are unequal; otherwise false. - 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 is a then returns the binding /// notification's . If not, returns the object unchanged. /// - 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; } /// @@ -171,7 +173,7 @@ namespace Avalonia.Data /// If is a then returns the binding /// notification's . If not, returns the object unchanged. /// - 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 /// /// The object to compare. /// true if the two instances are equal; otherwise false. - public override bool Equals(object obj) + public override bool Equals(object? obj) { return Equals(obj as BindingNotification); } @@ -191,7 +193,7 @@ namespace Avalonia.Data /// /// The value to compare. /// true if the two instances are equal; otherwise false. - public bool Equals(BindingNotification other) + public bool Equals(BindingNotification? other) { return this == other; } @@ -234,28 +236,28 @@ namespace Avalonia.Data /// /// Sets the . /// - public void SetValue(object value) + public void SetValue(object? value) { _value = value; } - public BindingValue ToBindingValue() + public BindingValue ToBindingValue() { if (ErrorType == BindingErrorType.None) { - return HasValue ? new BindingValue(Value) : BindingValue.Unset; + return HasValue ? new BindingValue(Value) : BindingValue.Unset; } else if (ErrorType == BindingErrorType.Error) { - return BindingValue.BindingError( - Error, - HasValue ? new Optional(Value) : Optional.Empty); + return BindingValue.BindingError( + Error!, + HasValue ? new Optional(Value) : Optional.Empty); } else { - return BindingValue.DataValidationError( - Error, - HasValue ? new Optional(Value) : Optional.Empty); + return BindingValue.DataValidationError( + Error!, + HasValue ? new Optional(Value) : Optional.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; diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs index 42f941da0c..05900de01d 100644 --- a/src/Avalonia.Base/Data/BindingOperations.cs +++ b/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(target != null); - Contract.Requires(property != null); - Contract.Requires(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; diff --git a/src/Avalonia.Base/Data/BindingPriority.cs b/src/Avalonia.Base/Data/BindingPriority.cs index 68b2912ffe..e4fb11cf06 100644 --- a/src/Avalonia.Base/Data/BindingPriority.cs +++ b/src/Avalonia.Base/Data/BindingPriority.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace Avalonia.Data { /// @@ -41,4 +43,4 @@ namespace Avalonia.Data /// Unset = int.MaxValue, } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Data/BindingValue.cs b/src/Avalonia.Base/Data/BindingValue.cs index cc6a2799af..8e90bed8bc 100644 --- a/src/Avalonia.Base/Data/BindingValue.cs +++ b/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 /// public readonly struct BindingValue { - [AllowNull] private readonly T _value; + private readonly T _value; /// /// Initializes a new instance of the struct with a type of /// /// /// The value. - 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 /// /// is false. /// - public T Value => HasValue ? _value : throw new InvalidOperationException("BindingValue has no value."); + public T Value => HasValue ? _value! : throw new InvalidOperationException("BindingValue has no value."); /// /// 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 or /// . /// - public BindingValue WithValue([AllowNull] T value) + public BindingValue 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. /// /// The value. - [return: MaybeNull] - public T GetValueOrDefault() => HasValue ? _value : default; + public T? GetValueOrDefault() => HasValue ? _value : default; /// /// Gets the value of the binding value if present, otherwise a default value. /// /// The default value. /// The value. - public T GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue; + public T? GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue; /// /// 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. /// - [return: MaybeNull] - public TResult GetValueOrDefault() + public TResult? GetValueOrDefault() { 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 if the /// value is not present. /// - [return: MaybeNull] - public TResult GetValueOrDefault([AllowNull] TResult defaultValue) + public TResult? GetValueOrDefault(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(), - _ => new BindingValue((T?)value) + _ => new BindingValue((T)value!) }; } @@ -255,7 +251,7 @@ namespace Avalonia.Data /// Creates a binding value from an instance of the underlying value type. /// /// The value. - public static implicit operator BindingValue([AllowNull] T value) => new BindingValue(value); + public static implicit operator BindingValue(T value) => new BindingValue(value); /// /// Creates a binding value from an . @@ -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 /// The target type. /// The binding value. /// The cast value. - public static BindingValue Cast(this BindingValue value) + public static BindingValue Cast(this BindingValue value) { return value.Type switch { BindingValueType.DoNothing => BindingValue.DoNothing, BindingValueType.UnsetValue => BindingValue.Unset, - BindingValueType.Value => new BindingValue((T)value.Value), + BindingValueType.Value => new BindingValue((T)value.Value!), BindingValueType.BindingError => BindingValue.BindingError(value.Error!), BindingValueType.BindingErrorWithFallback => BindingValue.BindingError( value.Error!, - (T)value.Value), + (T)value.Value!), BindingValueType.DataValidationError => BindingValue.DataValidationError(value.Error!), BindingValueType.DataValidationErrorWithFallback => BindingValue.DataValidationError( value.Error!, - (T)value.Value), + (T)value.Value!), _ => throw new NotSupportedException("Invalid BindingValue type."), }; } diff --git a/src/Avalonia.Base/Data/Converters/BoolConverters.cs b/src/Avalonia.Base/Data/Converters/BoolConverters.cs index 3985c5e32f..6a45ab8593 100644 --- a/src/Avalonia.Base/Data/Converters/BoolConverters.cs +++ b/src/Avalonia.Base/Data/Converters/BoolConverters.cs @@ -1,5 +1,7 @@ using System.Linq; +#nullable enable + namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index 83f7e02c16..f99ac1f81d 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/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 { /// @@ -24,7 +26,7 @@ namespace Avalonia.Data.Converters /// A user-defined parameter. /// The culture to use. /// The converted value. - 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 /// A user-defined parameter. /// The culture to use. /// The converted value. - 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); } diff --git a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs index 988cc123f9..85dc6fc4f4 100644 --- a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs +++ b/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 { /// @@ -13,24 +15,24 @@ namespace Avalonia.Data.Converters /// The output type. public class FuncMultiValueConverter : IMultiValueConverter { - private readonly Func, TOut> _convert; + private readonly Func, TOut> _convert; /// /// Initializes a new instance of the class. /// /// The convert function. - public FuncMultiValueConverter(Func, TOut> convert) + public FuncMultiValueConverter(Func, TOut> convert) { _convert = convert; } /// - public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture) { //standard OfType skip null values, even they are valid for the Type - static IEnumerable OfTypeWithDefaultSupport(IList list) + static IEnumerable OfTypeWithDefaultSupport(IList list) { - foreach (object obj in list) + foreach (var obj in list) { if (obj is TIn result) { diff --git a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs index 2385d4981c..fe71f1da1c 100644 --- a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs +++ b/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 { /// @@ -12,23 +14,23 @@ namespace Avalonia.Data.Converters /// The output type. public class FuncValueConverter : IValueConverter { - private readonly Func _convert; + private readonly Func _convert; /// /// Initializes a new instance of the class. /// /// The convert function. - public FuncValueConverter(Func convert) + public FuncValueConverter(Func convert) { _convert = convert; } /// - 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(value)) { - return _convert((TIn)value); + return _convert((TIn?)value); } else { @@ -37,7 +39,7 @@ namespace Avalonia.Data.Converters } /// - 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(); } diff --git a/src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs index 0468835c41..e21746f571 100644 --- a/src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs +++ b/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 { /// @@ -22,6 +24,6 @@ namespace Avalonia.Data.Converters /// . Any exception thrown will be treated as /// an application exception. /// - object Convert(IList values, Type targetType, object parameter, CultureInfo culture); + object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture); } } diff --git a/src/Avalonia.Base/Data/Converters/IValueConverter.cs b/src/Avalonia.Base/Data/Converters/IValueConverter.cs index f31f4d522e..c591dbda32 100644 --- a/src/Avalonia.Base/Data/Converters/IValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/IValueConverter.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#nullable enable + namespace Avalonia.Data.Converters { /// @@ -21,7 +23,7 @@ namespace Avalonia.Data.Converters /// a in an error state. Any exceptions thrown will be /// treated as an application exception. /// - object Convert(object value, Type targetType, object parameter, CultureInfo culture); + object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture); /// /// Converts a value. @@ -36,6 +38,6 @@ namespace Avalonia.Data.Converters /// a in an error state. Any exceptions thrown will be /// treated as an application exception. /// - object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture); + object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture); } } diff --git a/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs b/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs index 251dbb458d..ee83776220 100644 --- a/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs +++ b/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 canExecute; readonly Action 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 _listener = new WeakReference(null); + readonly WeakReference _listener = new WeakReference(null); readonly PropertyChangedEventHandler _handler; - internal WeakReference Source { get; } = new WeakReference(null); + internal WeakReference Source { get; } = new WeakReference(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); diff --git a/src/Avalonia.Base/Data/Converters/ObjectConverters.cs b/src/Avalonia.Base/Data/Converters/ObjectConverters.cs index 755d826dda..3794d0ca3b 100644 --- a/src/Avalonia.Base/Data/Converters/ObjectConverters.cs +++ b/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. /// public static readonly IValueConverter IsNull = - new FuncValueConverter(x => x is null); + new FuncValueConverter(x => x is null); /// /// A value converter that returns true if the input object is not null. /// public static readonly IValueConverter IsNotNull = - new FuncValueConverter(x => !(x is null)); + new FuncValueConverter(x => x is not null); } } diff --git a/src/Avalonia.Base/Data/Converters/StringConverters.cs b/src/Avalonia.Base/Data/Converters/StringConverters.cs index 4b0a845770..a13cef349d 100644 --- a/src/Avalonia.Base/Data/Converters/StringConverters.cs +++ b/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. /// public static readonly IValueConverter IsNullOrEmpty = - new FuncValueConverter(string.IsNullOrEmpty); + new FuncValueConverter(string.IsNullOrEmpty); /// /// A value converter that returns true if the input string is not null or empty. /// public static readonly IValueConverter IsNotNullOrEmpty = - new FuncValueConverter(x => !string.IsNullOrEmpty(x)); + new FuncValueConverter(x => !string.IsNullOrEmpty(x)); } } diff --git a/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs index b190f06be5..d66a2d2cdf 100644 --- a/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs +++ b/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 { /// @@ -17,18 +19,16 @@ namespace Avalonia.Data.Converters /// /// An optional inner converter to be called before the format takes place. /// - public StringFormatMultiValueConverter(string format, IMultiValueConverter inner) + public StringFormatMultiValueConverter(string format, IMultiValueConverter? inner) { - Contract.Requires(format != null); - - Format = format; + Format = format ?? throw new ArgumentNullException(nameof(format)); Inner = inner; } /// /// Gets an inner value converter which will be called before the string format takes place. /// - public IMultiValueConverter Inner { get; } + public IMultiValueConverter? Inner { get; } /// /// Gets the format string. @@ -36,7 +36,7 @@ namespace Avalonia.Data.Converters public string Format { get; } /// - public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture) { return Inner == null ? string.Format(culture, Format, values.ToArray()) diff --git a/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs b/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs index fc695762b8..1b3451370a 100644 --- a/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#nullable enable + namespace Avalonia.Data.Converters { /// @@ -15,18 +17,16 @@ namespace Avalonia.Data.Converters /// /// An optional inner converter to be called before the format takes place. /// - public StringFormatValueConverter(string format, IValueConverter inner) + public StringFormatValueConverter(string format, IValueConverter? inner) { - Contract.Requires(format != null); - - Format = format; + Format = format ?? throw new ArgumentNullException(nameof(format)); Inner = inner; } /// /// Gets an inner value converter which will be called before the string format takes place. /// - public IValueConverter Inner { get; } + public IValueConverter? Inner { get; } /// /// Gets the format string. @@ -34,14 +34,14 @@ namespace Avalonia.Data.Converters public string Format { get; } /// - 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); } /// - 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"); } diff --git a/src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs index 0a33eeb2c1..ce60e12ae1 100644 --- a/src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs +++ b/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 reference) + protected override void StartListeningCore(WeakReference reference) { - if (reference.TryGetTarget(out object target) && target is IAvaloniaObject obj) + if (reference.TryGetTarget(out var target) && target is IAvaloniaObject obj) { - _subscription = new AvaloniaPropertyObservable(obj, _property).Subscribe(ValueChanged); + _subscription = new AvaloniaPropertyObservable(obj, _property).Subscribe(ValueChanged); } else { diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index c4f61dfedb..3f7a9722b7 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/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 { /// /// Binds to an expression on an object using a type value converter to convert the values /// that are sent and received. /// - public class BindingExpression : LightweightObservableBase, ISubject, IDescription + public class BindingExpression : LightweightObservableBase, ISubject, 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 _value; + InnerListener? _innerListener; + WeakReference? _value; /// /// Initializes a new instance of the 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(inner != null); - Contract.Requires(targetType != null); - Contract.Requires(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 /// /// Gets a parameter to pass to . /// - public object ConverterParameter { get; } + public object? ConverterParameter { get; } /// - string IDescription.Description => _inner.Expression; + string? IDescription.Description => _inner.Expression; /// public void OnCompleted() @@ -115,7 +117,7 @@ namespace Avalonia.Data.Core } /// - 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 observer, bool first) { @@ -196,7 +196,7 @@ namespace Avalonia.Data.Core } /// - 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, IDisposable + public class InnerListener : IObserver, 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(converted); + _owner._value = converted is not null ? new WeakReference(converted) : null; _owner.PublishNext(converted); } } diff --git a/src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs b/src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs index f66411c2c2..f52940bc80 100644 --- a/src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs +++ b/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 _getter; - private readonly Action _setter; + private readonly Func? _getter; + private readonly Action? _setter; - public ClrPropertyInfo(string name, Func getter, Action setter, Type propertyType) + public ClrPropertyInfo(string name, Func? getter, Action? 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 CreateSetter(PropertyInfo info) + static Action? 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>( + return Expression.Lambda>( Expression.Call(Expression.Convert(target, info.DeclaringType), info.SetMethod, Expression.Convert(value, info.SetMethod.GetParameters()[0].ParameterType)), target, value) .Compile(); } - static Func CreateGetter(PropertyInfo info) + static Func? CreateGetter(PropertyInfo info) { if (info.GetMethod == null) return null; diff --git a/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs b/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs index da6f407d81..908beeca33 100644 --- a/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs +++ b/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace Avalonia.Data.Core { public static class CommonPropertyNames diff --git a/src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs b/src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs index 4e142fbee9..115f617270 100644 --- a/src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs +++ b/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 { diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index 84cbe32318..25b0102891 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/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 UnsetReference = - new WeakReference(AvaloniaProperty.UnsetValue); + protected static readonly WeakReference UnsetReference = + new WeakReference(AvaloniaProperty.UnsetValue); - protected static readonly WeakReference NullReference = - new WeakReference(null); + protected static readonly WeakReference NullReference = + new WeakReference(null); - private WeakReference _target = UnsetReference; - private Action _subscriber; + private WeakReference _target = UnsetReference; + private Action? _subscriber; private bool _listening; - protected WeakReference LastValue { get; private set; } + protected WeakReference? 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 Target + public WeakReference Target { get { return _target; } set { - Contract.Requires(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 subscriber) + public void Subscribe(Action subscriber) { if (_subscriber != null) { @@ -73,9 +75,9 @@ namespace Avalonia.Data.Core _subscriber = null; } - protected virtual void StartListeningCore(WeakReference reference) + protected virtual void StartListeningCore(WeakReference 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(value) : NullReference; + LastValue = value != null ? new WeakReference(value) : NullReference; if (Next != null) { @@ -112,7 +120,7 @@ namespace Avalonia.Data.Core } else { - LastValue = notification.Value != null ? new WeakReference(notification.Value) : NullReference; + LastValue = notification.Value != null ? new WeakReference(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 { diff --git a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs index 7ecaa278d7..9e38c8bfba 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs +++ b/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 { /// /// Observes and sets the value of an expression on an object. /// - public class ExpressionObserver : LightweightObservableBase, IDescription + public class ExpressionObserver : LightweightObservableBase, IDescription { /// /// 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 _value; - private IReadOnlyList _transformNodes; + private object? _root; + private IDisposable? _rootSubscription; + private WeakReference? _value; + private IReadOnlyList? _transformNodes; /// /// Initializes a new instance of the class. @@ -65,18 +67,13 @@ namespace Avalonia.Data.Core /// A description of the expression. /// 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(root); + _root = new WeakReference(root == AvaloniaProperty.UnsetValue ? null : root); } /// @@ -88,11 +85,11 @@ namespace Avalonia.Data.Core /// A description of the expression. /// public ExpressionObserver( - IObservable rootObservable, + IObservable rootObservable, ExpressionNode node, - string description) + string? description) { - Contract.Requires(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. /// public ExpressionObserver( - Func rootGetter, + Func rootGetter, ExpressionNode node, IObservable update, - string description) + string? description) { - Contract.Requires(rootGetter != null); - Contract.Requires(update != null); + _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter)); + Description = description; - _node = node; - _node.Target = new WeakReference(rootGetter()); + _node = node ?? throw new ArgumentNullException(nameof(rootGetter)); + _node.Target = new WeakReference(rootGetter()); _root = update.Select(x => rootGetter()); } @@ -133,10 +130,10 @@ namespace Avalonia.Data.Core /// A description of the expression. If null, 's string representation will be used. /// public static ExpressionObserver Create( - T root, + T? root, Expression> 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 rootObservable, Expression> expression, bool enableDataValidation = false, - string description = null) + string? description = null) { - Contract.Requires(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> expression, IObservable update, bool enableDataValidation = false, - string description = null) + string? description = null) { - Contract.Requires(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. /// - 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 /// /// Gets a description of the expression being observed. /// - public string Description { get; } + public string? Description { get; } /// /// Gets the expression being observed. /// - public string Expression { get; } + public string? Expression { get; } /// /// Gets the type of the expression result or null if the expression could not be /// evaluated. /// - public Type ResultType => (Leaf as SettableNode)?.PropertyType; + public Type? ResultType => (Leaf as SettableNode)?.PropertyType; /// /// Gets the leaf node. @@ -278,7 +276,7 @@ namespace Avalonia.Data.Core _node.Unsubscribe(); } - protected override void Subscribed(IObserver observer, bool first) + protected override void Subscribed(IObserver observer, bool first) { if (!first && _value != null && _value.TryGetTarget(out var value)) { @@ -296,21 +294,21 @@ namespace Avalonia.Data.Core if (_root is IObservable observable) { _rootSubscription = observable.Subscribe( - x => _node.Target = new WeakReference(x != AvaloniaProperty.UnsetValue ? x : null), + x => _node.Target = new WeakReference(x != AvaloniaProperty.UnsetValue ? x : null), x => PublishCompleted(), () => PublishCompleted()); } else { - _node.Target = (WeakReference)_root; + _node.Target = (WeakReference)_root!; } } - private void ValueChanged(object value) + private void ValueChanged(object? value) { var broken = BindingNotification.ExtractError(value) as MarkupBindingChainException; - broken?.Commit(Description); - _value = new WeakReference(value); + broken?.Commit(Description ?? "{empty}"); + _value = new WeakReference(value); PublishNext(value); } } diff --git a/src/Avalonia.Base/Data/Core/ExpressionParseException.cs b/src/Avalonia.Base/Data/Core/ExpressionParseException.cs index b0d5c21dab..8fd7066a7b 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionParseException.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionParseException.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core { /// @@ -17,7 +19,7 @@ namespace Avalonia.Data.Core /// The column position of the error. /// The exception message. /// The exception that caused the parsing failure. - public ExpressionParseException(int column, string message, Exception innerException = null) + public ExpressionParseException(int column, string message, Exception? innerException = null) : base(message, innerException) { Column = column; diff --git a/src/Avalonia.Base/Data/Core/ITransformNode.cs b/src/Avalonia.Base/Data/Core/ITransformNode.cs index 843d09fc19..49c1dea7b9 100644 --- a/src/Avalonia.Base/Data/Core/ITransformNode.cs +++ b/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); } } diff --git a/src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs b/src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs index a3852cc371..22a58a245b 100644 --- a/src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs +++ b/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?; } diff --git a/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs b/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs index 47d5147ac2..ef4375a02c 100644 --- a/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs +++ b/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 reference) + protected override void StartListeningCore(WeakReference 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>(); + var inputs = new List>(); 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); } } diff --git a/src/Avalonia.Base/Data/Core/LogicalNotNode.cs b/src/Avalonia.Base/Data/Core/LogicalNotNode.cs index 6d09befeba..7ccec51e1a 100644 --- a/src/Avalonia.Base/Data/Core/LogicalNotNode.cs +++ b/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) diff --git a/src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs b/src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs index a9b31c7617..2dbeae41ae 100644 --- a/src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs +++ b/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 _nodes = new List(); + private IList? _nodes = new List(); 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) { diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs index d2035a592f..34409a3e36 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs +++ b/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 diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs index bcb6eeca41..caba0c2e78 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs +++ b/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 diff --git a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs index c1e8eb115d..b72989ce00 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/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 { /// @@ -28,12 +30,14 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// - public IPropertyAccessor Start(WeakReference reference, string propertyName) + public IPropertyAccessor? Start(WeakReference reference, string propertyName) { - Contract.Requires(reference != null); - Contract.Requires(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 + private class Accessor : PropertyAccessorBase, IObserver { private readonly WeakReference _reference; private readonly AvaloniaProperty _property; - private IDisposable _subscription; + private IDisposable? _subscription; public Accessor(WeakReference reference, AvaloniaProperty property) { - Contract.Requires(reference != null); - Contract.Requires(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.OnCompleted() + void IObserver.OnCompleted() { } - void IObserver.OnError(Exception error) + void IObserver.OnError(Exception error) { ExceptionDispatchInfo.Capture(error).Throw(); } - void IObserver.OnNext(object value) + void IObserver.OnNext(object? value) { PublishValue(value); } diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs index f052283b22..4d973d4aaa 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs +++ b/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 { /// @@ -12,9 +14,9 @@ namespace Avalonia.Data.Core.Plugins public class DataAnnotationsValidationPlugin : IDataValidationPlugin { /// - public bool Match(WeakReference reference, string memberName) + public bool Match(WeakReference 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 } /// - public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor inner) + public IPropertyAccessor Start(WeakReference 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 reference, string name, IPropertyAccessor inner) + public Accessor(WeakReference 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(); diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs b/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs index 63e2c3a97b..3e98d26fa1 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -11,7 +13,7 @@ namespace Avalonia.Data.Core.Plugins /// and convert any values received from the inner property accessor into /// s. /// - public abstract class DataValidationBase : PropertyAccessorBase, IObserver + public abstract class DataValidationBase : PropertyAccessorBase, IObserver { private readonly IPropertyAccessor _inner; @@ -25,31 +27,31 @@ namespace Avalonia.Data.Core.Plugins } /// - public override Type PropertyType => _inner.PropertyType; + public override Type? PropertyType => _inner.PropertyType; /// - public override object Value => _inner.Value; + public override object? Value => _inner.Value; /// - public override bool SetValue(object value, BindingPriority priority) => _inner.SetValue(value, priority); + public override bool SetValue(object? value, BindingPriority priority) => _inner.SetValue(value, priority); /// /// Should never be called: the inner should never notify /// completion. /// - void IObserver.OnCompleted() { } + void IObserver.OnCompleted() { } /// /// Should never be called: the inner should never notify /// an error. /// - void IObserver.OnError(Exception error) { } + void IObserver.OnError(Exception error) { } /// /// Called when the inner notifies with a new value. /// /// The value. - void IObserver.OnNext(object value) => InnerValueChanged(value); + void IObserver.OnNext(object? value) => InnerValueChanged(value); /// /// Begins listening to the inner . @@ -67,7 +69,7 @@ namespace Avalonia.Data.Core.Plugins /// Notifies the observer that the value has changed. The value will be wrapped in a /// if it is not already a binding notification. /// - protected virtual void InnerValueChanged(object value) + protected virtual void InnerValueChanged(object? value) { var notification = value as BindingNotification ?? new BindingNotification(value); PublishValue(notification); diff --git a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs index f7ab48943a..95855b09f4 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs @@ -1,6 +1,8 @@ using System; using System.Reflection; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -9,22 +11,22 @@ namespace Avalonia.Data.Core.Plugins public class ExceptionValidationPlugin : IDataValidationPlugin { /// - public bool Match(WeakReference reference, string memberName) => true; + public bool Match(WeakReference reference, string memberName) => true; /// - public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor inner) + public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor inner) { return new Validator(reference, name, inner); } private sealed class Validator : DataValidationBase { - public Validator(WeakReference reference, string name, IPropertyAccessor inner) + public Validator(WeakReference reference, string name, IPropertyAccessor inner) : base(inner) { } - public override bool SetValue(object value, BindingPriority priority) + public override bool SetValue(object? value, BindingPriority priority) { try { diff --git a/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs index 2a580fe75f..549327bc0f 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -13,7 +15,7 @@ namespace Avalonia.Data.Core.Plugins /// A weak reference to the object. /// The name of the member to validate. /// True if the plugin can handle the object; otherwise false. - bool Match(WeakReference reference, string memberName); + bool Match(WeakReference reference, string memberName); /// /// Starts monitoring the data validation state of a property on an object. @@ -25,7 +27,7 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// - IPropertyAccessor Start(WeakReference reference, + IPropertyAccessor Start(WeakReference reference, string propertyName, IPropertyAccessor inner); } diff --git a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs index 688fb8cdc2..59017b5aae 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -14,12 +16,12 @@ namespace Avalonia.Data.Core.Plugins /// /// The accessor has not been subscribed to yet. /// - Type PropertyType { get; } + Type? PropertyType { get; } /// /// Gets the current value of the property. /// - object Value { get; } + object? Value { get; } /// /// Sets the property value. @@ -33,13 +35,13 @@ namespace Avalonia.Data.Core.Plugins /// /// True if the property was set; false if the property could not be set. /// - bool SetValue(object value, BindingPriority priority); + bool SetValue(object? value, BindingPriority priority); /// /// Subscribes to the value of the member. /// /// A method that receives the values. - void Subscribe(Action listener); + void Subscribe(Action listener); /// /// Unsubscribes to the value of the member. diff --git a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs index b176225fba..4a391e8489 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -25,7 +27,7 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// - IPropertyAccessor Start(WeakReference reference, + IPropertyAccessor? Start(WeakReference reference, string propertyName); } } diff --git a/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs index 514e6f430a..543bcba601 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -12,7 +14,7 @@ namespace Avalonia.Data.Core.Plugins /// /// A weak reference to the value. /// True if the plugin can handle the value; otherwise false. - bool Match(WeakReference reference); + bool Match(WeakReference reference); /// /// Starts producing output based on the specified value. @@ -21,6 +23,6 @@ namespace Avalonia.Data.Core.Plugins /// /// An observable that produces the output for the value. /// - IObservable Start(WeakReference reference); + IObservable Start(WeakReference reference); } } diff --git a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs index 7be2a6db86..7b37b5d4e7 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs +++ b/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 { /// @@ -12,25 +14,25 @@ namespace Avalonia.Data.Core.Plugins public class IndeiValidationPlugin : IDataValidationPlugin { /// - public bool Match(WeakReference reference, string memberName) + public bool Match(WeakReference reference, string memberName) { - reference.TryGetTarget(out object target); + reference.TryGetTarget(out var target); return target is INotifyDataErrorInfo; } /// - public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor accessor) + public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor accessor) { return new Validator(reference, name, accessor); } private class Validator : DataValidationBase, IWeakSubscriber { - private readonly WeakReference _reference; + private readonly WeakReference _reference; private readonly string _name; - public Validator(WeakReference reference, string name, IPropertyAccessor inner) + public Validator(WeakReference 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() @@ -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; } diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index d600603d5c..d5bf8da029 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/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 { /// @@ -12,8 +14,8 @@ namespace Avalonia.Data.Core.Plugins /// 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?>(); /// public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null; @@ -27,12 +29,13 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// - public IPropertyAccessor Start(WeakReference reference, string propertyName) + public IPropertyAccessor? Start(WeakReference reference, string propertyName) { - Contract.Requires(reference != null); - Contract.Requires(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 { - private readonly WeakReference _reference; + private readonly WeakReference _reference; private readonly PropertyInfo _property; private bool _eventRaised; - public Accessor(WeakReference reference, PropertyInfo property) + public Accessor(WeakReference reference, PropertyInfo property) { - Contract.Requires(reference != null); - Contract.Requires(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; } diff --git a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs index 5d694f4cf9..e0d686586c 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs +++ b/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 reference, string methodName) + public IPropertyAccessor? Start(WeakReference reference, string methodName) { - Contract.Requires(reference != null); - Contract.Requires(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 reference, MethodInfo method, ParameterInfo[] parameters) + public Accessor(WeakReference reference, MethodInfo method, ParameterInfo[] parameters) { - Contract.Requires(reference != null); - Contract.Requires(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() { diff --git a/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs index 33a1d02211..7d77adfb00 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs +++ b/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 { /// @@ -10,16 +12,16 @@ namespace Avalonia.Data.Core.Plugins /// public class ObservableStreamPlugin : IStreamPlugin { - static MethodInfo observableSelect; + static MethodInfo? observableSelect; /// /// Checks whether this plugin handles the specified value. /// /// A weak reference to the value. /// True if the plugin can handle the value; otherwise false. - public virtual bool Match(WeakReference reference) + public virtual bool Match(WeakReference 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 /// /// An observable that produces the output for the value. /// - public virtual IObservable Start(WeakReference reference) + public virtual IObservable Start(WeakReference reference) { - reference.TryGetTarget(out object target); + if (!reference.TryGetTarget(out var target) || target is null) + return Observable.Empty(); // If the observable returns a reference type then we can cast it. - if (target is IObservable result) + if (target is IObservable result) { return result; }; @@ -98,6 +101,6 @@ namespace Avalonia.Data.Core.Plugins return observableSelect; } - private static object Box(T value) => (object)value; + private static object? Box(T value) => (object?)value; } } diff --git a/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs b/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs index 59e8e01a44..ba3fca1ab5 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -7,13 +9,13 @@ namespace Avalonia.Data.Core.Plugins /// public abstract class PropertyAccessorBase : IPropertyAccessor { - private Action _listener; + private Action? _listener; /// - public abstract Type PropertyType { get; } + public abstract Type? PropertyType { get; } /// - public abstract object Value { get; } + public abstract object? Value { get; } /// public void Dispose() @@ -25,12 +27,12 @@ namespace Avalonia.Data.Core.Plugins } /// - public abstract bool SetValue(object value, BindingPriority priority); + public abstract bool SetValue(object? value, BindingPriority priority); /// - public void Subscribe(Action listener) + public void Subscribe(Action listener) { - Contract.Requires(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. /// /// The value. - protected void PublishValue(object value) => _listener?.Invoke(value); + protected void PublishValue(object? value) => _listener?.Invoke(value); /// /// When overridden in a derived class, begins listening to the member. diff --git a/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs b/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs index eb2400807a..c2a3214c0d 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data.Core.Plugins { /// @@ -19,10 +21,10 @@ namespace Avalonia.Data.Core.Plugins } /// - public Type PropertyType => null; + public Type? PropertyType => null; /// - public object Value => _error; + public object? Value => _error; /// public void Dispose() @@ -30,7 +32,7 @@ namespace Avalonia.Data.Core.Plugins } /// - public bool SetValue(object value, BindingPriority priority) + public bool SetValue(object? value, BindingPriority priority) { return false; } diff --git a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs index debf050a97..d1a3568a55 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs +++ b/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 { /// @@ -16,9 +18,9 @@ namespace Avalonia.Data.Core.Plugins /// /// A weak reference to the value. /// True if the plugin can handle the value; otherwise false. - public virtual bool Match(WeakReference reference) + public virtual bool Match(WeakReference reference) { - reference.TryGetTarget(out object target); + reference.TryGetTarget(out var target); return target is Task; } @@ -30,9 +32,9 @@ namespace Avalonia.Data.Core.Plugins /// /// An observable that produces the output for the value. /// - public virtual IObservable Start(WeakReference reference) + public virtual IObservable Start(WeakReference 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(); + var subject = new Subject(); task.ContinueWith( x => HandleCompleted(task).Subscribe(subject), TaskScheduler.FromCurrentSynchronizationContext()) @@ -56,10 +58,10 @@ namespace Avalonia.Data.Core.Plugins } } - return Observable.Empty(); + return Observable.Empty(); } - private IObservable HandleCompleted(Task task) + private IObservable HandleCompleted(Task task) { var resultProperty = task.GetType().GetRuntimeProperty("Result"); diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index d5e835cabd..92c3b65307 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/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 reference) + protected override void StartListeningCore(WeakReference 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; } } diff --git a/src/Avalonia.Base/Data/Core/PropertyPath.cs b/src/Avalonia.Base/Data/Core/PropertyPath.cs index 665953c4a1..4ec402d7e1 100644 --- a/src/Avalonia.Base/Data/Core/PropertyPath.cs +++ b/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 diff --git a/src/Avalonia.Base/Data/Core/SettableNode.cs b/src/Avalonia.Base/Data/Core/SettableNode.cs index 363d3da0ef..3467a16123 100644 --- a/src/Avalonia.Base/Data/Core/SettableNode.cs +++ b/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; } } } diff --git a/src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs b/src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs index fa2929c6ef..75fe37f958 100644 --- a/src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs +++ b/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 diff --git a/src/Avalonia.Base/Data/Core/StreamNode.cs b/src/Avalonia.Base/Data/Core/StreamNode.cs index e868b71fcd..086fbb7ee2 100644 --- a/src/Avalonia.Base/Data/Core/StreamNode.cs +++ b/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 reference) + protected override void StartListeningCore(WeakReference reference) { _subscription = GetPlugin(reference)?.Start(reference).Subscribe(ValueChanged); } @@ -29,7 +31,7 @@ namespace Avalonia.Data.Core _subscription = null; } - private IStreamPlugin GetPlugin(WeakReference reference) + private IStreamPlugin? GetPlugin(WeakReference reference) { if (_customPlugin != null) { diff --git a/src/Avalonia.Base/Data/Core/TypeCastNode.cs b/src/Avalonia.Base/Data/Core/TypeCastNode.cs index 476fd5527f..2404c90fe2 100644 --- a/src/Avalonia.Base/Data/Core/TypeCastNode.cs +++ b/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 reference) + protected override void StartListeningCore(WeakReference reference) { - if (reference.TryGetTarget(out object target)) + if (reference.TryGetTarget(out var target)) { target = Cast(target); - reference = target == null ? NullReference : new WeakReference(target); + reference = target == null ? NullReference : new WeakReference(target); } base.StartListeningCore(reference); diff --git a/src/Avalonia.Base/Data/DataValidationException.cs b/src/Avalonia.Base/Data/DataValidationException.cs index e38e6c30ad..cafe74cd2c 100644 --- a/src/Avalonia.Base/Data/DataValidationException.cs +++ b/src/Avalonia.Base/Data/DataValidationException.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Data { /// @@ -11,11 +13,11 @@ namespace Avalonia.Data /// Initializes a new instance of the class. /// /// Data of validation error. - public DataValidationException(object errorData) : base(errorData?.ToString()) + public DataValidationException(object? errorData) : base(errorData?.ToString()) { ErrorData = errorData; } - public object ErrorData { get; } + public object? ErrorData { get; } } } diff --git a/src/Avalonia.Base/Data/IBinding.cs b/src/Avalonia.Base/Data/IBinding.cs index bc91cf8960..3b4add0269 100644 --- a/src/Avalonia.Base/Data/IBinding.cs +++ b/src/Avalonia.Base/Data/IBinding.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace Avalonia.Data { /// @@ -20,10 +22,10 @@ namespace Avalonia.Data /// /// A or null if the binding could not be resolved. /// - InstancedBinding Initiate( + InstancedBinding? Initiate( IAvaloniaObject target, - AvaloniaProperty targetProperty, - object anchor = null, + AvaloniaProperty? targetProperty, + object? anchor = null, bool enableDataValidation = false); } } diff --git a/src/Avalonia.Base/Data/IndexerBinding.cs b/src/Avalonia.Base/Data/IndexerBinding.cs index 4d07b53dd2..bc8fe85a70 100644 --- a/src/Avalonia.Base/Data/IndexerBinding.cs +++ b/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); diff --git a/src/Avalonia.Base/Data/IndexerDescriptor.cs b/src/Avalonia.Base/Data/IndexerDescriptor.cs index 3cf6767b89..3c02509063 100644 --- a/src/Avalonia.Base/Data/IndexerDescriptor.cs +++ b/src/Avalonia.Base/Data/IndexerDescriptor.cs @@ -1,12 +1,14 @@ using System; using System.Reactive; +#nullable enable + namespace Avalonia.Data { /// /// Holds a description of a binding for 's [] operator. /// - public class IndexerDescriptor : ObservableBase, IDescription + public class IndexerDescriptor : ObservableBase, IDescription { /// /// Gets or sets the binding mode. @@ -29,7 +31,7 @@ namespace Avalonia.Data /// /// Gets or sets the source property. /// - public AvaloniaProperty Property + public AvaloniaProperty? Property { get; set; @@ -38,7 +40,7 @@ namespace Avalonia.Data /// /// Gets or sets the source object. /// - public AvaloniaObject Source + public AvaloniaObject? Source { get; set; @@ -50,7 +52,7 @@ namespace Avalonia.Data /// /// If null, then . will be used. /// - public IObservable SourceObservable + public IObservable? SourceObservable { get; set; @@ -59,7 +61,7 @@ namespace Avalonia.Data /// /// Gets a description of the binding. /// - public string Description => $"{Source?.GetType().Name}.{Property.Name}"; + public string Description => $"{Source?.GetType().Name}.{Property?.Name}"; /// /// Makes a two-way binding. @@ -104,9 +106,14 @@ namespace Avalonia.Data } /// - protected override IDisposable SubscribeCore(IObserver observer) + protected override IDisposable SubscribeCore(IObserver 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); } } } diff --git a/src/Avalonia.Base/Data/InstancedBinding.cs b/src/Avalonia.Base/Data/InstancedBinding.cs index 0d20f25fe2..dd7fcb3007 100644 --- a/src/Avalonia.Base/Data/InstancedBinding.cs +++ b/src/Avalonia.Base/Data/InstancedBinding.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Subjects; +#nullable enable + namespace Avalonia.Data { /// @@ -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. /// - public InstancedBinding(ISubject subject, BindingMode mode, BindingPriority priority) + public InstancedBinding(ISubject subject, BindingMode mode, BindingPriority priority) { Contract.Requires(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 /// /// Gets the value or source of the binding. /// - public object Value { get; } + public object? Value { get; } /// /// Gets the as an observable. /// - public IObservable Observable => Value as IObservable; + public IObservable? Observable => Value as IObservable; /// /// Gets the as a subject. /// - public ISubject Subject => Value as ISubject; + public ISubject? Subject => Value as ISubject; /// /// Creates a new one-time binding with a fixed value. @@ -87,10 +89,10 @@ namespace Avalonia.Data /// The priority of the binding. /// An instance. public static InstancedBinding OneTime( - IObservable observable, + IObservable observable, BindingPriority priority = BindingPriority.LocalValue) { - Contract.Requires(observable != null); + _ = observable ?? throw new ArgumentNullException(nameof(observable)); return new InstancedBinding(observable, BindingMode.OneTime, priority); } @@ -102,10 +104,10 @@ namespace Avalonia.Data /// The priority of the binding. /// An instance. public static InstancedBinding OneWay( - IObservable observable, + IObservable observable, BindingPriority priority = BindingPriority.LocalValue) { - Contract.Requires(observable != null); + _ = observable ?? throw new ArgumentNullException(nameof(observable)); return new InstancedBinding(observable, BindingMode.OneWay, priority); } @@ -117,10 +119,10 @@ namespace Avalonia.Data /// The priority of the binding. /// An instance. public static InstancedBinding OneWayToSource( - ISubject subject, + ISubject subject, BindingPriority priority = BindingPriority.LocalValue) { - Contract.Requires(subject != null); + _ = subject ?? throw new ArgumentNullException(nameof(subject)); return new InstancedBinding(subject, BindingMode.OneWayToSource, priority); } @@ -132,10 +134,10 @@ namespace Avalonia.Data /// The priority of the binding. /// An instance. public static InstancedBinding TwoWay( - ISubject subject, + ISubject subject, BindingPriority priority = BindingPriority.LocalValue) { - Contract.Requires(subject != null); + _ = subject ?? throw new ArgumentNullException(nameof(subject)); return new InstancedBinding(subject, BindingMode.TwoWay, priority); } diff --git a/src/Avalonia.Base/Data/Optional.cs b/src/Avalonia.Base/Data/Optional.cs index 9dec399e35..d2283bf66b 100644 --- a/src/Avalonia.Base/Data/Optional.cs +++ b/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 /// public readonly struct Optional : IEquatable> { - [AllowNull] private readonly T _value; + private readonly T _value; /// /// Initializes a new instance of the struct with value. /// /// The value. - 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 . /// /// The cast optional value. - public Optional ToObject() => HasValue ? new Optional(_value) : default; + public Optional ToObject() => HasValue ? new Optional(_value) : default; /// 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. /// /// The value. - [return: MaybeNull] - public T GetValueOrDefault() => HasValue ? _value : default; + public T? GetValueOrDefault() => HasValue ? _value : default; /// /// Gets the value if present, otherwise a default value. /// /// The default value. /// The value. - public T GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue; + public T? GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue; /// /// 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. /// - [return: MaybeNull] - public TResult GetValueOrDefault() + public TResult? GetValueOrDefault() { 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 if the /// value is not present. /// - [return: MaybeNull] - public TResult GetValueOrDefault([AllowNull] TResult defaultValue) + public TResult? GetValueOrDefault(TResult defaultValue) { return HasValue ? _value is TResult result ? result : default @@ -116,7 +112,7 @@ namespace Avalonia.Data /// Creates an from an instance of the underlying value type. /// /// The value. - public static implicit operator Optional([AllowNull] T value) => new Optional(value); + public static implicit operator Optional(T value) => new Optional(value); /// /// Compares two s for inequality. @@ -162,9 +158,9 @@ namespace Avalonia.Data /// The target type. /// The binding value. /// The cast value. - public static Optional Cast(this Optional value) + public static Optional Cast(this Optional value) { - return value.HasValue ? new Optional((T)value.Value) : Optional.Empty; + return value.HasValue ? new Optional((T)value.Value!) : Optional.Empty; } } } diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs index 362736eb06..315e28f84a 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs @@ -44,7 +44,7 @@ namespace Avalonia.PropertyStore public StyledPropertyBase Property { get; } public BindingPriority Priority { get; private set; } public IObservable> Source { get; } - Optional IValue.GetValue() => _value.ToObject(); + Optional IValue.GetValue() => _value.ToObject(); public void BeginBatchUpdate() => _batchUpdate = true; @@ -121,8 +121,8 @@ namespace Avalonia.PropertyStore IValueSink sink, IAvaloniaObject owner, AvaloniaProperty property, - Optional oldValue, - Optional newValue) + Optional oldValue, + Optional newValue) { sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, diff --git a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs b/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs index dc4a1d88c1..7e904f57df 100644 --- a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs @@ -25,7 +25,19 @@ namespace Avalonia.PropertyStore public ConstantValueEntry( StyledPropertyBase property, - [AllowNull] T value, + T value, + BindingPriority priority, + IValueSink sink) + { + Property = property; + _value = value; + Priority = priority; + _sink = sink; + } + + public ConstantValueEntry( + StyledPropertyBase property, + Optional value, BindingPriority priority, IValueSink sink) { @@ -37,7 +49,7 @@ namespace Avalonia.PropertyStore public StyledPropertyBase Property { get; } public BindingPriority Priority { get; private set; } - Optional IValue.GetValue() => _value.ToObject(); + Optional IValue.GetValue() => _value.ToObject(); public Optional GetValue(BindingPriority maxPriority = BindingPriority.Animation) { @@ -59,8 +71,8 @@ namespace Avalonia.PropertyStore IValueSink sink, IAvaloniaObject owner, AvaloniaProperty property, - Optional oldValue, - Optional newValue) + Optional oldValue, + Optional newValue) { sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, diff --git a/src/Avalonia.Base/PropertyStore/IValue.cs b/src/Avalonia.Base/PropertyStore/IValue.cs index 7f5245bb45..bbf4739e6a 100644 --- a/src/Avalonia.Base/PropertyStore/IValue.cs +++ b/src/Avalonia.Base/PropertyStore/IValue.cs @@ -10,14 +10,14 @@ namespace Avalonia.PropertyStore internal interface IValue { BindingPriority Priority { get; } - Optional GetValue(); + Optional GetValue(); void Start(); void RaiseValueChanged( IValueSink sink, IAvaloniaObject owner, AvaloniaProperty property, - Optional oldValue, - Optional newValue); + Optional oldValue, + Optional newValue); } /// diff --git a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs index 8fe2ad7794..4afc90e71d 100644 --- a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs @@ -12,11 +12,11 @@ namespace Avalonia.PropertyStore /// The property type. internal class LocalValueEntry : IValue { - [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 IValue.GetValue() => new Optional(_value); + Optional IValue.GetValue() => new Optional(_value); public Optional GetValue(BindingPriority maxPriority) { @@ -30,8 +30,8 @@ namespace Avalonia.PropertyStore IValueSink sink, IAvaloniaObject owner, AvaloniaProperty property, - Optional oldValue, - Optional newValue) + Optional oldValue, + Optional newValue) { sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs index 556f1a6269..f5723cb3a7 100644 --- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs +++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs @@ -90,7 +90,7 @@ namespace Avalonia.PropertyStore public StyledPropertyBase Property { get; } public BindingPriority Priority { get; private set; } = BindingPriority.Unset; public IReadOnlyList> Entries => _entries; - Optional IValue.GetValue() => _value.ToObject(); + Optional IValue.GetValue() => _value.ToObject(); public void BeginBatchUpdate() { @@ -191,8 +191,8 @@ namespace Avalonia.PropertyStore IValueSink sink, IAvaloniaObject owner, AvaloniaProperty property, - Optional oldValue, - Optional newValue) + Optional oldValue, + Optional newValue) { sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs index 495f13e1a9..9f036fbd34 100644 --- a/src/Avalonia.Base/ValueStore.cs +++ b/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(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(property, default, BindingPriority.Unset, _sink); + var sentinel = new ConstantValueEntry(property, Optional.Empty, BindingPriority.Unset, _sink); _values.SetValue(property, sentinel); } @@ -479,7 +480,7 @@ namespace Avalonia return true; } - public void ValueChanged(AvaloniaProperty property, Optional oldValue) + public void ValueChanged(AvaloniaProperty property, Optional oldValue) { _notifications ??= new List(); @@ -506,7 +507,7 @@ namespace Avalonia private struct Notification { public AvaloniaProperty property; - public Optional oldValue; + public Optional oldValue; } } } diff --git a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs b/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs index f187199c00..edd3fb7d48 100644 --- a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs +++ b/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs @@ -19,7 +19,7 @@ namespace Avalonia.Styling private readonly IStyleable _target; private readonly StyledPropertyBase? _styledProperty; private readonly DirectPropertyBase? _directProperty; - private readonly InstancedBinding _binding; + private readonly InstancedBinding? _binding; private readonly Inner _inner; private BindingValue _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>.OnNext(BindingValue 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 { diff --git a/src/Avalonia.Styling/Styling/PropertySetterInstance.cs b/src/Avalonia.Styling/Styling/PropertySetterInstance.cs index bb0e342df6..48f462d006 100644 --- a/src/Avalonia.Styling/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Styling/Styling/PropertySetterInstance.cs @@ -16,14 +16,14 @@ namespace Avalonia.Styling private readonly IStyleable _target; private readonly StyledPropertyBase? _styledProperty; private readonly DirectPropertyBase? _directProperty; - private readonly T? _value; + private readonly T _value; private IDisposable? _subscription; private bool _isActive; public PropertySetterInstance( IStyleable target, StyledPropertyBase property, - T? value) + T value) { _target = target; _styledProperty = property; diff --git a/src/Avalonia.Styling/Styling/Setter.cs b/src/Avalonia.Styling/Styling/Setter.cs index 4c94ea02dd..168a882499 100644 --- a/src/Avalonia.Styling/Styling/Setter.cs +++ b/src/Avalonia.Styling/Styling/Setter.cs @@ -101,7 +101,7 @@ namespace Avalonia.Styling data.result = new PropertySetterInstance( data.target, property, - (T?)data.value); + (T)data.value!); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 95380be65f..087611bd59 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/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? GetConverter(AvaloniaProperty targetProperty) + private Func? GetConverter(AvaloniaProperty? targetProperty) { if (targetProperty?.PropertyType == typeof(IBrush)) { diff --git a/src/Markup/Avalonia.Markup/Data/Binding.cs b/src/Markup/Avalonia.Markup/Data/Binding.cs index 50be598d7f..c56afbfedf 100644 --- a/src/Markup/Avalonia.Markup/Data/Binding.cs +++ b/src/Markup/Avalonia.Markup/Data/Binding.cs @@ -61,7 +61,7 @@ namespace Avalonia.Data /// public Func? 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)); diff --git a/src/Markup/Avalonia.Markup/Data/BindingBase.cs b/src/Markup/Avalonia.Markup/Data/BindingBase.cs index c25ef49167..e69d9570e2 100644 --- a/src/Markup/Avalonia.Markup/Data/BindingBase.cs +++ b/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); /// - 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( diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs index 17b033f238..c331769601 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs @@ -66,9 +66,9 @@ namespace Avalonia.Data } /// - 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(values); - object converted; + object? converted; if (converter != null) { converted = converter.Convert(values, targetType, ConverterParameter, culture); diff --git a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs index 2aada7c5b6..8c33a7b721 100644 --- a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs @@ -30,9 +30,9 @@ namespace Avalonia.Data } /// - 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); } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs index ebdad881c3..a1b701f489 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs +++ b/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? typeResolver = null, + internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func? typeResolver = null, INameScope? nameScope = null) { if (string.IsNullOrWhiteSpace(expression)) diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs index 0a6f3f82ba..297eef9e80 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs +++ b/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); } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs index d6068a15d4..f9a3a61736 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs +++ b/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 reference) + protected override void StartListeningCore(WeakReference reference) { if (_nameScope.TryGetTarget(out var scope)) _subscription = NameScopeLocator.Track(scope, _name).Subscribe(ValueChanged); diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs index 4124032113..697612ca12 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs @@ -31,9 +31,9 @@ namespace Avalonia.Markup.Parsers.Nodes } } - protected override void StartListeningCore(WeakReference reference) + protected override void StartListeningCore(WeakReference 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); } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs index 2f1756fc55..11b0df2f35 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs +++ b/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; } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 648a3c6a34..3b2b99fb0c 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/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 diff --git a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs b/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs index 8f62163d81..2a1628ea7d 100644 --- a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs +++ b/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 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); } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index b7fc35db72..0a031cd5bf 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/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); }