ソースを参照

Warning cleanup 2 (#13696)

Julien Lebosquain 1 年間 前
コミット
3fa13d3b64
52 ファイル変更658 行追加517 行削除
  1. 1 1
      build/XUnit.props
  2. 1 1
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  3. 37 8
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  4. 1 1
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  5. 3 4
      samples/Generators.Sandbox/Controls/CustomTextBox.cs
  6. 29 21
      samples/GpuInterop/D3DDemo/D3D11DemoControl.cs
  7. 4 5
      samples/GpuInterop/DrawingSurfaceDemoBase.cs
  8. 6 6
      samples/GpuInterop/GpuDemo.axaml.cs
  9. 2 2
      samples/GpuInterop/VulkanDemo/VulkanCommandBufferPool.cs
  10. 7 7
      samples/GpuInterop/VulkanDemo/VulkanContent.cs
  11. 16 22
      samples/GpuInterop/VulkanDemo/VulkanContext.cs
  12. 15 15
      samples/GpuInterop/VulkanDemo/VulkanImage.cs
  13. 1 1
      samples/GpuInterop/VulkanDemo/VulkanSwapchain.cs
  14. 17 20
      samples/IntegrationTestApp/MainWindow.axaml.cs
  15. 4 8
      src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs
  16. 8 7
      src/Android/Avalonia.Android/AvaloniaMainActivity.cs
  17. 2 0
      src/Android/Avalonia.Android/AvaloniaView.cs
  18. 23 14
      src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
  19. 5 3
      src/Android/Avalonia.Android/Platform/AndroidSystemNavigationManager.cs
  20. 11 11
      src/Android/Avalonia.Android/Platform/ClipboardImpl.cs
  21. 1 3
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  22. 4 4
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  23. 26 19
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  24. 3 1
      src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs
  25. 1 1
      src/Tizen/Avalonia.Tizen/Avalonia.Tizen.csproj
  26. 26 17
      src/Tizen/Avalonia.Tizen/NuiAvaloniaView.cs
  27. 0 2
      src/Tizen/Avalonia.Tizen/NuiAvaloniaViewTextEditable.cs
  28. 2 2
      src/Tizen/Avalonia.Tizen/NuiGlPlatform.cs
  29. 5 8
      src/Tizen/Avalonia.Tizen/NuiTizenApplication.cs
  30. 19 15
      src/Tizen/Avalonia.Tizen/Platform/Permissions.cs
  31. 1 1
      src/Tizen/Avalonia.Tizen/Stubs.cs
  32. 17 5
      src/Tizen/Avalonia.Tizen/TizenPlatform.cs
  33. 7 3
      src/Tizen/Avalonia.Tizen/TizenThreadingInterface.cs
  34. 1 1
      src/Tizen/Avalonia.Tizen/TopLevelImpl.cs
  35. 36 34
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  36. 1 1
      src/iOS/Avalonia.iOS/Storage/IOSSecurityScopedStream.cs
  37. 10 5
      src/iOS/Avalonia.iOS/Storage/IOSStorageItem.cs
  38. 8 5
      src/iOS/Avalonia.iOS/TextInputResponder.cs
  39. 6 5
      tests/Avalonia.Base.UnitTests/DispatcherTests.cs
  40. 5 8
      tests/Avalonia.Base.UnitTests/Media/EffectTests.cs
  41. 3 3
      tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs
  42. 2 51
      tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs
  43. 1 1
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  44. 2 1
      tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs
  45. 2 10
      tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
  46. 114 110
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  47. 12 7
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs
  48. 7 7
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  49. 1 1
      tests/Avalonia.RenderTests/Media/TileBrushTests.cs
  50. 3 1
      tests/Avalonia.RenderTests/TestBase.cs
  51. 71 18
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
  52. 68 10
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

+ 1 - 1
build/XUnit.props

@@ -6,7 +6,7 @@
     <PackageReference Include="xunit.extensibility.core" Version="2.4.2" />
     <PackageReference Include="xunit.extensibility.execution" Version="2.4.2" />
     <PackageReference Include="xunit.runner.console" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
     <PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
   </ItemGroup>

+ 1 - 1
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@@ -160,7 +160,7 @@ namespace ControlCatalog.Pages
                 }
                 else
                 {
-                    SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result));
+                    SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result!));
                     results.ItemsSource = new[] { result };
                     resultsVisible.IsVisible = true;
                 }

+ 37 - 8
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Linq;
 using System.Reflection;
+using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Markup.Xaml;
@@ -14,26 +15,54 @@ namespace ControlCatalog.Pages
         private const string CustomFormat = "application/xxx-avalonia-controlcatalog-custom";
         public DragAndDropPage()
         {
-            this.InitializeComponent();
+            InitializeComponent();
             _dropState = this.Get<TextBlock>("DropState");
 
             int textCount = 0;
-            SetupDnd("Text", d => d.Set(DataFormats.Text,
-                $"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
 
-            SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
-            SetupDnd("Files", async d => d.Set(DataFormats.Files, new[] { await (VisualRoot as TopLevel)!.StorageProvider.TryGetFileFromPathAsync(Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName) }), DragDropEffects.Copy);
+            SetupDnd(
+                "Text",
+                d => d.Set(DataFormats.Text, $"Text was dragged {++textCount} times"),
+                DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
+
+            SetupDnd(
+                "Custom",
+                d => d.Set(CustomFormat, "Test123"),
+                DragDropEffects.Move);
+
+            SetupDnd(
+                "Files",
+                async d =>
+                {
+                    if (Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName is { } name &&
+                        TopLevel.GetTopLevel(this) is { } topLevel &&
+                        await topLevel.StorageProvider.TryGetFileFromPathAsync(name) is { } storageFile)
+                    {
+                        d.Set(DataFormats.Files, new[] { storageFile });
+                    }
+                },
+                DragDropEffects.Copy);
         }
 
-        void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)
+        private void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects) =>
+            SetupDnd(
+                suffix,
+                o =>
+                {
+                    factory(o);
+                    return Task.CompletedTask;
+                },
+                effects);
+
+        private void SetupDnd(string suffix, Func<DataObject, Task> factory, DragDropEffects effects)
         {
             var dragMe = this.Get<Border>("DragMe" + suffix);
             var dragState = this.Get<TextBlock>("DragState" + suffix);
 
-            async void DoDrag(object? sender, Avalonia.Input.PointerPressedEventArgs e)
+            async void DoDrag(object? sender, PointerPressedEventArgs e)
             {
                 var dragData = new DataObject();
-                factory(dragData);
+                await factory(dragData);
 
                 var result = await DragDrop.DoDragDrop(e, dragData, effects);
                 switch (result)

+ 1 - 1
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@@ -48,7 +48,7 @@ namespace ControlCatalog.ViewModels
 
                 foreach (var item in items)
                 {
-                    Items.Remove(item);
+                    Items.Remove(item!);
                 }
             });
 

+ 3 - 4
samples/Generators.Sandbox/Controls/CustomTextBox.cs

@@ -1,10 +1,9 @@
 using System;
 using Avalonia.Controls;
-using Avalonia.Styling;
 
 namespace Generators.Sandbox.Controls;
 
-public class CustomTextBox : TextBox, IStyleable
+public class CustomTextBox : TextBox
 {
-    Type IStyleable.StyleKey => typeof(TextBox);
-}
+    protected override Type StyleKeyOverride => typeof(TextBox);
+}

+ 29 - 21
samples/GpuInterop/D3DDemo/D3D11DemoControl.cs

@@ -20,21 +20,21 @@ namespace GpuInterop.D3DDemo;
 
 public class D3D11DemoControl : DrawingSurfaceDemoBase
 {
-    private D3DDevice _device;
-    private D3D11Swapchain _swapchain;
-    private SharpDX.Direct3D11.DeviceContext _context;
+    private D3DDevice? _device;
+    private D3D11Swapchain? _swapchain;
+    private DeviceContext? _context;
     private Matrix _view;
     private PixelSize _lastSize;
-    private Texture2D _depthBuffer;
-    private DepthStencilView _depthView;
+    private Texture2D? _depthBuffer;
+    private DepthStencilView? _depthView;
     private Matrix _proj;
-    private Buffer _constantBuffer;
-    private Stopwatch _st = Stopwatch.StartNew();
+    private Buffer? _constantBuffer;
+    private readonly Stopwatch _st = Stopwatch.StartNew();
 
     protected override (bool success, string info) InitializeGraphicsResources(Compositor compositor,
         CompositionDrawingSurface surface, ICompositionGpuInterop interop)
     {
-        if (interop?.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
+        if (interop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
                 .D3D11TextureGlobalSharedHandle) != true)
             return (false, "DXGI shared handle import is not supported by the current graphics backend");
         
@@ -60,8 +60,12 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
 
     protected override void FreeGraphicsResources()
     {
-        _swapchain.DisposeAsync();
-        _swapchain = null!;
+        if (_swapchain is not null)
+        {
+            _swapchain.DisposeAsync().GetAwaiter().GetResult();
+            _swapchain = null;
+        }
+
         Utilities.Dispose(ref _depthView);
         Utilities.Dispose(ref _depthBuffer);
         Utilities.Dispose(ref _constantBuffer);
@@ -80,10 +84,10 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
             _lastSize = pixelSize;
             Resize(pixelSize);
         }
-        using (_swapchain.BeginDraw(pixelSize, out var renderView))
+        using (_swapchain!.BeginDraw(pixelSize, out var renderView))
         {
             
-            _device.ImmediateContext.OutputMerger.SetTargets(_depthView, renderView);
+            _device!.ImmediateContext.OutputMerger.SetTargets(_depthView, renderView);
             var viewProj = Matrix.Multiply(_view, _proj);
             var context = _device.ImmediateContext;
 
@@ -101,10 +105,10 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
             
             var ypr = Matrix4x4.CreateFromYawPitchRoll(Yaw, Pitch, Roll);
             // Update WorldViewProj Matrix
-            var worldViewProj = Matrix.RotationX((float)Yaw) * Matrix.RotationY((float)Pitch)
-                                                                          * Matrix.RotationZ((float)Roll)
-                                                                          * Matrix.Scaling(new Vector3(scaleX, scaleY, 1))
-                                                                          * viewProj;
+            var worldViewProj = Matrix.RotationX(Yaw) * Matrix.RotationY(Pitch)
+                                                      * Matrix.RotationZ(Roll)
+                                                      * Matrix.Scaling(new Vector3(scaleX, scaleY, 1))
+                                                      * viewProj;
             worldViewProj.Transpose();
             context.UpdateSubresource(ref worldViewProj, _constantBuffer);
 
@@ -112,21 +116,25 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
             context.Draw(36, 0);
             
             
-            _context.Flush();
+            _context!.Flush();
         }
     }
 
     private void Resize(PixelSize size)
     {
         Utilities.Dispose(ref _depthBuffer);
+
+        if (_device is null)
+            return;
+
         _depthBuffer = new Texture2D(_device,
             new Texture2DDescription()
             {
                 Format = Format.D32_Float_S8X24_UInt,
                 ArraySize = 1,
                 MipLevels = 1,
-                Width = (int)size.Width,
-                Height = (int)size.Height,
+                Width = size.Width,
+                Height = size.Height,
                 SampleDescription = new SampleDescription(1, 0),
                 Usage = ResourceUsage.Default,
                 BindFlags = BindFlags.DepthStencil,
@@ -138,9 +146,9 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
         _depthView = new DepthStencilView(_device, _depthBuffer);
 
         // Setup targets and viewport for rendering
-        _device.ImmediateContext.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f));
+        _device.ImmediateContext.Rasterizer.SetViewport(new Viewport(0, 0, size.Width, size.Height, 0.0f, 1.0f));
         
         // Setup new projection matrix with correct aspect ratio
-        _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f);
+        _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, size.Width / (float) size.Height, 0.1f, 100.0f);
     }
 }

