From ae4e0741d6922f73b3eb89a16c878e5aea24ca1b Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 29 May 2021 23:07:16 -0400 Subject: [PATCH 1/4] Add MemberNotNullAttribute and MemberNotNullWhenAttribute attributes --- .../Metadata/NullableAttributes.cs | 81 ++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) 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 +} + From 522f6b97ae755be721538a6b0727c7388a481434 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 29 May 2021 23:07:40 -0400 Subject: [PATCH 2/4] Enable nullable on Avalonia.Diagnostics --- .../Avalonia.Diagnostics.csproj | 4 ++ .../Diagnostics/Controls/ThicknessEditor.cs | 8 ++-- .../Converters/BoolToOpacityConverter.cs | 11 +++-- .../Converters/EnumToCheckedConverter.cs | 4 +- .../Diagnostics/DevTools.cs | 4 +- .../Diagnostics/Models/ConsoleContext.cs | 4 +- .../Diagnostics/Models/EventChainLink.cs | 4 +- .../ViewModels/AvaloniaPropertyViewModel.cs | 42 +++++++------------ .../ViewModels/ClrPropertyViewModel.cs | 7 ++-- .../ViewModels/ConsoleViewModel.cs | 3 +- .../ViewModels/ControlDetailsViewModel.cs | 16 +++---- .../ViewModels/ControlLayoutViewModel.cs | 10 ++--- .../ViewModels/EventOwnerTreeNode.cs | 2 +- .../Diagnostics/ViewModels/EventTreeNode.cs | 19 ++++----- .../ViewModels/EventTreeNodeBase.cs | 6 +-- .../ViewModels/EventsPageViewModel.cs | 11 +++-- .../Diagnostics/ViewModels/FilterViewModel.cs | 17 ++++---- .../Diagnostics/ViewModels/FiredEvent.cs | 15 +++---- .../Diagnostics/ViewModels/LogicalTreeNode.cs | 8 ++-- .../Diagnostics/ViewModels/MainViewModel.cs | 19 +++++---- .../ViewModels/PropertyViewModel.cs | 2 +- .../ViewModels/ResourceSetterViewModel.cs | 2 +- .../Diagnostics/ViewModels/SetterViewModel.cs | 4 +- .../Diagnostics/ViewModels/TreeNode.cs | 16 +++---- .../ViewModels/TreeNodeCollection.cs | 29 ++++++++++--- .../ViewModels/TreePageViewModel.cs | 14 +++---- .../Diagnostics/ViewModels/ViewModelBase.cs | 10 ++--- .../Diagnostics/ViewModels/VisualTreeNode.cs | 8 ++-- .../Diagnostics/Views/ConsoleView.xaml.cs | 8 +++- .../Diagnostics/Views/MainView.xaml.cs | 6 ++- .../Diagnostics/Views/MainWindow.xaml.cs | 31 +++++++++----- .../Diagnostics/Views/TreePageView.xaml.cs | 14 ++++--- .../Diagnostics/VisualTreeDebug.cs | 2 +- 33 files changed, 198 insertions(+), 162 deletions(-) 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..78e808595f 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 @@ -74,7 +72,7 @@ namespace Avalonia.Diagnostics private static void DevToolsClosed(object sender, EventArgs e) { var window = (MainWindow)sender; - s_open.Remove(window.Root); + 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/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index a9353eba8b..21265f2cfc 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -1,5 +1,4 @@ -using System.ComponentModel; -using Avalonia.Collections; +using System.Diagnostics.CodeAnalysis; namespace Avalonia.Diagnostics.ViewModels { @@ -7,7 +6,7 @@ namespace Avalonia.Diagnostics.ViewModels { private readonly AvaloniaObject _target; private string _type; - private object _value; + private object? _value; private string _priority; private string _group; @@ -20,12 +19,6 @@ namespace Avalonia.Diagnostics.ViewModels $"[{property.OwnerType.Name}.{property.Name}]" : property.Name; - if (property.IsDirect) - { - _group = "Properties"; - Priority = "Direct"; - } - Update(); } @@ -34,11 +27,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 +45,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..19e4a702eb 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace Avalonia.Diagnostics.ViewModels @@ -7,7 +7,7 @@ namespace Avalonia.Diagnostics.ViewModels { private readonly object _target; private string _type; - private object _value; + private object? _value; public ClrPropertyViewModel(object o, PropertyInfo property) { @@ -47,11 +47,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 _state; + private ScriptState? _state; public ConsoleViewModel(Action updateContext) { _context = new ConsoleContext(this); + _input = string.Empty; _updateContext = updateContext; } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index b1ff8ae98d..74709cb91a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -18,10 +18,10 @@ namespace Avalonia.Diagnostics.ViewModels { private readonly IVisual _control; private readonly IDictionary> _propertyIndex; - private AvaloniaPropertyViewModel _selectedProperty; + private AvaloniaPropertyViewModel? _selectedProperty; private bool _snapshotStyles; private bool _showInactiveStyles; - private string _styleStatus; + private string? _styleStatus; public ControlDetailsViewModel(TreePageViewModel treePage, IVisual control) { @@ -83,7 +83,8 @@ namespace Avalonia.Diagnostics.ViewModels { foreach (var setter in style.Setters) { - if (setter is Setter regularSetter) + if (setter is Setter regularSetter + && regularSetter.Property != null) { var setterValue = regularSetter.Value; @@ -115,13 +116,14 @@ namespace Avalonia.Diagnostics.ViewModels } } - private (object resourceKey, bool isDynamic)? GetResourceInfo(object value) + private (object resourceKey, bool isDynamic)? GetResourceInfo(object? value) { if (value is StaticResourceExtension staticResource) { return (staticResource.ResourceKey, false); } - else if (value is DynamicResourceExtension dynamicResource) + else if (value is DynamicResourceExtension dynamicResource + && dynamicResource.ResourceKey != null) { return (dynamicResource.ResourceKey, true); } @@ -137,7 +139,7 @@ namespace Avalonia.Diagnostics.ViewModels public ObservableCollection PseudoClasses { get; } - public AvaloniaPropertyViewModel SelectedProperty + public AvaloniaPropertyViewModel? SelectedProperty { get => _selectedProperty; set => RaiseAndSetIfChanged(ref _selectedProperty, value); @@ -155,7 +157,7 @@ namespace Avalonia.Diagnostics.ViewModels set => RaiseAndSetIfChanged(ref _showInactiveStyles, value); } - public string StyleStatus + public string? StyleStatus { get => _styleStatus; set => RaiseAndSetIfChanged(ref _styleStatus, value); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs index b0718bc6ce..f691698488 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs @@ -12,14 +12,14 @@ namespace Avalonia.Diagnostics.ViewModels private readonly IVisual _control; private Thickness _borderThickness; private double _height; - private string _heightConstraint; + private string? _heightConstraint; private HorizontalAlignment _horizontalAlignment; private Thickness _marginThickness; private Thickness _paddingThickness; private bool _updatingFromControl; private VerticalAlignment _verticalAlignment; private double _width; - private string _widthConstraint; + private string? _widthConstraint; public ControlLayoutViewModel(IVisual control) { @@ -80,13 +80,13 @@ namespace Avalonia.Diagnostics.ViewModels private set => RaiseAndSetIfChanged(ref _height, value); } - public string WidthConstraint + public string? WidthConstraint { get => _widthConstraint; private set => RaiseAndSetIfChanged(ref _widthConstraint, value); } - public string HeightConstraint + public string? HeightConstraint { get => _heightConstraint; private set => RaiseAndSetIfChanged(ref _heightConstraint, value); @@ -112,7 +112,7 @@ namespace Avalonia.Diagnostics.ViewModels { if (_control is IAvaloniaObject ao) { - string CreateConstraintInfo(StyledProperty minProperty, StyledProperty maxProperty) + string? CreateConstraintInfo(StyledProperty minProperty, StyledProperty maxProperty) { bool hasMin = ao.IsSet(minProperty); bool hasMax = ao.IsSet(maxProperty); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventOwnerTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventOwnerTreeNode.cs index b56374d353..5b7ddc98ee 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventOwnerTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventOwnerTreeNode.cs @@ -27,7 +27,7 @@ namespace Avalonia.Diagnostics.ViewModels if (_updateChildren && value != null) { - foreach (var child in Children) + foreach (var child in Children!) { try { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs index ea54302ebd..f6f57b769d 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs @@ -11,16 +11,13 @@ namespace Avalonia.Diagnostics.ViewModels { private readonly EventsPageViewModel _parentViewModel; private bool _isRegistered; - private FiredEvent _currentEvent; + private FiredEvent? _currentEvent; public EventTreeNode(EventOwnerTreeNode parent, RoutedEvent @event, EventsPageViewModel vm) : base(parent, @event.Name) { - Contract.Requires(@event != null); - Contract.Requires(vm != null); - - Event = @event; - _parentViewModel = vm; + Event = @event ?? throw new ArgumentNullException(nameof(@event)); + _parentViewModel = vm ?? throw new ArgumentNullException(nameof(vm)); } public RoutedEvent Event { get; } @@ -73,7 +70,7 @@ namespace Avalonia.Diagnostics.ViewModels var handled = e.Handled; var route = e.Route; - Action handler = delegate + void handler() { if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e)) { @@ -98,14 +95,16 @@ namespace Avalonia.Diagnostics.ViewModels private static bool BelongsToDevTool(IVisual v) { - while (v != null) + var current = v; + + while (current != null) { - if (v is MainView || v is MainWindow) + if (current is MainView || current is MainWindow) { return true; } - v = v.VisualParent; + current = current.VisualParent; } return false; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNodeBase.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNodeBase.cs index c27cad29e8..e6d7335297 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNodeBase.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNodeBase.cs @@ -10,14 +10,14 @@ namespace Avalonia.Diagnostics.ViewModels private bool? _isEnabled = false; private bool _isVisible; - protected EventTreeNodeBase(EventTreeNodeBase parent, string text) + protected EventTreeNodeBase(EventTreeNodeBase? parent, string text) { Parent = parent; Text = text; IsVisible = true; } - public IAvaloniaReadOnlyList Children + public IAvaloniaReadOnlyList? Children { get; protected set; @@ -41,7 +41,7 @@ namespace Avalonia.Diagnostics.ViewModels set => RaiseAndSetIfChanged(ref _isVisible, value); } - public EventTreeNodeBase Parent + public EventTreeNodeBase? Parent { get; } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventsPageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventsPageViewModel.cs index 7a157dec62..fbcedb2e74 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventsPageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventsPageViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Linq; using Avalonia.Controls; using Avalonia.Diagnostics.Models; @@ -23,8 +22,8 @@ namespace Avalonia.Diagnostics.ViewModels }; private readonly MainViewModel _mainViewModel; - private FiredEvent _selectedEvent; - private EventTreeNodeBase _selectedNode; + private FiredEvent? _selectedEvent; + private EventTreeNodeBase? _selectedNode; public EventsPageViewModel(MainViewModel mainViewModel) { @@ -48,13 +47,13 @@ namespace Avalonia.Diagnostics.ViewModels public ObservableCollection RecordedEvents { get; } = new ObservableCollection(); - public FiredEvent SelectedEvent + public FiredEvent? SelectedEvent { get => _selectedEvent; set => RaiseAndSetIfChanged(ref _selectedEvent, value); } - public EventTreeNodeBase SelectedNode + public EventTreeNodeBase? SelectedNode { get => _selectedNode; set => RaiseAndSetIfChanged(ref _selectedNode, value); @@ -99,7 +98,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - static EventTreeNodeBase FindNode(EventTreeNodeBase node, RoutedEvent eventType) + static EventTreeNodeBase? FindNode(EventTreeNodeBase node, RoutedEvent eventType) { if (node is EventTreeNode eventNode && eventNode.Event == eventType) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs index 0d472a5d8f..e592c89892 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs @@ -11,10 +11,13 @@ namespace Avalonia.Diagnostics.ViewModels private readonly Dictionary _errors = new Dictionary(); private string _filterString = string.Empty; private bool _useRegexFilter, _useCaseSensitiveFilter, _useWholeWordFilter; - private string _processedFilter; - private Regex _filterRegex; + private Regex? _filterRegex; - public event EventHandler RefreshFilter; + public event EventHandler? RefreshFilter; + + public bool HasErrors => _errors.Count > 0; + + public event EventHandler? ErrorsChanged; public bool Filter(string input) { @@ -31,13 +34,11 @@ namespace Avalonia.Diagnostics.ViewModels } } - _processedFilter = FilterString.Trim(); - try { var options = RegexOptions.Compiled; var pattern = UseRegexFilter - ? _processedFilter : Regex.Escape(_processedFilter); + ? FilterString.Trim() : Regex.Escape(FilterString.Trim()); if (!UseCaseSensitiveFilter) { options |= RegexOptions.IgnoreCase; @@ -116,9 +117,5 @@ namespace Avalonia.Diagnostics.ViewModels yield return error; } } - - public bool HasErrors => _errors.Count > 0; - - public event EventHandler ErrorsChanged; } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs index 5fb528eead..32df2f8745 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs @@ -8,15 +8,12 @@ namespace Avalonia.Diagnostics.ViewModels internal class FiredEvent : ViewModelBase { private readonly RoutedEventArgs _eventArgs; - private EventChainLink _handledBy; + private EventChainLink? _handledBy; public FiredEvent(RoutedEventArgs eventArgs, EventChainLink originator) { - Contract.Requires(eventArgs != null); - Contract.Requires(originator != null); - - _eventArgs = eventArgs; - Originator = originator; + _eventArgs = eventArgs ?? throw new ArgumentNullException(nameof(eventArgs)); + Originator = originator ?? throw new ArgumentNullException(nameof(originator)); AddToChain(originator); } @@ -25,7 +22,7 @@ namespace Avalonia.Diagnostics.ViewModels return e == _eventArgs; } - public RoutedEvent Event => _eventArgs.RoutedEvent; + public RoutedEvent Event => _eventArgs.RoutedEvent!; public bool IsHandled => HandledBy?.Handled == true; @@ -38,7 +35,7 @@ namespace Avalonia.Diagnostics.ViewModels if (IsHandled) { return $"{Event.Name} on {Originator.HandlerName};" + Environment.NewLine + - $"strategies: {Event.RoutingStrategies}; handled by: {HandledBy.HandlerName}"; + $"strategies: {Event.RoutingStrategies}; handled by: {HandledBy!.HandlerName}"; } return $"{Event.Name} on {Originator.HandlerName}; strategies: {Event.RoutingStrategies}"; @@ -47,7 +44,7 @@ namespace Avalonia.Diagnostics.ViewModels public EventChainLink Originator { get; } - public EventChainLink HandledBy + public EventChainLink? HandledBy { get => _handledBy; set diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs index 38788ef8ee..04215fa8ae 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs @@ -7,22 +7,24 @@ namespace Avalonia.Diagnostics.ViewModels { internal class LogicalTreeNode : TreeNode { - public LogicalTreeNode(ILogical logical, TreeNode parent) + public LogicalTreeNode(ILogical logical, TreeNode? parent) : base((Control)logical, parent) { Children = new LogicalTreeNodeCollection(this, logical); } + public override TreeNodeCollection Children { get; } + public static LogicalTreeNode[] Create(object control) { var logical = control as ILogical; - return logical != null ? new[] { new LogicalTreeNode(logical, null) } : null; + return logical != null ? new[] { new LogicalTreeNode(logical, null) } : Array.Empty(); } internal class LogicalTreeNodeCollection : TreeNodeCollection { private readonly ILogical _control; - private IDisposable _subscription; + private IDisposable? _subscription; public LogicalTreeNodeCollection(TreeNode owner, ILogical control) : base(owner) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index 49263eafdc..4a945c253f 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -1,10 +1,11 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; + using Avalonia.Controls; using Avalonia.Diagnostics.Models; using Avalonia.Input; using Avalonia.Threading; -using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { @@ -17,8 +18,8 @@ namespace Avalonia.Diagnostics.ViewModels private readonly IDisposable _pointerOverSubscription; private ViewModelBase _content; private int _selectedTab; - private string _focusedControl; - private string _pointerOverElement; + private string? _focusedControl; + private string? _pointerOverElement; private bool _shouldVisualizeMarginPadding = true; private bool _shouldVisualizeDirtyRects; private bool _showFpsOverlay; @@ -84,6 +85,7 @@ namespace Avalonia.Diagnostics.ViewModels public ViewModelBase Content { get { return _content; } + [MemberNotNull(nameof(_content))] private set { if (_content is TreePageViewModel oldTree && @@ -114,34 +116,35 @@ namespace Avalonia.Diagnostics.ViewModels public int SelectedTab { get { return _selectedTab; } + [MemberNotNull(nameof(_content))] set { _selectedTab = value; switch (value) { - case 0: - Content = _logicalTree; - break; case 1: Content = _visualTree; break; case 2: Content = _events; break; + default: + Content = _logicalTree; + break; } RaisePropertyChanged(); } } - public string FocusedControl + public string? FocusedControl { get { return _focusedControl; } private set { RaiseAndSetIfChanged(ref _focusedControl, value); } } - public string PointerOverElement + public string? PointerOverElement { get { return _pointerOverElement; } private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs index e23d6f1471..779d74d554 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs @@ -18,7 +18,7 @@ namespace Avalonia.Diagnostics.ViewModels public abstract string Value { get; set; } public abstract void Update(); - protected static string ConvertToString(object value) + protected static string ConvertToString(object? value) { if (value is null) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs index a82e13fcfa..2655b0e31f 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs @@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics.ViewModels public IBrush Tint { get; } - public ResourceSetterViewModel(AvaloniaProperty property, object resourceKey, object resourceValue, bool isDynamic) : base(property, resourceValue) + public ResourceSetterViewModel(AvaloniaProperty property, object resourceKey, object? resourceValue, bool isDynamic) : base(property, resourceValue) { Key = resourceKey; Tint = isDynamic ? Brushes.Orange : Brushes.Brown; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs index e835f5a878..1f0e93d751 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs @@ -11,7 +11,7 @@ namespace Avalonia.Diagnostics.ViewModels public string Name { get; } - public object Value { get; } + public object? Value { get; } public bool IsActive { @@ -25,7 +25,7 @@ namespace Avalonia.Diagnostics.ViewModels set => RaiseAndSetIfChanged(ref _isVisible, value); } - public SetterViewModel(AvaloniaProperty property, object value) + public SetterViewModel(AvaloniaProperty property, object? value) { Property = property; Name = property.Name; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index 9363c28705..4cb470eeac 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -9,17 +9,18 @@ using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { - internal class TreeNode : ViewModelBase, IDisposable + internal abstract class TreeNode : ViewModelBase, IDisposable { - private IDisposable _classesSubscription; + private IDisposable? _classesSubscription; private string _classes; private bool _isExpanded; - public TreeNode(IVisual visual, TreeNode parent) + public TreeNode(IVisual visual, TreeNode? parent) { Parent = parent; Type = visual.GetType().Name; Visual = visual; + _classes = string.Empty; if (visual is IControl control) { @@ -51,10 +52,9 @@ namespace Avalonia.Diagnostics.ViewModels } } - public TreeNodeCollection Children + public abstract TreeNodeCollection Children { get; - protected set; } public string Classes @@ -63,7 +63,7 @@ namespace Avalonia.Diagnostics.ViewModels private set { RaiseAndSetIfChanged(ref _classes, value); } } - public string ElementName + public string? ElementName { get; } @@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.ViewModels set { RaiseAndSetIfChanged(ref _isExpanded, value); } } - public TreeNode Parent + public TreeNode? Parent { get; } @@ -92,7 +92,7 @@ namespace Avalonia.Diagnostics.ViewModels public void Dispose() { - _classesSubscription.Dispose(); + _classesSubscription?.Dispose(); Children.Dispose(); } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs index 8b4f03bd23..e01258f4cc 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs @@ -3,13 +3,15 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; + using Avalonia.Collections; namespace Avalonia.Diagnostics.ViewModels { internal abstract class TreeNodeCollection : IAvaloniaReadOnlyList, IDisposable { - private AvaloniaList _inner; + private AvaloniaList? _inner; public TreeNodeCollection(TreeNode owner) => Owner = owner; @@ -35,14 +37,30 @@ namespace Avalonia.Diagnostics.ViewModels public event NotifyCollectionChangedEventHandler CollectionChanged { - add => _inner.CollectionChanged += value; - remove => _inner.CollectionChanged -= value; + add + { + EnsureInitialized(); + _inner.CollectionChanged += value; + } + remove + { + EnsureInitialized(); + _inner.CollectionChanged -= value; + } } public event PropertyChangedEventHandler PropertyChanged { - add => _inner.PropertyChanged += value; - remove => _inner.PropertyChanged -= value; + add + { + EnsureInitialized(); + _inner.PropertyChanged += value; + } + remove + { + EnsureInitialized(); + _inner.PropertyChanged -= value; + } } public virtual void Dispose() @@ -66,6 +84,7 @@ namespace Avalonia.Diagnostics.ViewModels protected abstract void Initialize(AvaloniaList nodes); + [MemberNotNull(nameof(_inner))] private void EnsureInitialized() { if (_inner is null) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs index 85a7cb69a3..4b18cf414a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs @@ -6,8 +6,8 @@ namespace Avalonia.Diagnostics.ViewModels { internal class TreePageViewModel : ViewModelBase, IDisposable { - private TreeNode _selectedNode; - private ControlDetailsViewModel _details; + private TreeNode? _selectedNode; + private ControlDetailsViewModel? _details; public TreePageViewModel(MainViewModel mainView, TreeNode[] nodes) { @@ -29,7 +29,7 @@ namespace Avalonia.Diagnostics.ViewModels public TreeNode[] Nodes { get; protected set; } - public TreeNode SelectedNode + public TreeNode? SelectedNode { get => _selectedNode; private set @@ -44,7 +44,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - public ControlDetailsViewModel Details + public ControlDetailsViewModel? Details { get => _details; private set @@ -68,7 +68,7 @@ namespace Avalonia.Diagnostics.ViewModels _details?.Dispose(); } - public TreeNode FindNode(IControl control) + public TreeNode? FindNode(IControl control) { foreach (var node in Nodes) { @@ -104,7 +104,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private void ExpandNode(TreeNode node) + private void ExpandNode(TreeNode? node) { if (node != null) { @@ -113,7 +113,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private TreeNode FindNode(TreeNode node, IControl control) + private TreeNode? FindNode(TreeNode node, IControl control) { if (node.Visual == control) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs index 66e9c34657..0bc44d5778 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace Avalonia.Diagnostics.ViewModels { internal class ViewModelBase : INotifyPropertyChanged { - private PropertyChangedEventHandler _propertyChanged; + private PropertyChangedEventHandler? _propertyChanged; private List events = new List(); public event PropertyChangedEventHandler PropertyChanged @@ -20,7 +20,7 @@ namespace Avalonia.Diagnostics.ViewModels { } - protected bool RaiseAndSetIfChanged(ref T field, T value, [CallerMemberName] string propertyName = null) + protected bool RaiseAndSetIfChanged([NotNullIfNotNull("value")] ref T field, T value, [CallerMemberName] string propertyName = null!) { if (!EqualityComparer.Default.Equals(field, value)) { @@ -32,7 +32,7 @@ namespace Avalonia.Diagnostics.ViewModels return false; } - protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) + protected void RaisePropertyChanged([CallerMemberName] string propertyName = null!) { var e = new PropertyChangedEventArgs(propertyName); OnPropertyChanged(e); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs index bc40edf477..48fa636664 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs @@ -7,7 +7,7 @@ namespace Avalonia.Diagnostics.ViewModels { internal class VisualTreeNode : TreeNode { - public VisualTreeNode(IVisual visual, TreeNode parent) + public VisualTreeNode(IVisual visual, TreeNode? parent) : base(visual, parent) { Children = new VisualTreeNodeCollection(this, visual); @@ -20,16 +20,18 @@ namespace Avalonia.Diagnostics.ViewModels public bool IsInTemplate { get; private set; } + public override TreeNodeCollection Children { get; } + public static VisualTreeNode[] Create(object control) { var visual = control as IVisual; - return visual != null ? new[] { new VisualTreeNode(visual, null) } : null; + return visual != null ? new[] { new VisualTreeNode(visual, null) } : Array.Empty(); } internal class VisualTreeNodeCollection : TreeNodeCollection { private readonly IVisual _control; - private IDisposable _subscription; + private IDisposable? _subscription; public VisualTreeNodeCollection(TreeNode owner, IVisual control) : base(owner) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs index ae70b59fde..a018e4180e 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs @@ -40,12 +40,16 @@ namespace Avalonia.Diagnostics.Views private void InputKeyDown(object sender, KeyEventArgs e) { - var vm = (ConsoleViewModel)DataContext; + var vm = (ConsoleViewModel?)DataContext; + if (vm is null) + { + return; + } switch (e.Key) { case Key.Enter: - vm.Execute(); + _ = vm.Execute(); e.Handled = true; break; case Key.Up: diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs index 783709e54b..f30973fea9 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs @@ -27,7 +27,11 @@ namespace Avalonia.Diagnostics.Views public void ToggleConsole() { - var vm = (MainViewModel)DataContext; + var vm = (MainViewModel?)DataContext; + if (vm is null) + { + return; + } if (_consoleHeight == -1) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 330121321a..fdadf114c9 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -15,7 +15,7 @@ namespace Avalonia.Diagnostics.Views internal class MainWindow : Window, IStyleHost { private readonly IDisposable _keySubscription; - private TopLevel _root; + private TopLevel? _root; public MainWindow() { @@ -26,7 +26,7 @@ namespace Avalonia.Diagnostics.Views .Subscribe(RawKeyDown); } - public TopLevel Root + public TopLevel? Root { get => _root; set @@ -43,7 +43,7 @@ namespace Avalonia.Diagnostics.Views if (_root != null) { _root.Closed += RootClosed; - DataContext = new MainViewModel(value); + DataContext = new MainViewModel(_root); } else { @@ -53,15 +53,20 @@ namespace Avalonia.Diagnostics.Views } } - IStyleHost IStyleHost.StylingParent => null; + IStyleHost? IStyleHost.StylingParent => null; protected override void OnClosed(EventArgs e) { base.OnClosed(e); _keySubscription.Dispose(); - _root.Closed -= RootClosed; - _root = null; - ((MainViewModel)DataContext)?.Dispose(); + + if (_root != null) + { + _root.Closed -= RootClosed; + _root = null; + } + + ((MainViewModel?)DataContext)?.Dispose(); } private void InitializeComponent() @@ -71,12 +76,20 @@ namespace Avalonia.Diagnostics.Views private void RawKeyDown(RawKeyEventArgs e) { + var vm = (MainViewModel?)DataContext; + if (vm is null) + { + return; + } + const RawInputModifiers modifiers = RawInputModifiers.Control | RawInputModifiers.Shift; if (e.Modifiers == modifiers) { +#pragma warning disable CS0618 // Type or member is obsolete var point = (Root as IInputRoot)?.MouseDevice?.GetPosition(Root) ?? default; - +#pragma warning restore CS0618 // Type or member is obsolete + var control = Root.GetVisualsAt(point, x => { if (x is AdornerLayer || !x.IsVisible) return false; @@ -87,7 +100,6 @@ namespace Avalonia.Diagnostics.Views if (control != null) { - var vm = (MainViewModel)DataContext; vm.SelectControl((IControl)control); } } @@ -97,7 +109,6 @@ namespace Avalonia.Diagnostics.Views { var enable = e.Key == Key.S; - var vm = (MainViewModel)DataContext; vm.EnableSnapshotStyles(enable); } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs index 3e1a238b36..6ad6533e7a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs @@ -14,12 +14,13 @@ namespace Avalonia.Diagnostics.Views internal class TreePageView : UserControl { private readonly Panel _adorner; - private AdornerLayer _currentLayer; + private AdornerLayer? _currentLayer; private TreeView _tree; public TreePageView() { InitializeComponent(); + _tree = this.FindControl("tree"); _tree.ItemContainerGenerator.Index.Materialized += TreeViewItemMaterialized; _adorner = new Panel @@ -39,7 +40,13 @@ namespace Avalonia.Diagnostics.Views protected void AddAdorner(object sender, PointerEventArgs e) { - var node = (TreeNode)((Control)sender).DataContext; + var node = (TreeNode?)((Control)sender).DataContext; + var vm = (TreePageViewModel?)DataContext; + if (node is null || vm is null) + { + return; + } + var visual = (Visual)node.Visual; _currentLayer = AdornerLayer.GetAdornerLayer(visual); @@ -53,8 +60,6 @@ namespace Avalonia.Diagnostics.Views _currentLayer.Children.Add(_adorner); AdornerLayer.SetAdornedElement(_adorner, visual); - var vm = (TreePageViewModel) DataContext; - if (vm.MainView.ShouldVisualizeMarginPadding) { var paddingBorder = (Border)_adorner.Children[0]; @@ -90,7 +95,6 @@ namespace Avalonia.Diagnostics.Views private void InitializeComponent() { AvaloniaXamlLoader.Load(this); - _tree = this.FindControl("tree"); } private void TreeViewItemMaterialized(object sender, ItemContainerEventArgs e) diff --git a/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs b/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs index 6f699339e7..4adcd32302 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs @@ -17,7 +17,7 @@ namespace Avalonia.Diagnostics private static void PrintVisualTree(IVisual visual, StringBuilder builder, int indent) { - Control control = visual as Control; + Control? control = visual as Control; builder.Append(Indent(indent - 1)); From de0be9326d924ac4675da0880fc2dafd5866bbdf Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 29 May 2021 23:30:47 -0400 Subject: [PATCH 3/4] Fix .NET 5 warnings --- .../Diagnostics/DevTools.cs | 6 +++--- .../Diagnostics/ViewLocator.cs | 4 ++-- .../ViewModels/ClrPropertyViewModel.cs | 2 +- .../ViewModels/ControlDetailsViewModel.cs | 19 ++++++++++--------- .../ViewModels/ControlLayoutViewModel.cs | 2 +- .../Diagnostics/ViewModels/EventTreeNode.cs | 4 ++-- .../Diagnostics/ViewModels/FilterViewModel.cs | 5 +++-- .../Diagnostics/ViewModels/MainViewModel.cs | 2 +- .../ViewModels/PropertyViewModel.cs | 6 +++--- .../ViewModels/ResourceSetterViewModel.cs | 6 ++++-- .../Diagnostics/ViewModels/SetterViewModel.cs | 6 ++++-- .../ViewModels/TreeNodeCollection.cs | 4 ++-- .../Diagnostics/ViewModels/ViewModelBase.cs | 2 +- .../Diagnostics/Views/ConsoleView.xaml.cs | 6 +++--- .../Diagnostics/Views/EventsPageView.xaml.cs | 2 +- .../Diagnostics/Views/MainView.xaml.cs | 2 +- .../Diagnostics/Views/MainWindow.xaml.cs | 2 +- .../Diagnostics/Views/TreePageView.xaml.cs | 12 ++++++------ 18 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs index 78e808595f..0e36c8f9cb 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs @@ -22,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)) { @@ -69,9 +69,9 @@ 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; + var window = (MainWindow)sender!; s_open.Remove(window.Root!); window.Closed -= DevToolsClosed; } 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/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index 19e4a702eb..ab088a208c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -14,7 +14,7 @@ namespace Avalonia.Diagnostics.ViewModels _target = o; Property = property; - if (!property.DeclaringType.IsInterface) + if (property.DeclaringType == null || !property.DeclaringType.IsInterface) { Name = property.Name; } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 74709cb91a..1f7205eb16 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -250,7 +250,7 @@ namespace Avalonia.Diagnostics.ViewModels .Select(x => new ClrPropertyViewModel(o, x)); } - private void ControlPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void ControlPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (_propertyIndex.TryGetValue(e.Property, out var properties)) { @@ -263,9 +263,10 @@ namespace Avalonia.Diagnostics.ViewModels Layout.ControlPropertyChanged(sender, e); } - private void ControlPropertyChanged(object sender, PropertyChangedEventArgs e) + private void ControlPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (_propertyIndex.TryGetValue(e.PropertyName, out var properties)) + if (e.PropertyName != null + && _propertyIndex.TryGetValue(e.PropertyName, out var properties)) { foreach (var property in properties) { @@ -279,7 +280,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private void OnClassesChanged(object sender, NotifyCollectionChangedEventArgs e) + private void OnClassesChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (!SnapshotStyles) { @@ -351,10 +352,10 @@ namespace Avalonia.Diagnostics.ViewModels { public static PropertyComparer Instance { get; } = new PropertyComparer(); - public int Compare(PropertyViewModel x, PropertyViewModel y) + public int Compare(PropertyViewModel? x, PropertyViewModel? y) { - var groupX = GroupIndex(x.Group); - var groupY = GroupIndex(y.Group); + var groupX = GroupIndex(x?.Group); + var groupY = GroupIndex(y?.Group); if (groupX != groupY) { @@ -362,11 +363,11 @@ namespace Avalonia.Diagnostics.ViewModels } else { - return string.CompareOrdinal(x.Name, y.Name); + return string.CompareOrdinal(x?.Name, y?.Name); } } - private int GroupIndex(string group) + private int GroupIndex(string? group) { switch (group) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs index f691698488..4dc0c34c0a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs @@ -179,7 +179,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - public void ControlPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + public void ControlPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { try { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs index f6f57b769d..65fd81cc78 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs @@ -59,14 +59,14 @@ namespace Avalonia.Diagnostics.ViewModels } } - private void HandleEvent(object sender, RoutedEventArgs e) + private void HandleEvent(object? sender, RoutedEventArgs e) { if (!_isRegistered || IsEnabled == false) return; if (sender is IVisual v && BelongsToDevTool(v)) return; - var s = sender; + var s = sender!; var handled = e.Handled; var route = e.Route; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs index e592c89892..5b27236f2e 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs @@ -110,9 +110,10 @@ namespace Avalonia.Diagnostics.ViewModels } } - public IEnumerable GetErrors(string propertyName) + public IEnumerable GetErrors(string? propertyName) { - if (_errors.TryGetValue(propertyName, out var error)) + if (propertyName != null + && _errors.TryGetValue(propertyName, out var error)) { yield return error; } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index 4a945c253f..98afafe7d4 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -190,7 +190,7 @@ namespace Avalonia.Diagnostics.ViewModels FocusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name; } - private void KeyboardPropertyChanged(object sender, PropertyChangedEventArgs e) + private void KeyboardPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement)) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs index 779d74d554..bfd098985a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs @@ -31,13 +31,13 @@ namespace Avalonia.Diagnostics.ViewModels if (!converter.CanConvertTo(typeof(string)) || converter.GetType() == typeof(CollectionConverter)) { - return value.ToString(); + return value.ToString() ?? "(null)"; } return converter.ConvertToString(value); } - private static object InvokeParse(string s, Type targetType) + private static object? InvokeParse(string s, Type targetType) { var method = targetType.GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null); @@ -56,7 +56,7 @@ namespace Avalonia.Diagnostics.ViewModels throw new InvalidCastException("Unable to convert value."); } - protected static object ConvertFromString(string s, Type targetType) + protected static object? ConvertFromString(string s, Type targetType) { var converter = TypeDescriptor.GetConverter(targetType); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs index 2655b0e31f..e93dc7361b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs @@ -16,12 +16,14 @@ namespace Avalonia.Diagnostics.ViewModels public void CopyResourceKey() { - if (Key is null) + var textToCopy = Key?.ToString(); + + if (textToCopy is null) { return; } - CopyToClipboard(Key.ToString()); + CopyToClipboard(textToCopy); } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs index 1f0e93d751..38cbefcb93 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs @@ -36,12 +36,14 @@ namespace Avalonia.Diagnostics.ViewModels public void CopyValue() { - if (Value is null) + var textToCopy = Value?.ToString(); + + if (textToCopy is null) { return; } - CopyToClipboard(Value.ToString()); + CopyToClipboard(textToCopy); } public void CopyPropertyName() diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs index e01258f4cc..06a6fa9a74 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs @@ -35,7 +35,7 @@ namespace Avalonia.Diagnostics.ViewModels protected TreeNode Owner { get; } - public event NotifyCollectionChangedEventHandler CollectionChanged + public event NotifyCollectionChangedEventHandler? CollectionChanged { add { @@ -49,7 +49,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - public event PropertyChangedEventHandler PropertyChanged + public event PropertyChangedEventHandler? PropertyChanged { add { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs index 0bc44d5778..a2ee37c625 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs @@ -10,7 +10,7 @@ namespace Avalonia.Diagnostics.ViewModels private PropertyChangedEventHandler? _propertyChanged; private List events = new List(); - public event PropertyChangedEventHandler PropertyChanged + public event PropertyChangedEventHandler? PropertyChanged { add { _propertyChanged += value; events.Add("added"); } remove { _propertyChanged -= value; events.Add("removed"); } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs index a018e4180e..ab523fb75a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs @@ -30,15 +30,15 @@ namespace Avalonia.Diagnostics.Views AvaloniaXamlLoader.Load(this); } - private void HistoryChanged(object sender, NotifyCollectionChangedEventArgs e) + private void HistoryChanged(object? sender, NotifyCollectionChangedEventArgs e) { - if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems[0] is IControl control) + if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems?[0] is IControl control) { DispatcherTimer.RunOnce(control.BringIntoView, TimeSpan.Zero); } } - private void InputKeyDown(object sender, KeyEventArgs e) + private void InputKeyDown(object? sender, KeyEventArgs e) { var vm = (ConsoleViewModel?)DataContext; if (vm is null) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml.cs index 687a20c5f6..ba7ab41e35 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml.cs @@ -53,7 +53,7 @@ namespace Avalonia.Diagnostics.Views } } - private void OnRecordedEventsChanged(object sender, NotifyCollectionChangedEventArgs e) + private void OnRecordedEventsChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (sender is ObservableCollection events) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs index f30973fea9..b688ad7676 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs @@ -58,7 +58,7 @@ namespace Avalonia.Diagnostics.Views AvaloniaXamlLoader.Load(this); } - private void PreviewKeyDown(object sender, KeyEventArgs e) + private void PreviewKeyDown(object? sender, KeyEventArgs e) { if (e.Key == Key.Escape) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index fdadf114c9..bbb8e76551 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -114,6 +114,6 @@ namespace Avalonia.Diagnostics.Views } } - private void RootClosed(object sender, EventArgs e) => Close(); + private void RootClosed(object? sender, EventArgs e) => Close(); } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs index 6ad6533e7a..3543b1adea 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs @@ -38,9 +38,9 @@ namespace Avalonia.Diagnostics.Views }; } - protected void AddAdorner(object sender, PointerEventArgs e) + protected void AddAdorner(object? sender, PointerEventArgs e) { - var node = (TreeNode?)((Control)sender).DataContext; + var node = (TreeNode?)((Control)sender!).DataContext; var vm = (TreePageViewModel?)DataContext; if (node is null || vm is null) { @@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.Views return new Thickness(-input.Left, -input.Top, -input.Right, -input.Bottom); } - protected void RemoveAdorner(object sender, PointerEventArgs e) + protected void RemoveAdorner(object? sender, PointerEventArgs e) { foreach (var border in _adorner.Children.OfType()) { @@ -97,15 +97,15 @@ namespace Avalonia.Diagnostics.Views AvaloniaXamlLoader.Load(this); } - private void TreeViewItemMaterialized(object sender, ItemContainerEventArgs e) + private void TreeViewItemMaterialized(object? sender, ItemContainerEventArgs e) { var item = (TreeViewItem)e.Containers[0].ContainerControl; item.TemplateApplied += TreeViewItemTemplateApplied; } - private void TreeViewItemTemplateApplied(object sender, TemplateAppliedEventArgs e) + private void TreeViewItemTemplateApplied(object? sender, TemplateAppliedEventArgs e) { - var item = (TreeViewItem)sender; + var item = (TreeViewItem)sender!; // This depends on the default tree item template. // We want to handle events in the item header but exclude events coming from children. From 23b4168cbc5553a4c7b63c73fd5202546a497c59 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 29 May 2021 23:57:16 -0400 Subject: [PATCH 4/4] Temporary do not use MemberNotNull --- .../ViewModels/AvaloniaPropertyViewModel.cs | 7 +-- .../ViewModels/ClrPropertyViewModel.cs | 8 +-- .../Diagnostics/ViewModels/MainViewModel.cs | 8 +-- .../ViewModels/TreeNodeCollection.cs | 50 ++++--------------- 4 files changed, 23 insertions(+), 50 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index 21265f2cfc..63f68501a7 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -1,5 +1,3 @@ -using System.Diagnostics.CodeAnalysis; - namespace Avalonia.Diagnostics.ViewModels { internal class AvaloniaPropertyViewModel : PropertyViewModel @@ -10,7 +8,10 @@ namespace Avalonia.Diagnostics.ViewModels 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; @@ -47,7 +48,7 @@ namespace Avalonia.Diagnostics.ViewModels public override string Group => _group; - [MemberNotNull(nameof(_type), nameof(_group), nameof(_priority))] + // [MemberNotNull(nameof(_type), nameof(_group), nameof(_priority))] public override void Update() { if (Property.IsDirect) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index ab088a208c..6b2dbb7bae 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; +using System.Reflection; namespace Avalonia.Diagnostics.ViewModels { @@ -9,7 +8,10 @@ namespace Avalonia.Diagnostics.ViewModels private string _type; 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; @@ -47,7 +49,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - [MemberNotNull(nameof(_type))] + // [MemberNotNull(nameof(_type))] public override void Update() { var val = Property.GetValue(_target); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index 98afafe7d4..72491bebc2 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; using Avalonia.Controls; using Avalonia.Diagnostics.Models; @@ -24,7 +23,10 @@ namespace Avalonia.Diagnostics.ViewModels private bool _shouldVisualizeDirtyRects; private bool _showFpsOverlay; +#nullable disable + // Remove "nullable disable" after MemberNotNull will work on our CI. public MainViewModel(TopLevel root) +#nullable restore { _root = root; _logicalTree = new TreePageViewModel(this, LogicalTreeNode.Create(root)); @@ -85,7 +87,7 @@ namespace Avalonia.Diagnostics.ViewModels public ViewModelBase Content { get { return _content; } - [MemberNotNull(nameof(_content))] + // [MemberNotNull(nameof(_content))] private set { if (_content is TreePageViewModel oldTree && @@ -116,7 +118,7 @@ namespace Avalonia.Diagnostics.ViewModels public int SelectedTab { get { return _selectedTab; } - [MemberNotNull(nameof(_content))] + // [MemberNotNull(nameof(_content))] set { _selectedTab = value; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs index 06a6fa9a74..c007411f49 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs @@ -3,7 +3,6 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; using Avalonia.Collections; @@ -15,52 +14,22 @@ namespace Avalonia.Diagnostics.ViewModels public TreeNodeCollection(TreeNode owner) => Owner = owner; - public TreeNode this[int index] - { - get - { - EnsureInitialized(); - return _inner[index]; - } - } + public TreeNode this[int index] => EnsureInitialized()[index]; - public int Count - { - get - { - EnsureInitialized(); - return _inner.Count; - } - } + public int Count => EnsureInitialized().Count; protected TreeNode Owner { get; } public event NotifyCollectionChangedEventHandler? CollectionChanged { - add - { - EnsureInitialized(); - _inner.CollectionChanged += value; - } - remove - { - EnsureInitialized(); - _inner.CollectionChanged -= value; - } + add => EnsureInitialized().CollectionChanged += value; + remove => EnsureInitialized().CollectionChanged -= value; } public event PropertyChangedEventHandler? PropertyChanged { - add - { - EnsureInitialized(); - _inner.PropertyChanged += value; - } - remove - { - EnsureInitialized(); - _inner.PropertyChanged -= value; - } + add => EnsureInitialized().PropertyChanged += value; + remove => EnsureInitialized().PropertyChanged -= value; } public virtual void Dispose() @@ -76,22 +45,21 @@ namespace Avalonia.Diagnostics.ViewModels public IEnumerator GetEnumerator() { - EnsureInitialized(); - return _inner.GetEnumerator(); + return EnsureInitialized().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); protected abstract void Initialize(AvaloniaList nodes); - [MemberNotNull(nameof(_inner))] - private void EnsureInitialized() + private AvaloniaList EnsureInitialized() { if (_inner is null) { _inner = new AvaloniaList(); Initialize(_inner); } + return _inner; } } }