diff --git a/src/Avalonia.Base/Metadata/NullableAttributes.cs b/src/Avalonia.Base/Metadata/NullableAttributes.cs
index 91f5e81863..b6f0f3a47c 100644
--- a/src/Avalonia.Base/Metadata/NullableAttributes.cs
+++ b/src/Avalonia.Base/Metadata/NullableAttributes.cs
@@ -1,6 +1,5 @@
#pragma warning disable MA0048 // File name must match type name
#define INTERNAL_NULLABLE_ATTRIBUTES
-#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
// https://github.com/dotnet/corefx/blob/48363ac826ccf66fbe31a5dcb1dc2aab9a7dd768/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
@@ -10,6 +9,7 @@
namespace System.Diagnostics.CodeAnalysis
{
+#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
/// Specifies that null is allowed as an input even if the corresponding type disallows it.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
@@ -136,5 +136,82 @@ namespace System.Diagnostics.CodeAnalysis
/// Gets the condition parameter value.
public bool ParameterValue { get; }
}
-}
+#endif // NETSTANDARD2_0 attributes
+
+#if NETSTANDARD2_1 || NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_1 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
+ ///
+ /// Specifies that the method or property will ensure that the listed field and property members have
+ /// not- values.
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
#endif
+ sealed class MemberNotNullAttribute : Attribute
+ {
+ /// Gets field or property member names.
+ public string[] Members { get; }
+
+ /// Initializes the attribute with a field or property member.
+ /// The field or property member that is promised to be not-null.
+ public MemberNotNullAttribute(string member)
+ {
+ Members = new[] { member };
+ }
+
+ /// Initializes the attribute with the list of field and property members.
+ /// The list of field and property members that are promised to be not-null.
+ public MemberNotNullAttribute(params string[] members)
+ {
+ Members = members;
+ }
+ }
+
+ ///
+ /// Specifies that the method or property will ensure that the listed field and property members have
+ /// non- values when returning with the specified return value condition.
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class MemberNotNullWhenAttribute : Attribute
+ {
+ /// Gets the return value condition.
+ public bool ReturnValue { get; }
+
+ /// Gets field or property member names.
+ public string[] Members { get; }
+
+ /// Initializes the attribute with the specified return value condition and a field or property member.
+ ///
+ /// The return value condition. If the method returns this value,
+ /// the associated parameter will not be .
+ ///
+ /// The field or property member that is promised to be not-.
+ public MemberNotNullWhenAttribute(bool returnValue, string member)
+ {
+ ReturnValue = returnValue;
+ Members = new[] { member };
+ }
+
+ /// Initializes the attribute with the specified return value condition and list of field and property members.
+ ///
+ ///
+ /// The return value condition. If the method returns this value,
+ /// the associated parameter will not be .
+ ///
+ /// The list of field and property members that are promised to be not-null.
+ public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
+ {
+ ReturnValue = returnValue;
+ Members = members;
+ }
+ }
+#endif // NETSTANDARD2_1 attributes
+}
+
diff --git a/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs b/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
index 0a01767a07..57861163d6 100644
--- a/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
+++ b/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
@@ -3,6 +3,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using System;
+using Avalonia.Media.Immutable;
namespace Avalonia.Controls
{
@@ -90,7 +91,7 @@ namespace Avalonia.Controls
}
else
{
- _borderRenderHelper.Render(context, Bounds.Size, new Thickness(), CornerRadius, new SolidColorBrush(Material.FallbackColor), null, default);
+ _borderRenderHelper.Render(context, Bounds.Size, new Thickness(), CornerRadius, new ImmutableSolidColorBrush(Material.FallbackColor), null, default);
}
}
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 3bec46a9ac..5506ce05d6 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -6,6 +6,7 @@ using Avalonia.Metadata;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Avalonia.Layout;
+using Avalonia.Media.Immutable;
namespace Avalonia.Controls.Presenters
{
@@ -366,24 +367,26 @@ namespace Avalonia.Controls.Presenters
if (caretBrush is null)
{
- var backgroundColor = (Background as SolidColorBrush)?.Color;
+ var backgroundColor = (Background as ISolidColorBrush)?.Color;
if (backgroundColor.HasValue)
{
byte red = (byte)~(backgroundColor.Value.R);
byte green = (byte)~(backgroundColor.Value.G);
byte blue = (byte)~(backgroundColor.Value.B);
- caretBrush = new SolidColorBrush(Color.FromRgb(red, green, blue));
+ caretBrush = new ImmutableSolidColorBrush(Color.FromRgb(red, green, blue));
}
else
+ {
caretBrush = Brushes.Black;
+ }
}
if (_caretBlink)
{
var (p1, p2) = GetCaretPoints();
context.DrawLine(
- new Pen(caretBrush, 1),
+ new ImmutablePen(caretBrush, 1),
p1, p2);
}
}
diff --git a/src/Avalonia.Controls/TickBar.cs b/src/Avalonia.Controls/TickBar.cs
index 6ea5277a55..237bc2ce1d 100644
--- a/src/Avalonia.Controls/TickBar.cs
+++ b/src/Avalonia.Controls/TickBar.cs
@@ -1,6 +1,7 @@
using Avalonia.Collections;
using Avalonia.Layout;
using Avalonia.Media;
+using Avalonia.Media.Immutable;
using Avalonia.Utilities;
namespace Avalonia.Controls
@@ -295,7 +296,7 @@ namespace Avalonia.Controls
endPoint = pt;
}
- var pen = new Pen(Fill, 1.0d);
+ var pen = new ImmutablePen(Fill?.ToImmutable(), 1.0d);
// Is it Vertical?
if (Placement == TickBarPlacement.Left || Placement == TickBarPlacement.Right)
diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
index 3128753781..f8ab58d46e 100644
--- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
+++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Media;
+using Avalonia.Media.Immutable;
using Avalonia.Platform;
using Avalonia.Utilities;
@@ -114,9 +115,9 @@ namespace Avalonia.Controls.Utils
var borderThickness = _borderThickness.Top;
IPen pen = null;
- if (borderThickness > 0)
+ if (borderBrush != null && borderThickness > 0)
{
- pen = new Pen(borderBrush, borderThickness);
+ pen = new ImmutablePen(borderBrush.ToImmutable(), borderThickness);
}
var rect = new Rect(_size);
diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
index db8684747d..35de491668 100644
--- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
+++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
@@ -3,12 +3,16 @@
netstandard2.0
Avalonia
Avalonia.Diagnostics
+ enable
%(Filename)
+
+
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs
index e5b3b080e2..cb98fb70f3 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs
@@ -10,8 +10,8 @@ namespace Avalonia.Diagnostics.Controls
AvaloniaProperty.RegisterDirect(nameof(Thickness), o => o.Thickness,
(o, v) => o.Thickness = v, defaultBindingMode: BindingMode.TwoWay);
- public static readonly DirectProperty HeaderProperty =
- AvaloniaProperty.RegisterDirect(nameof(Header), o => o.Header,
+ public static readonly DirectProperty HeaderProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Header), o => o.Header,
(o, v) => o.Header = v);
public static readonly DirectProperty IsPresentProperty =
@@ -36,7 +36,7 @@ namespace Avalonia.Diagnostics.Controls
AvaloniaProperty.Register(nameof(Highlight));
private Thickness _thickness;
- private string _header;
+ private string? _header;
private bool _isPresent = true;
private double _left;
private double _top;
@@ -50,7 +50,7 @@ namespace Avalonia.Diagnostics.Controls
set => SetAndRaise(ThicknessProperty, ref _thickness, value);
}
- public string Header
+ public string? Header
{
get => _header;
set => SetAndRaise(HeaderProperty, ref _header, value);
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs b/src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs
index 63ac3ab62f..0b9044e65e 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs
@@ -8,12 +8,17 @@ namespace Avalonia.Diagnostics.Converters
{
public double Opacity { get; set; }
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
- return (bool)value ? 1d : Opacity;
+ if (value is bool boolean && boolean)
+ {
+ return 1d;
+ }
+
+ return Opacity;
}
- 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.Diagnostics/Diagnostics/Converters/EnumToCheckedConverter.cs b/src/Avalonia.Diagnostics/Diagnostics/Converters/EnumToCheckedConverter.cs
index 8d10981ba7..4863782f44 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Converters/EnumToCheckedConverter.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Converters/EnumToCheckedConverter.cs
@@ -7,12 +7,12 @@ namespace Avalonia.Diagnostics.Converters
{
internal class EnumToCheckedConverter : IValueConverter
{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Equals(value, parameter);
}
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is bool isChecked && isChecked)
{
diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
index 7942d22962..0e36c8f9cb 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
@@ -6,8 +6,6 @@ using Avalonia.Diagnostics.Views;
using Avalonia.Input;
using Avalonia.Interactivity;
-#nullable enable
-
namespace Avalonia.Diagnostics
{
public static class DevTools
@@ -24,7 +22,7 @@ namespace Avalonia.Diagnostics
public static IDisposable Attach(TopLevel root, DevToolsOptions options)
{
- void PreviewKeyDown(object sender, KeyEventArgs e)
+ void PreviewKeyDown(object? sender, KeyEventArgs e)
{
if (options.Gesture.Matches(e))
{
@@ -71,10 +69,10 @@ namespace Avalonia.Diagnostics
return Disposable.Create(() => window?.Close());
}
- private static void DevToolsClosed(object sender, EventArgs e)
+ private static void DevToolsClosed(object? sender, EventArgs e)
{
- var window = (MainWindow)sender;
- s_open.Remove(window.Root);
+ var window = (MainWindow)sender!;
+ s_open.Remove(window.Root!);
window.Closed -= DevToolsClosed;
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs b/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs
index 5927bd785e..4f4579c7d9 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs
@@ -22,8 +22,8 @@ The following commands are available:
clear(): Clear the output history
";
- public dynamic e { get; internal set; }
- public dynamic root { get; internal set; }
+ public dynamic? e { get; internal set; }
+ public dynamic? root { get; internal set; }
internal static object NoOutput { get; } = new object();
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs b/src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs
index 36fe12d89c..4f493bdcc2 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs
@@ -7,9 +7,7 @@ namespace Avalonia.Diagnostics.Models
{
public EventChainLink(object handler, bool handled, RoutingStrategies route)
{
- Contract.Requires(handler != null);
-
- Handler = handler;
+ Handler = handler ?? throw new ArgumentNullException(nameof(handler));
Handled = handled;
Route = route;
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
index be3564e781..16852001da 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
@@ -9,12 +9,12 @@ namespace Avalonia.Diagnostics
{
public IControl Build(object data)
{
- var name = data.GetType().FullName.Replace("ViewModel", "View");
+ var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type != null)
{
- return (Control)Activator.CreateInstance(type);
+ return (Control)Activator.CreateInstance(type)!;
}
else
{
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
index a9353eba8b..63f68501a7 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
@@ -1,17 +1,17 @@
-using System.ComponentModel;
-using Avalonia.Collections;
-
namespace Avalonia.Diagnostics.ViewModels
{
internal class AvaloniaPropertyViewModel : PropertyViewModel
{
private readonly AvaloniaObject _target;
private string _type;
- private object _value;
+ private object? _value;
private string _priority;
private string _group;
+#nullable disable
+ // Remove "nullable disable" after MemberNotNull will work on our CI.
public AvaloniaPropertyViewModel(AvaloniaObject o, AvaloniaProperty property)
+#nullable restore
{
_target = o;
Property = property;
@@ -20,12 +20,6 @@ namespace Avalonia.Diagnostics.ViewModels
$"[{property.OwnerType.Name}.{property.Name}]" :
property.Name;
- if (property.IsDirect)
- {
- _group = "Properties";
- Priority = "Direct";
- }
-
Update();
}
@@ -34,11 +28,7 @@ namespace Avalonia.Diagnostics.ViewModels
public override string Name { get; }
public bool IsAttached => Property.IsAttached;
- public string Priority
- {
- get => _priority;
- private set => RaiseAndSetIfChanged(ref _priority, value);
- }
+ public string Priority => _priority;
public override string Type => _type;
@@ -56,40 +46,37 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
- public override string Group
- {
- get => _group;
- }
+ public override string Group => _group;
+ // [MemberNotNull(nameof(_type), nameof(_group), nameof(_priority))]
public override void Update()
{
if (Property.IsDirect)
{
RaiseAndSetIfChanged(ref _value, _target.GetValue(Property), nameof(Value));
- RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
+ RaiseAndSetIfChanged(ref _type, _value?.GetType().Name ?? Property.PropertyType.Name, nameof(Type));
+ RaiseAndSetIfChanged(ref _priority, "Direct", nameof(Priority));
+
+ _group = "Properties";
}
else
{
var val = _target.GetDiagnostic(Property);
RaiseAndSetIfChanged(ref _value, val?.Value, nameof(Value));
- RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
+ RaiseAndSetIfChanged(ref _type, _value?.GetType().Name ?? Property.PropertyType.Name, nameof(Type));
if (val != null)
{
- SetGroup(IsAttached ? "Attached Properties" : "Properties");
- Priority = val.Priority.ToString();
+ RaiseAndSetIfChanged(ref _priority, val.Priority.ToString(), nameof(Priority));
+ RaiseAndSetIfChanged(ref _group, IsAttached ? "Attached Properties" : "Properties", nameof(Group));
}
else
{
- SetGroup(Priority = "Unset");
+ RaiseAndSetIfChanged(ref _priority, "Unset", nameof(Priority));
+ RaiseAndSetIfChanged(ref _group, "Unset", nameof(Group));
}
}
}
-
- private void SetGroup(string group)
- {
- RaiseAndSetIfChanged(ref _group, group, nameof(Group));
- }
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
index af5e254204..6b2dbb7bae 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
@@ -1,5 +1,4 @@
-using System.ComponentModel;
-using System.Reflection;
+using System.Reflection;
namespace Avalonia.Diagnostics.ViewModels
{
@@ -7,14 +6,17 @@ namespace Avalonia.Diagnostics.ViewModels
{
private readonly object _target;
private string _type;
- private object _value;
+ private object? _value;
+#nullable disable
+ // Remove "nullable disable" after MemberNotNull will work on our CI.
public ClrPropertyViewModel(object o, PropertyInfo property)
+#nullable restore
{
_target = o;
Property = property;
- if (!property.DeclaringType.IsInterface)
+ if (property.DeclaringType == null || !property.DeclaringType.IsInterface)
{
Name = property.Name;
}
@@ -47,11 +49,12 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
+ // [MemberNotNull(nameof(_type))]
public override void Update()
{
var val = Property.GetValue(_target);
RaiseAndSetIfChanged(ref _value, val, nameof(Value));
- RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
+ RaiseAndSetIfChanged(ref _type, _value?.GetType().Name ?? Property.PropertyType.Name, nameof(Type));
}
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs
index 0e0c44ded8..717b49d074 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs
@@ -15,11 +15,12 @@ namespace Avalonia.Diagnostics.ViewModels
private int _historyIndex = -1;
private string _input;
private bool _isVisible;
- private ScriptState