+ 4 - 5
samples/GpuInterop/DrawingSurfaceDemoBase.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Numerics;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia.Controls;
@@ -13,12 +12,12 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
 {
     private CompositionSurfaceVisual? _visual;
     private Compositor? _compositor;
-    private Action _update;
-    private string _info;
+    private readonly Action _update;
+    private string _info = string.Empty;
     private bool _updateQueued;
     private bool _initialized;
     
-    protected CompositionDrawingSurface Surface { get; private set; }
+    protected CompositionDrawingSurface? Surface { get; private set; }
 
     public DrawingSurfaceDemoBase()
     {
@@ -113,7 +112,7 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
     protected abstract void RenderFrame(PixelSize pixelSize);
     protected virtual bool SupportsDisco => false;
 
-    public void Update(GpuDemo parent, float yaw, float pitch, float roll, float disco)
+    public void Update(GpuDemo? parent, float yaw, float pitch, float roll, float disco)
     {
         ParentControl = parent;
         if (ParentControl != null)

+ 6 - 6
samples/GpuInterop/GpuDemo.axaml.cs

@@ -80,13 +80,13 @@ public class GpuDemo : UserControl
         set => SetAndRaise(DiscoVisibleProperty, ref _discoVisible, value);
     }
     
-    private IGpuDemo _demo;
+    private IGpuDemo? _demo;
 
-    public static readonly DirectProperty<GpuDemo, IGpuDemo> DemoProperty =
-        AvaloniaProperty.RegisterDirect<GpuDemo, IGpuDemo>("Demo", o => o.Demo,
+    public static readonly DirectProperty<GpuDemo, IGpuDemo?> DemoProperty =
+        AvaloniaProperty.RegisterDirect<GpuDemo, IGpuDemo?>("Demo", o => o.Demo,
             (o, v) => o._demo = v);
 
-    public IGpuDemo Demo
+    public IGpuDemo? Demo
     {
         get => _demo;
         set => SetAndRaise(DemoProperty, ref _demo, value);
@@ -102,7 +102,7 @@ public class GpuDemo : UserControl
            )
         {
             if (change.Property == DemoProperty)
-                ((IGpuDemo)change.OldValue)?.Update(null, 0, 0, 0, 0);
+                ((IGpuDemo?)change.OldValue)?.Update(null, 0, 0, 0, 0);
             _demo?.Update(this, Yaw, Pitch, Roll, Disco);
         }
 
@@ -112,5 +112,5 @@ public class GpuDemo : UserControl
 
 public interface IGpuDemo
 {
-    void Update(GpuDemo parent, float yaw, float pitch, float roll, float disco);
+    void Update(GpuDemo? parent, float yaw, float pitch, float roll, float disco);
 }

+ 2 - 2
samples/GpuInterop/VulkanDemo/VulkanCommandBufferPool.cs

@@ -13,7 +13,7 @@ namespace Avalonia.Vulkan
         private readonly CommandPool _commandPool;
 
         private readonly List<VulkanCommandBuffer> _usedCommandBuffers = new();
-        private object _lock = new object();
+        private readonly object _lock = new();
 
         public unsafe VulkanCommandBufferPool(Vk api, Device device, Queue queue, uint queueFamilyIndex)
         {
@@ -167,7 +167,7 @@ namespace Avalonia.Vulkan
                 ReadOnlySpan<PipelineStageFlags> waitDstStageMask = default,
                 ReadOnlySpan<Semaphore> signalSemaphores = default,
                 Fence? fence = null,
-                KeyedMutexSubmitInfo keyedMutex = null)
+                KeyedMutexSubmitInfo? keyedMutex = null)
             {
                 EndRecording();
 

+ 7 - 7
samples/GpuInterop/VulkanDemo/VulkanContent.cs

@@ -39,7 +39,7 @@ unsafe class VulkanContent : IDisposable
     {
         _context = context;
         var name = typeof(VulkanContent).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin"));
-        using (var sr = new BinaryReader(typeof(VulkanContent).Assembly.GetManifestResourceStream(name)))
+        using (var sr = new BinaryReader(typeof(VulkanContent).Assembly.GetManifestResourceStream(name)!))
         {
             var buf = new byte[sr.ReadInt32()];
             sr.Read(buf, 0, buf.Length);
@@ -115,7 +115,7 @@ unsafe class VulkanContent : IDisposable
     {
         var name = typeof(VulkanContent).Assembly.GetManifestResourceNames()
             .First(x => x.Contains((fragment ? "frag" : "vert") + ".spirv"));
-        using (var sr = typeof(VulkanContent).Assembly.GetManifestResourceStream(name))
+        using (var sr = typeof(VulkanContent).Assembly.GetManifestResourceStream(name)!)
         {
             using (var mem = new MemoryStream())
             {
@@ -158,7 +158,7 @@ unsafe class VulkanContent : IDisposable
         var commandBuffer = _context.Pool.CreateCommandBuffer();
         commandBuffer.BeginRecording();
 
-        _colorAttachment.TransitionLayout(commandBuffer.InternalHandle,
+        _colorAttachment!.TransitionLayout(commandBuffer.InternalHandle,
             ImageLayout.Undefined, AccessFlags.None,
             ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentWriteBit);
 
@@ -251,9 +251,9 @@ unsafe class VulkanContent : IDisposable
             }
         };
 
-        api.CmdBlitImage(commandBuffer.InternalHandle, _colorAttachment.InternalHandle.Value,
+        api.CmdBlitImage(commandBuffer.InternalHandle, _colorAttachment.InternalHandle,
             ImageLayout.TransferSrcOptimal,
-            image.InternalHandle.Value, ImageLayout.TransferDstOptimal, 1, srcBlitRegion, Filter.Linear);
+            image.InternalHandle, ImageLayout.TransferDstOptimal, 1, srcBlitRegion, Filter.Linear);
         
         commandBuffer.Submit();
     }
@@ -393,7 +393,7 @@ unsafe class VulkanContent : IDisposable
         
         var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0));
         var projection =
-            Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)((float)size.Width / size.Height),
+            Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)size.Width / size.Height,
                 0.01f, 1000);
         
         _colorAttachment = new VulkanImage(_context, (uint)Format.R8G8B8A8Unorm, size, false);
@@ -808,7 +808,7 @@ unsafe class VulkanContent : IDisposable
 
     static Stopwatch St = Stopwatch.StartNew();
     private bool _isInit;
-    private VulkanImage _colorAttachment;
+    private VulkanImage? _colorAttachment;
     private DescriptorSet _descriptorSet;
 
     [StructLayout(LayoutKind.Sequential, Pack = 4)]

+ 16 - 22
samples/GpuInterop/VulkanDemo/VulkanContext.cs

@@ -2,34 +2,31 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.InteropServices;
-using Avalonia;
 using Avalonia.Platform;
 using Avalonia.Rendering.Composition;
 using Avalonia.Vulkan;
 using Silk.NET.Core;
-using Silk.NET.Core.Native;
 using Silk.NET.Vulkan;
 using Silk.NET.Vulkan.Extensions.EXT;
 using Silk.NET.Vulkan.Extensions.KHR;
 using SilkNetDemo;
 using SkiaSharp;
 using D3DDevice = SharpDX.Direct3D11.Device;
-using DxgiDevice = SharpDX.DXGI.Device;
 
 namespace GpuInterop.VulkanDemo;
 
 public unsafe class VulkanContext : IDisposable
 {
-    public Vk Api { get; init; }
-    public Instance Instance { get; init; }
-    public PhysicalDevice PhysicalDevice { get; init; }
-    public Device Device { get; init; }
-    public Queue Queue { get; init; }
-    public uint QueueFamilyIndex { get; init; }
-    public VulkanCommandBufferPool Pool { get; init; }
-    public GRContext GrContext { get; init; }
-    public DescriptorPool DescriptorPool { get; init; }
-    public D3DDevice? D3DDevice { get; init; }
+    public required Vk Api { get; init; }
+    public required Instance Instance { get; init; }
+    public required PhysicalDevice PhysicalDevice { get; init; }
+    public required Device Device { get; init; }
+    public required Queue Queue { get; init; }
+    public required uint QueueFamilyIndex { get; init; }
+    public required VulkanCommandBufferPool Pool { get; init; }
+    public required GRContext GrContext { get; init; }
+    public required DescriptorPool DescriptorPool { get; init; }
+    public required D3DDevice? D3DDevice { get; init; }
 
     public static (VulkanContext? result, string info) TryCreate(ICompositionGpuInterop gpuInterop)
     {
@@ -58,10 +55,8 @@ public unsafe class VulkanContext : IDisposable
         enabledExtensions.Add("VK_EXT_debug_utils");
         if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
             enabledLayers.Add("VK_LAYER_KHRONOS_validation");
-        
 
-        Instance vkInstance = default;
-        Silk.NET.Vulkan.PhysicalDevice physicalDevice = default;
+
         Device device = default;
         DescriptorPool descriptorPool = default;
         VulkanCommandBufferPool? pool = null;
@@ -78,7 +73,7 @@ public unsafe class VulkanContext : IDisposable
                 EnabledExtensionCount = pRequiredExtensions.UCount,
                 PpEnabledLayerNames = pEnabledLayers,
                 EnabledLayerCount = pEnabledLayers.UCount
-            }, null, out vkInstance).ThrowOnError();
+            }, null, out var vkInstance).ThrowOnError();
 
 
             if (api.TryGetInstanceExtension(vkInstance, out ExtDebugUtils debugUtils))
@@ -95,7 +90,7 @@ public unsafe class VulkanContext : IDisposable
                     PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(LogCallback),
                 };
 
-                debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out var messenger);
+                debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out _);
             }
 
             var requireDeviceExtensions = new List<string>
@@ -158,11 +153,11 @@ public unsafe class VulkanContext : IDisposable
                 else if (gpuInterop.DeviceUuid != null)
                 {
                     if (!new Span<byte>(physicalDeviceIDProperties.DeviceUuid, 16)
-                            .SequenceEqual(gpuInterop?.DeviceUuid))
+                            .SequenceEqual(gpuInterop.DeviceUuid))
                         continue;
                 }
 
-                physicalDevice = physicalDevices[c];
+                var physicalDevice = physicalDevices[c];
 
                 var name = Marshal.PtrToStringAnsi(new IntPtr(physicalDeviceProperties2.Properties.DeviceName))!;
 
@@ -251,8 +246,7 @@ public unsafe class VulkanContext : IDisposable
                         RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                         d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
                             new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
-                    
-                    var dxgiDevice = d3dDevice?.QueryInterface<DxgiDevice>();
+
                     return (new VulkanContext
                     {
                         Api = api,

+ 15 - 15
samples/GpuInterop/VulkanDemo/VulkanImage.cs

@@ -24,23 +24,23 @@ public unsafe class VulkanImage : IDisposable
         private ImageLayout _currentLayout;
         private AccessFlags _currentAccessFlags;
         private ImageUsageFlags _imageUsageFlags { get; }
-        private ImageView? _imageView { get; set; }
+        private ImageView _imageView { get; set; }
         private DeviceMemory _imageMemory { get; set; }
-        private SharpDX.Direct3D11.Texture2D? _d3dTexture2D;
+        private readonly SharpDX.Direct3D11.Texture2D? _d3dTexture2D;
         
-        internal Image? InternalHandle { get; private set; }
+        internal Image InternalHandle { get; private set; }
         internal Format Format { get; }
-        internal ImageAspectFlags AspectFlags { get; private set; }
+        internal ImageAspectFlags AspectFlags { get; }
         
-        public ulong Handle => InternalHandle?.Handle ?? 0;
-        public ulong ViewHandle => _imageView?.Handle ?? 0;
+        public ulong Handle => InternalHandle.Handle;
+        public ulong ViewHandle => _imageView.Handle;
         public uint UsageFlags => (uint) _imageUsageFlags;
         public ulong MemoryHandle => _imageMemory.Handle;
         public DeviceMemory DeviceMemory => _imageMemory;
-        public uint MipLevels { get; private set; }
+        public uint MipLevels { get; }
         public Vk Api { get; }
         public PixelSize Size { get; }
-        public ulong MemorySize { get; private set; }
+        public ulong MemorySize { get; }
         public uint CurrentLayout => (uint) _currentLayout;
 
         public VulkanImage(VulkanContext vk, uint format, PixelSize size,
@@ -93,7 +93,7 @@ public unsafe class VulkanImage : IDisposable
                 .CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
             InternalHandle = image;
             
-            Api.GetImageMemoryRequirements(_device, InternalHandle.Value,
+            Api.GetImageMemoryRequirements(_device, InternalHandle,
                 out var memoryRequirements);
 
 
@@ -109,7 +109,7 @@ public unsafe class VulkanImage : IDisposable
             ImportMemoryWin32HandleInfoKHR handleImport = default;
             if (exportable && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
             {
-                 _d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice, size, Format);
+                 _d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice!, size, Format);
                  using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>();
                  
                  handleImport = new ImportMemoryWin32HandleInfoKHR
@@ -141,7 +141,7 @@ public unsafe class VulkanImage : IDisposable
             
             MemorySize = memoryRequirements.Size;
 
-            Api.BindImageMemory(_device, InternalHandle.Value, _imageMemory, 0).ThrowOnError();
+            Api.BindImageMemory(_device, InternalHandle, _imageMemory, 0).ThrowOnError();
             var componentMapping = new ComponentMapping(
                 ComponentSwizzle.Identity,
                 ComponentSwizzle.Identity,
@@ -155,7 +155,7 @@ public unsafe class VulkanImage : IDisposable
             var imageViewCreateInfo = new ImageViewCreateInfo
             {
                 SType = StructureType.ImageViewCreateInfo,
-                Image = InternalHandle.Value,
+                Image = InternalHandle,
                 ViewType = ImageViewType.Type2D,
                 Format = Format,
                 Components = componentMapping,
@@ -209,7 +209,7 @@ public unsafe class VulkanImage : IDisposable
             ImageLayout fromLayout, AccessFlags fromAccessFlags,
             ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
         {
-            VulkanMemoryHelper.TransitionLayout(Api, commandBuffer, InternalHandle.Value,
+            VulkanMemoryHelper.TransitionLayout(Api, commandBuffer, InternalHandle,
                 fromLayout,
                 fromAccessFlags,
                 destinationLayout, destinationAccessFlags,
@@ -241,8 +241,8 @@ public unsafe class VulkanImage : IDisposable
 
         public unsafe void Dispose()
         {
-            Api.DestroyImageView(_device, _imageView.Value, null);
-            Api.DestroyImage(_device, InternalHandle.Value, null);
+            Api.DestroyImageView(_device, _imageView, null);
+            Api.DestroyImage(_device, InternalHandle, null);
             Api.FreeMemory(_device, _imageMemory, null);
 
             _imageView = default;

+ 1 - 1
samples/GpuInterop/VulkanDemo/VulkanSwapchain.cs

@@ -146,6 +146,6 @@ class VulkanSwapchainImage : ISwapchainImage
         if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
             _lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
         else
-            _lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore, _availableSemaphore);
+            _lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore!, _availableSemaphore!);
     }
 }

+ 17 - 20
samples/IntegrationTestApp/MainWindow.axaml.cs

@@ -42,23 +42,20 @@ namespace IntegrationTestApp
         private void InitializeViewMenu()
         {
             var mainTabs = this.Get<TabControl>("MainTabs");
-            var viewMenu = (NativeMenuItem)NativeMenu.GetMenu(this).Items[1];
+            var viewMenu = (NativeMenuItem?)NativeMenu.GetMenu(this)?.Items[1];
 
-            if (mainTabs.Items is not null)
+            foreach (var tabItem in mainTabs.Items.Cast<TabItem>())
             {
-                foreach (TabItem tabItem in mainTabs.Items)
+                var menuItem = new NativeMenuItem
                 {
-                    var menuItem = new NativeMenuItem
-                    {
-                        Header = (string)tabItem.Header!,
-                        ToolTip = (string)tabItem.Header!,
-                        IsChecked = tabItem.IsSelected,
-                        ToggleType = NativeMenuItemToggleType.Radio,
-                    };
-
-                    menuItem.Click += (s, e) => tabItem.IsSelected = true;
-                    viewMenu?.Menu?.Items.Add(menuItem);
-                }
+                    Header = (string?)tabItem.Header,
+                    ToolTip = (string?)tabItem.Header,
+                    IsChecked = tabItem.IsSelected,
+                    ToggleType = NativeMenuItemToggleType.Radio,
+                };
+
+                menuItem.Click += (_, _) => tabItem.IsSelected = true;
+                viewMenu?.Menu?.Items.Add(menuItem);
             }
         }
 
@@ -77,7 +74,7 @@ namespace IntegrationTestApp
             var window = new ShowWindowTest
             {
                 WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex,
-                CanResize = canResizeCheckBox.IsChecked.Value,
+                CanResize = canResizeCheckBox.IsChecked ?? false,
             };
 
             if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
@@ -227,9 +224,9 @@ namespace IntegrationTestApp
             var gestureBorder2 = this.GetControl<Border>("GestureBorder2");
             var lastGesture = this.GetControl<TextBlock>("LastGesture");
             var resetGestures = this.GetControl<Button>("ResetGestures");
-            gestureBorder.Tapped += (s, e) => lastGesture.Text = "Tapped";
+            gestureBorder.Tapped += (_, _) => lastGesture.Text = "Tapped";
             
-            gestureBorder.DoubleTapped += (s, e) =>
+            gestureBorder.DoubleTapped += (_, _) =>
             {
                 lastGesture.Text = "DoubleTapped";
 
@@ -238,14 +235,14 @@ namespace IntegrationTestApp
                 gestureBorder2.IsVisible = true;
             };
 
-            gestureBorder2.DoubleTapped += (s, e) =>
+            gestureBorder2.DoubleTapped += (_, _) =>
             {
                 lastGesture.Text = "DoubleTapped2";
             };
 
-            Gestures.AddRightTappedHandler(gestureBorder, (s, e) => lastGesture.Text = "RightTapped");
+            Gestures.AddRightTappedHandler(gestureBorder, (_, _) => lastGesture.Text = "RightTapped");
             
-            resetGestures.Click += (s, e) =>
+            resetGestures.Click += (_, _) =>
             {
                 lastGesture.Text = string.Empty;
                 gestureBorder.IsVisible = true;

+ 4 - 8
src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs

@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+#nullable enable
 
 namespace Avalonia.Android
 {
@@ -11,9 +7,9 @@ namespace Avalonia.Android
         protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
 
         private static AppBuilder? s_appBuilder;
-        internal static object ViewContent;
+        internal static object? ViewContent;
 
-        public object Content
+        public object? Content
         {
             get
             {
@@ -51,7 +47,7 @@ namespace Avalonia.Android
                 View.Content = ViewContent;
             }
 
-            if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime)
+            if (Avalonia.Application.Current?.ApplicationLifetime is SingleViewLifetime lifetime)
             {
                 lifetime.View = View;
             }

+ 8 - 7
src/Android/Avalonia.Android/AvaloniaMainActivity.cs

@@ -1,9 +1,8 @@
 using System;
-using System.Diagnostics;
+using System.Runtime.Versioning;
 using Android.App;
 using Android.Content;
 using Android.Content.PM;
-using Android.Content.Res;
 using Android.OS;
 using Android.Runtime;
 using Android.Views;
@@ -37,6 +36,7 @@ namespace Avalonia.Android
             ActivityResult?.Invoke(requestCode, resultCode, data);
         }
 
+        [SupportedOSPlatform("android23.0")]
         public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
         {
             base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
@@ -47,7 +47,8 @@ namespace Avalonia.Android
 
     public abstract partial class AvaloniaMainActivity<TApp> : AvaloniaMainActivity  where TApp : Application, new()
     {
-        internal AvaloniaView View;
+        internal AvaloniaView View { get; set; }
+
         private GlobalLayoutListener _listener;
 
         protected override void OnCreate(Bundle savedInstanceState)
@@ -68,9 +69,9 @@ namespace Avalonia.Android
             base.OnResume();
 
             // Android only respects LayoutInDisplayCutoutMode value if it has been set once before window becomes visible.
-            if (Build.VERSION.SdkInt >= BuildVersionCodes.P)
+            if (OperatingSystem.IsAndroidVersionAtLeast(28) && Window is { Attributes: { } attributes })
             {
-                Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges;
+                attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges;
             }
         }
 
@@ -83,9 +84,9 @@ namespace Avalonia.Android
             base.OnDestroy();
         }
 
-        class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
+        private class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
         {
-            private AvaloniaView _view;
+            private readonly AvaloniaView _view;
 
             public GlobalLayoutListener(AvaloniaView view)
             {

+ 2 - 0
src/Android/Avalonia.Android/AvaloniaView.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.Versioning;
 using Android.Content;
 using Android.Content.Res;
 using Android.Runtime;
@@ -46,6 +47,7 @@ namespace Avalonia.Android
             return _view.View.DispatchKeyEvent(e);
         }
 
+        [SupportedOSPlatform("android24.0")]
         public override void OnVisibilityAggregated(bool isVisible)
         {
             base.OnVisibilityAggregated(isVisible);

+ 23 - 14
src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs

@@ -30,17 +30,25 @@ namespace Avalonia.Android.Platform
             {
                 _displayEdgeToEdge = value;
 
-                if(Build.VERSION.SdkInt >= BuildVersionCodes.P)
+                var window = _activity.Window;
+
+                if (OperatingSystem.IsAndroidVersionAtLeast(28) && window?.Attributes is { } attributes)
                 {
-                    _activity.Window.Attributes.LayoutInDisplayCutoutMode = value ? LayoutInDisplayCutoutMode.ShortEdges : LayoutInDisplayCutoutMode.Default;
+                    attributes.LayoutInDisplayCutoutMode = value ? LayoutInDisplayCutoutMode.ShortEdges : LayoutInDisplayCutoutMode.Default;
                 }
 
-                WindowCompat.SetDecorFitsSystemWindows(_activity.Window, !value);
+                if (window is not null)
+                {
+                    WindowCompat.SetDecorFitsSystemWindows(_activity.Window, !value);
+                }
 
                 if(value)
                 {
-                    _activity.Window.AddFlags(WindowManagerFlags.TranslucentStatus);
-                    _activity.Window.AddFlags(WindowManagerFlags.TranslucentNavigation);
+                    if (window is not null)
+                    {
+                        window.AddFlags(WindowManagerFlags.TranslucentStatus);
+                        window.AddFlags(WindowManagerFlags.TranslucentNavigation);
+                    }
                 }
                 else
                 {
@@ -57,14 +65,17 @@ namespace Avalonia.Android.Platform
 
             _callback.InsetsManager = this;
 
-            ViewCompat.SetOnApplyWindowInsetsListener(_activity.Window.DecorView, this);
+            if (_activity.Window is { } window)
+            {
+                ViewCompat.SetOnApplyWindowInsetsListener(window.DecorView, this);
 
-            ViewCompat.SetWindowInsetsAnimationCallback(_activity.Window.DecorView, _callback);
+                ViewCompat.SetWindowInsetsAnimationCallback(window.DecorView, _callback);
+            }
 
-            if(Build.VERSION.SdkInt < BuildVersionCodes.R)
+            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
             {
                 _usesLegacyLayouts = true;
-                _activity.Window.DecorView.ViewTreeObserver.AddOnGlobalLayoutListener(this);
+                _activity.Window?.DecorView.ViewTreeObserver?.AddOnGlobalLayoutListener(this);
             }
 
             DisplayEdgeToEdge = false;
@@ -74,7 +85,7 @@ namespace Avalonia.Android.Platform
         {
             get
             {
-                var insets = ViewCompat.GetRootWindowInsets(_activity.Window.DecorView);
+                var insets = _activity.Window is { } window ? ViewCompat.GetRootWindowInsets(window.DecorView) : null;
 
                 if (insets != null)
                 {
@@ -127,7 +138,7 @@ namespace Avalonia.Android.Platform
 
                     return compat.AppearanceLightStatusBars ? Controls.Platform.SystemBarTheme.Light : Controls.Platform.SystemBarTheme.Dark;
                 }
-                catch (Exception _)
+                catch (Exception)
                 {
                     return Controls.Platform.SystemBarTheme.Light;
                 }
@@ -136,8 +147,6 @@ namespace Avalonia.Android.Platform
             {
                 _statusBarTheme = value;
 
-                var isDefault = _statusBarTheme == null;
-
                 if (!_topLevel.View.IsShown)
                 {
                     return;
@@ -150,7 +159,7 @@ namespace Avalonia.Android.Platform
                     _isDefaultSystemBarLightTheme = compat.AppearanceLightStatusBars;
                 }
 
-                if (value == null && _isDefaultSystemBarLightTheme != null)
+                if (value == null)
                 {
                     value = (bool)_isDefaultSystemBarLightTheme ? Controls.Platform.SystemBarTheme.Light : Controls.Platform.SystemBarTheme.Dark;
                 }

+ 5 - 3
src/Android/Avalonia.Android/Platform/AndroidSystemNavigationManager.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using Avalonia.Interactivity;
 using Avalonia.Platform;
 
@@ -6,7 +8,7 @@ namespace Avalonia.Android.Platform
 {
     internal class AndroidSystemNavigationManagerImpl : ISystemNavigationManagerImpl
     {
-        public event EventHandler<RoutedEventArgs> BackRequested;
+        public event EventHandler<RoutedEventArgs>? BackRequested;
 
         public AndroidSystemNavigationManagerImpl(IActivityNavigationService? navigationService)
         {
@@ -16,7 +18,7 @@ namespace Avalonia.Android.Platform
             }
         }
 
-        private void OnBackRequested(object sender, AndroidBackRequestedEventArgs e)
+        private void OnBackRequested(object? sender, AndroidBackRequestedEventArgs e)
         {
             var routedEventArgs = new RoutedEventArgs();
 

+ 11 - 11
src/Android/Avalonia.Android/Platform/ClipboardImpl.cs

@@ -1,8 +1,8 @@
+#nullable enable
+
 using System;
 using System.Threading.Tasks;
-
 using Android.Content;
-
 using Avalonia.Input;
 using Avalonia.Input.Platform;
 
@@ -10,34 +10,34 @@ namespace Avalonia.Android.Platform
 {
     internal class ClipboardImpl : IClipboard
     {
-        private ClipboardManager? _clipboardManager;
+        private readonly ClipboardManager? _clipboardManager;
 
         internal ClipboardImpl(ClipboardManager? value)
         {
             _clipboardManager = value;
         }
 
-        public Task<string> GetTextAsync()
+        public Task<string?> GetTextAsync()
         {
             if (_clipboardManager?.HasPrimaryClip == true)
             {
-                return Task.FromResult<string>(_clipboardManager.PrimaryClip.GetItemAt(0).Text);
+                return Task.FromResult(_clipboardManager.PrimaryClip?.GetItemAt(0)?.Text);
             }
 
-            return Task.FromResult<string>(null);
+            return Task.FromResult<string?>(null);
         }
 
-        public Task SetTextAsync(string text)
+        public Task SetTextAsync(string? text)
         {
             if(_clipboardManager == null)
             {
                 return Task.CompletedTask;
             }
 
-            ClipData clip = ClipData.NewPlainText("text", text);
+            var clip = ClipData.NewPlainText("text", text);
             _clipboardManager.PrimaryClip = clip;
 
-            return Task.FromResult<object>(null);
+            return Task.CompletedTask;
         }
 
         public Task ClearAsync()
@@ -49,13 +49,13 @@ namespace Avalonia.Android.Platform
 
             _clipboardManager.PrimaryClip = null;
 
-            return Task.FromResult<object>(null);
+            return Task.CompletedTask;
         }
 
         public Task SetDataObjectAsync(IDataObject data) => throw new PlatformNotSupportedException();
 
         public Task<string[]> GetFormatsAsync() => throw new PlatformNotSupportedException();
 
-        public Task<object> GetDataAsync(string format) => throw new PlatformNotSupportedException();
+        public Task<object?> GetDataAsync(string format) => throw new PlatformNotSupportedException();
     }
 }

+ 1 - 3
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -347,7 +347,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
                     {
                         activity.SetTranslucent(true);
                         SetBlurBehind(activity, 0);
-                        activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
+                        activity.Window?.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
                     }
                 }
                 else if (level == WindowTransparencyLevel.Blur)
@@ -448,8 +448,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
     {
         private readonly AvaloniaInputConnection _inputConnection;
 
-        public event EventHandler<int> SelectionChanged;
-
         public EditableWrapper(AvaloniaInputConnection inputConnection)
         {
             _inputConnection = inputConnection;

+ 4 - 4
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@@ -55,7 +55,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
             var keySymbol = GetKeySymbol(e.UnicodeChar, physicalKey);
 
             var rawKeyEvent = new RawKeyEventArgs(
-                          AndroidKeyboardDevice.Instance,
+                          AndroidKeyboardDevice.Instance!,
                           Convert.ToUInt64(e.EventTime),
                           _view.InputRoot,
                           e.Action == KeyEventActions.Down ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
@@ -64,18 +64,18 @@ namespace Avalonia.Android.Platform.Specific.Helpers
                           physicalKey,
                           keySymbol);
 
-            _view.Input(rawKeyEvent);
+            _view.Input?.Invoke(rawKeyEvent);
 
             if ((e.Action == KeyEventActions.Down && e.UnicodeChar >= 32)
                 || unicodeTextInput != null)
             {
                 var rawTextEvent = new RawTextInputEventArgs(
-                  AndroidKeyboardDevice.Instance,
+                  AndroidKeyboardDevice.Instance!,
                   Convert.ToUInt64(e.EventTime),
                   _view.InputRoot,
                   unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString()
                   );
-                _view.Input(rawTextEvent);
+                _view.Input?.Invoke(rawTextEvent);
             }
 
             if (e.Action == KeyEventActions.Up)

+ 26 - 19
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@@ -172,29 +172,29 @@ internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
     {
     }
 
-    public async Task<IStorageFile?> CreateFileAsync(string name)
+    public Task<IStorageFile?> CreateFileAsync(string name)
     {
         var mimeType = MimeTypeMap.Singleton?.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(name)) ?? "application/octet-stream";
-        var newFile = Document.CreateFile(mimeType, name);
+        var newFile = Document?.CreateFile(mimeType, name);
 
         if(newFile == null)
         {
-            return null;
+            return Task.FromResult<IStorageFile?>(null);
         }
 
-        return new AndroidStorageFile(Activity, newFile.Uri, this);
+        return Task.FromResult<IStorageFile?>(new AndroidStorageFile(Activity, newFile.Uri, this));
     }
 
-    public async Task<IStorageFolder?> CreateFolderAsync(string name)
+    public Task<IStorageFolder?> CreateFolderAsync(string name)
     {
         var newFolder = Document?.CreateDirectory(name);
 
         if (newFolder == null)
         {
-            return null;
+            return Task.FromResult<IStorageFolder?>(null);
         }
 
-        return new AndroidStorageFolder(Activity, newFolder.Uri, false, this, PermissionRoot);
+        return Task.FromResult<IStorageFolder?>(new AndroidStorageFolder(Activity, newFolder.Uri, false, this, PermissionRoot));
     }
 
     public override async Task DeleteAsync()
@@ -286,15 +286,15 @@ internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
 
         return null;
 
-        async Task<AndroidStorageFolder?> MoveRecursively(AndroidStorageFolder storageFolder, AndroidStorageFolder destination)
+        static async Task<AndroidStorageFolder?> MoveRecursively(AndroidStorageFolder storageFolder, AndroidStorageFolder destination)
         {
-            destination = await destination.CreateFolderAsync(storageFolder.Name) as AndroidStorageFolder;
-
-            if (destination == null)
+            if (await destination.CreateFolderAsync(storageFolder.Name) is not AndroidStorageFolder newDestination)
             {
                 return null;
             }
 
+            destination = newDestination;
+
             await foreach (var file in storageFolder.GetItemsAsync())
             {
                 if (file is AndroidStorageFolder folder)
@@ -477,25 +477,32 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
 
         if (Activity != null && destination is AndroidStorageFolder storageFolder)
         {
-            if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
+            AndroidUri? movedUri = null;
+
+            if (OperatingSystem.IsAndroidVersionAtLeast(24))
             {
                 try
                 {
-                    var uri = DocumentsContract.MoveDocument(Activity.ContentResolver!, Uri, ((await GetParentAsync()) as AndroidStorageFolder)!.Uri, storageFolder.Document!.Uri);
-
-                    return new AndroidStorageFile(Activity, uri, storageFolder);
+                    if (Activity.ContentResolver is { } contentResolver &&
+                        storageFolder.Document?.Uri is { } targetParentUri &&
+                        await GetParentAsync() is AndroidStorageFolder parentFolder)
+                    {
+                        movedUri = DocumentsContract.MoveDocument(contentResolver, Uri, parentFolder.Uri, targetParentUri);
+                    }
                 }
-                catch (Exception ex)
+                catch (Exception)
                 {
                     // There are many reason why DocumentContract will fail to move a file. We fallback to copying.
                     return await MoveFileByCopy();
-                    
                 }
             }
-            else
+
+            if (movedUri is not null)
             {
-                return await MoveFileByCopy();
+                return new AndroidStorageFile(Activity, movedUri, storageFolder);
             }
+
+            return await MoveFileByCopy();
         }
 
         async Task<AndroidStorageFile?> MoveFileByCopy()

+ 3 - 1
src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using System.Buffers;
 using System.Runtime.InteropServices;
 using System.Text;

+ 1 - 1
src/Tizen/Avalonia.Tizen/Avalonia.Tizen.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0-tizen</TargetFrameworks>
+    <TargetFramework>net6.0-tizen</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
     <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>

+ 26 - 17
src/Tizen/Avalonia.Tizen/NuiAvaloniaView.cs

@@ -3,11 +3,9 @@ using Avalonia.Controls.Embedding;
 using Avalonia.Controls.Platform;
 using Avalonia.Input;
 using Avalonia.Input.TextInput;
-using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Rendering.Composition;
 using Avalonia.Rendering.Composition.Server;
-using Avalonia.Threading;
 using Tizen.NUI;
 using Tizen.NUI.BaseComponents;
 
@@ -22,21 +20,32 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
     private readonly NuiTouchHandler _touchHandler;
     private readonly NuiAvaloniaViewTextEditable _textEditor;
     private TizenRenderTimer? _renderTimer;
-    private TopLevelImpl _topLevelImpl;
-    private EmbeddableControlRoot _topLevel;
-    private TouchDevice _device = new();
-    private ServerCompositionTarget _compositionTargetServer;
+    private TopLevelImpl? _topLevelImpl;
+    private EmbeddableControlRoot? _topLevel;
+    private readonly TouchDevice _device = new();
+    private ServerCompositionTarget? _compositionTargetServer;
+    private IInputRoot? _inputRoot;
 
-    public IInputRoot InputRoot { get; set; }
     public INativeControlHostImpl NativeControlHost { get; }
-    internal TopLevelImpl TopLevelImpl => _topLevelImpl;
     public double Scaling => 1;
     public Size ClientSize => new(Size.Width, Size.Height);
 
+    public IInputRoot InputRoot
+    {
+        get => _inputRoot ?? throw new InvalidOperationException($"{nameof(InputRoot)} hasn't been set");
+        set => _inputRoot = value;
+    }
+
+    private TopLevel TopLevel
+        => _topLevel ?? throw new InvalidOperationException($"{nameof(NuiAvaloniaView)} hasn't been initialized");
+
+    internal TopLevelImpl TopLevelImpl
+        => _topLevelImpl ?? throw new InvalidOperationException($"{nameof(NuiAvaloniaView)} hasn't been initialized");
+
     public Control? Content
     {
-        get => _topLevel.Content as Control;
-        set => _topLevel.Content = value;
+        get => TopLevel.Content as Control;
+        set => TopLevel.Content = value;
     }
 
     internal NuiAvaloniaViewTextEditable TextEditor => _textEditor;
@@ -44,7 +53,7 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
 
     #region Setup
 
-    public event Action OnSurfaceInit;
+    public event Action? OnSurfaceInit;
 
     public NuiAvaloniaView() : base(ColorFormat.RGBA8888)
     {
@@ -73,7 +82,7 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
 
     private int GlRenderFrame()
     {
-        if (_renderTimer == null || _topLevel == null)
+        if (_renderTimer == null || _compositionTargetServer == null)
             return 0;
 
         var rev = _compositionTargetServer.Revision;
@@ -141,13 +150,13 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
 
     private bool OnTouchEvent(object source, TouchEventArgs e)
     {
-        _touchHandler?.Handle(e);
+        _touchHandler.Handle(e);
         return true;
     }
 
     private bool OnWheelEvent(object source, WheelEventArgs e)
     {
-        _touchHandler?.Handle(e);
+        _touchHandler.Handle(e);
         return true;
     }
 
@@ -169,9 +178,9 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
     {
         if (disposing)
         {
-            _topLevel.StopRendering();
-            _topLevel.Dispose();
-            _topLevelImpl.Dispose();
+            _topLevel?.StopRendering();
+            _topLevel?.Dispose();
+            _topLevelImpl?.Dispose();
             _device.Dispose();
         }
         base.Dispose(disposing);

+ 0 - 2
src/Tizen/Avalonia.Tizen/NuiAvaloniaViewTextEditable.cs

@@ -20,9 +20,7 @@ internal class NuiAvaloniaViewTextEditable
 
     public bool IsActive => _client != null && _keyboardPresented;
 
-#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
     public NuiAvaloniaViewTextEditable(NuiAvaloniaView avaloniaView)
-#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
     {
         _avaloniaView = avaloniaView;
         _singleLineTextInput = new NuiSingleLineTextInput

+ 2 - 2
src/Tizen/Avalonia.Tizen/NuiGlPlatform.cs

@@ -54,7 +54,7 @@ class GlContext : IGlContext
 
     public bool IsSharedWith(IGlContext context) => true;
     public bool CanCreateSharedContext => true;
-    public IGlContext CreateSharedContext(IEnumerable<GlVersion> preferredVersions = null)
+    public IGlContext CreateSharedContext(IEnumerable<GlVersion>? preferredVersions = null)
     {
         return this;
     }
@@ -78,7 +78,7 @@ class GlContext : IGlContext
         }
     }
 
-    public object TryGetFeature(Type featureType) => null;
+    public object? TryGetFeature(Type featureType) => null;
 
     public IntPtr GetProcAddress(string procName)
     {

+ 5 - 8
src/Tizen/Avalonia.Tizen/NuiTizenApplication.cs

@@ -3,7 +3,6 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Tizen.NUI;
 using Window = Tizen.NUI.Window;
 using Avalonia.Logging;
-using Avalonia.Threading;
 
 namespace Avalonia.Tizen;
 
@@ -23,7 +22,7 @@ public class NuiTizenApplication<TApp> : NUIApplication
             View = view;
         }
 
-        public Control MainView
+        public Control? MainView
         {
             get => View.Content;
             set => View.Content = value;
@@ -37,9 +36,7 @@ public class NuiTizenApplication<TApp> : NUIApplication
         Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Creating application");
 
         base.OnCreate();
-#pragma warning disable CS8601 // Possible null reference assignment.
-        TizenThreadingInterface.MainloopContext = SynchronizationContext.Current;
-#pragma warning restore CS8601 // Possible null reference assignment.
+        TizenThreadingInterface.MainloopContext = SynchronizationContext.Current!;
 
         Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Setup view");
         _lifetime = new SingleViewLifetime(new NuiAvaloniaView());
@@ -50,7 +47,7 @@ public class NuiTizenApplication<TApp> : NUIApplication
 
         Window.Instance.RenderingBehavior = RenderingBehaviorType.Continuously;
         Window.Instance.GetDefaultLayer().Add(_lifetime.View);
-        Window.Instance.KeyEvent += (s, e) => _lifetime?.View?.KeyboardHandler.Handle(e);
+        Window.Instance.KeyEvent += (_, e) => _lifetime?.View.KeyboardHandler.Handle(e);
     }
 
     private void ContinueSetupApplication()
@@ -64,10 +61,10 @@ public class NuiTizenApplication<TApp> : NUIApplication
         {
             CustomizeAppBuilder(builder);
 
-            builder.AfterSetup(_ => _lifetime.View.Initialise());
+            builder.AfterSetup(_ => _lifetime!.View.Initialise());
 
             Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Setup lifetime");
-            builder.SetupWithLifetime(_lifetime);
+            builder.SetupWithLifetime(_lifetime!);
         }, null);
     }
 }

+ 19 - 15
src/Tizen/Avalonia.Tizen/Platform/Permissions.cs

@@ -1,5 +1,4 @@
 using System.Security;
-using System.Security.Permissions;
 using Tizen.Applications;
 using Tizen.Security;
 
@@ -33,7 +32,7 @@ internal class Permissions
         }
     }
 
-    public static bool IsPrivilegeDeclared(string tizenPrivilege)
+    public static bool IsPrivilegeDeclared(string? tizenPrivilege)
     {
         var tizenPrivileges = tizenPrivilege;
 
@@ -48,21 +47,21 @@ internal class Permissions
         return true;
     }
 
-    public static void EnsureDeclared(params Privilege[] requiredPrivileges)
+    public static void EnsureDeclared(params Privilege[]? requiredPrivileges)
     {
         if (requiredPrivileges?.Any() != true)
             return;
 
-        foreach (var (tizenPrivilege, isRuntime) in requiredPrivileges)
+        foreach (var (tizenPrivilege, _) in requiredPrivileges)
         {
             if (!IsPrivilegeDeclared(tizenPrivilege))
                 throw new SecurityException($"You need to declare the privilege: `{tizenPrivilege}` in your tizen-manifest.xml");
         }
     }
 
-    public static Task<bool> CheckPrivilegeAsync(params Privilege[] requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, false);
-    public static Task<bool> RequestPrivilegeAsync(params Privilege[] requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, true);
-    private static async Task<bool> CheckPrivilegeAsync(Privilege[] requiredPrivileges, bool ask)
+    public static Task<bool> CheckPrivilegeAsync(params Privilege[]? requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, false);
+    public static Task<bool> RequestPrivilegeAsync(params Privilege[]? requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, true);
+    private static async Task<bool> CheckPrivilegeAsync(Privilege[]? requiredPrivileges, bool ask)
     {
         var ret = global::Tizen.System.Information.TryGetValue("http://tizen.org/feature/profile", out string profile);
         if (!ret || (ret && (!profile.Equals("mobile") || !profile.Equals("wearable"))))
@@ -77,7 +76,7 @@ internal class Permissions
 
         var tizenPrivileges = requiredPrivileges.Where(p => p.IsRuntime);
 
-        foreach (var (tizenPrivilege, isRuntime) in tizenPrivileges)
+        foreach (var (tizenPrivilege, _) in tizenPrivileges)
         {
             var checkResult = PrivacyPrivilegeManager.CheckPermission(tizenPrivilege);
             if (checkResult == CheckResult.Ask)
@@ -87,16 +86,21 @@ internal class Permissions
                     var tcs = new TaskCompletionSource<bool>();
                     PrivacyPrivilegeManager.GetResponseContext(tizenPrivilege)
                         .TryGetTarget(out var context);
-                    void OnResponseFetched(object sender, RequestResponseEventArgs e)
+
+                    void OnResponseFetched(object? sender, RequestResponseEventArgs e)
                     {
                         tcs.TrySetResult(e.result == RequestResult.AllowForever);
                     }
-                    context.ResponseFetched += OnResponseFetched;
-                    PrivacyPrivilegeManager.RequestPermission(tizenPrivilege);
-                    var result = await tcs.Task;
-                    context.ResponseFetched -= OnResponseFetched;
-                    if (result)
-                        continue;
+
+                    if (context != null)
+                    {
+                        context.ResponseFetched += OnResponseFetched;
+                        PrivacyPrivilegeManager.RequestPermission(tizenPrivilege);
+                        var result = await tcs.Task;
+                        context.ResponseFetched -= OnResponseFetched;
+                        if (result)
+                            continue;
+                    }
                 }
                 return false;
             }

+ 1 - 1
src/Tizen/Avalonia.Tizen/Stubs.cs

@@ -9,7 +9,7 @@ internal class WindowingPlatformStub : IWindowingPlatform
 
     public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
 
-    public ITrayIconImpl CreateTrayIcon() => null;
+    public ITrayIconImpl? CreateTrayIcon() => null;
 }
 
 internal class PlatformIconLoaderStub : IPlatformIconLoader

+ 17 - 5
src/Tizen/Avalonia.Tizen/TizenPlatform.cs

@@ -1,21 +1,33 @@
 using Avalonia.Input;
 using Avalonia.Input.Platform;
-using Avalonia.OpenGL.Egl;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Rendering.Composition;
 using Avalonia.Tizen.Platform.Input;
 using Avalonia.Tizen.Platform;
-using Avalonia.Threading;
 
 namespace Avalonia.Tizen;
 
 internal class TizenPlatform
 {
     public static readonly TizenPlatform Instance = new();
-    internal static NuiGlPlatform GlPlatform { get; set; }
-    internal static Compositor Compositor { get; private set; }
-    internal static TizenThreadingInterface ThreadingInterface { get; private set; } = new();
+
+    private static NuiGlPlatform? s_glPlatform;
+    private static Compositor? s_compositor;
+
+    internal static NuiGlPlatform GlPlatform
+    {
+        get => s_glPlatform ?? throw new InvalidOperationException($"{nameof(TizenPlatform)} hasn't been initialized");
+        private set => s_glPlatform = value;
+    }
+
+    internal static Compositor Compositor
+    {
+        get => s_compositor ?? throw new InvalidOperationException($"{nameof(TizenPlatform)} hasn't been initialized");
+        private set => s_compositor = value;
+    }
+
+    internal static TizenThreadingInterface ThreadingInterface { get; } = new();
 
     public static void Initialize()
     {   

+ 7 - 3
src/Tizen/Avalonia.Tizen/TizenThreadingInterface.cs

@@ -7,9 +7,13 @@ internal class TizenThreadingInterface : IPlatformThreadingInterface
     internal event Action? TickExecuted;
     private bool _signaled;
 
-#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
-    internal static SynchronizationContext MainloopContext { get; set; }
-#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+    private static SynchronizationContext? s_mainloopContext;
+
+    internal static SynchronizationContext MainloopContext
+    {
+        get => s_mainloopContext ?? throw new InvalidOperationException($"{nameof(MainloopContext)} hasn't been set");
+        set => s_mainloopContext = value;
+    }
 
     public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
     {

+ 1 - 1
src/Tizen/Avalonia.Tizen/TopLevelImpl.cs

@@ -110,7 +110,7 @@ internal class TopLevelImpl : ITopLevelImpl
     internal void TextInput(string text)
     {
         if (Input == null) return;
-        var args = new RawTextInputEventArgs(TizenKeyboardDevice.Instance, (ulong)DateTime.Now.Ticks, _view.InputRoot, text);
+        var args = new RawTextInputEventArgs(TizenKeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, _view.InputRoot, text);
 
         Input(args);
     }

+ 36 - 34
src/iOS/Avalonia.iOS/AvaloniaView.cs

@@ -1,3 +1,5 @@
+#nullable enable
+
 using System;
 using System.Collections.Generic;
 using Avalonia.Controls;
@@ -12,7 +14,6 @@ using Avalonia.Input.TextInput;
 using Avalonia.iOS.Storage;
 using Avalonia.Platform;
 using Avalonia.Platform.Storage;
-using Avalonia.Rendering;
 using Avalonia.Rendering.Composition;
 using CoreAnimation;
 using Foundation;
@@ -25,12 +26,15 @@ namespace Avalonia.iOS
 {
     public partial class AvaloniaView : UIView, ITextInputMethodImpl
     {
-        internal IInputRoot InputRoot { get; private set; }
-        private TopLevelImpl _topLevelImpl;
-        private EmbeddableControlRoot _topLevel;
-        private TouchHandler _touches;
-        private TextInputMethodClient _client;
-        private IAvaloniaViewController _controller;
+        internal IInputRoot InputRoot
+            => _inputRoot ?? throw new InvalidOperationException($"{nameof(IWindowImpl.SetInputRoot)} must have been called");
+
+        private readonly TopLevelImpl _topLevelImpl;
+        private readonly EmbeddableControlRoot _topLevel;
+        private readonly TouchHandler _touches;
+        private TextInputMethodClient? _client;
+        private IAvaloniaViewController? _controller;
+        private IInputRoot? _inputRoot;
 
         public AvaloniaView()
         {
@@ -60,7 +64,7 @@ namespace Avalonia.iOS
         public override bool CanResignFirstResponder => true;
 
         /// <inheritdoc />
-        public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection)
+        public override void TraitCollectionDidChange(UITraitCollection? previousTraitCollection)
         {
             base.TraitCollectionDidChange(previousTraitCollection);
             
@@ -91,7 +95,7 @@ namespace Avalonia.iOS
             private readonly IStorageProvider _storageProvider;
             internal readonly InsetsManager _insetsManager;
             private readonly ClipboardImpl _clipboard;
-            private IDisposable _paddingInsets;
+            private IDisposable? _paddingInsets;
 
             public AvaloniaView View => _view;
 
@@ -101,17 +105,17 @@ namespace Avalonia.iOS
                 _nativeControlHost = new NativeControlHostImpl(view);
                 _storageProvider = new IOSStorageProvider(view);
                 _insetsManager = new InsetsManager(view);
-                _insetsManager.DisplayEdgeToEdgeChanged += (sender, edgeToEdge) =>
+                _insetsManager.DisplayEdgeToEdgeChanged += (_, edgeToEdge) =>
                 {
                     // iOS doesn't add any paddings/margins to the application by itself.
                     // Application is fully responsible for safe area paddings.
-                    // So, unlikely to android, we need to "fake" safe area insets when edge to edge is disabled. 
+                    // So, unlikely to android, we need to "fake" safe area insets when edge to edge is disabled.
                     _paddingInsets?.Dispose();
-                    if (!edgeToEdge)
+                    if (!edgeToEdge && view._controller is { } controller)
                     {
                         _paddingInsets = view._topLevel.SetValue(
                             TemplatedControl.PaddingProperty,
-                            view._controller.SafeAreaPadding,
+                            controller.SafeAreaPadding,
                             BindingPriority.Style); // lower priority, so it can be redefined by user
                     }
                 };
@@ -132,19 +136,19 @@ namespace Avalonia.iOS
 
             public void SetInputRoot(IInputRoot inputRoot)
             {
-                _view.InputRoot = inputRoot;
+                _view._inputRoot = inputRoot;
             }
 
             public Point PointToClient(PixelPoint point) => new Point(point.X, point.Y);
 
             public PixelPoint PointToScreen(Point point) => new PixelPoint((int)point.X, (int)point.Y);
 
-            public void SetCursor(ICursorImpl _)
+            public void SetCursor(ICursorImpl? cursor)
             {
                 // no-op
             }
 
-            public IPopupImpl CreatePopup()
+            public IPopupImpl? CreatePopup()
             {
                 // In-window popups
                 return null;
@@ -158,18 +162,16 @@ namespace Avalonia.iOS
             public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height);
             public Size? FrameSize => null;
             public double RenderScaling => _view.ContentScaleFactor;
-            public IEnumerable<object> Surfaces { get; set; }
-            public Action<RawInputEventArgs> Input { get; set; }
-            public Action<Rect> Paint { get; set; }
-            public Action<Size, WindowResizeReason> Resized { get; set; }
-            public Action<double> ScalingChanged { get; set; }
-            public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
-            public Action Closed { get; set; }
-
-            public Action LostFocus { get; set; }
-
-            // legacy no-op
-            public IMouseDevice MouseDevice { get; } = new MouseDevice();
+            public IEnumerable<object> Surfaces { get; set; } = Array.Empty<object>();
+            public Action<RawInputEventArgs>? Input { get; set; }
+            public Action<Rect>? Paint { get; set; }
+            public Action<Size, WindowResizeReason>? Resized { get; set; }
+            public Action<double>? ScalingChanged { get; set; }
+            public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
+            public Action? Closed { get; set; }
+
+            public Action? LostFocus { get; set; }
+
             public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
 
             public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
@@ -226,13 +228,13 @@ namespace Avalonia.iOS
             return new Class(typeof(CAEAGLLayer));
         }
 
-        public override void TouchesBegan(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt);
+        public override void TouchesBegan(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
 
-        public override void TouchesMoved(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt);
+        public override void TouchesMoved(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
 
-        public override void TouchesEnded(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt);
+        public override void TouchesEnded(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
 
-        public override void TouchesCancelled(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt);
+        public override void TouchesCancelled(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
 
         public override void LayoutSubviews()
         {
@@ -240,9 +242,9 @@ namespace Avalonia.iOS
             base.LayoutSubviews();
         }
 
-        public Control Content
+        public Control? Content
         {
-            get => (Control)_topLevel.Content;
+            get => (Control?)_topLevel.Content;
             set => _topLevel.Content = value;
         }
     }

+ 1 - 1
src/iOS/Avalonia.iOS/Storage/IOSSecurityScopedStream.cs

@@ -17,7 +17,7 @@ internal sealed class IOSSecurityScopedStream : Stream
     internal IOSSecurityScopedStream(NSUrl url, FileAccess access)
     {
         _document = new UIDocument(url);
-        var path = _document.FileUrl.Path;
+        var path = _document.FileUrl.Path!;
         _url = url;
         _url.StartAccessingSecurityScopedResource();
         _stream = File.Open(path, FileMode.Open, access);

+ 10 - 5
src/iOS/Avalonia.iOS/Storage/IOSStorageItem.cs

@@ -47,7 +47,12 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
             Logger.TryGet(LogEventLevel.Error, LogArea.IOSPlatform)?.
                 Log(this, "GetBasicPropertiesAsync returned an error: {ErrorCode} {ErrorMessage}", error.Code, error.LocalizedFailureReason);
         }
-        return Task.FromResult(new StorageItemProperties(attributes?.Size, (DateTime)attributes?.CreationDate, (DateTime)attributes?.ModificationDate));
+
+        var properties = attributes is null ?
+            new StorageItemProperties() :
+            new StorageItemProperties(attributes.Size, (DateTime)attributes.CreationDate, (DateTime)attributes.ModificationDate);
+
+        return Task.FromResult(properties);
     }
 
     public Task<IStorageFolder?> GetParentAsync()
@@ -62,7 +67,7 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
             : Task.FromException(new NSErrorException(error));
     }
 
-    public async Task<IStorageItem?> MoveAsync(IStorageFolder destination)
+    public Task<IStorageItem?> MoveAsync(IStorageFolder destination)
     {
         if (destination is not IOSStorageFolder folder)
         {
@@ -75,8 +80,8 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
         if (NSFileManager.DefaultManager.Move(folder.Url, newPath, out var error))
         {
             return isDir
-                ? new IOSStorageFolder(newPath)
-                : new IOSStorageFile(newPath);
+                ? Task.FromResult<IStorageItem?>(new IOSStorageFolder(newPath))
+                : Task.FromResult<IStorageItem?>(new IOSStorageFile(newPath));
         }
 
         if (error is not null)
@@ -84,7 +89,7 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
             throw new NSErrorException(error);
         }
 
-        return null;
+        return Task.FromResult<IStorageItem?>(null);
     }
 
     public Task ReleaseBookmarkAsync()

+ 8 - 5
src/iOS/Avalonia.iOS/TextInputResponder.cs

@@ -159,17 +159,20 @@ partial class AvaloniaView
         {
             Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "Triggering key press {key}", key);
 
-            _view._topLevelImpl.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot,
-                RawKeyEventType.KeyDown, key, RawInputModifiers.None, physicalKey, keySymbol));
+            if (_view._topLevelImpl.Input is { } input)
+            {
+                input.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot,
+                    RawKeyEventType.KeyDown, key, RawInputModifiers.None, physicalKey, keySymbol));
 
-            _view._topLevelImpl.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot,
-                RawKeyEventType.KeyUp, key, RawInputModifiers.None, physicalKey, keySymbol));
+                input.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot,
+                    RawKeyEventType.KeyUp, key, RawInputModifiers.None, physicalKey, keySymbol));
+            }
         }
 
         private void TextInput(string text)
         {
             Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "Triggering text input {text}", text);
-            _view._topLevelImpl.Input(new RawTextInputEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot, text));
+            _view._topLevelImpl.Input?.Invoke(new RawTextInputEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot, text));
         }
 
         void IUIKeyInput.InsertText(string text)

+ 6 - 5
tests/Avalonia.Base.UnitTests/DispatcherTests.cs

@@ -14,8 +14,8 @@ public class DispatcherTests
 {
     class SimpleDispatcherImpl : IDispatcherImpl, IDispatcherImplWithPendingInput
     {
-        private Thread _loopThread = Thread.CurrentThread;
-        private object _lock = new();
+        private readonly Thread _loopThread = Thread.CurrentThread;
+        private readonly object _lock = new();
         public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _loopThread;
         public void Signal()
         {
@@ -221,7 +221,8 @@ public class DispatcherTests
                 0 => 1,
                 1 => 4,
                 2 => 8,
-                3 => 10
+                3 => 10,
+                _ => throw new InvalidOperationException($"Unexpected value {c}")
             };
             
             Assert.Equal(Enumerable.Range(0, expectedCount), actions);
@@ -380,7 +381,7 @@ public class DispatcherTests
     class WaitHelper : SynchronizationContext, NonPumpingLockHelper.IHelperImpl
     {
         public int WaitCount;
-        public int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
+        public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
         {
             WaitCount++;
             return base.Wait(waitHandles, waitAll, millisecondsTimeout);
@@ -502,4 +503,4 @@ public class DispatcherTests
             t.GetAwaiter().GetResult();
         }
     }
-}
+}

+ 5 - 8
tests/Avalonia.Base.UnitTests/Media/EffectTests.cs

@@ -25,9 +25,8 @@ public class EffectTests
      InlineData("drop-shadow ( 10 20 30 #ffff00ff ) ", 10, 20, 30, 0xffff00ff),
      InlineData("drop-shadow(10 20 30 red)", 10, 20, 30, 0xffff0000),
      InlineData("drop-shadow ( 10   20   30 red  ) ", 10, 20, 30, 0xffff0000),
-     InlineData("drop-shadow(10 20 30 rgba(100, 30, 45, 90%))", 10, 20, 30, 0x90641e2d),
-     InlineData("drop-shadow(10 20 30  rgba(100, 30, 45, 90%) ) ", 10, 20, 30, 0x90641e2d),
-
+     InlineData("drop-shadow(10 20 30 rgba(100, 30, 45, 90%))", 10, 20, 30, 0xe6641e2d),
+     InlineData("drop-shadow(10 20 30  rgba(100, 30, 45, 90%) ) ", 10, 20, 30, 0xe6641e2d)
     ]
     public void Parse_Parses_DropShadow(string s, double x, double y, double r, uint color)
     {
@@ -36,6 +35,7 @@ public class EffectTests
         Assert.Equal(y, effect.OffsetY);
         Assert.Equal(r, effect.BlurRadius);
         Assert.Equal(1, effect.Opacity);
+        Assert.Equal(color, effect.Color.ToUInt32());
     }
 
     [Theory,
@@ -44,7 +44,7 @@ public class EffectTests
      InlineData("blur()"),
      InlineData("blur(123"),
      InlineData("blur(aaab)"),
-     InlineData("drop-shadow(-10  -20 -30)"),
+     InlineData("drop-shadow(-10  -20 -30)")
     ]
     public void Invalid_Effect_Parse_Fails(string b)
     {
@@ -58,10 +58,7 @@ public class EffectTests
      InlineData("drop-shadow(10 15 5)", 0, 0, 16, 21),
      InlineData("drop-shadow(0 0 5)", 6, 6, 6, 6),
      InlineData("drop-shadow(3 3 5)", 3, 3, 9, 9)
-
-
     ]
-
     public static void PaddingIsCorrectlyCalculated(string effect, double left, double top, double right, double bottom)
     {
         var padding = Effect.Parse(effect).GetEffectOutputPadding();
@@ -70,4 +67,4 @@ public class EffectTests
         Assert.Equal(right, padding.Right);
         Assert.Equal(bottom, padding.Bottom);
     }
-}
+}

+ 3 - 3
tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs

@@ -525,12 +525,12 @@ namespace Avalonia.Base.UnitTests.Styling
 
         private class TestConverter : IValueConverter
         {
-            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+            public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
             {
-                return value.ToString() + "bar";
+                return value + "bar";
             }
 
-            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();
             }

+ 2 - 51
tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs

@@ -1,11 +1,7 @@
-using System;
-using System.Linq;
+using System.Linq;
 using Avalonia.Automation.Peers;
-using Avalonia.Automation.Provider;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Templates;
-using Avalonia.Platform;
-using Avalonia.UnitTests;
 using Avalonia.VisualTree;
 using Xunit;
 
@@ -41,7 +37,7 @@ namespace Avalonia.Controls.UnitTests.Automation
             {
                 var contentControl = new ContentControl
                 {
-                    Template = new FuncControlTemplate<ContentControl>((o, ns) =>
+                    Template = new FuncControlTemplate<ContentControl>((o, _) =>
                         new ContentPresenter
                         {
                             Name = "PART_ContentPresenter",
@@ -180,50 +176,5 @@ namespace Avalonia.Controls.UnitTests.Automation
         {
             return ControlAutomationPeer.CreatePeerForElement(control);
         }
-
-        private class TestControl : Control
-        {
-            protected override AutomationPeer OnCreateAutomationPeer()
-            {
-                return new TestAutomationPeer(this);
-            }
-        }
-
-        private class AutomationTestRoot : TestRoot
-        {
-            protected override AutomationPeer OnCreateAutomationPeer()
-            {
-                return new TestRootAutomationPeer(this);
-            }
-        }
-
-        private class TestAutomationPeer : ControlAutomationPeer
-        {
-            public TestAutomationPeer( Control owner)
-                : base(owner)
-            {
-            }
-        }
-
-        private class TestRootAutomationPeer : ControlAutomationPeer, IRootProvider
-        {
-            public TestRootAutomationPeer(Control owner)
-                : base(owner)
-            {
-            }
-
-            public ITopLevelImpl PlatformImpl => throw new System.NotImplementedException();
-            public event EventHandler? FocusChanged;
-
-            public AutomationPeer GetFocus()
-            {
-                throw new System.NotImplementedException();
-            }
-
-            public AutomationPeer GetPeerFromPoint(Point p)
-            {
-                throw new System.NotImplementedException();
-            }
-        }
     }
 }

+ 1 - 1
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@@ -2424,7 +2424,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 set => base.SelectionMode = value;
             }
 
-            public new bool MoveSelection(NavigationDirection direction, bool wrap)
+            public bool MoveSelection(NavigationDirection direction, bool wrap)
             {
                 return base.MoveSelection(direction, wrap);
             }

+ 2 - 1
tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs

@@ -1,8 +1,9 @@
 using Avalonia.Data;
 using Avalonia.UnitTests;
-
 using Xunit;
 
+#pragma warning disable CS0618 // Type or member is obsolete -- we're testing these members
+
 namespace Avalonia.Controls.Primitives.UnitTests
 {
     public class ToggleButtonTests

+ 2 - 10
tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs

@@ -1,4 +1,3 @@
-using System;
 using System.ComponentModel;
 using Avalonia.Markup.Xaml.Converters;
 using Avalonia.Markup.Xaml.XamlIl.Runtime;
@@ -13,8 +12,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
         public AvaloniaPropertyConverterTest()
         {
             // Ensure properties are registered.
-            var foo = Class1.FooProperty;
-            var attached = AttachedOwner.AttachedProperty;
+            _ = Class1.FooProperty;
+            _ = AttachedOwner.AttachedProperty;
         }
 
         [Fact]
@@ -114,13 +113,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
         {
             public static readonly StyledProperty<string> FooProperty =
                 AvaloniaProperty.Register<Class1, string>("Foo");
-
-            public ThemeVariant ThemeVariant
-            {
-                get => throw new NotImplementedException();
-            }
-
-            public event EventHandler ThemeVariantChanged;
         }
 
         private class AttachedOwner

+ 114 - 110
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -48,7 +50,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -74,7 +76,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -100,7 +102,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding Path=StringProperty}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -132,7 +134,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     </ItemsControl>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<ItemsControl>("itemsControl");
+                var textBlock = window.GetControl<ItemsControl>("itemsControl");
 
                 var dataContext = new TestDataContext
                 {
@@ -162,7 +164,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding StaticProperty}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
                 textBlock.DataContext = new TestDataContext();
 
                 Assert.Equal(TestDataContext.StaticProperty, textBlock.Text);
@@ -181,7 +183,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding StringProperty, DataType=local:TestDataContext}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -206,7 +208,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding StringProperty, DataType={x:Type local:TestDataContext}}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -232,7 +234,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding TaskProperty^}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -258,7 +260,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding ObservableProperty^}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 DelayedBinding.ApplyBindings(textBlock);
 
@@ -289,7 +291,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding ListProperty[3]}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -315,7 +317,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding ArrayProperty[3]}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -341,7 +343,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding ObservableCollectionProperty[3]}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -371,10 +373,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock DataContext='{CompiledBinding StringProperty}' Text='{CompiledBinding}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 window.ApplyTemplate();
-                window.Presenter.ApplyTemplate();
+                window.Presenter!.ApplyTemplate();
 
                 var dataContext = new TestDataContext
                 {
@@ -400,7 +402,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding NonIntegerIndexerProperty[Test]}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext();
 
@@ -429,7 +431,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding NonIntegerIndexerInterfaceProperty[Test]}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext();
 
@@ -463,7 +465,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Name='target' Content='{CompiledBinding StringProperty}' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<ContentControl>("target");
+                var target = window.GetControl<ContentControl>("target");
 
                 var dataContext = new TestDataContext();
 
@@ -473,9 +475,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
                 window.ApplyTemplate();
                 target.ApplyTemplate();
-                target.Presenter.UpdateChild();
+                target.Presenter!.UpdateChild();
 
-                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text);
+                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
             }
         }
 
@@ -560,7 +562,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     </ItemsControl>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<ItemsControl>("target");
+                var target = window.GetControl<ItemsControl>("target");
 
                 var dataContext = new TestDataContext();
 
@@ -570,9 +572,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
                 window.ApplyTemplate();
                 target.ApplyTemplate();
-                target.Presenter.ApplyTemplate();
+                target.Presenter!.ApplyTemplate();
 
-                Assert.Equal(dataContext.ListProperty[0], (string)((ContentPresenter)target.Presenter.Panel.Children[0]).Content);
+                Assert.Equal(dataContext.ListProperty[0], (string?)((ContentPresenter)target.Presenter.Panel!.Children[0]).Content);
             }
         }
         
@@ -598,8 +600,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
         </local:DataGridLikeControl.Columns>
     </local:DataGridLikeControl>
 </Window>");
-                var target = window.FindControl<DataGridLikeControl>("target");
-                var column = target!.Columns.Single();
+                var target = window.GetControl<DataGridLikeControl>("target");
+                var column = target.Columns.Single();
 
                 var dataContext = new TestDataContext();
 
@@ -611,13 +613,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 target.ApplyTemplate();
 
                 // Assert DataGridLikeColumn.Binding data type.
-                var compiledPath = ((CompiledBindingExtension)column.Binding).Path;
+                var compiledPath = ((CompiledBindingExtension)column.Binding!).Path;
                 var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
                 Assert.Equal(typeof(int), node.Property.PropertyType);
                 
                 // Assert DataGridLikeColumn.Template data type by evaluating the template.
                 var firstItem = dataContext.ListProperty[0];
-                var textBlockFromTemplate = (TextBlock)column.Template.Build(firstItem);
+                var textBlockFromTemplate = (TextBlock)column.Template!.Build(firstItem)!;
                 textBlockFromTemplate.DataContext = firstItem;
                 Assert.Equal(firstItem.Length.ToString(), textBlockFromTemplate.Text);
             }
@@ -645,8 +647,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
         </local:DataGridLikeControl.Columns>
     </local:DataGridLikeControl>
 </Window>");
-                var target = window.FindControl<DataGridLikeControl>("target");
-                var column = target!.Columns.Single();
+                var target = window.GetControl<DataGridLikeControl>("target");
+                var column = target.Columns.Single();
 
                 var dataContext = new TestDataContext();
                 dataContext.ListProperty.Add("Test");
@@ -656,13 +658,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 target.ApplyTemplate();
 
                 // Assert DataGridLikeColumn.Binding data type.
-                var compiledPath = ((CompiledBindingExtension)column.Binding).Path;
+                var compiledPath = ((CompiledBindingExtension)column.Binding!).Path;
                 var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
                 Assert.Equal(typeof(int), node.Property.PropertyType);
                 
                 // Assert DataGridLikeColumn.Template data type by evaluating the template.
                 var firstItem = dataContext.ListProperty[0];
-                var textBlockFromTemplate = (TextBlock)column.Template.Build(firstItem);
+                var textBlockFromTemplate = (TextBlock)column.Template!.Build(firstItem)!;
                 textBlockFromTemplate.DataContext = firstItem;
                 Assert.Equal(firstItem.Length.ToString(), textBlockFromTemplate.Text);
             }
@@ -707,7 +709,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<ContentControl>("target");
+                var target = window.GetControl<ContentControl>("target");
 
                 var dataContext = new TestDataContext();
 
@@ -717,9 +719,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
                 window.ApplyTemplate();
                 target.ApplyTemplate();
-                target.Presenter.UpdateChild();
+                target.Presenter!.UpdateChild();
 
-                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text);
+                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
             }
         }
         
@@ -740,7 +742,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<ContentControl>("target");
+                var target = window.GetControl<ContentControl>("target");
 
                 var dataContext = new TestDataContext();
 
@@ -750,9 +752,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
                 window.ApplyTemplate();
                 target.ApplyTemplate();
-                target.Presenter.UpdateChild();
+                target.Presenter!.UpdateChild();
 
-                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text);
+                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
             }
         }
 
@@ -773,7 +775,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<ContentControl>("target");
+                var target = window.GetControl<ContentControl>("target");
 
                 var dataContext = new TestDataContext();
 
@@ -783,9 +785,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
                 window.ApplyTemplate();
                 target.ApplyTemplate();
-                target.Presenter.UpdateChild();
+                target.Presenter!.UpdateChild();
 
-                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text);
+                Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
             }
         }
         
@@ -805,7 +807,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     </StackPanel>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("text2");
+                var textBlock = window.GetControl<TextBlock>("text2");
 
                 var dataContext = new TestDataContext
                 {
@@ -834,7 +836,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     </StackPanel>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("text2");
+                var textBlock = window.GetControl<TextBlock>("text2");
 
                 var dataContext = new TestDataContext
                 {
@@ -861,10 +863,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding Title, RelativeSource={RelativeSource AncestorType=Window}}' x:Name='text'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<TextBlock>("text");
+                var target = window.GetControl<TextBlock>("text");
 
                 window.ApplyTemplate();
-                window.Presenter.ApplyTemplate();
+                window.Presenter!.ApplyTemplate();
                 target.ApplyTemplate();
 
                 Assert.Equal("test", target.Text);
@@ -885,10 +887,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}' x:Name='text'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<TextBlock>("text");
+                var target = window.GetControl<TextBlock>("text");
 
                 window.ApplyTemplate();
-                window.Presenter.ApplyTemplate();
+                window.Presenter!.ApplyTemplate();
                 target.ApplyTemplate();
 
                 Assert.Equal("test", target.Text);
@@ -1003,10 +1005,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding Length, Source=Test}' x:Name='text'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var target = window.FindControl<TextBlock>("text");
+                var target = window.GetControl<TextBlock>("text");
 
                 window.ApplyTemplate();
-                window.Presenter.ApplyTemplate();
+                window.Presenter!.ApplyTemplate();
                 target.ApplyTemplate();
 
                 Assert.Equal("Test".Length.ToString(), target.Text);
@@ -1030,7 +1032,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 </Window>";
 
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 Assert.Equal("foobar", textBlock.Text);
             }
@@ -1054,7 +1056,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 </Window>";
 
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 Assert.Equal("foobar", textBlock.Text);
             }
@@ -1079,7 +1081,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 </Window>";
 
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 Assert.Equal("foobar", textBlock.Text);
             }
@@ -1105,7 +1107,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 </Window>";
 
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 Assert.Equal("foobar", textBlock.Text);
             }
@@ -1125,7 +1127,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 </Window>";
 
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 Assert.Equal(Brushes.Red.Color, contentControl.Content);
             }
@@ -1145,7 +1147,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{Binding StringProperty}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -1188,7 +1190,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Content='{CompiledBinding $parent.Title}' Name='contentControl' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 Assert.Equal("foo", contentControl.Content);
             }
@@ -1208,7 +1210,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 </Window>";
 
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 window.DataContext = new TestDataContext() { StringProperty = "Foo" };
 
@@ -1230,7 +1232,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 </Window>";
 
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 window.DataContext = new TestDataContext() { StringProperty = "Foo" };
 
@@ -1267,7 +1269,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext)}' Name='contentControl' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 var dataContext = new TestDataContext();
 
@@ -1290,7 +1292,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext)}' Name='contentControl' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 var dataContext = "foo";
 
@@ -1313,7 +1315,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext).StringProperty}' Name='contentControl' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 var dataContext = new TestDataContext
                 {
@@ -1339,7 +1341,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Content='{CompiledBinding $parent.DataContext(local:TestDataContext).StringProperty}' Name='contentControl' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 var dataContext = new TestDataContext
                 {
@@ -1365,7 +1367,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Content='{CompiledBinding ((local:TestData)ObjectsArrayProperty[0]).StringProperty}' Name='contentControl' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 var data = new TestData()
                 {
@@ -1395,7 +1397,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext).StringProperty}' Name='contentControl' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var contentControl = window.FindControl<ContentControl>("contentControl");
+                var contentControl = window.GetControl<ContentControl>("contentControl");
 
                 var dataContext = new TestDataContext
                 {
@@ -1425,7 +1427,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -1451,7 +1453,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding StringFormat=bar-\{0\}}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -1477,7 +1479,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding .}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -1503,7 +1505,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Text='{CompiledBinding Path=., StringFormat=bar-\{0\}}' Name='textBlock' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext
                 {
@@ -1550,10 +1552,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Name='textBlock' Text='{CompiledBinding $self.Name}' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 window.ApplyTemplate();
-                window.Presenter.ApplyTemplate();
+                window.Presenter!.ApplyTemplate();
 
                 Assert.Equal(textBlock.Name, textBlock.Text);
             }
@@ -1577,10 +1579,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <Button Name='button' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var button = window.FindControl<Button>("button");
+                var button = window.GetControl<Button>("button");
 
                 window.ApplyTemplate();
-                window.Presenter.ApplyTemplate();
+                window.Presenter!.ApplyTemplate();
 
                 Assert.True(button.IsVisible);
 
@@ -1612,12 +1614,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
                 window.DataContext = new MethodDataContext();
 
-                Assert.IsAssignableFrom(typeof(Action), window.FindControl<ContentControl>("action").Content);
-                Assert.IsAssignableFrom(typeof(Func<int>), window.FindControl<ContentControl>("func").Content);
-                Assert.IsAssignableFrom(typeof(Action<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.FindControl<ContentControl>("action16").Content);
-                Assert.IsAssignableFrom(typeof(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.FindControl<ContentControl>("func16").Content);
-                Assert.True(typeof(Delegate).IsAssignableFrom(window.FindControl<ContentControl>("customvoid").Content.GetType()));
-                Assert.True(typeof(Delegate).IsAssignableFrom(window.FindControl<ContentControl>("customint").Content.GetType()));
+                Assert.IsAssignableFrom(typeof(Action), window.GetControl<ContentControl>("action").Content);
+                Assert.IsAssignableFrom(typeof(Func<int>), window.GetControl<ContentControl>("func").Content);
+                Assert.IsAssignableFrom(typeof(Action<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.GetControl<ContentControl>("action16").Content);
+                Assert.IsAssignableFrom(typeof(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.GetControl<ContentControl>("func16").Content);
+                Assert.True(typeof(Delegate).IsAssignableFrom(window.GetControl<ContentControl>("customvoid").Content!.GetType()));
+                Assert.True(typeof(Delegate).IsAssignableFrom(window.GetControl<ContentControl>("customint").Content!.GetType()));
             }
         }
 
@@ -1634,7 +1636,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <Button Name='button' Command='{CompiledBinding Method}'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var button = window.FindControl<Button>("button");
+                var button = window.GetControl<Button>("button");
                 var vm = new MethodAsCommandDataContext();
 
                 button.DataContext = vm;
@@ -1659,7 +1661,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <Button Name='button' Command='{CompiledBinding Method1}' CommandParameter='5'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var button = window.FindControl<Button>("button");
+                var button = window.GetControl<Button>("button");
                 var vm = new MethodAsCommandDataContext();
 
                 button.DataContext = vm;
@@ -1684,7 +1686,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Name='textBlock' Text='{CompiledBinding Method}'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
                 var vm = new MethodAsCommandDataContext();
 
                 textBlock.DataContext = vm;
@@ -1710,7 +1712,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <Button Name='button' Command='{CompiledBinding Do}' CommandParameter='{CompiledBinding Parameter, Mode=OneTime}'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var button = window.FindControl<Button>("button");
+                var button = window.GetControl<Button>("button");
                 var vm = new MethodAsCommandDataContext()
                 {
                     Parameter = commandParameter
@@ -1738,7 +1740,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <Button Name='button' Command='{CompiledBinding Do}'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var button = window.FindControl<Button>("button");
+                var button = window.GetControl<Button>("button");
                 var vm = new MethodAsCommandDataContext()
                 {
                     Parameter = null,
@@ -1770,7 +1772,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
         x:DataType='local:TestDataContext'
         X='{CompiledBinding StringProperty}' />";
                 var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var compiledPath = ((CompiledBindingExtension)control.X).Path;
+                var compiledPath = ((CompiledBindingExtension)control.X!).Path;
 
                 var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
                 Assert.Equal(typeof(string), node.Property.PropertyType);
@@ -1788,7 +1790,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
         xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
         X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />";
                 var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var compiledPath = ((CompiledBindingExtension)control.X).Path;
+                var compiledPath = ((CompiledBindingExtension)control.X!).Path;
 
                 var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
                 Assert.Equal(typeof(string), node.Property.PropertyType);
@@ -1807,7 +1809,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
         X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />";
                 var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(xaml),
                     new RuntimeXamlLoaderConfiguration { UseCompiledBindingsByDefault = true });
-                var compiledPath = ((CompiledBindingExtension)control.X).Path;
+                var compiledPath = ((CompiledBindingExtension)control.X!).Path;
 
                 var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
                 Assert.Equal(typeof(string), node.Property.PropertyType);
@@ -1830,7 +1832,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <ComboBox x:Name='comboBox' ItemsSource='{Binding GenericProperty}' SelectedItem='{Binding GenericProperty.CurrentItem}' />
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var comboBox = window.FindControl<ComboBox>("comboBox");
+                var comboBox = window.GetControl<ComboBox>("comboBox");
 
                 var dataContext = new TestDataContext();
                 dataContext.GenericProperty.Add(123);
@@ -1857,7 +1859,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     <TextBlock Name='textBlock' Text='{{Binding DecimalValue, StringFormat=c2}}'/>
 </Window>";
                 var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var textBlock = window.FindControl<TextBlock>("textBlock");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
 
                 var dataContext = new TestDataContext();
                 window.DataContext = dataContext;
@@ -1887,7 +1889,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
     public interface IHasProperty
     {
-        string StringProperty { get; set; }
+        string? StringProperty { get; set; }
     }
 
     public interface IHasPropertyDerived : IHasProperty
@@ -1902,34 +1904,34 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     {
         public static IValueConverter Instance { get; } = new AppendConverter();
 
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
             => string.Format("{0}+{1}+{2}", value, parameter, culture);
 
-        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();
 
     }
 
     public class TestData
     {
-        public string StringProperty { get; set; }
+        public string? StringProperty { get; set; }
     }
 
     public class TestDataContextBaseClass {}
     
     public class TestDataContext : TestDataContextBaseClass, IHasPropertyDerived, IHasExplicitProperty
     {
-        public string StringProperty { get; set; }
+        public string? StringProperty { get; set; }
 
-        public Task<string> TaskProperty { get; set; }
+        public Task<string>? TaskProperty { get; set; }
 
-        public IObservable<string> ObservableProperty { get; set; }
+        public IObservable<string>? ObservableProperty { get; set; }
 
         public ObservableCollection<string> ObservableCollectionProperty { get; set; } = new ObservableCollection<string>();
 
-        public string[] ArrayProperty { get; set; }
+        public string[]? ArrayProperty { get; set; }
 
-        public object[] ObjectsArrayProperty { get; set; }
+        public object[]? ObjectsArrayProperty { get; set; }
 
         public List<string> ListProperty { get; set; } = new List<string>();
 
@@ -1966,7 +1968,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
     public class ListItemCollectionView<T> : List<T>
     {
-        public T CurrentItem { get; set; }
+        public T? CurrentItem { get; set; }
     }
     
     public class MethodDataContext
@@ -1983,15 +1985,16 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
     public class MethodAsCommandDataContext : INotifyPropertyChanged
     {
-        public event PropertyChangedEventHandler PropertyChanged;
+        public event PropertyChangedEventHandler? PropertyChanged;
 
         public string Method() => Value = "Called";
         public string Method1(int i) => Value = $"Called {i}";
         public string Method2(int i, int j) => Value = $"Called {i},{j}";
         public string Value { get; private set; } = "Not called";
 
-        object _parameter;
-        public object Parameter
+        private object? _parameter;
+
+        public object? Parameter
         {
             get => _parameter;
             set
@@ -2010,7 +2013,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             Value = $"Do {parameter}";
         }
 
-        [Metadata.DependsOn(nameof(Parameter))]
+        [DependsOn(nameof(Parameter))]
         public bool CanDo(object parameter)
         {
             return ReferenceEquals(null, Parameter) == false;
@@ -2020,22 +2023,22 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     public class CustomDataTemplate : IDataTemplate
     {
         [DataType]
-        public Type FancyDataType { get; set; }
+        public Type? FancyDataType { get; set; }
 
         [Content]
         [TemplateContent]
-        public object Content { get; set; }
+        public object? Content { get; set; }
 
-        public bool Match(object data) => FancyDataType?.IsInstanceOfType(data) ?? true;
+        public bool Match(object? data) => FancyDataType?.IsInstanceOfType(data) ?? true;
 
-        public Control Build(object data) => TemplateContent.Load(Content)?.Result;
+        public Control? Build(object? data) => TemplateContent.Load(Content)?.Result;
     }
     
     public class CustomDataTemplateInherit : CustomDataTemplate { }
 
     public class AssignBindingControl : Control
     {
-        [AssignBinding] public IBinding X { get; set; }
+        [AssignBinding] public IBinding? X { get; set; }
     }
 
     public class DataGridLikeControl : Control
@@ -2046,8 +2049,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 x => x.Items,
                 (x, v) => x.Items = v);
 
-        private IEnumerable _items;
-        public IEnumerable Items
+        private IEnumerable? _items;
+
+        public IEnumerable? Items
         {
             get => _items;
             set => SetAndRaise(ItemsProperty, ref _items, value);
@@ -2060,9 +2064,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     {
         [AssignBinding]
         [InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))]
-        public IBinding Binding { get; set; }
+        public IBinding? Binding { get; set; }
         
         [InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))]
-        public IDataTemplate Template { get; set; }
+        public IDataTemplate? Template { get; set; }
     }
 }

+ 12 - 7
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs

@@ -1,8 +1,9 @@
-using System;
+#nullable enable
+
+using System;
 using System.Collections.Generic;
 using Avalonia.Controls;
 using Avalonia.Media;
-using Avalonia.Metadata;
 using Avalonia.Styling;
 using Avalonia.UnitTests;
 using Xunit;
@@ -40,9 +41,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             {
                 var compiled = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
                 var userControl = Assert.IsType<UserControl>(compiled[1]);
-                var border = userControl.FindControl<Border>("border");
+                var border = userControl.GetControl<Border>("border");
 
-                var brush = (ISolidColorBrush)border.Background;
+                var brush = (ISolidColorBrush)border.Background!;
                 Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
@@ -130,17 +131,21 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     {
         private readonly Dictionary<object, IResourceProvider> _langs = new();
 
-        public IResourceHost Owner { get; private set; }
+        public IResourceHost? Owner { get; private set; }
 
         public bool HasResources => true;
 
-        public event EventHandler OwnerChanged;
+        public event EventHandler? OwnerChanged
+        {
+            add { }
+            remove { }
+        }
 
         public void AddOwner(IResourceHost owner) => Owner = owner;
 
         public void RemoveOwner(IResourceHost owner) => Owner = null;
 
-        public bool TryGetResource(object key, ThemeVariant theme, out object? value)
+        public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
         {
             if (_langs.TryGetValue("English", out var res))
             {

+ 7 - 7
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@@ -1,16 +1,16 @@
+#nullable enable
+
 using System;
 using System.IO;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
-using Avalonia.Controls;
 using Avalonia.Controls.Platform.Surfaces;
-using Avalonia.Controls.Shapes;
-using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using Xunit;
 using Path = System.IO.Path;
+
 #pragma warning disable CS0649
 
 #if AVALONIA_SKIA
@@ -69,7 +69,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
             var fmt = new PixelFormat(fmte);
             var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt;
             var fb = new Framebuffer(fmt, new PixelSize(80, 80));
-            var r = Avalonia.AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
+            var r = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
             using(var cpuContext = r.CreateBackendContext(null))
             using (var target = cpuContext.CreateRenderTarget(new object[] { fb }))
             using (var ctx = target.CreateDrawingContext())
@@ -94,7 +94,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
                     var rc = new Rect(0, 0, 60, 60);
                     ctx.DrawBitmap(bmp.PlatformImpl, 1, rc, rc);
                 }
-                rtb.Save(System.IO.Path.Combine(OutputPath, testName + ".out.png"));
+                rtb.Save(Path.Combine(OutputPath, testName + ".out.png"));
             }
             CompareImagesNoRenderer(testName);
         }
@@ -123,7 +123,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
 
             var name = nameof(WriteableBitmapShouldBeUsable) + "_" + fmt;
 
-            writeableBitmap.Save(System.IO.Path.Combine(OutputPath, name + ".out.png"));
+            writeableBitmap.Save(Path.Combine(OutputPath, name + ".out.png"));
             CompareImagesNoRenderer(name);
 
         }
@@ -177,7 +177,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
 
                 var testName = nameof(BitmapsShouldSupportTranscoders_Lenna) + "_" + formatName + names[step];
 
-                var path = System.IO.Path.Combine(OutputPath, testName + ".out.png");
+                var path = Path.Combine(OutputPath, testName + ".out.png");
                 fixed (byte* pData = data)
                 {
                     Bitmap? b = null;

+ 1 - 1
tests/Avalonia.RenderTests/Media/TileBrushTests.cs

@@ -44,7 +44,6 @@ public class DrawingBrushTests: TestBase
 
 #if AVALONIA_SKIA
     [Fact]
-#endif
     public async Task DrawingBrushIsProperlyUpscaled()
     {
         Decorator target = new Decorator
@@ -66,6 +65,7 @@ public class DrawingBrushTests: TestBase
         await RenderToFile(target);
         CompareImages();
     }
+#endif
 
     GeometryDrawing CreateDrawing()
     {

+ 3 - 1
tests/Avalonia.RenderTests/TestBase.cs

@@ -83,7 +83,7 @@ namespace Avalonia.Direct2D1.RenderTests
             get;
         }
 
-        protected async Task RenderToFile(Control target, [CallerMemberName] string testName = "", double dpi = 96)
+        protected Task RenderToFile(Control target, [CallerMemberName] string testName = "", double dpi = 96)
         {
             if (!Directory.Exists(OutputPath))
             {
@@ -124,6 +124,8 @@ namespace Avalonia.Direct2D1.RenderTests
                 }
                 writableBitmap.Save(compositedPath);
             }
+
+            return Task.CompletedTask;
         }
 
         class BitmapFramebufferSurface : IFramebufferPlatformSurface

+ 71 - 18
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@@ -1,8 +1,9 @@
-using System;
+#nullable enable
+
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using Avalonia.Media;
-using Avalonia.Media.Imaging;
 using Avalonia.Media.TextFormatting;
 using Avalonia.Media.TextFormatting.Unicode;
 using Avalonia.UnitTests;
@@ -30,10 +31,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                     new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Single(textLine.TextRuns);
 
                 var textRun = textLine.TextRuns[0];
 
+                Assert.NotNull(textRun.Properties);
+
                 Assert.Equal(defaultProperties.Typeface, textRun.Properties.Typeface);
 
                 Assert.Equal(defaultProperties.ForegroundBrush, textRun.Properties.ForegroundBrush);
@@ -57,6 +62,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                     new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(5, textLine.TextRuns.Count);
 
                 Assert.Equal(50, textLine.Length);
@@ -120,6 +127,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                     new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(5, textLine.TextRuns.Count);
 
                 Assert.Equal(14, textLine.Length);
@@ -153,6 +162,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                     new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(text.Length, textLine.Length);
 
                 for (var i = 0; i < GenericTextRunPropertiesRuns.Length; i++)
@@ -186,6 +197,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(numberOfRuns, textLine.TextRuns.Count);
             }
         }
@@ -207,6 +220,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(1, textLine.TextRuns.Count);
             }
         }
@@ -228,6 +243,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var firstRun = textLine.TextRuns[0];
 
                 Assert.Equal(4, firstRun.Length);
@@ -249,7 +266,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
 
                 var formatter = new TextFormatterImpl();
 
-                var line = formatter.FormatLine(textSource, 0, 33, paragraphProperties);
+                formatter.FormatLine(textSource, 0, 33, paragraphProperties);
 
                 textSource = new SingleBufferTextSource(text, defaultProperties);
 
@@ -262,6 +279,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     var textLine =
                         formatter.FormatLine(textSource, currentPosition, 1, paragraphProperties);
 
+                    Assert.NotNull(textLine);
+
                     if (text.Length - currentPosition > expectedCharactersPerLine)
                     {
                         Assert.Equal(expectedCharactersPerLine, textLine.Length);
@@ -318,6 +337,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         formatter.FormatLine(textSource, currentPosition, paragraphWidth,
                             new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap));
 
+                    Assert.NotNull(textLine);
+
                     var end = textLine.FirstTextSourceIndex + textLine.Length - 1;
 
                     Assert.True(expected.Contains(end));
@@ -351,6 +372,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties, lineHeight: 50));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(50, textLine.Height);
             }
         }
@@ -381,6 +404,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     var textLine =
                         formatter.FormatLine(textSource, textSourceIndex, 200, paragraphProperties);
 
+                    Assert.NotNull(textLine);
+
                     Assert.True(textLine.Width <= 200);
 
                     textSourceIndex += textLine.Length;
@@ -407,6 +432,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     var textLine =
                         formatter.FormatLine(textSource, textSourceIndex, 3, paragraphProperties);
 
+                    Assert.NotNull(textLine);
+
                     Assert.NotEqual(0, textLine.Length);
 
                     textSourceIndex += textLine.Length;
@@ -454,6 +481,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         formatter.FormatLine(textSource, currentPosition, 300,
                             new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow));
 
+                    Assert.NotNull(textLine);
+
                     currentPosition += textLine.Length;
 
                     if (textLine.Width > 300 || currentHeight + textLine.Height > 240)
@@ -502,6 +531,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine =
                     formatter.FormatLine(textSource, 0, 100, paragraphProperties);
 
+                Assert.NotNull(textLine);
+
                 var expectedOffset = 0d;
 
                 switch (textAlignment)
@@ -534,13 +565,15 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var formatter = new TextFormatterImpl();
 
                 var textPosition = 87;
-                TextLineBreak lastBreak = null;
+                TextLineBreak? lastBreak = null;
 
                 while (textPosition < text.Length)
                 {
                     var textLine =
                         formatter.FormatLine(textSource, textPosition, 50, paragraphProperties, lastBreak);
 
+                    Assert.NotNull(textLine);
+
                     Assert.Equal(textLine.Length, textLine.TextRuns.Sum(x => x.Length));
 
                     textPosition += textLine.Length;
@@ -564,6 +597,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine =
                     formatter.FormatLine(textSource, 0, 33, paragraphProperties);
 
+                Assert.NotNull(textLine);
+
                 var remainingRunsLineBreak = Assert.IsType<WrappingTextLineBreak>(textLine.TextLineBreak);
                 var remainingRuns = remainingRunsLineBreak.AcquireRemainingRuns();
                 Assert.NotNull(remainingRuns);
@@ -592,6 +627,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var expectedTextLine = formatter.FormatLine(new SingleBufferTextSource(text, defaultProperties),
                     0, double.PositiveInfinity, paragraphProperties);
 
+                Assert.NotNull(expectedTextLine);
+
                 var expectedRuns = expectedTextLine.TextRuns.Cast<ShapedTextRun>().ToList();
 
                 var expectedGlyphs = expectedRuns
@@ -613,6 +650,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         var textLine =
                             formatter.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
 
+                        Assert.NotNull(textLine);
+
                         var shapedRuns = textLine.TextRuns.Cast<ShapedTextRun>().ToList();
 
                         var actualGlyphs = shapedRuns
@@ -637,6 +676,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine =
                     TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(3, textLine.TextRuns.Count);
 
                 Assert.True(textLine.TextRuns[1] is RectangleRun);
@@ -655,6 +696,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine =
                     TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
 
+                Assert.NotNull(textLine);
+
                 Assert.NotNull(textLine.TextLineBreak);
 
                 Assert.Equal(TextRun.DefaultTextSourceLength, textLine.Length);
@@ -690,20 +733,22 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
 
                 var pos = 0;
 
-                TextLineBreak previousLineBreak = null;
-                TextLine textLine = null;
+                TextLineBreak? previousLineBreak = null;
+                TextLine? textLine = null;
 
                 while (pos < text.Length)
                 {
                     textLine = TextFormatter.Current.FormatLine(textSource, pos, 30, paragraphProperties, previousLineBreak);
 
+                    Assert.NotNull(textLine);
+
                     pos += textLine.Length;
 
                     previousLineBreak = textLine.TextLineBreak;
                 }
 
                 Assert.NotNull(textLine);
-
+                Assert.NotNull(textLine.TextLineBreak);
                 Assert.NotNull(textLine.TextLineBreak.TextEndOfLine);
             }
         }
@@ -729,6 +774,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine =
                     TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties);
 
+                Assert.NotNull(textLine);
+
                 void VerifyHit(int offset)
                 {
                     var glyphCenter = textLine.GetTextBounds(offset, 1)[0].Rectangle.Center;
@@ -756,6 +803,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var textLine =
                     TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties);
 
+                Assert.NotNull(textLine);
+
                 var bounds = textLine.GetTextBounds(0, 3);
 
                 Assert.Equal(1, bounds.Count);
@@ -779,6 +828,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
             {
                 var source = new ListTextSource(new RectangleRun(new Rect(0, 0, 200, 10), Brushes.Aqua));
                 var textLine = TextFormatter.Current.FormatLine(source, 0, 100, paragraphProperties);
+                Assert.NotNull(textLine);
                 Assert.Equal(200d, textLine.WidthIncludingTrailingWhitespace);
             }
         }
@@ -822,6 +872,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     if (textLine.TextLineBreak is {} eol && eol.TextEndOfLine is TextEndOfParagraph)
                         break;
                 }
+
+                Assert.NotEmpty(lines);
             }
         }
 
@@ -832,13 +884,13 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 DefaultTextRunProperties = defaultTextRunProperties;
             }
 
-            public override FlowDirection FlowDirection { get; }
-            public override TextAlignment TextAlignment { get; }
-            public override double LineHeight { get; }
-            public override bool FirstLineInParagraph { get; }
+            public override FlowDirection FlowDirection => default;
+            public override TextAlignment TextAlignment => default;
+            public override double LineHeight => default;
+            public override bool FirstLineInParagraph => default;
             public override TextRunProperties DefaultTextRunProperties { get; }
-            public override TextWrapping TextWrapping { get; }
-            public override double Indent { get; }
+            public override TextWrapping TextWrapping => default;
+            public override double Indent => default;
             public override double DefaultIncrementalTab => 64;
         }
         
@@ -858,12 +910,12 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 var source = new ListTextSource(text);
                 
                 var textLine = TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties);
+                Assert.NotNull(textLine);
                 
                 var backspaceHit = textLine.GetBackspaceCaretCharacterHit(new CharacterHit(2));
                 Assert.Equal(1, backspaceHit.FirstCharacterIndex);
                 Assert.Equal(0, backspaceHit.TrailingLength);
             }
-            
         }
 
         [Fact]
@@ -878,6 +930,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
 
                 var textLine = TextFormatter.Current.FormatLine(new SimpleTextSource(text, defaultRunProperties), 0, 120, paragraphProperties);
 
+                Assert.NotNull(textLine);
                 Assert.Equal(3, textLine.TextRuns.Count);
             }
         }
@@ -913,7 +966,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
 
         private class EmptyTextSource : ITextSource
         {
-            public TextRun GetTextRun(int textSourceIndex)
+            public TextRun? GetTextRun(int textSourceIndex)
             {
                 return null;
             }
@@ -936,7 +989,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 _text = text;
             }
 
-            public TextRun GetTextRun(int textSourceIndex)
+            public TextRun? GetTextRun(int textSourceIndex)
             {
                 if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length)
                 {
@@ -954,7 +1007,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
         
         private class ListTextSource : ITextSource
         {
-            private List<TextRun> _runs = new();
+            private readonly List<TextRun> _runs;
 
             public ListTextSource(params TextRun[] runs) : this((IEnumerable<TextRun>)runs)
             {
@@ -966,7 +1019,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 _runs = runs.ToList();
             }
             
-            public TextRun GetTextRun(int textSourceIndex)
+            public TextRun? GetTextRun(int textSourceIndex)
             {
                 var off = 0;
                 for (var c = 0; c < _runs.Count; c++)

+ 68 - 10
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
@@ -33,6 +35,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity,
                             new GenericTextParagraphProperties(defaultProperties));
 
+                    Assert.NotNull(textLine);
+
                     var firstCharacterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(int.MinValue));
 
                     Assert.Equal(textLine.FirstTextSourceIndex, firstCharacterHit.FirstCharacterIndex);
@@ -61,6 +65,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity,
                             new GenericTextParagraphProperties(defaultProperties));
 
+                    Assert.NotNull(textLine);
+
                     var lastCharacterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(int.MaxValue));
 
                     Assert.Equal(textLine.FirstTextSourceIndex + textLine.Length,
@@ -88,6 +94,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var clusters = new List<int>();
 
                 foreach (var textRun in textLine.TextRuns.OrderBy(x => TextTestHelper.GetStartCharIndex(x.Text)))
@@ -135,6 +143,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var clusters = new List<int>();
 
                 foreach (var textRun in textLine.TextRuns.OrderBy(x => TextTestHelper.GetStartCharIndex(x.Text)))
@@ -187,6 +197,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var clusters = BuildGlyphClusters(textLine);
 
                 var nextCharacterHit = new CharacterHit(0);
@@ -246,6 +258,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var clusters = textLine.TextRuns
                     .Cast<ShapedTextRun>()
                     .SelectMany(x => x.ShapedBuffer, (_, glyph) => glyph.GlyphCluster)
@@ -296,6 +310,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var currentDistance = 0.0;
 
                 foreach (var run in textLine.TextRuns)
@@ -341,6 +357,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var isRightToLeft = IsRightToLeft(textLine);
                 var rects = BuildRects(textLine);
                 var glyphClusters = BuildGlyphClusters(textLine);
@@ -394,6 +412,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.False(textLine.HasCollapsed);
 
                 TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties, FlowDirection.LeftToRight));
@@ -427,6 +447,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(4, textLine.TextRuns.Count);
 
                 var currentHit = textLine.GetNextCaretCharacterHit(new CharacterHit(0));
@@ -465,6 +487,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 Assert.Equal(4, textLine.TextRuns.Count);
 
                 var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3, 1));
@@ -503,6 +527,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var characterHit = textLine.GetCharacterHitFromDistance(50);
 
                 Assert.Equal(5, characterHit.FirstCharacterIndex);
@@ -529,6 +555,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(1));
 
                 Assert.Equal(14, distance);
@@ -553,6 +581,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(10));
 
                 Assert.Equal(72.01171875, distance);
@@ -585,6 +615,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var textBounds = textLine.GetTextBounds(0, 10);
 
                 Assert.Equal(1, textBounds.Count);
@@ -625,6 +657,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var textBounds = textLine.GetTextBounds(0, Environment.NewLine.Length);
 
                 Assert.Equal(1, textBounds.Count);
@@ -652,13 +686,15 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var textRuns = textLine.TextRuns.Cast<ShapedTextRun>().ToList();
 
                 var lineWidth = textLine.WidthIncludingTrailingWhitespace;
 
                 var textBounds = textLine.GetTextBounds(0, text.Length);
 
-                TextBounds lastBounds = null;
+                TextBounds? lastBounds = null;
 
                 var runBounds = textBounds.SelectMany(x => x.TextRunBounds).ToList();
 
@@ -711,6 +747,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, 1000,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var characterHit = textLine.GetCharacterHitFromDistance(1000);
 
                 Assert.Equal(10, characterHit.FirstCharacterIndex);
@@ -732,6 +770,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var characterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(9, 1));
 
                 Assert.Equal(10, characterHit.FirstCharacterIndex);
@@ -784,6 +824,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(20, 1));
 
                 Assert.Equal(20, characterHit.FirstCharacterIndex);
@@ -836,6 +878,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 20, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var characterHit = textLine.GetCharacterHitFromDistance(double.PositiveInfinity);
 
                 Assert.Equal(40, characterHit.FirstCharacterIndex + characterHit.TrailingLength);
@@ -866,7 +910,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
         {
             private const string Text = "_A_A";
 
-            public TextRun GetTextRun(int textSourceIndex)
+            public TextRun? GetTextRun(int textSourceIndex)
             {
                 switch (textSourceIndex)
                 {
@@ -1004,6 +1048,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                     formatter.FormatLine(textSource, 0, double.PositiveInfinity,
                         new GenericTextParagraphProperties(defaultProperties));
 
+                Assert.NotNull(textLine);
+
                 var textBounds = textLine.GetTextBounds(0, textLine.Length);
 
                 Assert.Equal(1, textBounds.Count);
@@ -1047,16 +1093,18 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
                         true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
 
+                Assert.NotNull(textLine);
+
                 var textBounds = textLine.GetTextBounds(0, 3);
 
-                var firstRun = textLine.TextRuns[0] as ShapedTextRun;
+                var firstRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[0]);
 
                 Assert.Equal(1, textBounds.Count);
                 Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
 
                 textBounds = textLine.GetTextBounds(3, 4);
 
-                var secondRun = textLine.TextRuns[1] as ShapedTextRun;
+                var secondRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[1]);
 
                 Assert.Equal(1, textBounds.Count);
                 Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
@@ -1094,16 +1142,18 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left,
                         true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
 
+                Assert.NotNull(textLine);
+
                 var textBounds = textLine.GetTextBounds(0, 4);
 
-                var secondRun = textLine.TextRuns[1] as ShapedTextRun;
+                var secondRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[1]);
 
                 Assert.Equal(1, textBounds.Count);
                 Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
 
                 textBounds = textLine.GetTextBounds(4, 3);
 
-                var firstRun = textLine.TextRuns[0] as ShapedTextRun;
+                var firstRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[0]);
 
                 Assert.Equal(1, textBounds.Count);
 
@@ -1146,6 +1196,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
                         true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
 
+                Assert.NotNull(textLine);
+
                 var textBounds = textLine.GetTextBounds(0, 1);
 
                 Assert.Equal(1, textBounds.Count);
@@ -1173,6 +1225,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
                         true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
 
+                Assert.NotNull(textLine);
+
                 var textBounds = textLine.GetTextBounds(3, 1);
 
                 Assert.Equal(1, textBounds.Count);
@@ -1200,6 +1254,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
                         true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
 
+                Assert.NotNull(textLine);
+
                 var bounds = textLine.GetTextBounds(6, 1);
 
                 Assert.Equal(1, bounds.Count);
@@ -1249,6 +1305,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
                         true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
 
+                Assert.NotNull(textLine);
+
                 var bounds = textLine.GetTextBounds(0, text.Length);
 
                 Assert.Equal(4, bounds.Count);
@@ -1259,8 +1317,6 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
             }
         }
 
-       
-
         [Fact]
         public void Should_GetPreviousCharacterHit_Non_Trailing()
         {
@@ -1278,6 +1334,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                         new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
                         true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
 
+                Assert.NotNull(textLine);
+
                 var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(10, 1));
             }
         }
@@ -1291,7 +1349,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
                 _textRuns = textRuns;
             }
 
-            public TextRun GetTextRun(int textSourceIndex)
+            public TextRun? GetTextRun(int textSourceIndex)
             {
                 var currentPosition = 0;