Browse Source

Merge branch 'master' into robloo-SplitButton

pull/7422/head
Max Katz 4 years ago
committed by GitHub
parent
commit
509a856ea0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 41
      Avalonia.sln
  2. 1
      build/CoreLibraries.props
  3. 2
      build/SharedVersion.props
  4. 4
      samples/ControlCatalog.Android/Assets/AboutAssets.txt
  5. 220
      samples/ControlCatalog/Pages/PointersPage.cs
  6. 37
      samples/ControlCatalog/Pages/ScreenPage.cs
  7. 22
      samples/ControlCatalog/Pages/ViewboxPage.xaml
  8. 19
      samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
  9. 3
      samples/RenderDemo/MainWindow.xaml
  10. 54
      samples/RenderDemo/Pages/ClippingPage.xaml
  11. 17
      samples/RenderDemo/Pages/ClippingPage.xaml.cs
  12. 13
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  13. 7
      samples/RenderDemo/Pages/FormattedTextPage.axaml
  14. 60
      samples/RenderDemo/Pages/FormattedTextPage.axaml.cs
  15. 7
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  16. 5
      src/Android/Avalonia.Android/AndroidPlatform.cs
  17. 2
      src/Android/Avalonia.Android/AppBuilder.cs
  18. 4
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  19. 18
      src/Android/Avalonia.Android/RuntimeInfo.cs
  20. 1
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  21. 5
      src/Avalonia.Base/Platform/IRuntimePlatform.cs
  22. 7
      src/Avalonia.Base/Threading/Dispatcher.cs
  23. 15
      src/Avalonia.Base/Threading/JobRunner.cs
  24. 2
      src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs
  25. 9
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  26. 6
      src/Avalonia.Controls/ApiCompatBaseline.txt
  27. 35
      src/Avalonia.Controls/AppBuilderBase.cs
  28. 5
      src/Avalonia.Controls/Application.cs
  29. 26
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  30. 6
      src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
  31. 2
      src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs
  32. 309
      src/Avalonia.Controls/AutoCompleteBox.cs
  33. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  34. 12
      src/Avalonia.Controls/Border.cs
  35. 225
      src/Avalonia.Controls/Button.cs
  36. 14
      src/Avalonia.Controls/ButtonSpinner.cs
  37. 76
      src/Avalonia.Controls/Calendar/Calendar.cs
  38. 6
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  39. 87
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  40. 2
      src/Avalonia.Controls/Calendar/CalendarDateRange.cs
  41. 6
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  42. 100
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  43. 2
      src/Avalonia.Controls/Calendar/DateTimeHelper.cs
  44. 4
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  45. 2
      src/Avalonia.Controls/Canvas.cs
  46. 2
      src/Avalonia.Controls/Chrome/CaptionButtons.cs
  47. 4
      src/Avalonia.Controls/Chrome/TitleBar.cs
  48. 40
      src/Avalonia.Controls/ComboBox.cs
  49. 14
      src/Avalonia.Controls/ContentControl.cs
  50. 14
      src/Avalonia.Controls/ContextMenu.cs
  51. 2
      src/Avalonia.Controls/ContextRequestedEventArgs.cs
  52. 23
      src/Avalonia.Controls/Control.cs
  53. 35
      src/Avalonia.Controls/ControlExtensions.cs
  54. 3
      src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
  55. 4
      src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
  56. 2
      src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
  57. 4
      src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
  58. 29
      src/Avalonia.Controls/DataValidationErrors.cs
  59. 60
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  60. 103
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  61. 28
      src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
  62. 6
      src/Avalonia.Controls/DateTimePickers/PickerPresenterBase.cs
  63. 56
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  64. 75
      src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
  65. 10
      src/Avalonia.Controls/Decorator.cs
  66. 40
      src/Avalonia.Controls/DefinitionBase.cs
  67. 6
      src/Avalonia.Controls/DefinitionList.cs
  68. 2
      src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
  69. 4
      src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs
  70. 20
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  71. 3
      src/Avalonia.Controls/Expander.cs
  72. 7
      src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
  73. 2
      src/Avalonia.Controls/Flyouts/Flyout.cs
  74. 20
      src/Avalonia.Controls/Flyouts/FlyoutBase.cs
  75. 8
      src/Avalonia.Controls/Flyouts/MenuFlyout.cs
  76. 13
      src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs
  77. 12
      src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
  78. 2
      src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
  79. 26
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  80. 11
      src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
  81. 2
      src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs
  82. 12
      src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
  83. 8
      src/Avalonia.Controls/Generators/TreeContainerIndex.cs
  84. 23
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  85. 165
      src/Avalonia.Controls/Grid.cs
  86. 2
      src/Avalonia.Controls/GridLength.cs
  87. 62
      src/Avalonia.Controls/GridSplitter.cs
  88. 40
      src/Avalonia.Controls/HotkeyManager.cs
  89. 6
      src/Avalonia.Controls/IContentControl.cs
  90. 2
      src/Avalonia.Controls/IControl.cs
  91. 2
      src/Avalonia.Controls/IHeadered.cs
  92. 2
      src/Avalonia.Controls/IMenuElement.cs
  93. 4
      src/Avalonia.Controls/IMenuItem.cs
  94. 4
      src/Avalonia.Controls/IScrollAnchorProvider.cs
  95. 2
      src/Avalonia.Controls/IVirtualizingPanel.cs
  96. 56
      src/Avalonia.Controls/ItemsControl.cs
  97. 8
      src/Avalonia.Controls/ItemsSourceView.cs
  98. 8
      src/Avalonia.Controls/Label.cs
  99. 14
      src/Avalonia.Controls/LayoutTransformControl.cs
  100. 12
      src/Avalonia.Controls/ListBox.cs

41
Avalonia.sln

@ -60,20 +60,17 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}"
EndProject EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "PlatformSupport", "src\Shared\PlatformSupport\PlatformSupport.shproj", "{E4D9629C-F168-4224-3F51-A5E482FFBC42}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup", "src\Markup\Avalonia.Markup\Avalonia.Markup.csproj", "{6417E941-21BC-467B-A771-0DE389353CE6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup", "src\Markup\Avalonia.Markup\Avalonia.Markup.csproj", "{6417E941-21BC-467B-A771-0DE389353CE6}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.UnitTests", "tests\Avalonia.Markup.UnitTests\Avalonia.Markup.UnitTests.csproj", "{8EF392D5-1416-45AA-9956-7CBBC3229E8A}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.UnitTests", "tests\Avalonia.Markup.UnitTests\Avalonia.Markup.UnitTests.csproj", "{8EF392D5-1416-45AA-9956-7CBBC3229E8A}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BindingDemo", "samples\BindingDemo\BindingDemo.csproj", "{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BindingDemo", "samples\BindingDemo\BindingDemo.csproj", "{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}"
EndProject EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "RenderHelpers", "src\Shared\RenderHelpers\RenderHelpers.shproj", "{3C4C0CB4-0C0F-4450-A37B-148C84FF905F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skia", "Skia", "{3743B0F2-CC41-4F14-A8C8-267F579BF91E}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skia", "Skia", "{3743B0F2-CC41-4F14-A8C8-267F579BF91E}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}"
@ -235,15 +232,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "sampl
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\SampleControls\ControlSamples.csproj", "{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\SampleControls\ControlSamples.csproj", "{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}"
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 5
src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
Ad-Hoc|iPhone = Ad-Hoc|iPhone Ad-Hoc|iPhone = Ad-Hoc|iPhone
@ -2169,6 +2160,30 @@ Global
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.Build.0 = Release|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.Build.0 = Release|Any CPU
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.Build.0 = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.ActiveCfg = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.Build.0 = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -2187,11 +2202,9 @@ Global
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C} {3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{E4D9629C-F168-4224-3F51-A5E482FFBC42} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{6417E941-21BC-467B-A771-0DE389353CE6} = {8B6A8209-894F-4BA1-B880-965FD453982C} {6417E941-21BC-467B-A771-0DE389353CE6} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098} {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{3C4C0CB4-0C0F-4450-A37B-148C84FF905F} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{7B92AF71-6287-4693-9DCB-BD5B6E927E23} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} {7B92AF71-6287-4693-9DCB-BD5B6E927E23} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}
{FF69B927-C545-49AE-8E16-3D14D621AA12} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} {FF69B927-C545-49AE-8E16-3D14D621AA12} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}
{4488AD85-1495-4809-9AA4-DDFE0A48527E} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1} {4488AD85-1495-4809-9AA4-DDFE0A48527E} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}

1
build/CoreLibraries.props

@ -17,5 +17,6 @@
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.MicroCom/Avalonia.MicroCom.csproj" /> <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.MicroCom/Avalonia.MicroCom.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj" Condition="'$(TargetFramework)' != 'netstandard2.0'" /> <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
build/SharedVersion.props

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Avalonia</Product> <Product>Avalonia</Product>
<Version>0.10.999</Version> <Version>0.10.999</Version>
<Copyright>Copyright 2021 &#169; The AvaloniaUI Project</Copyright> <Copyright>Copyright 2022 &#169; The AvaloniaUI Project</Copyright>
<PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl> <PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl>
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl> <RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>

4
samples/ControlCatalog.Android/Assets/AboutAssets.txt

@ -1,7 +1,7 @@
Any raw assets you want to be deployed with your application can be placed in Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset". this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with you package and will be accessible using Android's These files will be deployed with your package and will be accessible using Android's
AssetManager, like this: AssetManager, like this:
public class ReadAsset : Activity public class ReadAsset : Activity
@ -16,4 +16,4 @@ public class ReadAsset : Activity
Additionally, some Android functions will automatically load asset files: Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

220
samples/ControlCatalog/Pages/PointersPage.cs

@ -1,15 +1,37 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages;
public class PointersPage : Decorator
{ {
public class PointersPage : Control public PointersPage()
{
Child = new TabControl
{
Items = new[]
{
new TabItem() { Header = "Contacts", Content = new PointerContactsTab() },
new TabItem() { Header = "IntermediatePoints", Content = new PointerIntermediatePointsTab() }
}
};
}
class PointerContactsTab : Control
{ {
class PointerInfo class PointerInfo
{ {
@ -45,7 +67,7 @@ namespace ControlCatalog.Pages
private Dictionary<IPointer, PointerInfo> _pointers = new Dictionary<IPointer, PointerInfo>(); private Dictionary<IPointer, PointerInfo> _pointers = new Dictionary<IPointer, PointerInfo>();
public PointersPage() public PointerContactsTab()
{ {
ClipToBounds = true; ClipToBounds = true;
} }
@ -104,4 +126,196 @@ namespace ControlCatalog.Pages
} }
} }
} }
public class PointerIntermediatePointsTab : Decorator
{
public PointerIntermediatePointsTab()
{
this[TextBlock.ForegroundProperty] = Brushes.Black;
var slider = new Slider
{
Margin = new Thickness(5),
Minimum = 0,
Maximum = 500
};
var status = new TextBlock()
{
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
};
Child = new Grid
{
Children =
{
new PointerCanvas(slider, status),
new Border
{
Background = Brushes.LightYellow,
Child = new StackPanel
{
Children =
{
new StackPanel
{
Orientation = Orientation.Horizontal,
Children =
{
new TextBlock { Text = "Thread sleep:" },
new TextBlock()
{
[!TextBlock.TextProperty] =slider.GetObservable(Slider.ValueProperty)
.Select(x=>x.ToString()).ToBinding()
}
}
},
slider
}
},
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top,
Width = 300,
Height = 60
},
status
}
};
}
class PointerCanvas : Control
{
private readonly Slider _slider;
private readonly TextBlock _status;
private int _events;
private Stopwatch _stopwatch = Stopwatch.StartNew();
private Dictionary<int, PointerPoints> _pointers = new();
class PointerPoints
{
struct CanvasPoint
{
public IBrush Brush;
public Point Point;
public double Radius;
}
readonly CanvasPoint[] _points = new CanvasPoint[1000];
int _index;
public void Render(DrawingContext context)
{
CanvasPoint? prev = null;
for (var c = 0; c < _points.Length; c++)
{
var i = (c + _index) % _points.Length;
var pt = _points[i];
if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null)
context.DrawLine(new Pen(Brushes.Black), prev.Value.Point, pt.Point);
prev = pt;
if (pt.Brush != null)
context.DrawEllipse(pt.Brush, null, pt.Point, pt.Radius, pt.Radius);
}
}
void AddPoint(Point pt, IBrush brush, double radius)
{
_points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius };
_index = (_index + 1) % _points.Length;
}
public void HandleEvent(PointerEventArgs e, Visual v)
{
e.Handled = true;
if (e.RoutedEvent == PointerPressedEvent)
AddPoint(e.GetPosition(v), Brushes.Green, 10);
else if (e.RoutedEvent == PointerReleasedEvent)
AddPoint(e.GetPosition(v), Brushes.Red, 10);
else
{
var pts = e.GetIntermediatePoints(v);
for (var c = 0; c < pts.Count; c++)
{
var pt = pts[c];
AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black,
c == pts.Count - 1 ? 5 : 2);
}
}
}
}
public PointerCanvas(Slider slider, TextBlock status)
{
_slider = slider;
_status = status;
DispatcherTimer.Run(() =>
{
if (_stopwatch.Elapsed.TotalSeconds > 1)
{
_status.Text = "Events per second: " + (_events / _stopwatch.Elapsed.TotalSeconds);
_stopwatch.Restart();
_events = 0;
}
return this.GetVisualRoot() != null;
}, TimeSpan.FromMilliseconds(10));
}
void HandleEvent(PointerEventArgs e)
{
_events++;
Thread.Sleep((int)_slider.Value);
InvalidateVisual();
if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch)
{
_pointers.Remove(e.Pointer.Id);
return;
}
if (!_pointers.TryGetValue(e.Pointer.Id, out var pt))
_pointers[e.Pointer.Id] = pt = new PointerPoints();
pt.HandleEvent(e, this);
}
public override void Render(DrawingContext context)
{
context.FillRectangle(Brushes.White, Bounds);
foreach(var pt in _pointers.Values)
pt.Render(context);
base.Render(context);
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (e.ClickCount == 2)
{
_pointers.Clear();
InvalidateVisual();
return;
}
HandleEvent(e);
base.OnPointerPressed(e);
}
protected override void OnPointerMoved(PointerEventArgs e)
{
HandleEvent(e);
base.OnPointerMoved(e);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
HandleEvent(e);
base.OnPointerReleased(e);
}
}
}
} }

37
samples/ControlCatalog/Pages/ScreenPage.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Globalization;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
@ -49,25 +50,33 @@ namespace ControlCatalog.Pages
context.DrawRectangle(p, boundsRect); context.DrawRectangle(p, boundsRect);
context.DrawRectangle(p, workingAreaRect); context.DrawRectangle(p, workingAreaRect);
var text = new FormattedText() { Typeface = new Typeface("Arial"), FontSize = 18 };
text.Text = $"Bounds: {screen.Bounds.TopLeft} {screen.Bounds.Width}:{screen.Bounds.Height}"; var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}");
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height));
text.Text = $"WorkArea: {screen.WorkingArea.TopLeft} {screen.WorkingArea.Width}:{screen.WorkingArea.Height}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text);
text.Text = $"Scaling: {screen.PixelDensity * 100}%"; formattedText =
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text); CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20));
text.Text = $"Primary: {screen.Primary}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text); formattedText = CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text); formattedText = CreateFormattedText($"Primary: {screen.Primary}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60));
formattedText =
CreateFormattedText(
$"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 80));
} }
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10)); context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10));
} }
private FormattedText CreateFormattedText(string textToFormat)
{
return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
Typeface.Default, 12, Brushes.Green);
}
} }
} }

22
samples/ControlCatalog/Pages/ViewboxPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:collections="clr-namespace:System.Collections;assembly=netstandard"
x:Class="ControlCatalog.Pages.ViewboxPage"> x:Class="ControlCatalog.Pages.ViewboxPage">
<Grid RowDefinitions="Auto,*,*"> <Grid RowDefinitions="Auto,*,*">
@ -12,8 +13,8 @@
<Border HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="Orange" Width="200" Height="200"> <Border HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="Orange" Width="200" Height="200">
<Border VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="CornflowerBlue" Width="{Binding #WidthSlider.Value}" Height="{Binding #HeightSlider.Value}" > <Border VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="CornflowerBlue" Width="{Binding #WidthSlider.Value}" Height="{Binding #HeightSlider.Value}" >
<Viewbox <Viewbox
Stretch="{Binding #StretchSelector.SelectedItem}" Stretch="{Binding #StretchSelector.SelectedItem, FallbackValue={x:Static Stretch.Uniform}}"
StretchDirection="{Binding #StretchDirectionSelector.SelectedItem}"> StretchDirection="{Binding #StretchDirectionSelector.SelectedItem, FallbackValue={x:Static StretchDirection.Both}}">
<Ellipse Width="50" Height="50" Fill="CornflowerBlue" /> <Ellipse Width="50" Height="50" Fill="CornflowerBlue" />
</Viewbox> </Viewbox>
</Border> </Border>
@ -25,9 +26,22 @@
<TextBlock Text="Height" /> <TextBlock Text="Height" />
<Slider Minimum="10" Maximum="200" Value="100" x:Name="HeightSlider" TickFrequency="25" TickPlacement="TopLeft" /> <Slider Minimum="10" Maximum="200" Value="100" x:Name="HeightSlider" TickFrequency="25" TickPlacement="TopLeft" />
<TextBlock Text="Stretch" /> <TextBlock Text="Stretch" />
<ComboBox x:Name="StretchSelector" HorizontalAlignment="Stretch" Margin="0,0,0,2" /> <ComboBox x:Name="StretchSelector" HorizontalAlignment="Stretch" SelectedIndex="0" Margin="0,0,0,2">
<collections:ArrayList>
<Stretch>Uniform</Stretch>
<Stretch>UniformToFill</Stretch>
<Stretch>Fill</Stretch>
<Stretch>None</Stretch>
</collections:ArrayList>
</ComboBox>
<TextBlock Text="Stretch Direction" /> <TextBlock Text="Stretch Direction" />
<ComboBox x:Name="StretchDirectionSelector" HorizontalAlignment="Stretch" /> <ComboBox x:Name="StretchDirectionSelector" SelectedIndex="0" HorizontalAlignment="Stretch">
<collections:ArrayList>
<StretchDirection>Both</StretchDirection>
<StretchDirection>DownOnly</StretchDirection>
<StretchDirection>UpOnly</StretchDirection>
</collections:ArrayList>
</ComboBox>
</StackPanel> </StackPanel>
</Grid> </Grid>

19
samples/ControlCatalog/Pages/ViewboxPage.xaml.cs

@ -1,6 +1,5 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -9,24 +8,6 @@ namespace ControlCatalog.Pages
public ViewboxPage() public ViewboxPage()
{ {
InitializeComponent(); InitializeComponent();
var stretchSelector = this.FindControl<ComboBox>("StretchSelector");
stretchSelector.Items = new[]
{
Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None
};
stretchSelector.SelectedIndex = 0;
var stretchDirectionSelector = this.FindControl<ComboBox>("StretchDirectionSelector");
stretchDirectionSelector.Items = new[]
{
StretchDirection.Both, StretchDirection.DownOnly, StretchDirection.UpOnly
};
stretchDirectionSelector.SelectedIndex = 0;
} }
private void InitializeComponent() private void InitializeComponent()

3
samples/RenderDemo/MainWindow.xaml

@ -57,6 +57,9 @@
<TabItem Header="GlyphRun"> <TabItem Header="GlyphRun">
<pages:GlyphRunPage /> <pages:GlyphRunPage />
</TabItem> </TabItem>
<TabItem Header="FormattedText">
<pages:FormattedTextPage />
</TabItem>
<TabItem Header="LineBounds"> <TabItem Header="LineBounds">
<pages:LineBoundsPage /> <pages:LineBoundsPage />
</TabItem> </TabItem>

54
samples/RenderDemo/Pages/ClippingPage.xaml

@ -19,30 +19,36 @@
</Style> </Style>
</Styles> </Styles>
</Grid.Styles> </Grid.Styles>
<Border Name="clipped" <Border Name="clipped"
Background="Yellow" Background="Yellow"
Width="100" Width="100"
Height="100" Height="100"
Clip="M 58.625 0.07421875 Classes.clip="{Binding #useMask.IsChecked}">
C 50.305778 0.26687364 42.411858 7.0346526 41.806641 15.595703 <Border.Styles>
C 42.446442 22.063923 39.707425 13.710754 36.982422 12.683594 <Style Selector="Border.clip">
C 29.348395 6.1821635 16.419398 8.4359222 11.480469 17.195312 <Setter Property="Clip"
C 6.0935256 25.476803 9.8118851 37.71125 18.8125 41.6875 Value="M 58.625 0.07421875
C 9.1554771 40.62945 -0.070876925 49.146842 0.21679688 58.857422 C 50.305778 0.26687364 42.411858 7.0346526 41.806641 15.595703
C 0.21545578 60.872512 0.56758794 62.88911 1.2617188 64.78125 C 42.446442 22.063923 39.707425 13.710754 36.982422 12.683594
C 4.3821886 74.16708 16.298268 78.921772 25.03125 74.326172 C 29.348395 6.1821635 16.419398 8.4359222 11.480469 17.195312
C 28.266843 72.062552 26.298191 74.214838 25.414062 76.398438 C 6.0935256 25.476803 9.8118851 37.71125 18.8125 41.6875
C 21.407348 85.589198 27.295992 97.294293 37.097656 99.501953 C 9.1554771 40.62945 -0.070876925 49.146842 0.21679688 58.857422
C 46.864883 102.3541 57.82177 94.726518 58.539062 84.580078 C 0.21545578 60.872512 0.56758794 62.88911 1.2617188 64.78125
C 58.142158 79.498998 59.307538 83.392694 61.207031 85.433594 C 4.3821886 74.16708 16.298268 78.921772 25.03125 74.326172
C 67.532324 93.056874 80.440232 93.192029 86.882812 85.630859 C 28.266843 72.062552 26.298191 74.214838 25.414062 76.398438
C 93.836392 78.456939 92.396838 65.538666 84.115234 60.009766 C 21.407348 85.589198 27.295992 97.294293 37.097656 99.501953
C 79.783641 57.904836 83.569793 58.802369 86.375 58.193359 C 46.864883 102.3541 57.82177 94.726518 58.539062 84.580078
C 96.383335 56.457569 102.87506 44.824101 99.083984 35.394531 C 58.142158 79.498998 59.307538 83.392694 61.207031 85.433594
C 95.963498 26.008711 84.047451 21.254079 75.314453 25.849609 C 67.532324 93.056874 80.440232 93.192029 86.882812 85.630859
C 72.078834 28.113269 74.047517 25.960974 74.931641 23.777344 C 93.836392 78.456939 92.396838 65.538666 84.115234 60.009766
C 78.93827 14.586564 73.049722 2.8815081 63.248047 0.67382812 C 79.783641 57.904836 83.569793 58.802369 86.375 58.193359
C 61.721916 0.22817968 60.165597 0.038541919 58.625 0.07421875 z "> C 96.383335 56.457569 102.87506 44.824101 99.083984 35.394531
C 95.963498 26.008711 84.047451 21.254079 75.314453 25.849609
C 72.078834 28.113269 74.047517 25.960974 74.931641 23.777344
C 78.93827 14.586564 73.049722 2.8815081 63.248047 0.67382812
C 61.721916 0.22817968 60.165597 0.038541919 58.625 0.07421875 z " />
</Style>
</Border.Styles>
<Border Name="clipChild" Background="Red" Margin="4"> <Border Name="clipChild" Background="Red" Margin="4">
<!-- Setting opacity puts the TextBox on a new layer --> <!-- Setting opacity puts the TextBox on a new layer -->
<TextBox Text="Avalonia" Opacity="0.9" VerticalAlignment="Center"/> <TextBox Text="Avalonia" Opacity="0.9" VerticalAlignment="Center"/>

17
samples/RenderDemo/Pages/ClippingPage.xaml.cs

@ -1,35 +1,18 @@
using System;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace RenderDemo.Pages namespace RenderDemo.Pages
{ {
public class ClippingPage : UserControl public class ClippingPage : UserControl
{ {
private Geometry _clip;
public ClippingPage() public ClippingPage()
{ {
InitializeComponent(); InitializeComponent();
WireUpCheckbox();
} }
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private void WireUpCheckbox()
{
var useMask = this.FindControl<CheckBox>("useMask");
var clipped = this.FindControl<Border>("clipped");
_clip = clipped.Clip;
useMask.Click += (s, e) => clipped.Clip = clipped.Clip == null ? _clip : null;
}
} }
} }

13
samples/RenderDemo/Pages/CustomSkiaPage.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
@ -41,7 +42,10 @@ namespace RenderDemo.Pages
{ {
var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas; var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
if (canvas == null) if (canvas == null)
context.DrawText(Brushes.Black, new Point(), _noSkia.PlatformImpl); using (var c = new DrawingContext(context, false))
{
c.DrawText(_noSkia, new Point());
}
else else
{ {
canvas.Save(); canvas.Save();
@ -108,10 +112,9 @@ namespace RenderDemo.Pages
public override void Render(DrawingContext context) public override void Render(DrawingContext context)
{ {
var noSkia = new FormattedText() var noSkia = new FormattedText("Current rendering API is not Skia", CultureInfo.CurrentCulture,
{ FlowDirection.LeftToRight, Typeface.Default, 12, Brushes.Black);
Text = "Current rendering API is not Skia"
};
context.Custom(new CustomDrawOp(new Rect(0, 0, Bounds.Width, Bounds.Height), noSkia)); context.Custom(new CustomDrawOp(new Rect(0, 0, Bounds.Width, Bounds.Height), noSkia));
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background); Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
} }

7
samples/RenderDemo/Pages/FormattedTextPage.axaml

@ -0,0 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="RenderDemo.Pages.FormattedTextPage">
</UserControl>

60
samples/RenderDemo/Pages/FormattedTextPage.axaml.cs

@ -0,0 +1,60 @@
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace RenderDemo.Pages
{
public class FormattedTextPage : UserControl
{
public FormattedTextPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public override void Render(DrawingContext context)
{
const string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor";
// Create the initial formatted text string.
var formattedText = new FormattedText(
testString,
CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface("Verdana"),
32,
Brushes.Black) { MaxTextWidth = 300, MaxTextHeight = 240 };
// Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
// Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
// The font size is calculated in terms of points -- not as device-independent pixels.
formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5);
// Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
formattedText.SetFontWeight(FontWeight.Bold, 6, 11);
var gradient = new LinearGradientBrush
{
GradientStops =
new GradientStops { new GradientStop(Colors.Orange, 0), new GradientStop(Colors.Teal, 1) },
StartPoint = new RelativePoint(0,0, RelativeUnit.Relative),
EndPoint = new RelativePoint(0,1, RelativeUnit.Relative)
};
// Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
formattedText.SetForegroundBrush(gradient, 6, 11);
// Use an Italic font style beginning at the 28th character and continuing for 28 characters.
formattedText.SetFontStyle(FontStyle.Italic, 28, 28);
context.DrawText(formattedText, new Point(10, 0));
}
}
}

7
samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

@ -13,6 +13,7 @@ namespace RenderDemo.Pages
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random(); private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1]; private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20; private float _fontSize = 20;
private int _direction = 10; private int _direction = 10;
@ -38,7 +39,7 @@ namespace RenderDemo.Pages
private void UpdateGlyphRun() private void UpdateGlyphRun()
{ {
var c = (uint)_rand.Next(65, 90); var c = (char)_rand.Next(65, 90);
if (_fontSize + _direction > 200) if (_fontSize + _direction > 200)
{ {
@ -54,6 +55,8 @@ namespace RenderDemo.Pages
_glyphIndices[0] = _glyphTypeface.GetGlyph(c); _glyphIndices[0] = _glyphTypeface.GetGlyph(c);
_characters[0] = c;
var scale = (double)_fontSize / _glyphTypeface.DesignEmHeight; var scale = (double)_fontSize / _glyphTypeface.DesignEmHeight;
var drawingGroup = new DrawingGroup(); var drawingGroup = new DrawingGroup();
@ -61,7 +64,7 @@ namespace RenderDemo.Pages
var glyphRunDrawing = new GlyphRunDrawing var glyphRunDrawing = new GlyphRunDrawing
{ {
Foreground = Brushes.Black, Foreground = Brushes.Black,
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices), GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices)
}; };
drawingGroup.Children.Add(glyphRunDrawing); drawingGroup.Children.Add(glyphRunDrawing);

5
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -10,7 +10,7 @@ using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Shared.PlatformSupport; using Avalonia.PlatformSupport;
using Avalonia.Skia; using Avalonia.Skia;
namespace Avalonia namespace Avalonia
@ -59,8 +59,7 @@ namespace Avalonia.Android
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoaderStub>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoaderStub>()
.Bind<IRenderTimer>().ToConstant(new ChoreographerTimer()) .Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>() .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
.Bind<IAssetLoader>().ToConstant(new AssetLoader(appType.Assembly));
SkiaPlatform.Initialize(); SkiaPlatform.Initialize();

2
src/Android/Avalonia.Android/AppBuilder.cs

@ -1,5 +1,5 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Shared.PlatformSupport; using Avalonia.PlatformSupport;
namespace Avalonia namespace Avalonia
{ {

4
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -5,9 +5,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj">
<SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> <ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" /> <Import Project="..\..\..\build\AndroidWorkarounds.props" />
</Project> </Project>

18
src/Android/Avalonia.Android/RuntimeInfo.cs

@ -1,18 +0,0 @@
using Avalonia.Platform;
namespace Avalonia.Shared.PlatformSupport
{
internal partial class StandardRuntimePlatform
{
public RuntimePlatformInfo GetRuntimeInfo() => new RuntimePlatformInfo
{
IsCoreClr = false,
IsDesktop = false,
IsMobile = true,
IsDotNetFramework = false,
IsMono = true,
IsUnix = true,
OperatingSystem = OperatingSystemType.Android
};
}
}

1
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -146,6 +146,7 @@ namespace Avalonia.Collections
{ {
if (_inner.TryGetValue(key, out var value)) if (_inner.TryGetValue(key, out var value))
{ {
_inner.Remove(key);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));

5
src/Avalonia.Base/Platform/IRuntimePlatform.cs

@ -1,5 +1,4 @@
using System; using System;
using System.Reflection;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
@ -23,6 +22,7 @@ namespace Avalonia.Platform
public OperatingSystemType OperatingSystem { get; set; } public OperatingSystemType OperatingSystem { get; set; }
public bool IsDesktop { get; set; } public bool IsDesktop { get; set; }
public bool IsMobile { get; set; } public bool IsMobile { get; set; }
public bool IsBrowser { get; set; }
public bool IsCoreClr { get; set; } public bool IsCoreClr { get; set; }
public bool IsMono { get; set; } public bool IsMono { get; set; }
public bool IsDotNetFramework { get; set; } public bool IsDotNetFramework { get; set; }
@ -36,6 +36,7 @@ namespace Avalonia.Platform
Linux, Linux,
OSX, OSX,
Android, Android,
iOS iOS,
Browser
} }
} }

7
src/Avalonia.Base/Threading/Dispatcher.cs

@ -74,6 +74,13 @@ namespace Avalonia.Threading
/// </summary> /// </summary>
/// <param name="minimumPriority"></param> /// <param name="minimumPriority"></param>
public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority); public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
/// <summary>
/// Use this method to check if there are more prioritized tasks
/// </summary>
/// <param name="minimumPriority"></param>
public bool HasJobsWithPriority(DispatcherPriority minimumPriority) =>
_jobRunner.HasJobsWithPriority(minimumPriority);
/// <inheritdoc/> /// <inheritdoc/>
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)

15
src/Avalonia.Base/Threading/JobRunner.cs

@ -121,6 +121,21 @@ namespace Avalonia.Threading
return null; return null;
} }
public bool HasJobsWithPriority(DispatcherPriority minimumPriority)
{
for (int c = (int)minimumPriority; c < (int)DispatcherPriority.MaxValue; c++)
{
var q = _queues[c];
lock (q)
{
if (q.Count > 0)
return true;
}
}
return false;
}
private interface IJob private interface IJob
{ {
/// <summary> /// <summary>

2
src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
public struct ImmutableReadOnlyListStructEnumerator<T> : IEnumerator, IEnumerator<T> public struct ImmutableReadOnlyListStructEnumerator<T> : IEnumerator<T>
{ {
private readonly IReadOnlyList<T> _readOnlyList; private readonly IReadOnlyList<T> _readOnlyList;
private int _pos; private int _pos;

9
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -2215,7 +2215,14 @@ namespace Avalonia.Controls
/// <param name="e">PointerWheelEventArgs</param> /// <param name="e">PointerWheelEventArgs</param>
protected override void OnPointerWheelChanged(PointerWheelEventArgs e) protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{ {
e.Handled = e.Handled || UpdateScroll(e.Delta * DATAGRID_mouseWheelDelta); if(UpdateScroll(e.Delta * DATAGRID_mouseWheelDelta))
{
e.Handled = true;
}
else
{
e.Handled = e.Handled || !ScrollViewer.GetIsScrollChainingEnabled(this);
}
} }
internal bool UpdateScroll(Vector delta) internal bool UpdateScroll(Vector delta)

6
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -43,6 +43,10 @@ MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.Off
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation.
MembersMustExist : Member 'protected Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.CreateFormattedText()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.FormattedText.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Int32 Avalonia.Controls.Presenters.TextPresenter.GetCaretIndex(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.TextPresenter.InvalidateFormattedText()' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract.
@ -63,4 +67,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor
MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract.
Total Issues: 64 Total Issues: 68

35
src/Avalonia.Controls/AppBuilderBase.cs

@ -14,9 +14,9 @@ namespace Avalonia.Controls
public abstract class AppBuilderBase<TAppBuilder> where TAppBuilder : AppBuilderBase<TAppBuilder>, new() public abstract class AppBuilderBase<TAppBuilder> where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
{ {
private static bool s_setupWasAlreadyCalled; private static bool s_setupWasAlreadyCalled;
private Action _optionsInitializers; private Action? _optionsInitializers;
private Func<Application> _appFactory; private Func<Application>? _appFactory;
private IApplicationLifetime _lifetime; private IApplicationLifetime? _lifetime;
/// <summary> /// <summary>
/// Gets or sets the <see cref="IRuntimePlatform"/> instance. /// Gets or sets the <see cref="IRuntimePlatform"/> instance.
@ -31,32 +31,32 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the <see cref="Application"/> instance being initialized. /// Gets the <see cref="Application"/> instance being initialized.
/// </summary> /// </summary>
public Application Instance { get; private set; } public Application? Instance { get; private set; }
/// <summary> /// <summary>
/// Gets the type of the Instance (even if it's not created yet) /// Gets the type of the Instance (even if it's not created yet)
/// </summary> /// </summary>
public Type ApplicationType { get; private set; } public Type? ApplicationType { get; private set; }
/// <summary> /// <summary>
/// Gets or sets a method to call the initialize the windowing subsystem. /// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary> /// </summary>
public Action WindowingSubsystemInitializer { get; private set; } public Action? WindowingSubsystemInitializer { get; private set; }
/// <summary> /// <summary>
/// Gets the name of the currently selected windowing subsystem. /// Gets the name of the currently selected windowing subsystem.
/// </summary> /// </summary>
public string WindowingSubsystemName { get; private set; } public string? WindowingSubsystemName { get; private set; }
/// <summary> /// <summary>
/// Gets or sets a method to call the initialize the windowing subsystem. /// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary> /// </summary>
public Action RenderingSubsystemInitializer { get; private set; } public Action? RenderingSubsystemInitializer { get; private set; }
/// <summary> /// <summary>
/// Gets the name of the currently selected rendering subsystem. /// Gets the name of the currently selected rendering subsystem.
/// </summary> /// </summary>
public string RenderingSubsystemName { get; private set; } public string? RenderingSubsystemName { get; private set; }
/// <summary> /// <summary>
/// Gets or sets a method to call after the <see cref="Application"/> is setup. /// Gets or sets a method to call after the <see cref="Application"/> is setup.
@ -126,7 +126,7 @@ namespace Avalonia.Controls
/// <typeparam name="TMainWindow">The window type.</typeparam> /// <typeparam name="TMainWindow">The window type.</typeparam>
/// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param> /// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
[Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")]
public void Start<TMainWindow>(Func<object> dataContextProvider = null) public void Start<TMainWindow>(Func<object>? dataContextProvider = null)
where TMainWindow : Window, new() where TMainWindow : Window, new()
{ {
AfterSetup(builder => AfterSetup(builder =>
@ -134,7 +134,7 @@ namespace Avalonia.Controls
var window = new TMainWindow(); var window = new TMainWindow();
if (dataContextProvider != null) if (dataContextProvider != null)
window.DataContext = dataContextProvider(); window.DataContext = dataContextProvider();
((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime) ((IClassicDesktopStyleApplicationLifetime)builder.Instance!.ApplicationLifetime!)
.MainWindow = window; .MainWindow = window;
}); });
@ -155,7 +155,7 @@ namespace Avalonia.Controls
public void Start(AppMainDelegate main, string[] args) public void Start(AppMainDelegate main, string[] args)
{ {
Setup(); Setup();
main(Instance, args); main(Instance!, args);
} }
/// <summary> /// <summary>
@ -226,8 +226,8 @@ namespace Avalonia.Controls
var platformClassName = assemblyName.Replace("Avalonia.", string.Empty) + "Platform"; var platformClassName = assemblyName.Replace("Avalonia.", string.Empty) + "Platform";
var platformClassFullName = assemblyName + "." + platformClassName; var platformClassFullName = assemblyName + "." + platformClassName;
var platformClass = assembly.GetType(platformClassFullName); var platformClass = assembly.GetType(platformClassFullName);
var init = platformClass.GetRuntimeMethod("Initialize", Type.EmptyTypes); var init = platformClass!.GetRuntimeMethod("Initialize", Type.EmptyTypes);
init.Invoke(null, null); init!.Invoke(null, null);
}; };
public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules());
@ -251,7 +251,7 @@ namespace Avalonia.Controls
where constructor.GetParameters().Length == 0 && !constructor.IsStatic where constructor.GetParameters().Length == 0 && !constructor.IsStatic
select constructor).Single() into constructor select constructor).Single() into constructor
select (Action)(() => constructor.Invoke(Array.Empty<object>())); select (Action)(() => constructor.Invoke(Array.Empty<object>()));
Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke(); Delegate.Combine(moduleInitializers.ToArray())!.DynamicInvoke();
} }
/// <summary> /// <summary>
@ -292,6 +292,11 @@ namespace Avalonia.Controls
throw new InvalidOperationException("No rendering system configured."); throw new InvalidOperationException("No rendering system configured.");
} }
if (_appFactory == null)
{
throw new InvalidOperationException("No Application factory configured.");
}
if (s_setupWasAlreadyCalled && CheckSetup) if (s_setupWasAlreadyCalled && CheckSetup)
{ {
throw new InvalidOperationException("Setup was already called on one of AppBuilder instances"); throw new InvalidOperationException("Setup was already called on one of AppBuilder instances");

5
src/Avalonia.Controls/Application.cs

@ -13,7 +13,6 @@ using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
#nullable enable
namespace Avalonia namespace Avalonia
{ {
@ -177,13 +176,13 @@ namespace Avalonia
/// </summary> /// </summary>
public IApplicationLifetime? ApplicationLifetime { get; set; } public IApplicationLifetime? ApplicationLifetime { get; set; }
event Action<IReadOnlyList<IStyle>> IGlobalStyles.GlobalStylesAdded event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesAdded
{ {
add => _stylesAdded += value; add => _stylesAdded += value;
remove => _stylesAdded -= value; remove => _stylesAdded -= value;
} }
event Action<IReadOnlyList<IStyle>> IGlobalStyles.GlobalStylesRemoved event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesRemoved
{ {
add => _stylesRemoved += value; add => _stylesRemoved += value;
remove => _stylesRemoved -= value; remove => _stylesRemoved -= value;

26
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -15,26 +15,26 @@ namespace Avalonia.Controls.ApplicationLifetimes
public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable
{ {
private int _exitCode; private int _exitCode;
private CancellationTokenSource _cts; private CancellationTokenSource? _cts;
private bool _isShuttingDown; private bool _isShuttingDown;
private HashSet<Window> _windows = new HashSet<Window>(); private HashSet<Window> _windows = new HashSet<Window>();
private static ClassicDesktopStyleApplicationLifetime _activeLifetime; private static ClassicDesktopStyleApplicationLifetime? _activeLifetime;
static ClassicDesktopStyleApplicationLifetime() static ClassicDesktopStyleApplicationLifetime()
{ {
Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened); Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened);
Window.WindowClosedEvent.AddClassHandler(typeof(Window), WindowClosedEvent); Window.WindowClosedEvent.AddClassHandler(typeof(Window), WindowClosedEvent);
} }
private static void WindowClosedEvent(object sender, RoutedEventArgs e) private static void WindowClosedEvent(object? sender, RoutedEventArgs e)
{ {
_activeLifetime?._windows.Remove((Window)sender); _activeLifetime?._windows.Remove((Window)sender!);
_activeLifetime?.HandleWindowClosed((Window)sender); _activeLifetime?.HandleWindowClosed((Window)sender!);
} }
private static void OnWindowOpened(object sender, RoutedEventArgs e) private static void OnWindowOpened(object? sender, RoutedEventArgs e)
{ {
_activeLifetime?._windows.Add((Window)sender); _activeLifetime?._windows.Add((Window)sender!);
} }
public ClassicDesktopStyleApplicationLifetime() public ClassicDesktopStyleApplicationLifetime()
@ -46,24 +46,24 @@ namespace Avalonia.Controls.ApplicationLifetimes
} }
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<ControlledApplicationLifetimeStartupEventArgs> Startup; public event EventHandler<ControlledApplicationLifetimeStartupEventArgs>? Startup;
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested; public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<ControlledApplicationLifetimeExitEventArgs> Exit; public event EventHandler<ControlledApplicationLifetimeExitEventArgs>? Exit;
/// <summary> /// <summary>
/// Gets the arguments passed to the AppBuilder Start method. /// Gets the arguments passed to the AppBuilder Start method.
/// </summary> /// </summary>
public string[] Args { get; set; } public string[]? Args { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public ShutdownMode ShutdownMode { get; set; } public ShutdownMode ShutdownMode { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public Window MainWindow { get; set; } public Window? MainWindow { get; set; }
public IReadOnlyList<Window> Windows => _windows.ToList(); public IReadOnlyList<Window> Windows => _windows.ToList();
@ -183,7 +183,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
return true; return true;
} }
private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e) => DoShutdown(e); private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e);
} }
public class ClassicDesktopStyleApplicationLifetimeOptions public class ClassicDesktopStyleApplicationLifetimeOptions

6
src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs

@ -20,7 +20,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
/// <see cref="ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime{T}(T, string[], ShutdownMode)"/> /// <see cref="ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime{T}(T, string[], ShutdownMode)"/>
/// method. /// method.
/// </summary> /// </summary>
string[] Args { get; } string[]? Args { get; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="ShutdownMode"/>. This property indicates whether the application is shutdown explicitly or implicitly. /// Gets or sets the <see cref="ShutdownMode"/>. This property indicates whether the application is shutdown explicitly or implicitly.
@ -38,7 +38,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
/// <value> /// <value>
/// The main window. /// The main window.
/// </value> /// </value>
Window MainWindow { get; set; } Window? MainWindow { get; set; }
IReadOnlyList<Window> Windows { get; } IReadOnlyList<Window> Windows { get; }
@ -58,6 +58,6 @@ namespace Avalonia.Controls.ApplicationLifetimes
/// will try to close each non-owned open window, invoking the <see cref="Window.Closing"/> event on each and allowing /// will try to close each non-owned open window, invoking the <see cref="Window.Closing"/> event on each and allowing
/// each window to cancel the shutdown of the application. Windows cannot however prevent OS shutdown. /// each window to cancel the shutdown of the application. Windows cannot however prevent OS shutdown.
/// </remarks> /// </remarks>
event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested; event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;
} }
} }

2
src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs

@ -2,6 +2,6 @@ namespace Avalonia.Controls.ApplicationLifetimes
{ {
public interface ISingleViewApplicationLifetime : IApplicationLifetime public interface ISingleViewApplicationLifetime : IApplicationLifetime
{ {
Control MainView { get; set; } Control? MainView { get; set; }
} }
} }

309
src/Avalonia.Controls/AutoCompleteBox.cs

@ -69,7 +69,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <value>The text that is used to determine which items to display in /// <value>The text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value> /// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public string Parameter { get; private set; } public string? Parameter { get; private set; }
/// <summary> /// <summary>
/// Initializes a new instance of the /// Initializes a new instance of the
@ -79,7 +79,7 @@ namespace Avalonia.Controls
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// property, which is used to filter items for the /// property, which is used to filter items for the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param> /// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatingEventArgs(string parameter) public PopulatingEventArgs(string? parameter)
{ {
Parameter = parameter; Parameter = parameter;
} }
@ -98,7 +98,7 @@ namespace Avalonia.Controls
/// <typeparam name="T">The type used for filtering the /// <typeparam name="T">The type used for filtering the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />. This type can /// <see cref="T:Avalonia.Controls.AutoCompleteBox" />. This type can
/// be either a string or an object.</typeparam> /// be either a string or an object.</typeparam>
public delegate bool AutoCompleteFilterPredicate<T>(string search, T item); public delegate bool AutoCompleteFilterPredicate<T>(string? search, T item);
/// <summary> /// <summary>
/// Specifies how text in the text box portion of the /// Specifies how text in the text box portion of the
@ -245,7 +245,7 @@ namespace Avalonia.Controls
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />. /// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.
/// This type can be either a string or an object. /// This type can be either a string or an object.
/// </typeparam> /// </typeparam>
public delegate string AutoCompleteSelector<T>(string search, T item); public delegate string AutoCompleteSelector<T>(string? search, T item);
/// <summary> /// <summary>
/// Represents a control that provides a text box for user input and a /// Represents a control that provides a text box for user input and a
@ -275,19 +275,19 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private const string ElementTextBox = "PART_TextBox"; private const string ElementTextBox = "PART_TextBox";
private IEnumerable _itemsEnumerable; private IEnumerable? _itemsEnumerable;
/// <summary> /// <summary>
/// Gets or sets a local cached copy of the items data. /// Gets or sets a local cached copy of the items data.
/// </summary> /// </summary>
private List<object> _items; private List<object>? _items;
/// <summary> /// <summary>
/// Gets or sets the observable collection that contains references to /// Gets or sets the observable collection that contains references to
/// all of the items in the generated view of data that is provided to /// all of the items in the generated view of data that is provided to
/// the selection-style control adapter. /// the selection-style control adapter.
/// </summary> /// </summary>
private AvaloniaList<object> _view; private AvaloniaList<object>? _view;
/// <summary> /// <summary>
/// Gets or sets a value to ignore a number of pending change handlers. /// Gets or sets a value to ignore a number of pending change handlers.
@ -338,7 +338,7 @@ namespace Avalonia.Controls
/// Gets or sets the DispatcherTimer used for the MinimumPopulateDelay /// Gets or sets the DispatcherTimer used for the MinimumPopulateDelay
/// condition for auto completion. /// condition for auto completion.
/// </summary> /// </summary>
private DispatcherTimer _delayTimer; private DispatcherTimer? _delayTimer;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether a read-only dependency /// Gets or sets a value indicating whether a read-only dependency
@ -351,47 +351,47 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// The TextBox template part. /// The TextBox template part.
/// </summary> /// </summary>
private TextBox _textBox; private TextBox? _textBox;
private IDisposable _textBoxSubscriptions; private IDisposable? _textBoxSubscriptions;
/// <summary> /// <summary>
/// The SelectionAdapter. /// The SelectionAdapter.
/// </summary> /// </summary>
private ISelectionAdapter _adapter; private ISelectionAdapter? _adapter;
/// <summary> /// <summary>
/// A control that can provide updated string values from a binding. /// A control that can provide updated string values from a binding.
/// </summary> /// </summary>
private BindingEvaluator<string> _valueBindingEvaluator; private BindingEvaluator<string>? _valueBindingEvaluator;
/// <summary> /// <summary>
/// A weak subscription for the collection changed event. /// A weak subscription for the collection changed event.
/// </summary> /// </summary>
private IDisposable _collectionChangeSubscription; private IDisposable? _collectionChangeSubscription;
private Func<string, CancellationToken, Task<IEnumerable<object>>> _asyncPopulator; private Func<string?, CancellationToken, Task<IEnumerable<object>>>? _asyncPopulator;
private CancellationTokenSource _populationCancellationTokenSource; private CancellationTokenSource? _populationCancellationTokenSource;
private bool _itemTemplateIsFromValueMemberBinding = true; private bool _itemTemplateIsFromValueMemberBinding = true;
private bool _settingItemTemplateFromValueMemberBinding; private bool _settingItemTemplateFromValueMemberBinding;
private object _selectedItem; private object? _selectedItem;
private bool _isDropDownOpen; private bool _isDropDownOpen;
private bool _isFocused = false; private bool _isFocused = false;
private string _text = string.Empty; private string? _text = string.Empty;
private string _searchText = string.Empty; private string? _searchText = string.Empty;
private AutoCompleteFilterPredicate<object> _itemFilter; private AutoCompleteFilterPredicate<object?>? _itemFilter;
private AutoCompleteFilterPredicate<string> _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith); private AutoCompleteFilterPredicate<string?>? _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith);
private AutoCompleteSelector<object> _itemSelector; private AutoCompleteSelector<object>? _itemSelector;
private AutoCompleteSelector<string> _textSelector; private AutoCompleteSelector<string?>? _textSelector;
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent = public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox)); RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox));
public static readonly StyledProperty<string> WatermarkProperty = public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>(); TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>();
/// <summary> /// <summary>
@ -479,8 +479,8 @@ namespace Avalonia.Controls
/// <value>The identifier the /// <value>The identifier the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SelectedItem" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.SelectedItem" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, object> SelectedItemProperty = public static readonly DirectProperty<AutoCompleteBox, object?> SelectedItemProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, object>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, object?>(
nameof(SelectedItem), nameof(SelectedItem),
o => o.SelectedItem, o => o.SelectedItem,
(o, v) => o.SelectedItem = v, (o, v) => o.SelectedItem = v,
@ -495,7 +495,7 @@ namespace Avalonia.Controls
/// <value>The identifier for the /// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, string> TextProperty = public static readonly DirectProperty<AutoCompleteBox, string?> TextProperty =
TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>( TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>(
o => o.Text, o => o.Text,
(o, v) => o.Text = v, (o, v) => o.Text = v,
@ -510,8 +510,8 @@ namespace Avalonia.Controls
/// <value>The identifier for the /// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, string> SearchTextProperty = public static readonly DirectProperty<AutoCompleteBox, string?> SearchTextProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, string>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, string?>(
nameof(SearchText), nameof(SearchText),
o => o.SearchText, o => o.SearchText,
unsetValue: string.Empty); unsetValue: string.Empty);
@ -535,8 +535,8 @@ namespace Avalonia.Controls
/// <value>The identifier for the /// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<object>> ItemFilterProperty = public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?> ItemFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<object>>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?>(
nameof(ItemFilter), nameof(ItemFilter),
o => o.ItemFilter, o => o.ItemFilter,
(o, v) => o.ItemFilter = v); (o, v) => o.ItemFilter = v);
@ -549,8 +549,8 @@ namespace Avalonia.Controls
/// <value>The identifier for the /// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<string>> TextFilterProperty = public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?> TextFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<string>>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?>(
nameof(TextFilter), nameof(TextFilter),
o => o.TextFilter, o => o.TextFilter,
(o, v) => o.TextFilter = v, (o, v) => o.TextFilter = v,
@ -564,8 +564,8 @@ namespace Avalonia.Controls
/// <value>The identifier for the /// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>> ItemSelectorProperty = public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>?> ItemSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>?>(
nameof(ItemSelector), nameof(ItemSelector),
o => o.ItemSelector, o => o.ItemSelector,
(o, v) => o.ItemSelector = v); (o, v) => o.ItemSelector = v);
@ -578,8 +578,8 @@ namespace Avalonia.Controls
/// <value>The identifier for the /// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string>> TextSelectorProperty = public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string?>?> TextSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string>>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string?>?>(
nameof(TextSelector), nameof(TextSelector),
o => o.TextSelector, o => o.TextSelector,
(o, v) => o.TextSelector = v); (o, v) => o.TextSelector = v);
@ -592,14 +592,14 @@ namespace Avalonia.Controls
/// <value>The identifier for the /// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// dependency property.</value> /// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, IEnumerable> ItemsProperty = public static readonly DirectProperty<AutoCompleteBox, IEnumerable?> ItemsProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, IEnumerable>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, IEnumerable?>(
nameof(Items), nameof(Items),
o => o.Items, o => o.Items,
(o, v) => o.Items = v); (o, v) => o.Items = v);
public static readonly DirectProperty<AutoCompleteBox, Func<string, CancellationToken, Task<IEnumerable<object>>>> AsyncPopulatorProperty = public static readonly DirectProperty<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?> AsyncPopulatorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string, CancellationToken, Task<IEnumerable<object>>>>( AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?>(
nameof(AsyncPopulator), nameof(AsyncPopulator),
o => o.AsyncPopulator, o => o.AsyncPopulator,
(o, v) => o.AsyncPopulator = v); (o, v) => o.AsyncPopulator = v);
@ -640,7 +640,7 @@ namespace Avalonia.Controls
/// <param name="e">The event data.</param> /// <param name="e">The event data.</param>
private void OnControlIsEnabledChanged(AvaloniaPropertyChangedEventArgs e) private void OnControlIsEnabledChanged(AvaloniaPropertyChangedEventArgs e)
{ {
bool isEnabled = (bool)e.NewValue; bool isEnabled = (bool)e.NewValue!;
if (!isEnabled) if (!isEnabled)
{ {
IsDropDownOpen = false; IsDropDownOpen = false;
@ -655,7 +655,7 @@ namespace Avalonia.Controls
/// <param name="e">Event arguments.</param> /// <param name="e">Event arguments.</param>
private void OnMinimumPopulateDelayChanged(AvaloniaPropertyChangedEventArgs e) private void OnMinimumPopulateDelayChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var newValue = (TimeSpan)e.NewValue; var newValue = (TimeSpan)e.NewValue!;
// Stop any existing timer // Stop any existing timer
if (_delayTimer != null) if (_delayTimer != null)
@ -695,8 +695,8 @@ namespace Avalonia.Controls
return; return;
} }
bool oldValue = (bool)e.OldValue; bool oldValue = (bool)e.OldValue!;
bool newValue = (bool)e.NewValue; bool newValue = (bool)e.NewValue!;
if (newValue) if (newValue)
{ {
@ -750,7 +750,7 @@ namespace Avalonia.Controls
/// <param name="e">Event arguments.</param> /// <param name="e">Event arguments.</param>
private void OnTextPropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnTextPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
TextUpdated((string)e.NewValue, false); TextUpdated((string?)e.NewValue, false);
} }
private void OnSearchTextPropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnSearchTextPropertyChanged(AvaloniaPropertyChangedEventArgs e)
@ -778,7 +778,7 @@ namespace Avalonia.Controls
/// <param name="e">Event arguments.</param> /// <param name="e">Event arguments.</param>
private void OnFilterModePropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnFilterModePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
AutoCompleteFilterMode mode = (AutoCompleteFilterMode)e.NewValue; AutoCompleteFilterMode mode = (AutoCompleteFilterMode)e.NewValue!;
// Sets the filter predicate for the new value // Sets the filter predicate for the new value
TextFilter = AutoCompleteSearch.GetFilter(mode); TextFilter = AutoCompleteSearch.GetFilter(mode);
@ -790,7 +790,7 @@ namespace Avalonia.Controls
/// <param name="e">Event arguments.</param> /// <param name="e">Event arguments.</param>
private void OnItemFilterPropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnItemFilterPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
AutoCompleteFilterPredicate<object> value = e.NewValue as AutoCompleteFilterPredicate<object>; var value = e.NewValue as AutoCompleteFilterPredicate<object>;
// If null, revert to the "None" predicate // If null, revert to the "None" predicate
if (value == null) if (value == null)
@ -810,7 +810,7 @@ namespace Avalonia.Controls
/// <param name="e">Event arguments.</param> /// <param name="e">Event arguments.</param>
private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
OnItemsChanged((IEnumerable)e.NewValue); OnItemsChanged((IEnumerable?)e.NewValue);
} }
private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e)
@ -818,7 +818,7 @@ namespace Avalonia.Controls
if (!_settingItemTemplateFromValueMemberBinding) if (!_settingItemTemplateFromValueMemberBinding)
_itemTemplateIsFromValueMemberBinding = false; _itemTemplateIsFromValueMemberBinding = false;
} }
private void OnValueMemberBindingChanged(IBinding value) private void OnValueMemberBindingChanged(IBinding? value)
{ {
if(_itemTemplateIsFromValueMemberBinding) if(_itemTemplateIsFromValueMemberBinding)
{ {
@ -828,7 +828,8 @@ namespace Avalonia.Controls
(o, _) => (o, _) =>
{ {
var control = new ContentControl(); var control = new ContentControl();
control.Bind(ContentControl.ContentProperty, value); if (value is not null)
control.Bind(ContentControl.ContentProperty, value);
return control; return control;
}); });
@ -975,7 +976,7 @@ namespace Avalonia.Controls
/// <value>The <see cref="T:Avalonia.Data.IBinding" /> object used /// <value>The <see cref="T:Avalonia.Data.IBinding" /> object used
/// when binding to a collection property.</value> /// when binding to a collection property.</value>
[AssignBinding] [AssignBinding]
public IBinding ValueMemberBinding public IBinding? ValueMemberBinding
{ {
get { return _valueBindingEvaluator?.ValueBinding; } get { return _valueBindingEvaluator?.ValueBinding; }
set set
@ -998,7 +999,7 @@ namespace Avalonia.Controls
/// then displayed in the text box, the SelectedItem property will be /// then displayed in the text box, the SelectedItem property will be
/// a null reference. /// a null reference.
/// </remarks> /// </remarks>
public object SelectedItem public object? SelectedItem
{ {
get { return _selectedItem; } get { return _selectedItem; }
set { SetAndRaise(SelectedItemProperty, ref _selectedItem, value); } set { SetAndRaise(SelectedItemProperty, ref _selectedItem, value); }
@ -1010,7 +1011,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <value>The text in the text box portion of the /// <value>The text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value> /// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value>
public string Text public string? Text
{ {
get { return _text; } get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); } set { SetAndRaise(TextProperty, ref _text, value); }
@ -1029,7 +1030,7 @@ namespace Avalonia.Controls
/// Text property, but is set after the TextChanged event occurs /// Text property, but is set after the TextChanged event occurs
/// and before the Populating event. /// and before the Populating event.
/// </remarks> /// </remarks>
public string SearchText public string? SearchText
{ {
get { return _searchText; } get { return _searchText; }
private set private set
@ -1071,7 +1072,7 @@ namespace Avalonia.Controls
set { SetValue(FilterModeProperty, value); } set { SetValue(FilterModeProperty, value); }
} }
public string Watermark public string? Watermark
{ {
get { return GetValue(WatermarkProperty); } get { return GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); } set { SetValue(WatermarkProperty, value); }
@ -1091,7 +1092,7 @@ namespace Avalonia.Controls
/// The filter mode is automatically set to Custom if you set the /// The filter mode is automatically set to Custom if you set the
/// ItemFilter property. /// ItemFilter property.
/// </remarks> /// </remarks>
public AutoCompleteFilterPredicate<object> ItemFilter public AutoCompleteFilterPredicate<object?>? ItemFilter
{ {
get { return _itemFilter; } get { return _itemFilter; }
set { SetAndRaise(ItemFilterProperty, ref _itemFilter, value); } set { SetAndRaise(ItemFilterProperty, ref _itemFilter, value); }
@ -1111,7 +1112,7 @@ namespace Avalonia.Controls
/// The search mode is automatically set to Custom if you set the /// The search mode is automatically set to Custom if you set the
/// TextFilter property. /// TextFilter property.
/// </remarks> /// </remarks>
public AutoCompleteFilterPredicate<string> TextFilter public AutoCompleteFilterPredicate<string?>? TextFilter
{ {
get { return _textFilter; } get { return _textFilter; }
set { SetAndRaise(TextFilterProperty, ref _textFilter, value); } set { SetAndRaise(TextFilterProperty, ref _textFilter, value); }
@ -1127,7 +1128,7 @@ namespace Avalonia.Controls
/// text and one of the items specified by the /// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />. /// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </value> /// </value>
public AutoCompleteSelector<object> ItemSelector public AutoCompleteSelector<object>? ItemSelector
{ {
get { return _itemSelector; } get { return _itemSelector; }
set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); } set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); }
@ -1145,13 +1146,13 @@ namespace Avalonia.Controls
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way. /// in a text-based way.
/// </value> /// </value>
public AutoCompleteSelector<string> TextSelector public AutoCompleteSelector<string?>? TextSelector
{ {
get { return _textSelector; } get { return _textSelector; }
set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); } set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); }
} }
public Func<string, CancellationToken, Task<IEnumerable<object>>> AsyncPopulator public Func<string?, CancellationToken, Task<IEnumerable<object>>>? AsyncPopulator
{ {
get { return _asyncPopulator; } get { return _asyncPopulator; }
set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); } set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); }
@ -1165,7 +1166,7 @@ namespace Avalonia.Controls
/// <value>The collection that is used to generate the items of the /// <value>The collection that is used to generate the items of the
/// drop-down portion of the /// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value> /// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value>
public IEnumerable Items public IEnumerable? Items
{ {
get { return _itemsEnumerable; } get { return _itemsEnumerable; }
set { SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); } set { SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); }
@ -1174,12 +1175,12 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the drop down popup control. /// Gets or sets the drop down popup control.
/// </summary> /// </summary>
private Popup DropDownPopup { get; set; } private Popup? DropDownPopup { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Text template part. /// Gets or sets the Text template part.
/// </summary> /// </summary>
private TextBox TextBox private TextBox? TextBox
{ {
get { return _textBox; } get { return _textBox; }
set set
@ -1243,7 +1244,7 @@ namespace Avalonia.Controls
/// use with AutoCompleteBox or deriving from AutoCompleteBox to /// use with AutoCompleteBox or deriving from AutoCompleteBox to
/// create a custom control. /// create a custom control.
/// </remarks> /// </remarks>
protected ISelectionAdapter SelectionAdapter protected ISelectionAdapter? SelectionAdapter
{ {
get { return _adapter; } get { return _adapter; }
set set
@ -1279,10 +1280,10 @@ namespace Avalonia.Controls
/// A <see cref="T:Avalonia.Controls.ISelectionAdapter" /> object, /// A <see cref="T:Avalonia.Controls.ISelectionAdapter" /> object,
/// if possible. Otherwise, null. /// if possible. Otherwise, null.
/// </returns> /// </returns>
protected virtual ISelectionAdapter GetSelectionAdapterPart(INameScope nameScope) protected virtual ISelectionAdapter? GetSelectionAdapterPart(INameScope nameScope)
{ {
ISelectionAdapter adapter = null; ISelectionAdapter? adapter = null;
SelectingItemsControl selector = nameScope.Find<SelectingItemsControl>(ElementSelector); SelectingItemsControl? selector = nameScope.Find<SelectingItemsControl>(ElementSelector);
if (selector != null) if (selector != null)
{ {
// Check if it is already an IItemsSelector // Check if it is already an IItemsSelector
@ -1316,7 +1317,7 @@ namespace Avalonia.Controls
// Set the template parts. Individual part setters remove and add // Set the template parts. Individual part setters remove and add
// any event handlers. // any event handlers.
Popup popup = e.NameScope.Find<Popup>(ElementPopup); Popup? popup = e.NameScope.Find<Popup>(ElementPopup);
if (popup != null) if (popup != null)
{ {
DropDownPopup = popup; DropDownPopup = popup;
@ -1358,7 +1359,7 @@ namespace Avalonia.Controls
/// that contains the event data.</param> /// that contains the event data.</param>
protected override void OnKeyDown(KeyEventArgs e) protected override void OnKeyDown(KeyEventArgs e)
{ {
Contract.Requires<ArgumentNullException>(e != null); _ = e ?? throw new ArgumentNullException(nameof(e));
base.OnKeyDown(e); base.OnKeyDown(e);
@ -1453,7 +1454,7 @@ namespace Avalonia.Controls
/// otherwise, false.</returns> /// otherwise, false.</returns>
protected bool HasFocus() protected bool HasFocus()
{ {
IVisual focused = FocusManager.Instance.Current; IVisual? focused = FocusManager.Instance?.Current;
while (focused != null) while (focused != null)
{ {
@ -1464,11 +1465,11 @@ namespace Avalonia.Controls
// This helps deal with popups that may not be in the same // This helps deal with popups that may not be in the same
// visual tree // visual tree
IVisual parent = focused.GetVisualParent(); IVisual? parent = focused.GetVisualParent();
if (parent == null) if (parent == null)
{ {
// Try the logical parent. // Try the logical parent.
IControl element = focused as IControl; IControl? element = focused as IControl;
if (element != null) if (element != null)
{ {
parent = element.Parent; parent = element.Parent;
@ -1519,7 +1520,7 @@ namespace Avalonia.Controls
/// Occurs when the text in the text box portion of the /// Occurs when the text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> changes. /// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> changes.
/// </summary> /// </summary>
public event EventHandler TextChanged; public event EventHandler? TextChanged;
/// <summary> /// <summary>
/// Occurs when the /// Occurs when the
@ -1535,7 +1536,7 @@ namespace Avalonia.Controls
/// In this case, if you want possible matches to appear, you must /// In this case, if you want possible matches to appear, you must
/// provide the logic for populating the selection adapter. /// provide the logic for populating the selection adapter.
/// </remarks> /// </remarks>
public event EventHandler<PopulatingEventArgs> Populating; public event EventHandler<PopulatingEventArgs>? Populating;
/// <summary> /// <summary>
/// Occurs when the /// Occurs when the
@ -1544,35 +1545,35 @@ namespace Avalonia.Controls
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" />
/// property. /// property.
/// </summary> /// </summary>
public event EventHandler<PopulatedEventArgs> Populated; public event EventHandler<PopulatedEventArgs>? Populated;
/// <summary> /// <summary>
/// Occurs when the value of the /// Occurs when the value of the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// property is changing from false to true. /// property is changing from false to true.
/// </summary> /// </summary>
public event EventHandler<CancelEventArgs> DropDownOpening; public event EventHandler<CancelEventArgs>? DropDownOpening;
/// <summary> /// <summary>
/// Occurs when the value of the /// Occurs when the value of the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// property has changed from false to true and the drop-down is open. /// property has changed from false to true and the drop-down is open.
/// </summary> /// </summary>
public event EventHandler DropDownOpened; public event EventHandler? DropDownOpened;
/// <summary> /// <summary>
/// Occurs when the /// Occurs when the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// property is changing from true to false. /// property is changing from true to false.
/// </summary> /// </summary>
public event EventHandler<CancelEventArgs> DropDownClosing; public event EventHandler<CancelEventArgs>? DropDownClosing;
/// <summary> /// <summary>
/// Occurs when the /// Occurs when the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// property was changed from true to false and the drop-down is open. /// property was changed from true to false and the drop-down is open.
/// </summary> /// </summary>
public event EventHandler DropDownClosed; public event EventHandler? DropDownClosed;
/// <summary> /// <summary>
/// Occurs when the selected item in the drop-down portion of the /// Occurs when the selected item in the drop-down portion of the
@ -1740,7 +1741,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The source object.</param> /// <param name="sender">The source object.</param>
/// <param name="e">The event data.</param> /// <param name="e">The event data.</param>
private void DropDownPopup_Closed(object sender, EventArgs e) private void DropDownPopup_Closed(object? sender, EventArgs e)
{ {
// Force the drop down dependency property to be false. // Force the drop down dependency property to be false.
if (IsDropDownOpen) if (IsDropDownOpen)
@ -1760,7 +1761,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The source object.</param> /// <param name="sender">The source object.</param>
/// <param name="e">The event arguments.</param> /// <param name="e">The event arguments.</param>
private void PopulateDropDown(object sender, EventArgs e) private void PopulateDropDown(object? sender, EventArgs e)
{ {
if (_delayTimer != null) if (_delayTimer != null)
{ {
@ -1786,7 +1787,7 @@ namespace Avalonia.Controls
PopulateComplete(); PopulateComplete();
} }
} }
private bool TryPopulateAsync(string searchText) private bool TryPopulateAsync(string? searchText)
{ {
_populationCancellationTokenSource?.Cancel(false); _populationCancellationTokenSource?.Cancel(false);
_populationCancellationTokenSource?.Dispose(); _populationCancellationTokenSource?.Dispose();
@ -1804,12 +1805,12 @@ namespace Avalonia.Controls
return true; return true;
} }
private async Task PopulateAsync(string searchText, CancellationToken cancellationToken) private async Task PopulateAsync(string? searchText, CancellationToken cancellationToken)
{ {
try try
{ {
IEnumerable<object> result = await _asyncPopulator.Invoke(searchText, cancellationToken); IEnumerable<object> result = await _asyncPopulator!.Invoke(searchText, cancellationToken);
var resultList = result.ToList(); var resultList = result.ToList();
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
@ -1878,9 +1879,9 @@ namespace Avalonia.Controls
/// <param name="clearDataContext">A value indicating whether to clear /// <param name="clearDataContext">A value indicating whether to clear
/// the data context after the lookup is performed.</param> /// the data context after the lookup is performed.</param>
/// <returns>Formatted Value.</returns> /// <returns>Formatted Value.</returns>
private string FormatValue(object value, bool clearDataContext) private string? FormatValue(object? value, bool clearDataContext)
{ {
string result = FormatValue(value); string? result = FormatValue(value);
if(clearDataContext && _valueBindingEvaluator != null) if(clearDataContext && _valueBindingEvaluator != null)
{ {
_valueBindingEvaluator.ClearDataContext(); _valueBindingEvaluator.ClearDataContext();
@ -1902,7 +1903,7 @@ namespace Avalonia.Controls
/// <remarks> /// <remarks>
/// Override this method to provide a custom string conversion. /// Override this method to provide a custom string conversion.
/// </remarks> /// </remarks>
protected virtual string FormatValue(object value) protected virtual string? FormatValue(object? value)
{ {
if (_valueBindingEvaluator != null) if (_valueBindingEvaluator != null)
{ {
@ -1923,7 +1924,7 @@ namespace Avalonia.Controls
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
// Call the central updated text method as a user-initiated action // Call the central updated text method as a user-initiated action
TextUpdated(_textBox.Text, true); TextUpdated(_textBox!.Text, true);
}); });
} }
@ -1933,7 +1934,7 @@ namespace Avalonia.Controls
/// text changed events when there is a change. /// text changed events when there is a change.
/// </summary> /// </summary>
/// <param name="value">The new string value.</param> /// <param name="value">The new string value.</param>
private void UpdateTextValue(string value) private void UpdateTextValue(string? value)
{ {
UpdateTextValue(value, null); UpdateTextValue(value, null);
} }
@ -1949,7 +1950,7 @@ namespace Avalonia.Controls
/// underlying text dependency property is updated. In a non-user /// underlying text dependency property is updated. In a non-user
/// interaction, the text box value is updated. When user initiated is /// interaction, the text box value is updated. When user initiated is
/// null, all values are updated.</param> /// null, all values are updated.</param>
private void UpdateTextValue(string value, bool? userInitiated) private void UpdateTextValue(string? value, bool? userInitiated)
{ {
bool callTextChanged = false; bool callTextChanged = false;
// Update the Text dependency property // Update the Text dependency property
@ -1987,7 +1988,7 @@ namespace Avalonia.Controls
/// <param name="userInitiated">A value indicating whether the update /// <param name="userInitiated">A value indicating whether the update
/// is a user-initiated action. This should be a True value when the /// is a user-initiated action. This should be a True value when the
/// TextUpdated method is called from a TextBox event handler.</param> /// TextUpdated method is called from a TextBox event handler.</param>
private void TextUpdated(string newText, bool userInitiated) private void TextUpdated(string? newText, bool userInitiated)
{ {
// Only process this event if it is coming from someone outside // Only process this event if it is coming from someone outside
// setting the Text dependency property directly. // setting the Text dependency property directly.
@ -2087,7 +2088,7 @@ namespace Avalonia.Controls
bool objectFiltering = FilterMode == AutoCompleteFilterMode.Custom && TextFilter == null; bool objectFiltering = FilterMode == AutoCompleteFilterMode.Custom && TextFilter == null;
int view_index = 0; int view_index = 0;
int view_count = _view.Count; int view_count = _view!.Count;
List<object> items = _items; List<object> items = _items;
foreach (object item in items) foreach (object item in items)
{ {
@ -2096,7 +2097,7 @@ namespace Avalonia.Controls
{ {
if (stringFiltering) if (stringFiltering)
{ {
inResults = TextFilter(text, FormatValue(item)); inResults = TextFilter!(text, FormatValue(item));
} }
else else
{ {
@ -2166,7 +2167,7 @@ namespace Avalonia.Controls
/// adapter's ItemsSource to the view if appropriate. /// adapter's ItemsSource to the view if appropriate.
/// </summary> /// </summary>
/// <param name="newValue">The new enumerable reference.</param> /// <param name="newValue">The new enumerable reference.</param>
private void OnItemsChanged(IEnumerable newValue) private void OnItemsChanged(IEnumerable? newValue)
{ {
// Remove handler for oldValue.CollectionChanged (if present) // Remove handler for oldValue.CollectionChanged (if present)
_collectionChangeSubscription?.Dispose(); _collectionChangeSubscription?.Dispose();
@ -2198,28 +2199,28 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The object that raised the event.</param> /// <param name="sender">The object that raised the event.</param>
/// <param name="e">The event data.</param> /// <param name="e">The event data.</param>
private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void ItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
// Update the cache // Update the cache
if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null) if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
{ {
for (int index = 0; index < e.OldItems.Count; index++) for (int index = 0; index < e.OldItems.Count; index++)
{ {
_items.RemoveAt(e.OldStartingIndex); _items!.RemoveAt(e.OldStartingIndex);
} }
} }
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null && _items.Count >= e.NewStartingIndex) if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null && _items!.Count >= e.NewStartingIndex)
{ {
for (int index = 0; index < e.NewItems.Count; index++) for (int index = 0; index < e.NewItems.Count; index++)
{ {
_items.Insert(e.NewStartingIndex + index, e.NewItems[index]); _items.Insert(e.NewStartingIndex + index, e.NewItems[index]!);
} }
} }
if (e.Action == NotifyCollectionChangedAction.Replace && e.NewItems != null && e.OldItems != null) if (e.Action == NotifyCollectionChangedAction.Replace && e.NewItems != null && e.OldItems != null)
{ {
for (int index = 0; index < e.NewItems.Count; index++) for (int index = 0; index < e.NewItems.Count; index++)
{ {
_items[e.NewStartingIndex] = e.NewItems[index]; _items![e.NewStartingIndex] = e.NewItems[index]!;
} }
} }
@ -2228,7 +2229,7 @@ namespace Avalonia.Controls
{ {
for (int index = 0; index < e.OldItems.Count; index++) for (int index = 0; index < e.OldItems.Count; index++)
{ {
_view.Remove(e.OldItems[index]); _view!.Remove(e.OldItems[index]!);
} }
} }
@ -2270,7 +2271,7 @@ namespace Avalonia.Controls
RefreshView(); RefreshView();
// Fire the Populated event containing the read-only view data. // Fire the Populated event containing the read-only view data.
PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection<object>(_view)); PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection<object>(_view!));
OnPopulated(populated); OnPopulated(populated);
if (SelectionAdapter != null && SelectionAdapter.Items != _view) if (SelectionAdapter != null && SelectionAdapter.Items != _view)
@ -2278,7 +2279,7 @@ namespace Avalonia.Controls
SelectionAdapter.Items = _view; SelectionAdapter.Items = _view;
} }
bool isDropDownOpen = _userCalledPopulate && (_view.Count > 0); bool isDropDownOpen = _userCalledPopulate && (_view!.Count > 0);
if (isDropDownOpen != IsDropDownOpen) if (isDropDownOpen != IsDropDownOpen)
{ {
_ignorePropertyChange = true; _ignorePropertyChange = true;
@ -2306,20 +2307,20 @@ namespace Avalonia.Controls
private void UpdateTextCompletion(bool userInitiated) private void UpdateTextCompletion(bool userInitiated)
{ {
// By default this method will clear the selected value // By default this method will clear the selected value
object newSelectedItem = null; object? newSelectedItem = null;
string text = Text; string? text = Text;
// Text search is StartsWith explicit and only when enabled, in // Text search is StartsWith explicit and only when enabled, in
// line with WPF's ComboBox lookup. When in use it will associate // line with WPF's ComboBox lookup. When in use it will associate
// a Value with the Text if it is found in ItemsSource. This is // a Value with the Text if it is found in ItemsSource. This is
// only valid when there is data and the user initiated the action. // only valid when there is data and the user initiated the action.
if (_view.Count > 0) if (_view!.Count > 0)
{ {
if (IsTextCompletionEnabled && TextBox != null && userInitiated) if (IsTextCompletionEnabled && TextBox != null && userInitiated)
{ {
int currentLength = TextBox.Text?.Length ?? 0; int currentLength = TextBox.Text?.Length ?? 0;
int selectionStart = TextBoxSelectionStart; int selectionStart = TextBoxSelectionStart;
if (selectionStart == text.Length && selectionStart > _textSelectionStart) if (selectionStart == text?.Length && selectionStart > _textSelectionStart)
{ {
// When the FilterMode dependency property is set to // When the FilterMode dependency property is set to
// either StartsWith or StartsWithCaseSensitive, the // either StartsWith or StartsWithCaseSensitive, the
@ -2327,7 +2328,7 @@ namespace Avalonia.Controls
// performance on the lookup. It assumes that the // performance on the lookup. It assumes that the
// FilterMode the user has selected is an acceptable // FilterMode the user has selected is an acceptable
// case sensitive matching function for their scenario. // case sensitive matching function for their scenario.
object top = FilterMode == AutoCompleteFilterMode.StartsWith || FilterMode == AutoCompleteFilterMode.StartsWithCaseSensitive object? top = FilterMode == AutoCompleteFilterMode.StartsWith || FilterMode == AutoCompleteFilterMode.StartsWithCaseSensitive
? _view[0] ? _view[0]
: TryGetMatch(text, _view, AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); : TryGetMatch(text, _view, AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
@ -2335,18 +2336,18 @@ namespace Avalonia.Controls
if (top != null) if (top != null)
{ {
newSelectedItem = top; newSelectedItem = top;
string topString = FormatValue(top, true); string? topString = FormatValue(top, true);
// Only replace partially when the two words being the same // Only replace partially when the two words being the same
int minLength = Math.Min(topString.Length, Text.Length); int minLength = Math.Min(topString?.Length ?? 0, Text?.Length ?? 0);
if (AutoCompleteSearch.Equals(Text.Substring(0, minLength), topString.Substring(0, minLength))) if (AutoCompleteSearch.Equals(Text?.Substring(0, minLength), topString?.Substring(0, minLength)))
{ {
// Update the text // Update the text
UpdateTextValue(topString); UpdateTextValue(topString);
// Select the text past the user's caret // Select the text past the user's caret
TextBox.SelectionStart = currentLength; TextBox.SelectionStart = currentLength;
TextBox.SelectionEnd = topString.Length; TextBox.SelectionEnd = topString?.Length ?? 0;
} }
} }
} }
@ -2392,8 +2393,11 @@ namespace Avalonia.Controls
/// <param name="predicate">The predicate to use for the partial or /// <param name="predicate">The predicate to use for the partial or
/// exact match.</param> /// exact match.</param>
/// <returns>Returns the object or null.</returns> /// <returns>Returns the object or null.</returns>
private object TryGetMatch(string searchText, AvaloniaList<object> view, AutoCompleteFilterPredicate<string> predicate) private object? TryGetMatch(string? searchText, AvaloniaList<object> view, AutoCompleteFilterPredicate<string?>? predicate)
{ {
if (predicate is null)
return null;
if (view != null && view.Count > 0) if (view != null && view.Count > 0)
{ {
foreach (object o in view) foreach (object o in view)
@ -2428,9 +2432,9 @@ namespace Avalonia.Controls
/// that is displayed in the text box part. /// that is displayed in the text box part.
/// </summary> /// </summary>
/// <param name="newItem">The new item.</param> /// <param name="newItem">The new item.</param>
private void OnSelectedItemChanged(object newItem) private void OnSelectedItemChanged(object? newItem)
{ {
string text; string? text;
if (newItem == null) if (newItem == null)
{ {
@ -2461,9 +2465,9 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The source object.</param> /// <param name="sender">The source object.</param>
/// <param name="e">The selection changed event data.</param> /// <param name="e">The selection changed event data.</param>
private void OnAdapterSelectionChanged(object sender, SelectionChangedEventArgs e) private void OnAdapterSelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
SelectedItem = _adapter.SelectedItem; SelectedItem = _adapter!.SelectedItem;
} }
//TODO Check UpdateTextCompletion //TODO Check UpdateTextCompletion
@ -2472,7 +2476,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The source object.</param> /// <param name="sender">The source object.</param>
/// <param name="e">The event data.</param> /// <param name="e">The event data.</param>
private void OnAdapterSelectionComplete(object sender, RoutedEventArgs e) private void OnAdapterSelectionComplete(object? sender, RoutedEventArgs e)
{ {
IsDropDownOpen = false; IsDropDownOpen = false;
@ -2482,7 +2486,7 @@ namespace Avalonia.Controls
// Text should not be selected // Text should not be selected
ClearTextBoxSelection(); ClearTextBoxSelection();
TextBox.Focus(); TextBox!.Focus();
} }
/// <summary> /// <summary>
@ -2490,7 +2494,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The source object.</param> /// <param name="sender">The source object.</param>
/// <param name="e">The event data.</param> /// <param name="e">The event data.</param>
private void OnAdapterSelectionCanceled(object sender, RoutedEventArgs e) private void OnAdapterSelectionCanceled(object? sender, RoutedEventArgs e)
{ {
UpdateTextValue(SearchText); UpdateTextValue(SearchText);
@ -2510,7 +2514,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="FilterMode">The built-in search mode.</param> /// <param name="FilterMode">The built-in search mode.</param>
/// <returns>Returns the string-based comparison function.</returns> /// <returns>Returns the string-based comparison function.</returns>
public static AutoCompleteFilterPredicate<string> GetFilter(AutoCompleteFilterMode FilterMode) public static AutoCompleteFilterPredicate<string?>? GetFilter(AutoCompleteFilterMode FilterMode)
{ {
switch (FilterMode) switch (FilterMode)
{ {
@ -2566,9 +2570,11 @@ namespace Avalonia.Controls
/// <param name="value">The string value to search for.</param> /// <param name="value">The string value to search for.</param>
/// <param name="comparison">The string comparison type.</param> /// <param name="comparison">The string comparison type.</param>
/// <returns>Returns true when the substring is found.</returns> /// <returns>Returns true when the substring is found.</returns>
private static bool Contains(string s, string value, StringComparison comparison) private static bool Contains(string? s, string? value, StringComparison comparison)
{ {
return s.IndexOf(value, comparison) >= 0; if (s is not null && value is not null)
return s.IndexOf(value, comparison) >= 0;
return false;
} }
/// <summary> /// <summary>
@ -2577,9 +2583,11 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool StartsWith(string text, string value) public static bool StartsWith(string? text, string? value)
{ {
return value.StartsWith(text, StringComparison.CurrentCultureIgnoreCase); if (value is not null && text is not null)
return value.StartsWith(text, StringComparison.CurrentCultureIgnoreCase);
return false;
} }
/// <summary> /// <summary>
@ -2588,9 +2596,11 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool StartsWithCaseSensitive(string text, string value) public static bool StartsWithCaseSensitive(string? text, string? value)
{ {
return value.StartsWith(text, StringComparison.CurrentCulture); if (value is not null && text is not null)
return value.StartsWith(text, StringComparison.CurrentCulture);
return false;
} }
/// <summary> /// <summary>
@ -2599,9 +2609,11 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool StartsWithOrdinal(string text, string value) public static bool StartsWithOrdinal(string? text, string? value)
{ {
return value.StartsWith(text, StringComparison.OrdinalIgnoreCase); if (value is not null && text is not null)
return value.StartsWith(text, StringComparison.OrdinalIgnoreCase);
return false;
} }
/// <summary> /// <summary>
@ -2610,9 +2622,11 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool StartsWithOrdinalCaseSensitive(string text, string value) public static bool StartsWithOrdinalCaseSensitive(string? text, string? value)
{ {
return value.StartsWith(text, StringComparison.Ordinal); if (value is not null && text is not null)
return value.StartsWith(text, StringComparison.Ordinal);
return false;
} }
/// <summary> /// <summary>
@ -2622,7 +2636,7 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool Contains(string text, string value) public static bool Contains(string? text, string? value)
{ {
return Contains(value, text, StringComparison.CurrentCultureIgnoreCase); return Contains(value, text, StringComparison.CurrentCultureIgnoreCase);
} }
@ -2633,7 +2647,7 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool ContainsCaseSensitive(string text, string value) public static bool ContainsCaseSensitive(string? text, string? value)
{ {
return Contains(value, text, StringComparison.CurrentCulture); return Contains(value, text, StringComparison.CurrentCulture);
} }
@ -2644,7 +2658,7 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool ContainsOrdinal(string text, string value) public static bool ContainsOrdinal(string? text, string? value)
{ {
return Contains(value, text, StringComparison.OrdinalIgnoreCase); return Contains(value, text, StringComparison.OrdinalIgnoreCase);
} }
@ -2655,7 +2669,7 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool ContainsOrdinalCaseSensitive(string text, string value) public static bool ContainsOrdinalCaseSensitive(string? text, string? value)
{ {
return Contains(value, text, StringComparison.Ordinal); return Contains(value, text, StringComparison.Ordinal);
} }
@ -2666,9 +2680,9 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool Equals(string text, string value) public static bool Equals(string? text, string? value)
{ {
return value.Equals(text, StringComparison.CurrentCultureIgnoreCase); return string.Equals(value, text, StringComparison.CurrentCultureIgnoreCase);
} }
/// <summary> /// <summary>
@ -2677,9 +2691,9 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool EqualsCaseSensitive(string text, string value) public static bool EqualsCaseSensitive(string? text, string? value)
{ {
return value.Equals(text, StringComparison.CurrentCulture); return string.Equals(value, text, StringComparison.CurrentCulture);
} }
/// <summary> /// <summary>
@ -2688,9 +2702,9 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool EqualsOrdinal(string text, string value) public static bool EqualsOrdinal(string? text, string? value)
{ {
return value.Equals(text, StringComparison.OrdinalIgnoreCase); return string.Equals(value, text, StringComparison.OrdinalIgnoreCase);
} }
/// <summary> /// <summary>
@ -2699,9 +2713,9 @@ namespace Avalonia.Controls
/// <param name="text">The AutoCompleteBox prefix text.</param> /// <param name="text">The AutoCompleteBox prefix text.</param>
/// <param name="value">The item's string value.</param> /// <param name="value">The item's string value.</param>
/// <returns>Returns true if the condition is met.</returns> /// <returns>Returns true if the condition is met.</returns>
public static bool EqualsOrdinalCaseSensitive(string text, string value) public static bool EqualsOrdinalCaseSensitive(string? text, string? value)
{ {
return value.Equals(text, StringComparison.Ordinal); return string.Equals(value, text, StringComparison.Ordinal);
} }
} }
@ -2715,7 +2729,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the string value binding used by the control. /// Gets or sets the string value binding used by the control.
/// </summary> /// </summary>
private IBinding _binding; private IBinding? _binding;
#region public T Value #region public T Value
@ -2739,13 +2753,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the value binding. /// Gets or sets the value binding.
/// </summary> /// </summary>
public IBinding ValueBinding public IBinding? ValueBinding
{ {
get { return _binding; } get { return _binding; }
set set
{ {
_binding = value; _binding = value;
AvaloniaObjectExtensions.Bind(this, ValueProperty, value); if (value is not null)
AvaloniaObjectExtensions.Bind(this, ValueProperty, value);
} }
} }
@ -2760,7 +2775,7 @@ namespace Avalonia.Controls
/// setting the initial binding to the provided parameter. /// setting the initial binding to the provided parameter.
/// </summary> /// </summary>
/// <param name="binding">The initial string value binding.</param> /// <param name="binding">The initial string value binding.</param>
public BindingEvaluator(IBinding binding) public BindingEvaluator(IBinding? binding)
: this() : this()
{ {
ValueBinding = binding; ValueBinding = binding;
@ -2802,7 +2817,7 @@ namespace Avalonia.Controls
/// <param name="o">The object to use as the data context.</param> /// <param name="o">The object to use as the data context.</param>
/// <returns>Returns the evaluated T value of the bound dependency /// <returns>Returns the evaluated T value of the bound dependency
/// property.</returns> /// property.</returns>
public T GetDynamicValue(object o) public T GetDynamicValue(object? o)
{ {
DataContext = o; DataContext = o;
return Value; return Value;

1
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -18,4 +18,5 @@
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\JetBrains.Annotations.props" /> <Import Project="..\..\build\JetBrains.Annotations.props" />
<Import Project="..\..\build\ApiDiff.props" /> <Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project> </Project>

12
src/Avalonia.Controls/Border.cs

@ -17,14 +17,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Background"/> property. /// Defines the <see cref="Background"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IBrush> BackgroundProperty = public static readonly StyledProperty<IBrush?> BackgroundProperty =
AvaloniaProperty.Register<Border, IBrush>(nameof(Background)); AvaloniaProperty.Register<Border, IBrush?>(nameof(Background));
/// <summary> /// <summary>
/// Defines the <see cref="BorderBrush"/> property. /// Defines the <see cref="BorderBrush"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IBrush> BorderBrushProperty = public static readonly StyledProperty<IBrush?> BorderBrushProperty =
AvaloniaProperty.Register<Border, IBrush>(nameof(BorderBrush)); AvaloniaProperty.Register<Border, IBrush?>(nameof(BorderBrush));
/// <summary> /// <summary>
/// Defines the <see cref="BorderThickness"/> property. /// Defines the <see cref="BorderThickness"/> property.
@ -91,7 +91,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets a brush with which to paint the background. /// Gets or sets a brush with which to paint the background.
/// </summary> /// </summary>
public IBrush Background public IBrush? Background
{ {
get { return GetValue(BackgroundProperty); } get { return GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); } set { SetValue(BackgroundProperty, value); }
@ -100,7 +100,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets a brush with which to paint the border. /// Gets or sets a brush with which to paint the border.
/// </summary> /// </summary>
public IBrush BorderBrush public IBrush? BorderBrush
{ {
get { return GetValue(BorderBrushProperty); } get { return GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); } set { SetValue(BorderBrushProperty, value); }

225
src/Avalonia.Controls/Button.cs

@ -42,30 +42,30 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Command"/> property. /// Defines the <see cref="Command"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<Button, ICommand> CommandProperty = public static readonly DirectProperty<Button, ICommand?> CommandProperty =
AvaloniaProperty.RegisterDirect<Button, ICommand>(nameof(Command), AvaloniaProperty.RegisterDirect<Button, ICommand?>(nameof(Command),
button => button.Command, (button, command) => button.Command = command, enableDataValidation: true); button => button.Command, (button, command) => button.Command = command, enableDataValidation: true);
/// <summary> /// <summary>
/// Defines the <see cref="HotKey"/> property. /// Defines the <see cref="HotKey"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<KeyGesture> HotKeyProperty = public static readonly StyledProperty<KeyGesture?> HotKeyProperty =
HotKeyManager.HotKeyProperty.AddOwner<Button>(); HotKeyManager.HotKeyProperty.AddOwner<Button>();
/// <summary> /// <summary>
/// Defines the <see cref="CommandParameter"/> property. /// Defines the <see cref="CommandParameter"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<object> CommandParameterProperty = public static readonly StyledProperty<object?> CommandParameterProperty =
AvaloniaProperty.Register<Button, object>(nameof(CommandParameter)); AvaloniaProperty.Register<Button, object?>(nameof(CommandParameter));
/// <summary> /// <summary>
/// Defines the <see cref="IsDefaultProperty"/> property. /// Defines the <see cref="IsDefault"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<bool> IsDefaultProperty = public static readonly StyledProperty<bool> IsDefaultProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsDefault)); AvaloniaProperty.Register<Button, bool>(nameof(IsDefault));
/// <summary> /// <summary>
/// Defines the <see cref="IsCancelProperty"/> property. /// Defines the <see cref="IsCancel"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<bool> IsCancelProperty = public static readonly StyledProperty<bool> IsCancelProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsCancel)); AvaloniaProperty.Register<Button, bool>(nameof(IsCancel));
@ -76,6 +76,9 @@ namespace Avalonia.Controls
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent = public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<Button, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble); RoutedEvent.Register<Button, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="IsPressed"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsPressedProperty = public static readonly StyledProperty<bool> IsPressedProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsPressed)); AvaloniaProperty.Register<Button, bool>(nameof(IsPressed));
@ -85,9 +88,9 @@ namespace Avalonia.Controls
public static readonly StyledProperty<FlyoutBase> FlyoutProperty = public static readonly StyledProperty<FlyoutBase> FlyoutProperty =
AvaloniaProperty.Register<Button, FlyoutBase>(nameof(Flyout)); AvaloniaProperty.Register<Button, FlyoutBase>(nameof(Flyout));
private ICommand _command; private ICommand? _command;
private bool _commandCanExecute = true; private bool _commandCanExecute = true;
private KeyGesture _hotkey; private KeyGesture? _hotkey;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="Button"/> class. /// Initializes static members of the <see cref="Button"/> class.
@ -95,13 +98,12 @@ namespace Avalonia.Controls
static Button() static Button()
{ {
FocusableProperty.OverrideDefaultValue(typeof(Button), true); FocusableProperty.OverrideDefaultValue(typeof(Button), true);
CommandProperty.Changed.Subscribe(CommandChanged);
CommandParameterProperty.Changed.Subscribe(CommandParameterChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
IsCancelProperty.Changed.Subscribe(IsCancelChanged);
AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler<Button>((lbl, args) => lbl.OnAccessKey(args)); AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler<Button>((lbl, args) => lbl.OnAccessKey(args));
} }
/// <summary>
/// Initializes a new instance of the <see cref="Button"/> class.
/// </summary>
public Button() public Button()
{ {
UpdatePseudoClasses(IsPressed); UpdatePseudoClasses(IsPressed);
@ -110,10 +112,10 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Raised when the user clicks the button. /// Raised when the user clicks the button.
/// </summary> /// </summary>
public event EventHandler<RoutedEventArgs> Click public event EventHandler<RoutedEventArgs>? Click
{ {
add { AddHandler(ClickEvent, value); } add => AddHandler(ClickEvent, value);
remove { RemoveHandler(ClickEvent, value); } remove => RemoveHandler(ClickEvent, value);
} }
/// <summary> /// <summary>
@ -121,35 +123,35 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public ClickMode ClickMode public ClickMode ClickMode
{ {
get { return GetValue(ClickModeProperty); } get => GetValue(ClickModeProperty);
set { SetValue(ClickModeProperty, value); } set => SetValue(ClickModeProperty, value);
} }
/// <summary> /// <summary>
/// Gets or sets an <see cref="ICommand"/> to be invoked when the button is clicked. /// Gets or sets an <see cref="ICommand"/> to be invoked when the button is clicked.
/// </summary> /// </summary>
public ICommand Command public ICommand? Command
{ {
get { return _command; } get => _command;
set { SetAndRaise(CommandProperty, ref _command, value); } set => SetAndRaise(CommandProperty, ref _command, value);
} }
/// <summary> /// <summary>
/// Gets or sets an <see cref="KeyGesture"/> associated with this control /// Gets or sets an <see cref="KeyGesture"/> associated with this control
/// </summary> /// </summary>
public KeyGesture HotKey public KeyGesture? HotKey
{ {
get { return GetValue(HotKeyProperty); } get => GetValue(HotKeyProperty);
set { SetValue(HotKeyProperty, value); } set => SetValue(HotKeyProperty, value);
} }
/// <summary> /// <summary>
/// Gets or sets a parameter to be passed to the <see cref="Command"/>. /// Gets or sets a parameter to be passed to the <see cref="Command"/>.
/// </summary> /// </summary>
public object CommandParameter public object? CommandParameter
{ {
get { return GetValue(CommandParameterProperty); } get => GetValue(CommandParameterProperty);
set { SetValue(CommandParameterProperty, value); } set => SetValue(CommandParameterProperty, value);
} }
/// <summary> /// <summary>
@ -158,8 +160,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public bool IsDefault public bool IsDefault
{ {
get { return GetValue(IsDefaultProperty); } get => GetValue(IsDefaultProperty);
set { SetValue(IsDefaultProperty, value); } set => SetValue(IsDefaultProperty, value);
} }
/// <summary> /// <summary>
@ -168,18 +170,21 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public bool IsCancel public bool IsCancel
{ {
get { return GetValue(IsCancelProperty); } get => GetValue(IsCancelProperty);
set { SetValue(IsCancelProperty, value); } set => SetValue(IsCancelProperty, value);
} }
/// <summary>
/// Gets or sets a value indicating whether the button is currently pressed.
/// </summary>
public bool IsPressed public bool IsPressed
{ {
get { return GetValue(IsPressedProperty); } get => GetValue(IsPressedProperty);
private set { SetValue(IsPressedProperty, value); } private set => SetValue(IsPressedProperty, value);
} }
/// <summary> /// <summary>
/// Gets or sets the Flyout that should be shown with this button /// Gets or sets the Flyout that should be shown with this button.
/// </summary> /// </summary>
public FlyoutBase Flyout public FlyoutBase Flyout
{ {
@ -187,7 +192,8 @@ namespace Avalonia.Controls
set => SetValue(FlyoutProperty, value); set => SetValue(FlyoutProperty, value);
} }
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute; /// <inheritdoc/>
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
@ -224,6 +230,7 @@ namespace Avalonia.Controls
} }
} }
/// <inheritdoc/>
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
if (_hotkey != null) // Control attached again, set Hotkey to create a hotkey manager for this control if (_hotkey != null) // Control attached again, set Hotkey to create a hotkey manager for this control
@ -240,6 +247,7 @@ namespace Avalonia.Controls
} }
} }
/// <inheritdoc/>
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
// This will cause the hotkey manager to dispose the observer and the reference to this control // This will cause the hotkey manager to dispose the observer and the reference to this control
@ -358,12 +366,14 @@ namespace Avalonia.Controls
} }
} }
} }
/// <inheritdoc/>
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{ {
IsPressed = false; IsPressed = false;
} }
/// <inheritdoc/>
protected override void OnLostFocus(RoutedEventArgs e) protected override void OnLostFocus(RoutedEventArgs e)
{ {
base.OnLostFocus(e); base.OnLostFocus(e);
@ -371,119 +381,93 @@ namespace Avalonia.Controls
IsPressed = false; IsPressed = false;
} }
/// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == IsPressedProperty) if (change.Property == CommandProperty)
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>());
}
else if (change.Property == FlyoutProperty)
{ {
// If flyout is changed while one is already open, make sure we if (((ILogical)this).IsAttachedToLogicalTree)
// close the old one first
if (change.OldValue.GetValueOrDefault() is FlyoutBase oldFlyout &&
oldFlyout.IsOpen)
{ {
oldFlyout.Hide(); if (change.OldValue.GetValueOrDefault() is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= CanExecuteChanged;
}
if (change.NewValue.GetValueOrDefault() is ICommand newCommand)
{
newCommand.CanExecuteChanged += CanExecuteChanged;
}
} }
}
}
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value) CanExecuteChanged(this, EventArgs.Empty);
{ }
base.UpdateDataValidation(property, value); else if (change.Property == CommandParameterProperty)
if (property == CommandProperty)
{ {
if (value.Type == BindingValueType.BindingError) CanExecuteChanged(this, EventArgs.Empty);
}
else if (change.Property == IsCancelProperty)
{
var isCancel = change.NewValue.GetValueOrDefault<bool>();
if (VisualRoot is IInputElement inputRoot)
{ {
if (_commandCanExecute) if (isCancel)
{ {
_commandCanExecute = false; ListenForCancel(inputRoot);
UpdateIsEffectivelyEnabled(); }
else
{
StopListeningForCancel(inputRoot);
} }
} }
} }
} else if (change.Property == IsDefaultProperty)
/// <summary>
/// Called when the <see cref="Command"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void CommandChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is Button button)
{ {
if (((ILogical)button).IsAttachedToLogicalTree) var isDefault = change.NewValue.GetValueOrDefault<bool>();
if (VisualRoot is IInputElement inputRoot)
{ {
if (e.OldValue is ICommand oldCommand) if (isDefault)
{ {
oldCommand.CanExecuteChanged -= button.CanExecuteChanged; ListenForDefault(inputRoot);
} }
else
if (e.NewValue is ICommand newCommand)
{ {
newCommand.CanExecuteChanged += button.CanExecuteChanged; StopListeningForDefault(inputRoot);
} }
} }
button.CanExecuteChanged(button, EventArgs.Empty);
} }
} else if (change.Property == IsPressedProperty)
/// <summary>
/// Called when the <see cref="CommandParameter"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void CommandParameterChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is Button button)
{ {
button.CanExecuteChanged(button, EventArgs.Empty); UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>());
} }
} else if (change.Property == FlyoutProperty)
/// <summary>
/// Called when the <see cref="IsDefault"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void IsDefaultChanged(AvaloniaPropertyChangedEventArgs e)
{
var button = e.Sender as Button;
var isDefault = (bool)e.NewValue;
if (button?.VisualRoot is IInputElement inputRoot)
{ {
if (isDefault) // If flyout is changed while one is already open, make sure we
{ // close the old one first
button.ListenForDefault(inputRoot); if (change.OldValue.GetValueOrDefault() is FlyoutBase oldFlyout &&
} oldFlyout.IsOpen)
else
{ {
button.StopListeningForDefault(inputRoot); oldFlyout.Hide();
} }
} }
} }
/// <summary> /// <inheritdoc/>
/// Called when the <see cref="IsCancel"/> property changes. protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
/// </summary>
/// <param name="e">The event args.</param>
private static void IsCancelChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var button = e.Sender as Button; base.UpdateDataValidation(property, value);
var isCancel = (bool)e.NewValue; if (property == CommandProperty)
if (button?.VisualRoot is IInputElement inputRoot)
{ {
if (isCancel) if (value.Type == BindingValueType.BindingError)
{
button.ListenForCancel(inputRoot);
}
else
{ {
button.StopListeningForCancel(inputRoot); if (_commandCanExecute)
{
_commandCanExecute = false;
UpdateIsEffectivelyEnabled();
}
} }
} }
} }
@ -493,7 +477,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void CanExecuteChanged(object sender, EventArgs e) private void CanExecuteChanged(object? sender, EventArgs e)
{ {
var canExecute = Command == null || Command.CanExecute(CommandParameter); var canExecute = Command == null || Command.CanExecute(CommandParameter);
@ -545,7 +529,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void RootDefaultKeyDown(object sender, KeyEventArgs e) private void RootDefaultKeyDown(object? sender, KeyEventArgs e)
{ {
if (e.Key == Key.Enter && IsVisible && IsEnabled) if (e.Key == Key.Enter && IsVisible && IsEnabled)
{ {
@ -558,7 +542,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void RootCancelKeyDown(object sender, KeyEventArgs e) private void RootCancelKeyDown(object? sender, KeyEventArgs e)
{ {
if (e.Key == Key.Escape && IsVisible && IsEnabled) if (e.Key == Key.Escape && IsVisible && IsEnabled)
{ {
@ -566,6 +550,9 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Updates the visual state of the control by applying latest PseudoClasses.
/// </summary>
private void UpdatePseudoClasses(bool isPressed) private void UpdatePseudoClasses(bool isPressed)
{ {
PseudoClasses.Set(":pressed", isPressed); PseudoClasses.Set(":pressed", isPressed);

14
src/Avalonia.Controls/ButtonSpinner.cs

@ -42,11 +42,11 @@ namespace Avalonia.Controls
UpdatePseudoClasses(ButtonSpinnerLocation); UpdatePseudoClasses(ButtonSpinnerLocation);
} }
private Button _decreaseButton; private Button? _decreaseButton;
/// <summary> /// <summary>
/// Gets or sets the DecreaseButton template part. /// Gets or sets the DecreaseButton template part.
/// </summary> /// </summary>
private Button DecreaseButton private Button? DecreaseButton
{ {
get { return _decreaseButton; } get { return _decreaseButton; }
set set
@ -63,11 +63,11 @@ namespace Avalonia.Controls
} }
} }
private Button _increaseButton; private Button? _increaseButton;
/// <summary> /// <summary>
/// Gets or sets the IncreaseButton template part. /// Gets or sets the IncreaseButton template part.
/// </summary> /// </summary>
private Button IncreaseButton private Button? IncreaseButton
{ {
get get
{ {
@ -241,8 +241,8 @@ namespace Avalonia.Controls
{ {
if (e.Sender is ButtonSpinner spinner) if (e.Sender is ButtonSpinner spinner)
{ {
var oldValue = (bool)e.OldValue; var oldValue = (bool)e.OldValue!;
var newValue = (bool)e.NewValue; var newValue = (bool)e.NewValue!;
spinner.OnAllowSpinChanged(oldValue, newValue); spinner.OnAllowSpinChanged(oldValue, newValue);
} }
} }
@ -268,7 +268,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void OnButtonClick(object sender, RoutedEventArgs e) private void OnButtonClick(object? sender, RoutedEventArgs e)
{ {
if (AllowSpin) if (AllowSpin)
{ {

76
src/Avalonia.Controls/Calendar/Calendar.cs

@ -240,11 +240,11 @@ namespace Avalonia.Controls
private bool _isShiftPressed; private bool _isShiftPressed;
private bool _displayDateIsChanging = false; private bool _displayDateIsChanging = false;
internal CalendarDayButton FocusButton { get; set; } internal CalendarDayButton? FocusButton { get; set; }
internal CalendarButton FocusCalendarButton { get; set; } internal CalendarButton? FocusCalendarButton { get; set; }
internal Panel Root { get; set; } internal Panel? Root { get; set; }
internal CalendarItem MonthControl internal CalendarItem? MonthControl
{ {
get get
{ {
@ -280,7 +280,7 @@ namespace Avalonia.Controls
private void OnFirstDayOfWeekChanged(AvaloniaPropertyChangedEventArgs e) private void OnFirstDayOfWeekChanged(AvaloniaPropertyChangedEventArgs e)
{ {
if (IsValidFirstDayOfWeek(e.NewValue)) if (IsValidFirstDayOfWeek(e.NewValue!))
{ {
UpdateMonths(); UpdateMonths();
} }
@ -373,9 +373,9 @@ namespace Avalonia.Controls
/// <param name="e">The DependencyPropertyChangedEventArgs.</param> /// <param name="e">The DependencyPropertyChangedEventArgs.</param>
private void OnDisplayModePropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnDisplayModePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
CalendarMode mode = (CalendarMode)e.NewValue; CalendarMode mode = (CalendarMode)e.NewValue!;
CalendarMode oldMode = (CalendarMode)e.OldValue; CalendarMode oldMode = (CalendarMode)e.OldValue!;
CalendarItem monthControl = MonthControl; CalendarItem? monthControl = MonthControl;
if (monthControl != null) if (monthControl != null)
{ {
@ -459,7 +459,7 @@ namespace Avalonia.Controls
} }
private void OnSelectionModeChanged(AvaloniaPropertyChangedEventArgs e) private void OnSelectionModeChanged(AvaloniaPropertyChangedEventArgs e)
{ {
if (IsValidSelectionMode(e.NewValue)) if (IsValidSelectionMode(e.NewValue!))
{ {
_displayDateIsChanging = true; _displayDateIsChanging = true;
SelectedDate = null; SelectedDate = null;
@ -656,7 +656,7 @@ namespace Avalonia.Controls
{ {
FocusButton.IsCurrent = false; FocusButton.IsCurrent = false;
} }
FocusButton = FindDayButtonFromDay(LastSelectedDate.Value); FocusButton = FindDayButtonFromDay(LastSelectedDate!.Value);
if (FocusButton != null) if (FocusButton != null)
{ {
FocusButton.IsCurrent = HasFocusInternal; FocusButton.IsCurrent = HasFocusInternal;
@ -754,11 +754,11 @@ namespace Avalonia.Controls
private void OnDisplayDateChanged(AvaloniaPropertyChangedEventArgs e) private void OnDisplayDateChanged(AvaloniaPropertyChangedEventArgs e)
{ {
UpdateDisplayDate(this, (DateTime)e.NewValue, (DateTime)e.OldValue); UpdateDisplayDate(this, (DateTime)e.NewValue!, (DateTime)e.OldValue!);
} }
private static void UpdateDisplayDate(Calendar c, DateTime addedDate, DateTime removedDate) private static void UpdateDisplayDate(Calendar c, DateTime addedDate, DateTime removedDate)
{ {
Contract.Requires<ArgumentNullException>(c != null); _ = c ?? throw new ArgumentNullException(nameof(c));
// If DisplayDate < DisplayDateStart, DisplayDate = DisplayDateStart // If DisplayDate < DisplayDateStart, DisplayDate = DisplayDateStart
if (DateTime.Compare(addedDate, c.DisplayDateRangeStart) < 0) if (DateTime.Compare(addedDate, c.DisplayDateRangeStart) < 0)
@ -871,7 +871,7 @@ namespace Avalonia.Controls
if (cal.SelectedDates.Count > 0) if (cal.SelectedDates.Count > 0)
{ {
selectedDateMin = cal.SelectedDates[0]; selectedDateMin = cal.SelectedDates[0];
Debug.Assert(DateTime.Compare(cal.SelectedDate.Value, selectedDateMin) == 0, "The SelectedDate should be the minimum selected date!"); Debug.Assert(DateTime.Compare(cal.SelectedDate!.Value, selectedDateMin) == 0, "The SelectedDate should be the minimum selected date!");
} }
else else
{ {
@ -959,7 +959,7 @@ namespace Avalonia.Controls
if (cal.SelectedDates.Count > 0) if (cal.SelectedDates.Count > 0)
{ {
selectedDateMax = cal.SelectedDates[0]; selectedDateMax = cal.SelectedDates[0];
Debug.Assert(DateTime.Compare(cal.SelectedDate.Value, selectedDateMax) == 0, "The SelectedDate should be the maximum SelectedDate!"); Debug.Assert(DateTime.Compare(cal.SelectedDate!.Value, selectedDateMax) == 0, "The SelectedDate should be the maximum SelectedDate!");
} }
else else
{ {
@ -1003,9 +1003,9 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
internal bool CalendarDatePickerDisplayDateFlag { get; set; } internal bool CalendarDatePickerDisplayDateFlag { get; set; }
internal CalendarDayButton FindDayButtonFromDay(DateTime day) internal CalendarDayButton? FindDayButtonFromDay(DateTime day)
{ {
CalendarItem monthControl = MonthControl; CalendarItem? monthControl = MonthControl;
// REMOVE_RTM: should be updated if we support MultiCalendar // REMOVE_RTM: should be updated if we support MultiCalendar
int count = RowsPerMonth * ColumnsPerMonth; int count = RowsPerMonth * ColumnsPerMonth;
@ -1054,7 +1054,7 @@ namespace Avalonia.Controls
internal void OnHeaderClick() internal void OnHeaderClick()
{ {
Debug.Assert(DisplayMode == CalendarMode.Year || DisplayMode == CalendarMode.Decade, "The DisplayMode should be Year or Decade"); Debug.Assert(DisplayMode == CalendarMode.Year || DisplayMode == CalendarMode.Decade, "The DisplayMode should be Year or Decade");
CalendarItem monthControl = MonthControl; CalendarItem? monthControl = MonthControl;
if (monthControl != null && monthControl.MonthView != null && monthControl.YearView != null) if (monthControl != null && monthControl.MonthView != null && monthControl.YearView != null)
{ {
monthControl.MonthView.IsVisible = false; monthControl.MonthView.IsVisible = false;
@ -1065,7 +1065,7 @@ namespace Avalonia.Controls
internal void ResetStates() internal void ResetStates()
{ {
CalendarItem monthControl = MonthControl; CalendarItem? monthControl = MonthControl;
int count = RowsPerMonth * ColumnsPerMonth; int count = RowsPerMonth * ColumnsPerMonth;
if (monthControl != null) if (monthControl != null)
{ {
@ -1083,7 +1083,7 @@ namespace Avalonia.Controls
internal void UpdateMonths() internal void UpdateMonths()
{ {
CalendarItem monthControl = MonthControl; CalendarItem? monthControl = MonthControl;
if (monthControl != null) if (monthControl != null)
{ {
switch (DisplayMode) switch (DisplayMode)
@ -1163,6 +1163,8 @@ namespace Avalonia.Controls
{ {
if (HoverEnd != null && HoverStart != null) if (HoverEnd != null && HoverStart != null)
{ {
Debug.Assert(MonthControl is not null);
int startIndex, endIndex, i; int startIndex, endIndex, i;
CalendarItem monthControl = MonthControl; CalendarItem monthControl = MonthControl;
@ -1173,7 +1175,7 @@ namespace Avalonia.Controls
for (i = startIndex; i <= endIndex; i++) for (i = startIndex; i <= endIndex; i++)
{ {
if (monthControl.MonthView.Children[i] is CalendarDayButton b) if (monthControl.MonthView!.Children[i] is CalendarDayButton b)
{ {
b.IsSelected = true; b.IsSelected = true;
var d = b.DataContext as DateTime?; var d = b.DataContext as DateTime?;
@ -1201,6 +1203,8 @@ namespace Avalonia.Controls
{ {
if (HoverEnd != null && HoverStart != null) if (HoverEnd != null && HoverStart != null)
{ {
Debug.Assert(MonthControl is not null);
CalendarItem monthControl = MonthControl; CalendarItem monthControl = MonthControl;
if (HoverEndIndex != null && HoverStartIndex != null) if (HoverEndIndex != null && HoverStartIndex != null)
@ -1212,7 +1216,7 @@ namespace Avalonia.Controls
{ {
for (i = startIndex; i <= endIndex; i++) for (i = startIndex; i <= endIndex; i++)
{ {
if (monthControl.MonthView.Children[i] is CalendarDayButton b) if (monthControl.MonthView!.Children[i] is CalendarDayButton b)
{ {
var d = b.DataContext as DateTime?; var d = b.DataContext as DateTime?;
@ -1231,7 +1235,7 @@ namespace Avalonia.Controls
// It is SingleRange // It is SingleRange
for (i = startIndex; i <= endIndex; i++) for (i = startIndex; i <= endIndex; i++)
{ {
((CalendarDayButton)monthControl.MonthView.Children[i]).IsSelected = false; ((CalendarDayButton)monthControl.MonthView!.Children[i]).IsSelected = false;
} }
} }
} }
@ -1239,6 +1243,11 @@ namespace Avalonia.Controls
} }
internal void SortHoverIndexes(out int startIndex, out int endIndex) internal void SortHoverIndexes(out int startIndex, out int endIndex)
{ {
Debug.Assert(HoverStart.HasValue);
Debug.Assert(HoverEnd.HasValue);
Debug.Assert(HoverStartIndex.HasValue);
Debug.Assert(HoverEndIndex.HasValue);
if (DateTimeHelper.CompareDays(HoverEnd.Value, HoverStart.Value) > 0) if (DateTimeHelper.CompareDays(HoverEnd.Value, HoverStart.Value) > 0)
{ {
startIndex = HoverStartIndex.Value; startIndex = HoverStartIndex.Value;
@ -1373,6 +1382,8 @@ namespace Avalonia.Controls
} }
private void OnMonthClick() private void OnMonthClick()
{ {
Debug.Assert(MonthControl is not null);
CalendarItem monthControl = MonthControl; CalendarItem monthControl = MonthControl;
if (monthControl != null && monthControl.YearView != null && monthControl.MonthView != null) if (monthControl != null && monthControl.YearView != null && monthControl.MonthView != null)
{ {
@ -1400,7 +1411,7 @@ namespace Avalonia.Controls
} }
} }
public event EventHandler<SelectionChangedEventArgs> SelectedDatesChanged; public event EventHandler<SelectionChangedEventArgs>? SelectedDatesChanged;
/// <summary> /// <summary>
/// Occurs when the /// Occurs when the
@ -1410,19 +1421,19 @@ namespace Avalonia.Controls
/// <remarks> /// <remarks>
/// This event occurs after DisplayDate is assigned its new value. /// This event occurs after DisplayDate is assigned its new value.
/// </remarks> /// </remarks>
public event EventHandler<CalendarDateChangedEventArgs> DisplayDateChanged; public event EventHandler<CalendarDateChangedEventArgs>? DisplayDateChanged;
/// <summary> /// <summary>
/// Occurs when the /// Occurs when the
/// <see cref="P:System.Windows.Controls.Calendar.DisplayMode" /> /// <see cref="P:System.Windows.Controls.Calendar.DisplayMode" />
/// property is changed. /// property is changed.
/// </summary> /// </summary>
public event EventHandler<CalendarModeChangedEventArgs> DisplayModeChanged; public event EventHandler<CalendarModeChangedEventArgs>? DisplayModeChanged;
/// <summary> /// <summary>
/// Inherited code: Requires comment. /// Inherited code: Requires comment.
/// </summary> /// </summary>
internal event EventHandler<PointerReleasedEventArgs> DayButtonMouseUp; internal event EventHandler<PointerReleasedEventArgs>? DayButtonMouseUp;
/// <summary> /// <summary>
/// This method adds the days that were selected by Keyboard to the /// This method adds the days that were selected by Keyboard to the
@ -1461,7 +1472,7 @@ namespace Avalonia.Controls
SelectedDates.ClearInternal(); SelectedDates.ClearInternal();
if (shift) if (shift)
{ {
CalendarDayButton b; CalendarDayButton? b;
_isShiftPressed = true; _isShiftPressed = true;
if (HoverStart == null) if (HoverStart == null)
{ {
@ -1513,6 +1524,8 @@ namespace Avalonia.Controls
} }
else else
{ {
Debug.Assert(HoverEndInternal is not null);
// For Home, End, PageUp and PageDown Keys there // For Home, End, PageUp and PageDown Keys there
// is no easy way to predict the index value // is no easy way to predict the index value
b = FindDayButtonFromDay(HoverEndInternal.Value); b = FindDayButtonFromDay(HoverEndInternal.Value);
@ -1524,6 +1537,7 @@ namespace Avalonia.Controls
} }
} }
Debug.Assert(HoverEnd is not null);
OnDayClick(HoverEnd.Value); OnDayClick(HoverEnd.Value);
HighlightDays(); HighlightDays();
} }
@ -1557,7 +1571,7 @@ namespace Avalonia.Controls
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (!HasFocusInternal && e.InitialPressMouseButton == MouseButton.Left) if (!HasFocusInternal && e.InitialPressMouseButton == MouseButton.Left)
{ {
FocusManager.Instance.Focus(this); FocusManager.Instance?.Focus(this);
} }
} }
@ -1876,8 +1890,8 @@ namespace Avalonia.Controls
// since DisplayDate is not equal to // since DisplayDate is not equal to
// DateTime.MaxValue we are sure selectedDate is\ // DateTime.MaxValue we are sure selectedDate is\
// not null // not null
selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1).Value; selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1)!.Value;
selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1).Value; selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1)!.Value;
} }
else else
{ {
@ -2098,7 +2112,7 @@ namespace Avalonia.Controls
if (Root != null) if (Root != null)
{ {
CalendarItem month = e.NameScope.Find<CalendarItem>(PART_ElementMonth); CalendarItem? month = e.NameScope.Find<CalendarItem>(PART_ElementMonth);
if (month != null) if (month != null)
{ {

6
src/Avalonia.Controls/Calendar/CalendarButton.cs

@ -45,7 +45,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Gets or sets the Calendar associated with this button. /// Gets or sets the Calendar associated with this button.
/// </summary> /// </summary>
internal Calendar Owner { get; set; } internal Calendar? Owner { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the button is focused. /// Gets or sets a value indicating whether the button is focused.
@ -120,7 +120,7 @@ namespace Avalonia.Controls.Primitives
/// stylus touches the tablet PC) while the mouse pointer is over a /// stylus touches the tablet PC) while the mouse pointer is over a
/// UIElement. /// UIElement.
/// </summary> /// </summary>
public event EventHandler<PointerPressedEventArgs> CalendarLeftMouseButtonDown; public event EventHandler<PointerPressedEventArgs>? CalendarLeftMouseButtonDown;
/// <summary> /// <summary>
/// Occurs when the left mouse button is released (or the tip of the /// Occurs when the left mouse button is released (or the tip of the
@ -128,7 +128,7 @@ namespace Avalonia.Controls.Primitives
/// stylus) is over a UIElement (or while a UIElement holds mouse /// stylus) is over a UIElement (or while a UIElement holds mouse
/// capture). /// capture).
/// </summary> /// </summary>
public event EventHandler<PointerReleasedEventArgs> CalendarLeftMouseButtonUp; public event EventHandler<PointerReleasedEventArgs>? CalendarLeftMouseButtonUp;
/// <summary> /// <summary>
/// Provides class handling for the MouseLeftButtonDown event that /// Provides class handling for the MouseLeftButtonDown event that

87
src/Avalonia.Controls/Calendar/CalendarDatePicker.cs

@ -123,15 +123,15 @@ namespace Avalonia.Controls
private const string ElementPopup = "PART_Popup"; private const string ElementPopup = "PART_Popup";
private const string ElementCalendar = "PART_Calendar"; private const string ElementCalendar = "PART_Calendar";
private Calendar _calendar; private Calendar? _calendar;
private string _defaultText; private string _defaultText;
private Button _dropDownButton; private Button? _dropDownButton;
//private Canvas _outsideCanvas; //private Canvas _outsideCanvas;
//private Canvas _outsidePopupCanvas; //private Canvas _outsidePopupCanvas;
private Popup _popUp; private Popup? _popUp;
private TextBox _textBox; private TextBox? _textBox;
private IDisposable _textBoxTextChangedSubscription; private IDisposable? _textBoxTextChangedSubscription;
private IDisposable _buttonPointerPressedSubscription; private IDisposable? _buttonPointerPressedSubscription;
private DateTime? _onOpenSelectedDate; private DateTime? _onOpenSelectedDate;
private bool _settingSelectedDate; private bool _settingSelectedDate;
@ -141,7 +141,7 @@ namespace Avalonia.Controls
private DateTime? _displayDateEnd; private DateTime? _displayDateEnd;
private bool _isDropDownOpen; private bool _isDropDownOpen;
private DateTime? _selectedDate; private DateTime? _selectedDate;
private string _text; private string? _text;
private bool _suspendTextChangeHandler = false; private bool _suspendTextChangeHandler = false;
private bool _isPopupClosing = false; private bool _isPopupClosing = false;
private bool _ignoreButtonClick = false; private bool _ignoreButtonClick = false;
@ -153,7 +153,7 @@ namespace Avalonia.Controls
/// A collection of dates that cannot be selected. The default value is /// A collection of dates that cannot be selected. The default value is
/// an empty collection. /// an empty collection.
/// </value> /// </value>
public CalendarBlackoutDatesCollection BlackoutDates { get; private set; } public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; }
public static readonly DirectProperty<CalendarDatePicker, DateTime> DisplayDateProperty = public static readonly DirectProperty<CalendarDatePicker, DateTime> DisplayDateProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, DateTime>( AvaloniaProperty.RegisterDirect<CalendarDatePicker, DateTime>(
@ -200,12 +200,12 @@ namespace Avalonia.Controls
defaultValue: "d", defaultValue: "d",
validate: IsValidDateFormatString); validate: IsValidDateFormatString);
public static readonly DirectProperty<CalendarDatePicker, string> TextProperty = public static readonly DirectProperty<CalendarDatePicker, string?> TextProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, string>( AvaloniaProperty.RegisterDirect<CalendarDatePicker, string?>(
nameof(Text), nameof(Text),
o => o.Text, o => o.Text,
(o, v) => o.Text = v); (o, v) => o.Text = v);
public static readonly StyledProperty<string> WatermarkProperty = public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<CalendarDatePicker>(); TextBox.WatermarkProperty.AddOwner<CalendarDatePicker>();
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty = public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
TextBox.UseFloatingWatermarkProperty.AddOwner<CalendarDatePicker>(); TextBox.UseFloatingWatermarkProperty.AddOwner<CalendarDatePicker>();
@ -361,13 +361,13 @@ namespace Avalonia.Controls
/// <exception cref="T:System.ArgumentOutOfRangeException"> /// <exception cref="T:System.ArgumentOutOfRangeException">
/// The text entered parses to a date that is not selectable. /// The text entered parses to a date that is not selectable.
/// </exception> /// </exception>
public string Text public string? Text
{ {
get { return _text; } get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); } set { SetAndRaise(TextProperty, ref _text, value); }
} }
public string Watermark public string? Watermark
{ {
get { return GetValue(WatermarkProperty); } get { return GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); } set { SetValue(WatermarkProperty, value); }
@ -401,26 +401,26 @@ namespace Avalonia.Controls
/// Occurs when the drop-down /// Occurs when the drop-down
/// <see cref="T:Avalonia.Controls.Calendar" /> is closed. /// <see cref="T:Avalonia.Controls.Calendar" /> is closed.
/// </summary> /// </summary>
public event EventHandler CalendarClosed; public event EventHandler? CalendarClosed;
/// <summary> /// <summary>
/// Occurs when the drop-down /// Occurs when the drop-down
/// <see cref="T:Avalonia.Controls.Calendar" /> is opened. /// <see cref="T:Avalonia.Controls.Calendar" /> is opened.
/// </summary> /// </summary>
public event EventHandler CalendarOpened; public event EventHandler? CalendarOpened;
/// <summary> /// <summary>
/// Occurs when <see cref="P:Avalonia.Controls.DatePicker.Text" /> /// Occurs when <see cref="P:Avalonia.Controls.DatePicker.Text" />
/// is assigned a value that cannot be interpreted as a date. /// is assigned a value that cannot be interpreted as a date.
/// </summary> /// </summary>
public event EventHandler<CalendarDatePickerDateValidationErrorEventArgs> DateValidationError; public event EventHandler<CalendarDatePickerDateValidationErrorEventArgs>? DateValidationError;
/// <summary> /// <summary>
/// Occurs when the /// Occurs when the
/// <see cref="P:Avalonia.Controls.CalendarDatePicker.SelectedDate" /> /// <see cref="P:Avalonia.Controls.CalendarDatePicker.SelectedDate" />
/// property is changed. /// property is changed.
/// </summary> /// </summary>
public event EventHandler<SelectionChangedEventArgs> SelectedDateChanged; public event EventHandler<SelectionChangedEventArgs>? SelectedDateChanged;
static CalendarDatePicker() static CalendarDatePicker()
{ {
@ -579,14 +579,14 @@ namespace Avalonia.Controls
private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e) private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var oldValue = (bool)e.OldValue; var oldValue = (bool)e.OldValue!;
var value = (bool)e.NewValue; var value = (bool)e.NewValue!;
if (_popUp != null && _popUp.Child != null) if (_popUp != null && _popUp.Child != null)
{ {
if (value != oldValue) if (value != oldValue)
{ {
if (_calendar.DisplayMode != CalendarMode.Month) if (_calendar!.DisplayMode != CalendarMode.Month)
{ {
_calendar.DisplayMode = CalendarMode.Month; _calendar.DisplayMode = CalendarMode.Month;
} }
@ -660,7 +660,7 @@ namespace Avalonia.Controls
if (date != null) if (date != null)
{ {
string s = DateTimeToString((DateTime)date); string? s = DateTimeToString((DateTime)date);
Text = s; Text = s;
} }
} }
@ -679,8 +679,8 @@ namespace Avalonia.Controls
} }
private void OnTextChanged(AvaloniaPropertyChangedEventArgs e) private void OnTextChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var oldValue = (string)e.OldValue; var oldValue = (string?)e.OldValue;
var value = (string)e.NewValue; var value = (string?)e.NewValue;
if (!_suspendTextChangeHandler) if (!_suspendTextChangeHandler)
{ {
@ -731,7 +731,7 @@ namespace Avalonia.Controls
} }
private void OnDateSelected(DateTime? addedDate, DateTime? removedDate) private void OnDateSelected(DateTime? addedDate, DateTime? removedDate)
{ {
EventHandler<SelectionChangedEventArgs> handler = this.SelectedDateChanged; EventHandler<SelectionChangedEventArgs>? handler = this.SelectedDateChanged;
if (null != handler) if (null != handler)
{ {
Collection<DateTime> addedItems = new Collection<DateTime>(); Collection<DateTime> addedItems = new Collection<DateTime>();
@ -759,23 +759,23 @@ namespace Avalonia.Controls
CalendarOpened?.Invoke(this, e); CalendarOpened?.Invoke(this, e);
} }
private void Calendar_DayButtonMouseUp(object sender, PointerReleasedEventArgs e) private void Calendar_DayButtonMouseUp(object? sender, PointerReleasedEventArgs e)
{ {
Focus(); Focus();
IsDropDownOpen = false; IsDropDownOpen = false;
} }
private void Calendar_DisplayDateChanged(object sender, CalendarDateChangedEventArgs e) private void Calendar_DisplayDateChanged(object? sender, CalendarDateChangedEventArgs e)
{ {
if (e.AddedDate != this.DisplayDate) if (e.AddedDate != this.DisplayDate)
{ {
SetValue(DisplayDateProperty, (DateTime) e.AddedDate); SetValue(DisplayDateProperty, (DateTime) e.AddedDate!);
} }
} }
private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e) private void Calendar_SelectedDatesChanged(object? sender, SelectionChangedEventArgs e)
{ {
Debug.Assert(e.AddedItems.Count < 2, "There should be less than 2 AddedItems!"); Debug.Assert(e.AddedItems.Count < 2, "There should be less than 2 AddedItems!");
if (e.AddedItems.Count > 0 && SelectedDate.HasValue && DateTime.Compare((DateTime)e.AddedItems[0], SelectedDate.Value) != 0) if (e.AddedItems.Count > 0 && SelectedDate.HasValue && DateTime.Compare((DateTime)e.AddedItems[0]!, SelectedDate.Value) != 0)
{ {
SelectedDate = (DateTime?)e.AddedItems[0]; SelectedDate = (DateTime?)e.AddedItems[0];
} }
@ -796,7 +796,7 @@ namespace Avalonia.Controls
} }
} }
} }
private void Calendar_PointerReleased(object sender, PointerReleasedEventArgs e) private void Calendar_PointerReleased(object? sender, PointerReleasedEventArgs e)
{ {
if (e.InitialPressMouseButton == MouseButton.Left) if (e.InitialPressMouseButton == MouseButton.Left)
@ -804,10 +804,9 @@ namespace Avalonia.Controls
e.Handled = true; e.Handled = true;
} }
} }
private void Calendar_KeyDown(object sender, KeyEventArgs e) private void Calendar_KeyDown(object? sender, KeyEventArgs e)
{ {
Calendar c = sender as Calendar; Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender));
Contract.Requires<ArgumentNullException>(c != null);
if (!e.Handled && (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape) && c.DisplayMode == CalendarMode.Month) if (!e.Handled && (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape) && c.DisplayMode == CalendarMode.Month)
{ {
@ -820,11 +819,11 @@ namespace Avalonia.Controls
} }
} }
} }
private void TextBox_GotFocus(object sender, RoutedEventArgs e) private void TextBox_GotFocus(object? sender, RoutedEventArgs e)
{ {
IsDropDownOpen = false; IsDropDownOpen = false;
} }
private void TextBox_KeyDown(object sender, KeyEventArgs e) private void TextBox_KeyDown(object? sender, KeyEventArgs e)
{ {
if (!e.Handled) if (!e.Handled)
{ {
@ -840,11 +839,11 @@ namespace Avalonia.Controls
_suspendTextChangeHandler = false; _suspendTextChangeHandler = false;
} }
} }
private void DropDownButton_PointerPressed(object sender, PointerPressedEventArgs e) private void DropDownButton_PointerPressed(object? sender, PointerPressedEventArgs e)
{ {
_ignoreButtonClick = _isPopupClosing; _ignoreButtonClick = _isPopupClosing;
} }
private void DropDownButton_Click(object sender, RoutedEventArgs e) private void DropDownButton_Click(object? sender, RoutedEventArgs e)
{ {
if (!_ignoreButtonClick) if (!_ignoreButtonClick)
{ {
@ -855,7 +854,7 @@ namespace Avalonia.Controls
_ignoreButtonClick = false; _ignoreButtonClick = false;
} }
} }
private void PopUp_Closed(object sender, EventArgs e) private void PopUp_Closed(object? sender, EventArgs e)
{ {
IsDropDownOpen = false; IsDropDownOpen = false;
@ -891,7 +890,7 @@ namespace Avalonia.Controls
private void OpenPopUp() private void OpenPopUp()
{ {
_onOpenSelectedDate = SelectedDate; _onOpenSelectedDate = SelectedDate;
_popUp.IsOpen = true; _popUp!.IsOpen = true;
} }
/// <summary> /// <summary>
@ -914,7 +913,7 @@ namespace Avalonia.Controls
{ {
newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat()); newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());
if (Calendar.IsValidDateSelection(this._calendar, newSelectedDate)) if (Calendar.IsValidDateSelection(this._calendar!, newSelectedDate))
{ {
return newSelectedDate; return newSelectedDate;
} }
@ -941,7 +940,7 @@ namespace Avalonia.Controls
} }
return null; return null;
} }
private string DateTimeToString(DateTime d) private string? DateTimeToString(DateTime d)
{ {
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat(); DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
@ -982,7 +981,7 @@ namespace Avalonia.Controls
{ {
SetSelectedDate(); SetSelectedDate();
IsDropDownOpen = true; IsDropDownOpen = true;
_calendar.Focus(); _calendar!.Focus();
} }
private void SetSelectedDate() private void SetSelectedDate()
{ {
@ -1001,7 +1000,7 @@ namespace Avalonia.Controls
// ex: SelectedDate = DateTime(1008,12,19) but when // ex: SelectedDate = DateTime(1008,12,19) but when
// "12/19/08" is parsed it is interpreted as // "12/19/08" is parsed it is interpreted as
// DateTime(2008,12,19) // DateTime(2008,12,19)
string selectedDate = DateTimeToString(SelectedDate.Value); string? selectedDate = DateTimeToString(SelectedDate.Value);
if (selectedDate == s) if (selectedDate == s)
{ {
return; return;
@ -1053,7 +1052,7 @@ namespace Avalonia.Controls
// SelectedDate value: // SelectedDate value:
if (SelectedDate != null) if (SelectedDate != null)
{ {
string newtext = this.DateTimeToString(SelectedDate.Value); string? newtext = this.DateTimeToString(SelectedDate.Value);
SetValue(TextProperty, newtext); SetValue(TextProperty, newtext);
return SelectedDate; return SelectedDate;
} }

2
src/Avalonia.Controls/Calendar/CalendarDateRange.cs

@ -65,7 +65,7 @@ namespace Avalonia.Controls
/// <returns>Inherited code: Requires comment 2.</returns> /// <returns>Inherited code: Requires comment 2.</returns>
internal bool ContainsAny(CalendarDateRange range) internal bool ContainsAny(CalendarDateRange range)
{ {
Contract.Requires<ArgumentNullException>(range != null); _ = range ?? throw new ArgumentNullException(nameof(range));
int start = DateTime.Compare(Start, range.Start); int start = DateTime.Compare(Start, range.Start);

6
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@ -40,7 +40,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Gets or sets the Calendar associated with this button. /// Gets or sets the Calendar associated with this button.
/// </summary> /// </summary>
internal Calendar Owner { get; set; } internal Calendar? Owner { get; set; }
internal int Index { get; set; } internal int Index { get; set; }
/// <summary> /// <summary>
@ -177,7 +177,7 @@ namespace Avalonia.Controls.Primitives
/// stylus touches the tablet PC) while the mouse pointer is over a /// stylus touches the tablet PC) while the mouse pointer is over a
/// UIElement. /// UIElement.
/// </summary> /// </summary>
public event EventHandler<PointerPressedEventArgs> CalendarDayButtonMouseDown; public event EventHandler<PointerPressedEventArgs>? CalendarDayButtonMouseDown;
/// <summary> /// <summary>
/// Occurs when the left mouse button is released (or the tip of the /// Occurs when the left mouse button is released (or the tip of the
@ -185,7 +185,7 @@ namespace Avalonia.Controls.Primitives
/// stylus) is over a UIElement (or while a UIElement holds mouse /// stylus) is over a UIElement (or while a UIElement holds mouse
/// capture). /// capture).
/// </summary> /// </summary>
public event EventHandler<PointerReleasedEventArgs> CalendarDayButtonMouseUp; public event EventHandler<PointerReleasedEventArgs>? CalendarDayButtonMouseUp;
/// <summary> /// <summary>
/// Provides class handling for the MouseLeftButtonDown event that /// Provides class handling for the MouseLeftButtonDown event that

100
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -33,10 +33,10 @@ namespace Avalonia.Controls.Primitives
private const string PART_ElementMonthView = "MonthView"; private const string PART_ElementMonthView = "MonthView";
private const string PART_ElementYearView = "YearView"; private const string PART_ElementYearView = "YearView";
private Button _headerButton; private Button? _headerButton;
private Button _nextButton; private Button? _nextButton;
private Button _previousButton; private Button? _previousButton;
private ITemplate<IControl> _dayTitleTemplate; private ITemplate<IControl>? _dayTitleTemplate;
private DateTime _currentMonth; private DateTime _currentMonth;
private bool _isMouseLeftButtonDown = false; private bool _isMouseLeftButtonDown = false;
@ -45,11 +45,11 @@ namespace Avalonia.Controls.Primitives
private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar(); private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar();
private PointerPressedEventArgs _downEventArg; private PointerPressedEventArgs? _downEventArg;
private PointerPressedEventArgs _downEventArgYearView; private PointerPressedEventArgs? _downEventArgYearView;
internal Calendar Owner { get; set; } internal Calendar? Owner { get; set; }
internal CalendarDayButton CurrentButton { get; set; } internal CalendarDayButton? CurrentButton { get; set; }
public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>(); public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
public IBrush HeaderBackground public IBrush HeaderBackground
@ -57,13 +57,13 @@ namespace Avalonia.Controls.Primitives
get { return GetValue(HeaderBackgroundProperty); } get { return GetValue(HeaderBackgroundProperty); }
set { SetValue(HeaderBackgroundProperty, value); } set { SetValue(HeaderBackgroundProperty, value); }
} }
public static readonly DirectProperty<CalendarItem, ITemplate<IControl>> DayTitleTemplateProperty = public static readonly DirectProperty<CalendarItem, ITemplate<IControl>?> DayTitleTemplateProperty =
AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<IControl>>( AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<IControl>?>(
nameof(DayTitleTemplate), nameof(DayTitleTemplate),
o => o.DayTitleTemplate, o => o.DayTitleTemplate,
(o,v) => o.DayTitleTemplate = v, (o,v) => o.DayTitleTemplate = v,
defaultBindingMode: BindingMode.OneTime); defaultBindingMode: BindingMode.OneTime);
public ITemplate<IControl> DayTitleTemplate public ITemplate<IControl>? DayTitleTemplate
{ {
get { return _dayTitleTemplate; } get { return _dayTitleTemplate; }
set { SetAndRaise(DayTitleTemplateProperty, ref _dayTitleTemplate, value); } set { SetAndRaise(DayTitleTemplateProperty, ref _dayTitleTemplate, value); }
@ -73,7 +73,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that allows switching between month mode, year mode, /// Gets the button that allows switching between month mode, year mode,
/// and decade mode. /// and decade mode.
/// </summary> /// </summary>
internal Button HeaderButton internal Button? HeaderButton
{ {
get { return _headerButton; } get { return _headerButton; }
private set private set
@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that displays the next page of the calendar when it /// Gets the button that displays the next page of the calendar when it
/// is clicked. /// is clicked.
/// </summary> /// </summary>
internal Button NextButton internal Button? NextButton
{ {
get { return _nextButton; } get { return _nextButton; }
private set private set
@ -125,7 +125,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that displays the previous page of the calendar when /// Gets the button that displays the previous page of the calendar when
/// it is clicked. /// it is clicked.
/// </summary> /// </summary>
internal Button PreviousButton internal Button? PreviousButton
{ {
get { return _previousButton; } get { return _previousButton; }
private set private set
@ -156,11 +156,11 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Gets the Grid that hosts the content when in month mode. /// Gets the Grid that hosts the content when in month mode.
/// </summary> /// </summary>
internal Grid MonthView { get; set; } internal Grid? MonthView { get; set; }
/// <summary> /// <summary>
/// Gets the Grid that hosts the content when in year or decade mode. /// Gets the Grid that hosts the content when in year or decade mode.
/// </summary> /// </summary>
internal Grid YearView { get; set; } internal Grid? YearView { get; set; }
private void PopulateGrids() private void PopulateGrids()
{ {
@ -294,7 +294,7 @@ namespace Avalonia.Controls.Primitives
{ {
for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++) for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++)
{ {
var daytitle = MonthView.Children[childIndex]; var daytitle = MonthView!.Children[childIndex];
if (daytitle != null) if (daytitle != null)
{ {
if (Owner != null) if (Owner != null)
@ -495,8 +495,7 @@ namespace Avalonia.Controls.Primitives
for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++) for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++)
{ {
CalendarDayButton childButton = MonthView.Children[childIndex] as CalendarDayButton; CalendarDayButton childButton = (CalendarDayButton)MonthView!.Children[childIndex];
Contract.Requires<ArgumentNullException>(childButton != null);
childButton.Index = childIndex; childButton.Index = childIndex;
SetButtonState(childButton, dateToAdd); SetButtonState(childButton, dateToAdd);
@ -532,8 +531,7 @@ namespace Avalonia.Controls.Primitives
childIndex++; childIndex++;
for (int i = childIndex; i < count; i++) for (int i = childIndex; i < count; i++)
{ {
childButton = MonthView.Children[i] as CalendarDayButton; childButton = (CalendarDayButton)MonthView.Children[i];
Contract.Requires<ArgumentNullException>(childButton != null);
// button needs a content to occupy the necessary space // button needs a content to occupy the necessary space
// for the content presenter // for the content presenter
childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat()); childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat());
@ -626,10 +624,9 @@ namespace Avalonia.Controls.Primitives
private void SetMonthButtonsForYearMode() private void SetMonthButtonsForYearMode()
{ {
int count = 0; int count = 0;
foreach (object child in YearView.Children) foreach (object child in YearView!.Children)
{ {
CalendarButton childButton = child as CalendarButton; CalendarButton childButton = (CalendarButton)child;
Contract.Requires<ArgumentNullException>(childButton != null);
// There should be no time component. Time is 12:00 AM // There should be no time component. Time is 12:00 AM
DateTime day = new DateTime(_currentMonth.Year, count + 1, 1); DateTime day = new DateTime(_currentMonth.Year, count + 1, 1);
childButton.DataContext = day; childButton.DataContext = day;
@ -703,7 +700,7 @@ namespace Avalonia.Controls.Primitives
{ {
if (Owner != null && calendarButton != null && calendarButton.DataContext != null) if (Owner != null && calendarButton != null && calendarButton.DataContext != null)
{ {
Owner.FocusCalendarButton.IsCalendarButtonFocused = false; Owner.FocusCalendarButton!.IsCalendarButtonFocused = false;
Owner.FocusCalendarButton = calendarButton; Owner.FocusCalendarButton = calendarButton;
calendarButton.IsCalendarButtonFocused = Owner.HasFocusInternal; calendarButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
@ -722,10 +719,9 @@ namespace Avalonia.Controls.Primitives
{ {
int year; int year;
int count = -1; int count = -1;
foreach (object child in YearView.Children) foreach (object child in YearView!.Children)
{ {
CalendarButton childButton = child as CalendarButton; CalendarButton childButton = (CalendarButton)child;
Contract.Requires<ArgumentNullException>(childButton != null);
year = decade + count; year = decade + count;
if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year) if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year)
@ -797,7 +793,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
internal void HeaderButton_Click(object sender, RoutedEventArgs e) internal void HeaderButton_Click(object? sender, RoutedEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
@ -805,7 +801,7 @@ namespace Avalonia.Controls.Primitives
{ {
Owner.Focus(); Owner.Focus();
} }
Button b = (Button)sender; Button b = (Button)sender!;
DateTime d; DateTime d;
if (b.IsEnabled) if (b.IsEnabled)
@ -833,7 +829,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
} }
internal void PreviousButton_Click(object sender, RoutedEventArgs e) internal void PreviousButton_Click(object? sender, RoutedEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
@ -842,14 +838,14 @@ namespace Avalonia.Controls.Primitives
Owner.Focus(); Owner.Focus();
} }
Button b = (Button)sender; Button b = (Button)sender!;
if (b.IsEnabled) if (b.IsEnabled)
{ {
Owner.OnPreviousClick(); Owner.OnPreviousClick();
} }
} }
} }
internal void NextButton_Click(object sender, RoutedEventArgs e) internal void NextButton_Click(object? sender, RoutedEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
@ -857,7 +853,7 @@ namespace Avalonia.Controls.Primitives
{ {
Owner.Focus(); Owner.Focus();
} }
Button b = (Button)sender; Button b = (Button)sender!;
if (b.IsEnabled) if (b.IsEnabled)
{ {
@ -866,7 +862,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
internal void Cell_MouseEnter(object sender, PointerEventArgs e) internal void Cell_MouseEnter(object? sender, PointerEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
@ -878,7 +874,7 @@ namespace Avalonia.Controls.Primitives
{ {
case CalendarSelectionMode.SingleDate: case CalendarSelectionMode.SingleDate:
{ {
DateTime selectedDate = (DateTime)b.DataContext; DateTime selectedDate = (DateTime)b.DataContext!;
Owner.CalendarDatePickerDisplayDateFlag = true; Owner.CalendarDatePickerDisplayDateFlag = true;
if (Owner.SelectedDates.Count == 0) if (Owner.SelectedDates.Count == 0)
{ {
@ -906,7 +902,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
internal void Cell_MouseLeftButtonDown(object sender, PointerPressedEventArgs e) internal void Cell_MouseLeftButtonDown(object? sender, PointerPressedEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
@ -917,15 +913,14 @@ namespace Avalonia.Controls.Primitives
bool ctrl, shift; bool ctrl, shift;
CalendarExtensions.GetMetaKeyState(e.KeyModifiers, out ctrl, out shift); CalendarExtensions.GetMetaKeyState(e.KeyModifiers, out ctrl, out shift);
CalendarDayButton b = sender as CalendarDayButton; CalendarDayButton b = (CalendarDayButton)sender!;
if (b != null) if (b != null)
{ {
_isControlPressed = ctrl; _isControlPressed = ctrl;
if (b.IsEnabled && !b.IsBlackout) if (b.IsEnabled && !b.IsBlackout)
{ {
DateTime selectedDate = (DateTime)b.DataContext; DateTime selectedDate = (DateTime)b.DataContext!;
Contract.Requires<ArgumentNullException>(selectedDate != null);
_isMouseLeftButtonDown = true; _isMouseLeftButtonDown = true;
// null check is added for unit tests // null check is added for unit tests
if (e != null) if (e != null)
@ -1027,7 +1022,7 @@ namespace Avalonia.Controls.Primitives
if (Owner != null) if (Owner != null)
{ {
Owner.HoverEndIndex = b.Index; Owner.HoverEndIndex = b.Index;
Owner.HoverEnd = (DateTime)b.DataContext; Owner.HoverEnd = (DateTime)b.DataContext!;
if (Owner.HoverEnd != null && Owner.HoverStart != null) if (Owner.HoverEnd != null && Owner.HoverStart != null)
{ {
@ -1041,11 +1036,11 @@ namespace Avalonia.Controls.Primitives
} }
} }
} }
internal void Cell_MouseLeftButtonUp(object sender, PointerReleasedEventArgs e) internal void Cell_MouseLeftButtonUp(object? sender, PointerReleasedEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
CalendarDayButton b = sender as CalendarDayButton; CalendarDayButton? b = sender as CalendarDayButton;
if (b != null && !b.IsBlackout) if (b != null && !b.IsBlackout)
{ {
Owner.OnDayButtonMouseUp(e); Owner.OnDayButtonMouseUp(e);
@ -1094,14 +1089,13 @@ namespace Avalonia.Controls.Primitives
} }
} }
} }
private void Cell_Click(object sender, RoutedEventArgs e) private void Cell_Click(object? sender, RoutedEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
if (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange) if (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange)
{ {
CalendarDayButton b = sender as CalendarDayButton; CalendarDayButton b = (CalendarDayButton)sender!;
Contract.Requires<ArgumentNullException>(b != null);
if (b.IsSelected) if (b.IsSelected)
{ {
@ -1118,10 +1112,9 @@ namespace Avalonia.Controls.Primitives
_isControlPressed = false; _isControlPressed = false;
} }
private void Month_CalendarButtonMouseDown(object sender, PointerPressedEventArgs e) private void Month_CalendarButtonMouseDown(object? sender, PointerPressedEventArgs e)
{ {
CalendarButton b = sender as CalendarButton; CalendarButton b = (CalendarButton)sender!;
Contract.Requires<ArgumentNullException>(b != null);
_isMouseLeftButtonDownYearView = true; _isMouseLeftButtonDownYearView = true;
@ -1133,13 +1126,13 @@ namespace Avalonia.Controls.Primitives
UpdateYearViewSelection(b); UpdateYearViewSelection(b);
} }
internal void Month_CalendarButtonMouseUp(object sender, PointerReleasedEventArgs e) internal void Month_CalendarButtonMouseUp(object? sender, PointerReleasedEventArgs e)
{ {
_isMouseLeftButtonDownYearView = false; _isMouseLeftButtonDownYearView = false;
if (Owner != null) if (Owner != null)
{ {
DateTime newmonth = (DateTime)((CalendarButton)sender).DataContext; DateTime newmonth = (DateTime)((CalendarButton)sender!).DataContext!;
if (Owner.DisplayMode == CalendarMode.Year) if (Owner.DisplayMode == CalendarMode.Year)
{ {
@ -1155,12 +1148,11 @@ namespace Avalonia.Controls.Primitives
} }
} }
private void Month_MouseEnter(object sender, PointerEventArgs e) private void Month_MouseEnter(object? sender, PointerEventArgs e)
{ {
if (_isMouseLeftButtonDownYearView) if (_isMouseLeftButtonDownYearView)
{ {
CalendarButton b = sender as CalendarButton; CalendarButton b = (CalendarButton)sender!;
Contract.Requires<ArgumentNullException>(b != null);
UpdateYearViewSelection(b); UpdateYearViewSelection(b);
} }
} }

2
src/Avalonia.Controls/Calendar/DateTimeHelper.cs

@ -5,6 +5,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
namespace Avalonia.Controls namespace Avalonia.Controls
@ -73,6 +74,7 @@ namespace Avalonia.Controls
return newD; return newD;
} }
[return: NotNullIfNotNull("d")]
public static DateTime? DiscardTime(DateTime? d) public static DateTime? DiscardTime(DateTime? d)
{ {
if (d == null) if (d == null)

4
src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs

@ -49,7 +49,7 @@ namespace Avalonia.Controls.Primitives
private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems) private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems)
{ {
_owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, removedItems, addedItems)); _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems));
} }
/// <summary> /// <summary>
@ -119,7 +119,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
_owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _owner.RemovedItems, _addedItems)); _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, _owner.RemovedItems, _addedItems));
_owner.RemovedItems.Clear(); _owner.RemovedItems.Clear();
_owner.UpdateMonths(); _owner.UpdateMonths();
_isRangeAdded = false; _isRangeAdded = false;

2
src/Avalonia.Controls/Canvas.cs

@ -135,7 +135,7 @@ namespace Avalonia.Controls
/// <param name="from">The control from which movement begins.</param> /// <param name="from">The control from which movement begins.</param>
/// <param name="wrap">Whether to wrap around when the first or last item is reached.</param> /// <param name="wrap">Whether to wrap around when the first or last item is reached.</param>
/// <returns>The control.</returns> /// <returns>The control.</returns>
IInputElement INavigableContainer.GetControl(NavigationDirection direction, IInputElement from, bool wrap) IInputElement? INavigableContainer.GetControl(NavigationDirection direction, IInputElement? from, bool wrap)
{ {
// TODO: Implement this // TODO: Implement this
return null; return null;

2
src/Avalonia.Controls/Chrome/CaptionButtons.cs

@ -3,8 +3,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable
namespace Avalonia.Controls.Chrome namespace Avalonia.Controls.Chrome
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/Chrome/TitleBar.cs

@ -3,8 +3,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable
namespace Avalonia.Controls.Chrome namespace Avalonia.Controls.Chrome
{ {
/// <summary> /// <summary>
@ -36,7 +34,7 @@ namespace Avalonia.Controls.Chrome
} }
} }
IsVisible = window.PlatformImpl.NeedsManagedDecorations; IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false;
} }
} }

40
src/Avalonia.Controls/ComboBox.cs

@ -46,8 +46,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="SelectionBoxItem"/> property. /// Defines the <see cref="SelectionBoxItem"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<ComboBox, object> SelectionBoxItemProperty = public static readonly DirectProperty<ComboBox, object?> SelectionBoxItemProperty =
AvaloniaProperty.RegisterDirect<ComboBox, object>(nameof(SelectionBoxItem), o => o.SelectionBoxItem); AvaloniaProperty.RegisterDirect<ComboBox, object?>(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
/// <summary> /// <summary>
/// Defines the <see cref="VirtualizationMode"/> property. /// Defines the <see cref="VirtualizationMode"/> property.
@ -58,14 +58,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="PlaceholderText"/> property. /// Defines the <see cref="PlaceholderText"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<string> PlaceholderTextProperty = public static readonly StyledProperty<string?> PlaceholderTextProperty =
AvaloniaProperty.Register<ComboBox, string>(nameof(PlaceholderText)); AvaloniaProperty.Register<ComboBox, string?>(nameof(PlaceholderText));
/// <summary> /// <summary>
/// Defines the <see cref="PlaceholderForeground"/> property. /// Defines the <see cref="PlaceholderForeground"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IBrush> PlaceholderForegroundProperty = public static readonly StyledProperty<IBrush?> PlaceholderForegroundProperty =
AvaloniaProperty.Register<ComboBox, IBrush>(nameof(PlaceholderForeground)); AvaloniaProperty.Register<ComboBox, IBrush?>(nameof(PlaceholderForeground));
/// <summary> /// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property. /// Defines the <see cref="HorizontalContentAlignment"/> property.
@ -80,8 +80,8 @@ namespace Avalonia.Controls
ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>(); ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>();
private bool _isDropDownOpen; private bool _isDropDownOpen;
private Popup _popup; private Popup? _popup;
private object _selectionBoxItem; private object? _selectionBoxItem;
private readonly CompositeDisposable _subscriptionsOnOpen = new CompositeDisposable(); private readonly CompositeDisposable _subscriptionsOnOpen = new CompositeDisposable();
/// <summary> /// <summary>
@ -117,7 +117,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the item to display as the control's content. /// Gets or sets the item to display as the control's content.
/// </summary> /// </summary>
protected object SelectionBoxItem protected object? SelectionBoxItem
{ {
get { return _selectionBoxItem; } get { return _selectionBoxItem; }
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); } set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); }
@ -126,7 +126,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the PlaceHolder text. /// Gets or sets the PlaceHolder text.
/// </summary> /// </summary>
public string PlaceholderText public string? PlaceholderText
{ {
get { return GetValue(PlaceholderTextProperty); } get { return GetValue(PlaceholderTextProperty); }
set { SetValue(PlaceholderTextProperty, value); } set { SetValue(PlaceholderTextProperty, value); }
@ -135,7 +135,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the Brush that renders the placeholder text. /// Gets or sets the Brush that renders the placeholder text.
/// </summary> /// </summary>
public IBrush PlaceholderForeground public IBrush? PlaceholderForeground
{ {
get { return GetValue(PlaceholderForegroundProperty); } get { return GetValue(PlaceholderForegroundProperty); }
set { SetValue(PlaceholderForegroundProperty, value); } set { SetValue(PlaceholderForegroundProperty, value); }
@ -262,9 +262,9 @@ namespace Avalonia.Controls
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPointerReleased(PointerReleasedEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e)
{ {
if (!e.Handled) if (!e.Handled && e.Source is IVisual source)
{ {
if (_popup?.IsInsidePopup((IVisual)e.Source) == true) if (_popup?.IsInsidePopup(source) == true)
{ {
if (UpdateSelectionFromEventSource(e.Source)) if (UpdateSelectionFromEventSource(e.Source))
{ {
@ -304,7 +304,7 @@ namespace Avalonia.Controls
} }
} }
private void PopupClosed(object sender, EventArgs e) private void PopupClosed(object? sender, EventArgs e)
{ {
_subscriptionsOnOpen.Clear(); _subscriptionsOnOpen.Clear();
@ -314,7 +314,7 @@ namespace Avalonia.Controls
} }
} }
private void PopupOpened(object sender, EventArgs e) private void PopupOpened(object? sender, EventArgs e)
{ {
TryFocusSelectedItem(); TryFocusSelectedItem();
@ -326,7 +326,7 @@ namespace Avalonia.Controls
toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) => toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) =>
{ {
//eat wheel scroll event outside dropdown popup while it's open //eat wheel scroll event outside dropdown popup while it's open
if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel) if (IsDropDownOpen && (ev.Source as IVisual)?.GetVisualRoot() == toplevel)
{ {
ev.Handled = true; ev.Handled = true;
} }
@ -360,12 +360,12 @@ namespace Avalonia.Controls
var selectedIndex = SelectedIndex; var selectedIndex = SelectedIndex;
if (IsDropDownOpen && selectedIndex != -1) if (IsDropDownOpen && selectedIndex != -1)
{ {
var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); var container = ItemContainerGenerator!.ContainerFromIndex(selectedIndex);
if (container == null && SelectedIndex != -1) if (container == null && SelectedIndex != -1)
{ {
ScrollIntoView(Selection.SelectedIndex); ScrollIntoView(Selection.SelectedIndex);
container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); container = ItemContainerGenerator!.ContainerFromIndex(selectedIndex);
} }
if (container != null && CanFocus(container)) if (container != null && CanFocus(container))
@ -377,7 +377,7 @@ namespace Avalonia.Controls
private bool CanFocus(IControl control) => control.Focusable && control.IsEffectivelyEnabled && control.IsVisible; private bool CanFocus(IControl control) => control.Focusable && control.IsEffectivelyEnabled && control.IsVisible;
private void UpdateSelectionBoxItem(object item) private void UpdateSelectionBoxItem(object? item)
{ {
var contentControl = item as IContentControl; var contentControl = item as IContentControl;
@ -415,7 +415,7 @@ namespace Avalonia.Controls
private void SelectFocusedItem() private void SelectFocusedItem()
{ {
foreach (ItemContainerInfo dropdownItem in ItemContainerGenerator.Containers) foreach (ItemContainerInfo dropdownItem in ItemContainerGenerator!.Containers)
{ {
if (dropdownItem.ContainerControl.IsFocused) if (dropdownItem.ContainerControl.IsFocused)
{ {

14
src/Avalonia.Controls/ContentControl.cs

@ -17,14 +17,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Content"/> property. /// Defines the <see cref="Content"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<object> ContentProperty = public static readonly StyledProperty<object?> ContentProperty =
AvaloniaProperty.Register<ContentControl, object>(nameof(Content)); AvaloniaProperty.Register<ContentControl, object?>(nameof(Content));
/// <summary> /// <summary>
/// Defines the <see cref="ContentTemplate"/> property. /// Defines the <see cref="ContentTemplate"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IDataTemplate> ContentTemplateProperty = public static readonly StyledProperty<IDataTemplate?> ContentTemplateProperty =
AvaloniaProperty.Register<ContentControl, IDataTemplate>(nameof(ContentTemplate)); AvaloniaProperty.Register<ContentControl, IDataTemplate?>(nameof(ContentTemplate));
/// <summary> /// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property. /// Defines the <see cref="HorizontalContentAlignment"/> property.
@ -48,7 +48,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
[Content] [Content]
[DependsOn(nameof(ContentTemplate))] [DependsOn(nameof(ContentTemplate))]
public object Content public object? Content
{ {
get { return GetValue(ContentProperty); } get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); } set { SetValue(ContentProperty, value); }
@ -57,7 +57,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the data template used to display the content of the control. /// Gets or sets the data template used to display the content of the control.
/// </summary> /// </summary>
public IDataTemplate ContentTemplate public IDataTemplate? ContentTemplate
{ {
get { return GetValue(ContentTemplateProperty); } get { return GetValue(ContentTemplateProperty); }
set { SetValue(ContentTemplateProperty, value); } set { SetValue(ContentTemplateProperty, value); }
@ -66,7 +66,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the presenter from the control's template. /// Gets the presenter from the control's template.
/// </summary> /// </summary>
public IContentPresenter Presenter public IContentPresenter? Presenter
{ {
get; get;
private set; private set;

14
src/Avalonia.Controls/ContextMenu.cs

@ -14,8 +14,6 @@ using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Styling; using Avalonia.Styling;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
@ -368,7 +366,7 @@ namespace Avalonia.Controls
}); });
} }
private void PopupOpened(object sender, EventArgs e) private void PopupOpened(object? sender, EventArgs e)
{ {
_previousFocus = FocusManager.Instance?.Current; _previousFocus = FocusManager.Instance?.Current;
Focus(); Focus();
@ -376,12 +374,12 @@ namespace Avalonia.Controls
_popupHostChangedHandler?.Invoke(_popup!.Host); _popupHostChangedHandler?.Invoke(_popup!.Host);
} }
private void PopupClosing(object sender, CancelEventArgs e) private void PopupClosing(object? sender, CancelEventArgs e)
{ {
e.Cancel = CancelClosing(); e.Cancel = CancelClosing();
} }
private void PopupClosed(object sender, EventArgs e) private void PopupClosed(object? sender, EventArgs e)
{ {
foreach (var i in LogicalChildren) foreach (var i in LogicalChildren)
{ {
@ -411,7 +409,7 @@ namespace Avalonia.Controls
_popupHostChangedHandler?.Invoke(null); _popupHostChangedHandler?.Invoke(null);
} }
private void PopupKeyUp(object sender, KeyEventArgs e) private void PopupKeyUp(object? sender, KeyEventArgs e)
{ {
if (IsOpen) if (IsOpen)
{ {
@ -426,7 +424,7 @@ namespace Avalonia.Controls
} }
} }
private static void ControlContextRequested(object sender, ContextRequestedEventArgs e) private static void ControlContextRequested(object? sender, ContextRequestedEventArgs e)
{ {
if (sender is Control control if (sender is Control control
&& control.ContextMenu is ContextMenu contextMenu && control.ContextMenu is ContextMenu contextMenu
@ -439,7 +437,7 @@ namespace Avalonia.Controls
} }
} }
private static void ControlDetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) private static void ControlDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{ {
if (sender is Control control if (sender is Control control
&& control.ContextMenu is ContextMenu contextMenu) && control.ContextMenu is ContextMenu contextMenu)

2
src/Avalonia.Controls/ContextRequestedEventArgs.cs

@ -1,8 +1,6 @@
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>

23
src/Avalonia.Controls/Control.cs

@ -1,16 +1,16 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.VisualTree; using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
@ -60,7 +60,13 @@ namespace Avalonia.Controls
public static readonly RoutedEvent<ContextRequestedEventArgs> ContextRequestedEvent = public static readonly RoutedEvent<ContextRequestedEventArgs> ContextRequestedEvent =
RoutedEvent.Register<Control, ContextRequestedEventArgs>(nameof(ContextRequested), RoutedEvent.Register<Control, ContextRequestedEventArgs>(nameof(ContextRequested),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble); RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="FlowDirection"/> property.
/// </summary>
public static readonly AttachedProperty<FlowDirection> FlowDirectionProperty =
AvaloniaProperty.RegisterAttached<Control, Control, FlowDirection>(nameof(FlowDirection), inherits: true);
private DataTemplates? _dataTemplates; private DataTemplates? _dataTemplates;
private IControl? _focusAdorner; private IControl? _focusAdorner;
@ -108,11 +114,20 @@ namespace Avalonia.Controls
get => GetValue(TagProperty); get => GetValue(TagProperty);
set => SetValue(TagProperty, value); set => SetValue(TagProperty, value);
} }
/// <summary>
/// Gets or sets the text flow direction.
/// </summary>
public FlowDirection FlowDirection
{
get => GetValue(FlowDirectionProperty);
set => SetValue(FlowDirectionProperty, value);
}
/// <summary> /// <summary>
/// Occurs when the user has completed a context input gesture, such as a right-click. /// Occurs when the user has completed a context input gesture, such as a right-click.
/// </summary> /// </summary>
public event EventHandler<ContextRequestedEventArgs> ContextRequested public event EventHandler<ContextRequestedEventArgs>? ContextRequested
{ {
add => AddHandler(ContextRequestedEvent, value); add => AddHandler(ContextRequestedEvent, value);
remove => RemoveHandler(ContextRequestedEvent, value); remove => RemoveHandler(ContextRequestedEvent, value);

35
src/Avalonia.Controls/ControlExtensions.cs

@ -17,7 +17,7 @@ namespace Avalonia.Controls
/// <param name="control">The control.</param> /// <param name="control">The control.</param>
public static void BringIntoView(this IControl control) public static void BringIntoView(this IControl control)
{ {
Contract.Requires<ArgumentNullException>(control != null); _ = control ?? throw new ArgumentNullException(nameof(control));
control.BringIntoView(new Rect(control.Bounds.Size)); control.BringIntoView(new Rect(control.Bounds.Size));
} }
@ -29,7 +29,7 @@ namespace Avalonia.Controls
/// <param name="rect">The area of the control to being into view.</param> /// <param name="rect">The area of the control to being into view.</param>
public static void BringIntoView(this IControl control, Rect rect) public static void BringIntoView(this IControl control, Rect rect)
{ {
Contract.Requires<ArgumentNullException>(control != null); _ = control ?? throw new ArgumentNullException(nameof(control));
if (control.IsEffectivelyVisible) if (control.IsEffectivelyVisible)
{ {
@ -51,10 +51,10 @@ namespace Avalonia.Controls
/// <param name="control">The control to look in.</param> /// <param name="control">The control to look in.</param>
/// <param name="name">The name of the control to find.</param> /// <param name="name">The name of the control to find.</param>
/// <returns>The control or null if not found.</returns> /// <returns>The control or null if not found.</returns>
public static T FindControl<T>(this IControl control, string name) where T : class, IControl public static T? FindControl<T>(this IControl control, string name) where T : class, IControl
{ {
Contract.Requires<ArgumentNullException>(control != null); _ = control ?? throw new ArgumentNullException(nameof(control));
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
var nameScope = control.FindNameScope(); var nameScope = control.FindNameScope();
@ -66,6 +66,29 @@ namespace Avalonia.Controls
return nameScope.Find<T>(name); return nameScope.Find<T>(name);
} }
/// <summary>
/// Finds the named control in the scope of the specified control and throws if not found.
/// </summary>
/// <typeparam name="T">The type of the control to find.</typeparam>
/// <param name="control">The control to look in.</param>
/// <param name="name">The name of the control to find.</param>
/// <returns>The control.</returns>
public static T GetControl<T>(this IControl control, string name) where T : class, IControl
{
_ = control ?? throw new ArgumentNullException(nameof(control));
_ = name ?? throw new ArgumentNullException(nameof(name));
var nameScope = control.FindNameScope();
if (nameScope == null)
{
throw new InvalidOperationException("Could not find parent name scope.");
}
return nameScope.Find<T>(name) ??
throw new ArgumentException($"Could not find control named '{name}'.");
}
/// <summary> /// <summary>
/// Sets a pseudoclass depending on an observable trigger. /// Sets a pseudoclass depending on an observable trigger.
/// </summary> /// </summary>
@ -75,7 +98,7 @@ namespace Avalonia.Controls
/// <returns>A disposable used to cancel the subscription.</returns> /// <returns>A disposable used to cancel the subscription.</returns>
public static IDisposable Set(this IPseudoClasses classes, string name, IObservable<bool> trigger) public static IDisposable Set(this IPseudoClasses classes, string name, IObservable<bool> trigger)
{ {
Contract.Requires<ArgumentNullException>(classes != null); _ = classes ?? throw new ArgumentNullException(nameof(classes));
return trigger.Subscribe(x => classes.Set(name, x)); return trigger.Subscribe(x => classes.Set(name, x));
} }

3
src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs

@ -1,5 +1,4 @@
#nullable enable using System;
using System;
using System.Globalization; using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;

4
src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs

@ -16,7 +16,7 @@ namespace Avalonia.Controls.Converters
public bool Bottom { get; set; } = false; public bool Bottom { get; set; } = false;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is int scalarDepth) if (value is int scalarDepth)
{ {
@ -38,7 +38,7 @@ namespace Avalonia.Controls.Converters
} }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }

2
src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs

@ -11,7 +11,7 @@ namespace Avalonia.Controls.Converters
{ {
public static readonly MenuScrollingVisibilityConverter Instance = new MenuScrollingVisibilityConverter(); public static readonly MenuScrollingVisibilityConverter Instance = new MenuScrollingVisibilityConverter();
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture) public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{ {
if (parameter == null || if (parameter == null ||
values == null || values == null ||

4
src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs

@ -13,7 +13,7 @@ namespace Avalonia.Controls.Converters
/// </summary> /// </summary>
public class PlatformKeyGestureConverter : IValueConverter public class PlatformKeyGestureConverter : IValueConverter
{ {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is null) if (value is null)
{ {
@ -29,7 +29,7 @@ namespace Avalonia.Controls.Converters
} }
} }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

29
src/Avalonia.Controls/DataValidationErrors.cs

@ -21,8 +21,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the DataValidationErrors.Errors attached property. /// Defines the DataValidationErrors.Errors attached property.
/// </summary> /// </summary>
public static readonly AttachedProperty<IEnumerable<object>> ErrorsProperty = public static readonly AttachedProperty<IEnumerable<object>?> ErrorsProperty =
AvaloniaProperty.RegisterAttached<DataValidationErrors, Control, IEnumerable<object>>("Errors"); AvaloniaProperty.RegisterAttached<DataValidationErrors, Control, IEnumerable<object>?>("Errors");
/// <summary> /// <summary>
/// Defines the DataValidationErrors.HasErrors attached property. /// Defines the DataValidationErrors.HasErrors attached property.
@ -34,15 +34,15 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<DataValidationErrors, IDataTemplate>(nameof(ErrorTemplate)); AvaloniaProperty.Register<DataValidationErrors, IDataTemplate>(nameof(ErrorTemplate));
private Control _owner; private Control? _owner;
public static readonly DirectProperty<DataValidationErrors, Control> OwnerProperty = public static readonly DirectProperty<DataValidationErrors, Control?> OwnerProperty =
AvaloniaProperty.RegisterDirect<DataValidationErrors, Control>( AvaloniaProperty.RegisterDirect<DataValidationErrors, Control?>(
nameof(Owner), nameof(Owner),
o => o.Owner, o => o.Owner,
(o, v) => o.Owner = v); (o, v) => o.Owner = v);
public Control Owner public Control? Owner
{ {
get { return _owner; } get { return _owner; }
set { SetAndRaise(OwnerProperty, ref _owner, value); } set { SetAndRaise(OwnerProperty, ref _owner, value); }
@ -75,7 +75,7 @@ namespace Avalonia.Controls
private static void ErrorsChanged(AvaloniaPropertyChangedEventArgs e) private static void ErrorsChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var control = (Control)e.Sender; var control = (Control)e.Sender;
var errors = (IEnumerable<object>)e.NewValue; var errors = (IEnumerable<object>?)e.NewValue;
var hasErrors = false; var hasErrors = false;
if (errors != null && errors.Any()) if (errors != null && errors.Any())
@ -87,18 +87,18 @@ namespace Avalonia.Controls
{ {
var control = (Control)e.Sender; var control = (Control)e.Sender;
var classes = (IPseudoClasses)control.Classes; var classes = (IPseudoClasses)control.Classes;
classes.Set(":error", (bool)e.NewValue); classes.Set(":error", (bool)e.NewValue!);
} }
public static IEnumerable<object> GetErrors(Control control) public static IEnumerable<object>? GetErrors(Control control)
{ {
return control.GetValue(ErrorsProperty); return control.GetValue(ErrorsProperty);
} }
public static void SetErrors(Control control, IEnumerable<object> errors) public static void SetErrors(Control control, IEnumerable<object>? errors)
{ {
control.SetValue(ErrorsProperty, errors); control.SetValue(ErrorsProperty, errors);
} }
public static void SetError(Control control, Exception error) public static void SetError(Control control, Exception? error)
{ {
SetErrors(control, UnpackException(error)); SetErrors(control, UnpackException(error));
} }
@ -111,7 +111,7 @@ namespace Avalonia.Controls
return control.GetValue(HasErrorsProperty); return control.GetValue(HasErrorsProperty);
} }
private static IEnumerable<object> UnpackException(Exception exception) private static IEnumerable<object>? UnpackException(Exception? exception)
{ {
if (exception != null) if (exception != null)
{ {
@ -132,8 +132,9 @@ namespace Avalonia.Controls
private static object GetExceptionData(Exception exception) private static object GetExceptionData(Exception exception)
{ {
if (exception is DataValidationException dataValidationException) if (exception is DataValidationException dataValidationException &&
return dataValidationException.ErrorData; dataValidationException.ErrorData is object data)
return data;
return exception; return exception;
} }

60
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -93,15 +93,15 @@ namespace Avalonia.Controls
defaultBindingMode: BindingMode.TwoWay); defaultBindingMode: BindingMode.TwoWay);
// Template Items // Template Items
private Button _flyoutButton; private Button? _flyoutButton;
private TextBlock _dayText; private TextBlock? _dayText;
private TextBlock _monthText; private TextBlock? _monthText;
private TextBlock _yearText; private TextBlock? _yearText;
private Grid _container; private Grid? _container;
private Rectangle _spacer1; private Rectangle? _spacer1;
private Rectangle _spacer2; private Rectangle? _spacer2;
private Popup _popup; private Popup? _popup;
private DatePickerPresenter _presenter; private DatePickerPresenter? _presenter;
private bool _areControlsAvailable; private bool _areControlsAvailable;
@ -256,7 +256,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Raised when the <see cref="SelectedDate"/> changes /// Raised when the <see cref="SelectedDate"/> changes
/// </summary> /// </summary>
public event EventHandler<DatePickerSelectedValueChangedEventArgs> SelectedDateChanged; public event EventHandler<DatePickerSelectedValueChangedEventArgs>? SelectedDateChanged;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
@ -307,16 +307,16 @@ namespace Avalonia.Controls
} }
} }
private void OnDismissPicker(object sender, EventArgs e) private void OnDismissPicker(object? sender, EventArgs e)
{ {
_popup.Close(); _popup!.Close();
Focus(); Focus();
} }
private void OnConfirmed(object sender, EventArgs e) private void OnConfirmed(object? sender, EventArgs e)
{ {
_popup.Close(); _popup!.Close();
SelectedDate = _presenter.Date; SelectedDate = _presenter!.Date;
} }
private void SetGrid() private void SetGrid()
@ -325,7 +325,7 @@ namespace Avalonia.Controls
return; return;
var fmt = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; var fmt = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
var columns = new List<(TextBlock, int)> var columns = new List<(TextBlock?, int)>
{ {
(_monthText, MonthVisible ? fmt.IndexOf("m", StringComparison.OrdinalIgnoreCase) : -1), (_monthText, MonthVisible ? fmt.IndexOf("m", StringComparison.OrdinalIgnoreCase) : -1),
(_yearText, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1), (_yearText, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1),
@ -366,11 +366,11 @@ namespace Avalonia.Controls
var isSpacer1Visible = columnIndex > 1; var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2; var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated // ternary conditional operator is used to make sure grid cells will be validated
Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0); Grid.SetColumn(_spacer1!, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0); Grid.SetColumn(_spacer2!, isSpacer2Visible ? 3 : 0);
_spacer1.IsVisible = isSpacer1Visible; _spacer1!.IsVisible = isSpacer1Visible;
_spacer2.IsVisible = isSpacer2Visible; _spacer2!.IsVisible = isSpacer2Visible;
} }
private void SetSelectedDateText() private void SetSelectedDateText()
@ -382,37 +382,37 @@ namespace Avalonia.Controls
{ {
PseudoClasses.Set(":hasnodate", false); PseudoClasses.Set(":hasnodate", false);
var selDate = SelectedDate.Value; var selDate = SelectedDate.Value;
_monthText.Text = selDate.ToString(MonthFormat); _monthText!.Text = selDate.ToString(MonthFormat);
_yearText.Text = selDate.ToString(YearFormat); _yearText!.Text = selDate.ToString(YearFormat);
_dayText.Text = selDate.ToString(DayFormat); _dayText!.Text = selDate.ToString(DayFormat);
} }
else else
{ {
PseudoClasses.Set(":hasnodate", true); PseudoClasses.Set(":hasnodate", true);
_monthText.Text = "month"; _monthText!.Text = "month";
_yearText.Text = "year"; _yearText!.Text = "year";
_dayText.Text = "day"; _dayText!.Text = "day";
} }
} }
private void OnFlyoutButtonClicked(object sender, RoutedEventArgs e) private void OnFlyoutButtonClicked(object? sender, RoutedEventArgs e)
{ {
if (_presenter == null) if (_presenter == null)
throw new InvalidOperationException("No DatePickerPresenter found"); throw new InvalidOperationException("No DatePickerPresenter found");
_presenter.Date = SelectedDate ?? DateTimeOffset.Now; _presenter.Date = SelectedDate ?? DateTimeOffset.Now;
_popup.IsOpen = true; _popup!.IsOpen = true;
var deltaY = _presenter.GetOffsetForPopup(); var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior // The extra 5 px I think is related to default popup placement behavior
_popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5), _popup!.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom, Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY); Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
} }
protected virtual void OnSelectedDateChanged(object sender, DatePickerSelectedValueChangedEventArgs e) protected virtual void OnSelectedDateChanged(object? sender, DatePickerSelectedValueChangedEventArgs e)
{ {
SelectedDateChanged?.Invoke(sender, e); SelectedDateChanged?.Invoke(sender, e);
} }

103
src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs

@ -78,23 +78,23 @@ namespace Avalonia.Controls
x.YearVisible, (x, v) => x.YearVisible = v); x.YearVisible, (x, v) => x.YearVisible = v);
// Template Items // Template Items
private Grid _pickerContainer; private Grid? _pickerContainer;
private Button _acceptButton; private Button? _acceptButton;
private Button _dismissButton; private Button? _dismissButton;
private Rectangle _spacer1; private Rectangle? _spacer1;
private Rectangle _spacer2; private Rectangle? _spacer2;
private Panel _monthHost; private Panel? _monthHost;
private Panel _yearHost; private Panel? _yearHost;
private Panel _dayHost; private Panel? _dayHost;
private DateTimePickerPanel _monthSelector; private DateTimePickerPanel? _monthSelector;
private DateTimePickerPanel _yearSelector; private DateTimePickerPanel? _yearSelector;
private DateTimePickerPanel _daySelector; private DateTimePickerPanel? _daySelector;
private Button _monthUpButton; private Button? _monthUpButton;
private Button _dayUpButton; private Button? _dayUpButton;
private Button _yearUpButton; private Button? _yearUpButton;
private Button _monthDownButton; private Button? _monthDownButton;
private Button _dayDownButton; private Button? _dayDownButton;
private Button _yearDownButton; private Button? _yearDownButton;
private DateTimeOffset _date; private DateTimeOffset _date;
private string _dayFormat = "%d"; private string _dayFormat = "%d";
@ -308,9 +308,12 @@ namespace Avalonia.Controls
e.Handled = true; e.Handled = true;
break; break;
case Key.Tab: case Key.Tab:
var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next); if (FocusManager.Instance?.Current is IInputElement focus)
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None); {
e.Handled = true; var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
e.Handled = true;
}
break; break;
case Key.Enter: case Key.Enter:
Date = _syncDate; Date = _syncDate;
@ -332,13 +335,13 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true; _suppressUpdateSelection = true;
_monthSelector.MaximumValue = 12; _monthSelector!.MaximumValue = 12;
_monthSelector.MinimumValue = 1; _monthSelector.MinimumValue = 1;
_monthSelector.ItemFormat = MonthFormat; _monthSelector.ItemFormat = MonthFormat;
_daySelector.ItemFormat = DayFormat; _daySelector!.ItemFormat = DayFormat;
_yearSelector.MaximumValue = MaxYear.Year; _yearSelector!.MaximumValue = MaxYear.Year;
_yearSelector.MinimumValue = MinYear.Year; _yearSelector.MinimumValue = MinYear.Year;
_yearSelector.ItemFormat = YearFormat; _yearSelector.ItemFormat = YearFormat;
@ -375,7 +378,7 @@ namespace Avalonia.Controls
private void SetGrid() private void SetGrid()
{ {
var fmt = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; var fmt = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
var columns = new List<(Panel, int)> var columns = new List<(Panel?, int)>
{ {
(_monthHost, MonthVisible ? fmt.IndexOf("m", StringComparison.OrdinalIgnoreCase) : -1), (_monthHost, MonthVisible ? fmt.IndexOf("m", StringComparison.OrdinalIgnoreCase) : -1),
(_yearHost, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1), (_yearHost, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1),
@ -383,7 +386,7 @@ namespace Avalonia.Controls
}; };
columns.Sort((x, y) => x.Item2 - y.Item2); columns.Sort((x, y) => x.Item2 - y.Item2);
_pickerContainer.ColumnDefinitions.Clear(); _pickerContainer!.ColumnDefinitions.Clear();
var columnIndex = 0; var columnIndex = 0;
@ -416,18 +419,18 @@ namespace Avalonia.Controls
var isSpacer1Visible = columnIndex > 1; var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2; var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated // ternary conditional operator is used to make sure grid cells will be validated
Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0); Grid.SetColumn(_spacer1!, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0); Grid.SetColumn(_spacer2!, isSpacer2Visible ? 3 : 0);
_spacer1.IsVisible = isSpacer1Visible; _spacer1!.IsVisible = isSpacer1Visible;
_spacer2.IsVisible = isSpacer2Visible; _spacer2!.IsVisible = isSpacer2Visible;
} }
private void SetInitialFocus() private void SetInitialFocus()
{ {
int monthCol = MonthVisible ? Grid.GetColumn(_monthHost) : int.MaxValue; int monthCol = MonthVisible ? Grid.GetColumn(_monthHost!) : int.MaxValue;
int dayCol = DayVisible ? Grid.GetColumn(_dayHost) : int.MaxValue; int dayCol = DayVisible ? Grid.GetColumn(_dayHost!) : int.MaxValue;
int yearCol = YearVisible ? Grid.GetColumn(_yearHost) : int.MaxValue; int yearCol = YearVisible ? Grid.GetColumn(_yearHost!) : int.MaxValue;
if (monthCol < dayCol && monthCol < yearCol) if (monthCol < dayCol && monthCol < yearCol)
{ {
@ -443,39 +446,39 @@ namespace Avalonia.Controls
} }
} }
private void OnDismissButtonClicked(object sender, RoutedEventArgs e) private void OnDismissButtonClicked(object? sender, RoutedEventArgs e)
{ {
OnDismiss(); OnDismiss();
} }
private void OnAcceptButtonClicked(object sender, RoutedEventArgs e) private void OnAcceptButtonClicked(object? sender, RoutedEventArgs e)
{ {
Date = _syncDate; Date = _syncDate;
OnConfirmed(); OnConfirmed();
} }
private void OnSelectorButtonClick(object sender, RoutedEventArgs e) private void OnSelectorButtonClick(object? sender, RoutedEventArgs e)
{ {
if (sender == _monthUpButton) if (sender == _monthUpButton)
_monthSelector.ScrollUp(); _monthSelector!.ScrollUp();
else if (sender == _monthDownButton) else if (sender == _monthDownButton)
_monthSelector.ScrollDown(); _monthSelector!.ScrollDown();
else if (sender == _yearUpButton) else if (sender == _yearUpButton)
_yearSelector.ScrollUp(); _yearSelector!.ScrollUp();
else if (sender == _yearDownButton) else if (sender == _yearDownButton)
_yearSelector.ScrollDown(); _yearSelector!.ScrollDown();
else if (sender == _dayUpButton) else if (sender == _dayUpButton)
_daySelector.ScrollUp(); _daySelector!.ScrollUp();
else if (sender == _dayDownButton) else if (sender == _dayDownButton)
_daySelector.ScrollDown(); _daySelector!.ScrollDown();
} }
private void OnYearChanged(object sender, EventArgs e) private void OnYearChanged(object? sender, EventArgs e)
{ {
if (_suppressUpdateSelection) if (_suppressUpdateSelection)
return; return;
int maxDays = _calendar.GetDaysInMonth(_yearSelector.SelectedValue, _syncDate.Month); int maxDays = _calendar.GetDaysInMonth(_yearSelector!.SelectedValue, _syncDate.Month);
var newDate = new DateTimeOffset(_yearSelector.SelectedValue, _syncDate.Month, var newDate = new DateTimeOffset(_yearSelector.SelectedValue, _syncDate.Month,
_syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset); _syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset);
@ -487,7 +490,7 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true; _suppressUpdateSelection = true;
_daySelector.FormatDate = newDate.Date; _daySelector!.FormatDate = newDate.Date;
if (_daySelector.MaximumValue != maxDays) if (_daySelector.MaximumValue != maxDays)
_daySelector.MaximumValue = maxDays; _daySelector.MaximumValue = maxDays;
@ -497,19 +500,19 @@ namespace Avalonia.Controls
_suppressUpdateSelection = false; _suppressUpdateSelection = false;
} }
private void OnDayChanged(object sender, EventArgs e) private void OnDayChanged(object? sender, EventArgs e)
{ {
if (_suppressUpdateSelection) if (_suppressUpdateSelection)
return; return;
_syncDate = new DateTimeOffset(_syncDate.Year, _syncDate.Month, _daySelector.SelectedValue, 0, 0, 0, _syncDate.Offset); _syncDate = new DateTimeOffset(_syncDate.Year, _syncDate.Month, _daySelector!.SelectedValue, 0, 0, 0, _syncDate.Offset);
} }
private void OnMonthChanged(object sender, EventArgs e) private void OnMonthChanged(object? sender, EventArgs e)
{ {
if (_suppressUpdateSelection) if (_suppressUpdateSelection)
return; return;
int maxDays = _calendar.GetDaysInMonth(_syncDate.Year, _monthSelector.SelectedValue); int maxDays = _calendar.GetDaysInMonth(_syncDate.Year, _monthSelector!.SelectedValue);
var newDate = new DateTimeOffset(_syncDate.Year, _monthSelector.SelectedValue, var newDate = new DateTimeOffset(_syncDate.Year, _monthSelector.SelectedValue,
_syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset); _syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset);
@ -521,7 +524,7 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true; _suppressUpdateSelection = true;
_daySelector.FormatDate = newDate.Date; _daySelector!.FormatDate = newDate.Date;
_syncDate = newDate; _syncDate = newDate;
if (_daySelector.MaximumValue != maxDays) if (_daySelector.MaximumValue != maxDays)
@ -535,7 +538,7 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup() internal double GetOffsetForPopup()
{ {
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41; var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector.ItemHeight / 2); return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector!.ItemHeight / 2);
} }
} }
} }

28
src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs

@ -58,7 +58,7 @@ namespace Avalonia.Controls.Primitives
private Vector _offset; private Vector _offset;
private bool _hasInit; private bool _hasInit;
private bool _suppressUpdateOffset; private bool _suppressUpdateOffset;
private ListBoxItem _pressedItem; private ListBoxItem? _pressedItem;
public DateTimePickerPanel() public DateTimePickerPanel()
{ {
@ -271,9 +271,9 @@ namespace Avalonia.Controls.Primitives
public Size Viewport => new Size(0, ItemHeight); public Size Viewport => new Size(0, ItemHeight);
public event EventHandler ScrollInvalidated; public event EventHandler? ScrollInvalidated;
public event EventHandler SelectionChanged; public event EventHandler? SelectionChanged;
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
@ -523,40 +523,44 @@ namespace Avalonia.Controls.Primitives
return newValue; return newValue;
} }
private void OnItemPointerDown(object sender, PointerPressedEventArgs e) private void OnItemPointerDown(object? sender, PointerPressedEventArgs e)
{ {
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed &&
e.Source is IVisual source)
{ {
_pressedItem = GetItemFromSource((IVisual)e.Source); _pressedItem = GetItemFromSource(source);
e.Handled = true; e.Handled = true;
} }
} }
private void OnItemPointerUp(object sender, PointerReleasedEventArgs e) private void OnItemPointerUp(object? sender, PointerReleasedEventArgs e)
{ {
if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased && if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased &&
_pressedItem != null) _pressedItem != null &&
e.Source is IVisual source &&
GetItemFromSource(source) is ListBoxItem item &&
item.Tag is int tag)
{ {
SelectedValue = (int)GetItemFromSource((IVisual)e.Source).Tag; SelectedValue = tag;
_pressedItem = null; _pressedItem = null;
e.Handled = true; e.Handled = true;
} }
} }
//Helper to get ListBoxItem from pointerevent source //Helper to get ListBoxItem from pointerevent source
private ListBoxItem GetItemFromSource(IVisual src) private ListBoxItem? GetItemFromSource(IVisual src)
{ {
var item = src; var item = src;
while (item != null && !(item is ListBoxItem)) while (item != null && !(item is ListBoxItem))
{ {
item = item.VisualParent; item = item.VisualParent;
} }
return (ListBoxItem)item; return (ListBoxItem?)item;
} }
public bool BringIntoView(IControl target, Rect targetRect) { return false; } public bool BringIntoView(IControl target, Rect targetRect) { return false; }
public IControl GetControlInDirection(NavigationDirection direction, IControl from) { return null; } public IControl? GetControlInDirection(NavigationDirection direction, IControl from) { return null; }
public void RaiseScrollInvalidated(EventArgs e) public void RaiseScrollInvalidated(EventArgs e)
{ {

6
src/Avalonia.Controls/DateTimePickers/PickerPresenterBase.cs

@ -16,10 +16,10 @@ namespace Avalonia.Controls.Primitives
protected virtual void OnDismiss() protected virtual void OnDismiss()
{ {
Dismissed.Invoke(this, EventArgs.Empty); Dismissed?.Invoke(this, EventArgs.Empty);
} }
public event EventHandler Confirmed; public event EventHandler? Confirmed;
public event EventHandler Dismissed; public event EventHandler? Dismissed;
} }
} }

56
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -49,18 +49,18 @@ namespace Avalonia.Controls
defaultBindingMode: BindingMode.TwoWay); defaultBindingMode: BindingMode.TwoWay);
// Template Items // Template Items
private TimePickerPresenter _presenter; private TimePickerPresenter? _presenter;
private Button _flyoutButton; private Button? _flyoutButton;
private Border _firstPickerHost; private Border? _firstPickerHost;
private Border _secondPickerHost; private Border? _secondPickerHost;
private Border _thirdPickerHost; private Border? _thirdPickerHost;
private TextBlock _hourText; private TextBlock? _hourText;
private TextBlock _minuteText; private TextBlock? _minuteText;
private TextBlock _periodText; private TextBlock? _periodText;
private Rectangle _firstSplitter; private Rectangle? _firstSplitter;
private Rectangle _secondSplitter; private Rectangle? _secondSplitter;
private Grid _contentGrid; private Grid? _contentGrid;
private Popup _popup; private Popup? _popup;
private TimeSpan? _selectedTime; private TimeSpan? _selectedTime;
private int _minuteIncrement = 1; private int _minuteIncrement = 1;
@ -142,7 +142,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Raised when the <see cref="SelectedTime"/> property changes /// Raised when the <see cref="SelectedTime"/> property changes
/// </summary> /// </summary>
public event EventHandler<TimePickerSelectedValueChangedEventArgs> SelectedTimeChanged; public event EventHandler<TimePickerSelectedValueChangedEventArgs>? SelectedTimeChanged;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
@ -200,15 +200,15 @@ namespace Avalonia.Controls
var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *"; var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
_contentGrid.ColumnDefinitions = new ColumnDefinitions(columnsD); _contentGrid.ColumnDefinitions = new ColumnDefinitions(columnsD);
_thirdPickerHost.IsVisible = !use24HourClock; _thirdPickerHost!.IsVisible = !use24HourClock;
_secondSplitter.IsVisible = !use24HourClock; _secondSplitter!.IsVisible = !use24HourClock;
Grid.SetColumn(_firstPickerHost, 0); Grid.SetColumn(_firstPickerHost!, 0);
Grid.SetColumn(_secondPickerHost, 2); Grid.SetColumn(_secondPickerHost!, 2);
Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4); Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4);
Grid.SetColumn(_firstSplitter, 1); Grid.SetColumn(_firstSplitter!, 1);
Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3); Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3);
} }
@ -220,7 +220,7 @@ namespace Avalonia.Controls
var time = SelectedTime; var time = SelectedTime;
if (time.HasValue) if (time.HasValue)
{ {
var newTime = SelectedTime.Value; var newTime = SelectedTime!.Value;
if (ClockIdentifier == "12HourClock") if (ClockIdentifier == "12HourClock")
{ {
@ -252,30 +252,30 @@ namespace Avalonia.Controls
SelectedTimeChanged?.Invoke(this, new TimePickerSelectedValueChangedEventArgs(oldTime, newTime)); SelectedTimeChanged?.Invoke(this, new TimePickerSelectedValueChangedEventArgs(oldTime, newTime));
} }
private void OnFlyoutButtonClicked(object sender, Interactivity.RoutedEventArgs e) private void OnFlyoutButtonClicked(object? sender, Interactivity.RoutedEventArgs e)
{ {
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay; _presenter!.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
_popup.IsOpen = true; _popup!.IsOpen = true;
var deltaY = _presenter.GetOffsetForPopup(); var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior // The extra 5 px I think is related to default popup placement behavior
_popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5), _popup.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom, Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY); Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
} }
private void OnDismissPicker(object sender, EventArgs e) private void OnDismissPicker(object? sender, EventArgs e)
{ {
_popup.Close(); _popup!.Close();
Focus(); Focus();
} }
private void OnConfirmed(object sender, EventArgs e) private void OnConfirmed(object? sender, EventArgs e)
{ {
_popup.Close(); _popup!.Close();
SelectedTime = _presenter.Time; SelectedTime = _presenter!.Time;
} }
} }
} }

75
src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs

@ -40,20 +40,20 @@ namespace Avalonia.Controls
} }
// TemplateItems // TemplateItems
private Grid _pickerContainer; private Grid? _pickerContainer;
private Button _acceptButton; private Button? _acceptButton;
private Button _dismissButton; private Button? _dismissButton;
private Rectangle _spacer2; private Rectangle? _spacer2;
private Panel _periodHost; private Panel? _periodHost;
private DateTimePickerPanel _hourSelector; private DateTimePickerPanel? _hourSelector;
private DateTimePickerPanel _minuteSelector; private DateTimePickerPanel? _minuteSelector;
private DateTimePickerPanel _periodSelector; private DateTimePickerPanel? _periodSelector;
private Button _hourUpButton; private Button? _hourUpButton;
private Button _minuteUpButton; private Button? _minuteUpButton;
private Button _periodUpButton; private Button? _periodUpButton;
private Button _hourDownButton; private Button? _hourDownButton;
private Button _minuteDownButton; private Button? _minuteDownButton;
private Button _periodDownButton; private Button? _periodDownButton;
// Backing Fields // Backing Fields
private TimeSpan _time; private TimeSpan _time;
@ -156,9 +156,12 @@ namespace Avalonia.Controls
e.Handled = true; e.Handled = true;
break; break;
case Key.Tab: case Key.Tab:
var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next); if (FocusManager.Instance?.Current is IInputElement focus)
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None); {
e.Handled = true; var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
e.Handled = true;
}
break; break;
case Key.Enter: case Key.Enter:
OnConfirmed(); OnConfirmed();
@ -170,9 +173,9 @@ namespace Avalonia.Controls
protected override void OnConfirmed() protected override void OnConfirmed()
{ {
var hr = _hourSelector.SelectedValue; var hr = _hourSelector!.SelectedValue;
var min = _minuteSelector.SelectedValue; var min = _minuteSelector!.SelectedValue;
var per = _periodSelector.SelectedValue; var per = _periodSelector!.SelectedValue;
if (ClockIdentifier == "12HourClock") if (ClockIdentifier == "12HourClock")
{ {
@ -190,20 +193,20 @@ namespace Avalonia.Controls
return; return;
bool clock12 = ClockIdentifier == "12HourClock"; bool clock12 = ClockIdentifier == "12HourClock";
_hourSelector.MaximumValue = clock12 ? 12 : 23; _hourSelector!.MaximumValue = clock12 ? 12 : 23;
_hourSelector.MinimumValue = clock12 ? 1 : 0; _hourSelector.MinimumValue = clock12 ? 1 : 0;
_hourSelector.ItemFormat = "%h"; _hourSelector.ItemFormat = "%h";
var hr = Time.Hours; var hr = Time.Hours;
_hourSelector.SelectedValue = !clock12 ? hr : _hourSelector.SelectedValue = !clock12 ? hr :
hr > 12 ? hr - 12 : hr == 0 ? 12 : hr; hr > 12 ? hr - 12 : hr == 0 ? 12 : hr;
_minuteSelector.MaximumValue = 59; _minuteSelector!.MaximumValue = 59;
_minuteSelector.MinimumValue = 0; _minuteSelector.MinimumValue = 0;
_minuteSelector.Increment = MinuteIncrement; _minuteSelector.Increment = MinuteIncrement;
_minuteSelector.SelectedValue = Time.Minutes; _minuteSelector.SelectedValue = Time.Minutes;
_minuteSelector.ItemFormat = "mm"; _minuteSelector.ItemFormat = "mm";
_periodSelector.MaximumValue = 1; _periodSelector!.MaximumValue = 1;
_periodSelector.MinimumValue = 0; _periodSelector.MinimumValue = 0;
_periodSelector.SelectedValue = hr >= 12 ? 1 : 0; _periodSelector.SelectedValue = hr >= 12 ? 1 : 0;
@ -216,45 +219,45 @@ namespace Avalonia.Controls
bool use24HourClock = ClockIdentifier == "24HourClock"; bool use24HourClock = ClockIdentifier == "24HourClock";
var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *"; var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
_pickerContainer.ColumnDefinitions = new ColumnDefinitions(columnsD); _pickerContainer!.ColumnDefinitions = new ColumnDefinitions(columnsD);
_spacer2.IsVisible = !use24HourClock; _spacer2!.IsVisible = !use24HourClock;
_periodHost.IsVisible = !use24HourClock; _periodHost!.IsVisible = !use24HourClock;
Grid.SetColumn(_spacer2, use24HourClock ? 0 : 3); Grid.SetColumn(_spacer2, use24HourClock ? 0 : 3);
Grid.SetColumn(_periodHost, use24HourClock ? 0 : 4); Grid.SetColumn(_periodHost, use24HourClock ? 0 : 4);
} }
private void OnDismissButtonClicked(object sender, RoutedEventArgs e) private void OnDismissButtonClicked(object? sender, RoutedEventArgs e)
{ {
OnDismiss(); OnDismiss();
} }
private void OnAcceptButtonClicked(object sender, RoutedEventArgs e) private void OnAcceptButtonClicked(object? sender, RoutedEventArgs e)
{ {
OnConfirmed(); OnConfirmed();
} }
private void OnSelectorButtonClick(object sender, RoutedEventArgs e) private void OnSelectorButtonClick(object? sender, RoutedEventArgs e)
{ {
if (sender == _hourUpButton) if (sender == _hourUpButton)
_hourSelector.ScrollUp(); _hourSelector!.ScrollUp();
else if (sender == _hourDownButton) else if (sender == _hourDownButton)
_hourSelector.ScrollDown(); _hourSelector!.ScrollDown();
else if (sender == _minuteUpButton) else if (sender == _minuteUpButton)
_minuteSelector.ScrollUp(); _minuteSelector!.ScrollUp();
else if (sender == _minuteDownButton) else if (sender == _minuteDownButton)
_minuteSelector.ScrollDown(); _minuteSelector!.ScrollDown();
else if (sender == _periodUpButton) else if (sender == _periodUpButton)
_periodSelector.ScrollUp(); _periodSelector!.ScrollUp();
else if (sender == _periodDownButton) else if (sender == _periodDownButton)
_periodSelector.ScrollDown(); _periodSelector!.ScrollDown();
} }
internal double GetOffsetForPopup() internal double GetOffsetForPopup()
{ {
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41; var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector.ItemHeight / 2); return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector!.ItemHeight / 2);
} }
} }
} }

10
src/Avalonia.Controls/Decorator.cs

@ -11,8 +11,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Child"/> property. /// Defines the <see cref="Child"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IControl> ChildProperty = public static readonly StyledProperty<IControl?> ChildProperty =
AvaloniaProperty.Register<Decorator, IControl>(nameof(Child)); AvaloniaProperty.Register<Decorator, IControl?>(nameof(Child));
/// <summary> /// <summary>
/// Defines the <see cref="Padding"/> property. /// Defines the <see cref="Padding"/> property.
@ -33,7 +33,7 @@ namespace Avalonia.Controls
/// Gets or sets the decorated control. /// Gets or sets the decorated control.
/// </summary> /// </summary>
[Content] [Content]
public IControl Child public IControl? Child
{ {
get { return GetValue(ChildProperty); } get { return GetValue(ChildProperty); }
set { SetValue(ChildProperty, value); } set { SetValue(ChildProperty, value); }
@ -66,8 +66,8 @@ namespace Avalonia.Controls
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void ChildChanged(AvaloniaPropertyChangedEventArgs e) private void ChildChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var oldChild = (Control)e.OldValue; var oldChild = (Control?)e.OldValue;
var newChild = (Control)e.NewValue; var newChild = (Control?)e.NewValue;
if (oldChild != null) if (oldChild != null)
{ {

40
src/Avalonia.Controls/DefinitionBase.cs

@ -39,7 +39,7 @@ namespace Avalonia.Controls
string sharedSizeGroupId = SharedSizeGroup; string sharedSizeGroupId = SharedSizeGroup;
if (sharedSizeGroupId != null) if (sharedSizeGroupId != null)
{ {
SharedSizeScope privateSharedSizeScope = PrivateSharedSizeScope; SharedSizeScope? privateSharedSizeScope = PrivateSharedSizeScope;
if (privateSharedSizeScope != null) if (privateSharedSizeScope != null)
{ {
_sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId); _sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
@ -104,7 +104,7 @@ namespace Avalonia.Controls
/// </remarks> /// </remarks>
internal static void OnIsSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) internal static void OnIsSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
{ {
if ((bool)e.NewValue) if ((bool)e.NewValue!)
{ {
SharedSizeScope sharedStatesCollection = new SharedSizeScope(); SharedSizeScope sharedStatesCollection = new SharedSizeScope();
d.SetValue(PrivateSharedSizeScopeProperty, sharedStatesCollection); d.SetValue(PrivateSharedSizeScopeProperty, sharedStatesCollection);
@ -131,8 +131,8 @@ namespace Avalonia.Controls
} }
else else
{ {
GridUnitType oldUnitType = ((GridLength)e.OldValue).GridUnitType; GridUnitType oldUnitType = ((GridLength)e.OldValue!).GridUnitType;
GridUnitType newUnitType = ((GridLength)e.NewValue).GridUnitType; GridUnitType newUnitType = ((GridLength)e.NewValue!).GridUnitType;
if (oldUnitType != newUnitType) if (oldUnitType != newUnitType)
{ {
@ -300,7 +300,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
internal abstract double UserMaxSizeValueCache { get; } internal abstract double UserMaxSizeValueCache { get; }
internal Grid Parent { get; set; } internal Grid? Parent { get; set; }
/// <summary> /// <summary>
/// SetFlags is used to set or unset one or multiple /// SetFlags is used to set or unset one or multiple
@ -326,7 +326,7 @@ namespace Avalonia.Controls
if (definition.Parent != null) if (definition.Parent != null)
{ {
string sharedSizeGroupId = (string)e.NewValue; string sharedSizeGroupId = (string)e.NewValue!;
if (definition._sharedState != null) if (definition._sharedState != null)
{ {
@ -338,7 +338,7 @@ namespace Avalonia.Controls
if ((definition._sharedState == null) && (sharedSizeGroupId != null)) if ((definition._sharedState == null) && (sharedSizeGroupId != null))
{ {
SharedSizeScope privateSharedSizeScope = definition.PrivateSharedSizeScope; SharedSizeScope? privateSharedSizeScope = definition.PrivateSharedSizeScope;
if (privateSharedSizeScope != null) if (privateSharedSizeScope != null)
{ {
// if definition is not registered and both: shared size group id AND private shared scope // if definition is not registered and both: shared size group id AND private shared scope
@ -402,7 +402,7 @@ namespace Avalonia.Controls
if (definition.Parent != null) if (definition.Parent != null)
{ {
SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue; SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue!;
if (definition._sharedState != null) if (definition._sharedState != null)
{ {
@ -429,9 +429,9 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Private getter of shared state collection dynamic property. /// Private getter of shared state collection dynamic property.
/// </summary> /// </summary>
private SharedSizeScope PrivateSharedSizeScope private SharedSizeScope? PrivateSharedSizeScope
{ {
get { return (SharedSizeScope)GetValue(PrivateSharedSizeScopeProperty); } get { return (SharedSizeScope?)GetValue(PrivateSharedSizeScopeProperty); }
} }
/// <summary> /// <summary>
@ -462,7 +462,7 @@ namespace Avalonia.Controls
private double _sizeCache; // cache used for various purposes (sorting, caching, etc) during calculations private double _sizeCache; // cache used for various purposes (sorting, caching, etc) during calculations
private double _offset; // offset of the DefinitionBase from left / top corner (assuming LTR case) private double _offset; // offset of the DefinitionBase from left / top corner (assuming LTR case)
private SharedSizeState _sharedState; // reference to shared state object this instance is registered with private SharedSizeState? _sharedState; // reference to shared state object this instance is registered with
[System.Flags] [System.Flags]
private enum Flags : byte private enum Flags : byte
@ -488,7 +488,7 @@ namespace Avalonia.Controls
// check that sharedSizeGroup is not default // check that sharedSizeGroup is not default
Debug.Assert(sharedSizeGroup != null); Debug.Assert(sharedSizeGroup != null);
SharedSizeState sharedState = _registry[sharedSizeGroup] as SharedSizeState; SharedSizeState? sharedState = _registry[sharedSizeGroup] as SharedSizeState;
if (sharedState == null) if (sharedState == null)
{ {
sharedState = new SharedSizeState(this, sharedSizeGroup); sharedState = new SharedSizeState(this, sharedSizeGroup);
@ -567,7 +567,7 @@ namespace Avalonia.Controls
{ {
for (int i = 0, count = _registry.Count; i < count; ++i) for (int i = 0, count = _registry.Count; i < count; ++i)
{ {
Grid parentGrid = (Grid)(_registry[i].Parent); Grid parentGrid = (Grid)(_registry[i].Parent!);
parentGrid.Invalidate(); parentGrid.Invalidate();
} }
_broadcastInvalidation = false; _broadcastInvalidation = false;
@ -645,7 +645,7 @@ namespace Avalonia.Controls
/// OnLayoutUpdated handler. Validates that all participating definitions /// OnLayoutUpdated handler. Validates that all participating definitions
/// have updated min size value. Forces another layout update cycle if needed. /// have updated min size value. Forces another layout update cycle if needed.
/// </summary> /// </summary>
private void OnLayoutUpdated(object sender, EventArgs e) private void OnLayoutUpdated(object? sender, EventArgs e)
{ {
double sharedMinSize = 0; double sharedMinSize = 0;
@ -707,14 +707,14 @@ namespace Avalonia.Controls
if(!measureIsValid) if(!measureIsValid)
{ {
definitionBase.Parent.InvalidateMeasure(); definitionBase.Parent!.InvalidateMeasure();
} }
else if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache)) else if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
{ {
// if measure is valid then also need to check arrange. // if measure is valid then also need to check arrange.
// Note: definitionBase.SizeCache is volatile but at this point // Note: definitionBase.SizeCache is volatile but at this point
// it contains up-to-date final size // it contains up-to-date final size
definitionBase.Parent.InvalidateArrange(); definitionBase.Parent!.InvalidateArrange();
} }
// now we can restore the invariant, and clear the layout flag // now we can restore the invariant, and clear the layout flag
@ -724,7 +724,7 @@ namespace Avalonia.Controls
_minSize = sharedMinSize; _minSize = sharedMinSize;
_layoutUpdatedHost.LayoutUpdated -= _layoutUpdated; _layoutUpdatedHost!.LayoutUpdated -= _layoutUpdated;
_layoutUpdatedHost = null; _layoutUpdatedHost = null;
_broadcastInvalidation = true; _broadcastInvalidation = true;
@ -743,7 +743,7 @@ namespace Avalonia.Controls
private readonly EventHandler _layoutUpdated; private readonly EventHandler _layoutUpdated;
// Control for which layout updated event handler is registered // Control for which layout updated event handler is registered
private Control _layoutUpdatedHost; private Control? _layoutUpdatedHost;
// "true" when broadcasting of invalidation is needed // "true" when broadcasting of invalidation is needed
private bool _broadcastInvalidation; private bool _broadcastInvalidation;
@ -762,8 +762,8 @@ namespace Avalonia.Controls
/// Private shared size scope property holds a collection of shared state objects for the a given shared size scope. /// Private shared size scope property holds a collection of shared state objects for the a given shared size scope.
/// <see cref="OnIsSharedSizeScopePropertyChanged"/> /// <see cref="OnIsSharedSizeScopePropertyChanged"/>
/// </summary> /// </summary>
internal static readonly AttachedProperty<SharedSizeScope> PrivateSharedSizeScopeProperty = internal static readonly AttachedProperty<SharedSizeScope?> PrivateSharedSizeScopeProperty =
AvaloniaProperty.RegisterAttached<DefinitionBase, Control, SharedSizeScope>( AvaloniaProperty.RegisterAttached<DefinitionBase, Control, SharedSizeScope?>(
"PrivateSharedSizeScope", "PrivateSharedSizeScope",
defaultValue: null, defaultValue: null,
inherits: true); inherits: true);

6
src/Avalonia.Controls/DefinitionList.cs

@ -2,8 +2,6 @@ using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using Avalonia.Collections; using Avalonia.Collections;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
public abstract class DefinitionList<T> : AvaloniaList<T> where T : DefinitionBase public abstract class DefinitionList<T> : AvaloniaList<T> where T : DefinitionBase
@ -36,7 +34,7 @@ namespace Avalonia.Controls
} }
} }
internal void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) internal void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
var idx = 0; var idx = 0;
@ -62,7 +60,7 @@ namespace Avalonia.Controls
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var definition = (DefinitionBase) items[i]; var definition = (DefinitionBase) items[i]!;
if (wasRemoved) if (wasRemoved)
{ {

2
src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs

@ -1,8 +1,6 @@
using System; using System;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable
namespace Avalonia.Controls.Diagnostics namespace Avalonia.Controls.Diagnostics
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs

@ -1,6 +1,4 @@
#nullable enable namespace Avalonia.Controls.Diagnostics
namespace Avalonia.Controls.Diagnostics
{ {
/// <summary> /// <summary>
/// Helper class to provide diagnostics information for <see cref="ToolTip"/>. /// Helper class to provide diagnostics information for <see cref="ToolTip"/>.

20
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -12,7 +12,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
private double _scaling = 1; private double _scaling = 1;
private Size _clientSize; private Size _clientSize;
public IInputRoot InputRoot { get; private set; } public IInputRoot? InputRoot { get; private set; }
public bool IsDisposed { get; private set; } public bool IsDisposed { get; private set; }
public virtual void Dispose() public virtual void Dispose()
@ -47,12 +47,12 @@ namespace Avalonia.Controls.Embedding.Offscreen
} }
} }
public Action<RawInputEventArgs> Input { get; set; } public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect> Paint { get; set; } public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, PlatformResizeReason>? Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double>? ScalingChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1); public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
@ -63,18 +63,18 @@ namespace Avalonia.Controls.Embedding.Offscreen
public virtual PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1); public virtual PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1);
public virtual void SetCursor(ICursorImpl cursor) public virtual void SetCursor(ICursorImpl? cursor)
{ {
} }
public Action Closed { get; set; } public Action? Closed { get; set; }
public Action LostFocus { get; set; } public Action? LostFocus { get; set; }
public abstract IMouseDevice MouseDevice { get; } public abstract IMouseDevice MouseDevice { get; }
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { } public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { }
public WindowTransparencyLevel TransparencyLevel { get; private set; } public WindowTransparencyLevel TransparencyLevel { get; private set; }
public IPopupImpl CreatePopup() => null; public IPopupImpl? CreatePopup() => null;
} }
} }

3
src/Avalonia.Controls/Expander.cs

@ -1,11 +1,8 @@
using System.Threading; using System.Threading;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>

7
src/Avalonia.Controls/ExperimentalAcrylicBorder.cs

@ -17,7 +17,7 @@ namespace Avalonia.Controls
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper(); private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
private IDisposable _subscription; private IDisposable? _subscription;
static ExperimentalAcrylicBorder() static ExperimentalAcrylicBorder()
{ {
@ -46,11 +46,14 @@ namespace Avalonia.Controls
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
var tl = (e.Root as TopLevel); var tl = (TopLevel)e.Root;
_subscription = tl.GetObservable(TopLevel.ActualTransparencyLevelProperty) _subscription = tl.GetObservable(TopLevel.ActualTransparencyLevelProperty)
.Subscribe(x => .Subscribe(x =>
{ {
if (tl.PlatformImpl is null)
return;
switch (x) switch (x)
{ {
case WindowTransparencyLevel.Transparent: case WindowTransparencyLevel.Transparent:

2
src/Avalonia.Controls/Flyouts/Flyout.cs

@ -1,8 +1,6 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Metadata; using Avalonia.Metadata;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
public class Flyout : FlyoutBase public class Flyout : FlyoutBase

20
src/Avalonia.Controls/Flyouts/FlyoutBase.cs

@ -8,8 +8,6 @@ using Avalonia.Input.Raw;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Logging; using Avalonia.Logging;
#nullable enable
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
public abstract class FlyoutBase : AvaloniaObject, IPopupHostProvider public abstract class FlyoutBase : AvaloniaObject, IPopupHostProvider
@ -267,7 +265,7 @@ namespace Avalonia.Controls.Primitives
return true; return true;
} }
private void PlacementTarget_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) private void PlacementTarget_DetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{ {
_ = HideCore(false); _ = HideCore(false);
} }
@ -335,7 +333,7 @@ namespace Avalonia.Controls.Primitives
protected virtual void OnOpened() protected virtual void OnOpened()
{ {
Opened?.Invoke(this, null); Opened?.Invoke(this, EventArgs.Empty);
} }
protected virtual void OnClosing(CancelEventArgs args) protected virtual void OnClosing(CancelEventArgs args)
@ -345,7 +343,7 @@ namespace Avalonia.Controls.Primitives
protected virtual void OnClosed() protected virtual void OnClosed()
{ {
Closed?.Invoke(this, null); Closed?.Invoke(this, EventArgs.Empty);
} }
/// <summary> /// <summary>
@ -368,14 +366,14 @@ namespace Avalonia.Controls.Primitives
return popup; return popup;
} }
private void OnPopupOpened(object sender, EventArgs e) private void OnPopupOpened(object? sender, EventArgs e)
{ {
IsOpen = true; IsOpen = true;
_popupHostChangedHandler?.Invoke(Popup!.Host); _popupHostChangedHandler?.Invoke(Popup!.Host);
} }
private void OnPopupClosing(object sender, CancelEventArgs e) private void OnPopupClosing(object? sender, CancelEventArgs e)
{ {
if (IsOpen) if (IsOpen)
{ {
@ -383,7 +381,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
private void OnPopupClosed(object sender, EventArgs e) private void OnPopupClosed(object? sender, EventArgs e)
{ {
HideCore(false); HideCore(false);
@ -391,7 +389,7 @@ namespace Avalonia.Controls.Primitives
} }
// This method is handling both popup logical tree and target logical tree. // This method is handling both popup logical tree and target logical tree.
private void OnPlacementTargetOrPopupKeyUp(object sender, KeyEventArgs e) private void OnPlacementTargetOrPopupKeyUp(object? sender, KeyEventArgs e)
{ {
if (!e.Handled if (!e.Handled
&& IsOpen && IsOpen
@ -530,10 +528,10 @@ namespace Avalonia.Controls.Primitives
} }
} }
private static void OnControlContextRequested(object sender, ContextRequestedEventArgs e) private static void OnControlContextRequested(object? sender, ContextRequestedEventArgs e)
{ {
var control = (Control)sender;
if (!e.Handled if (!e.Handled
&& sender is Control control
&& control.ContextFlyout is FlyoutBase flyout) && control.ContextFlyout is FlyoutBase flyout)
{ {
if (control.ContextMenu != null) if (control.ContextMenu != null)

8
src/Avalonia.Controls/Flyouts/MenuFlyout.cs

@ -4,8 +4,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Metadata; using Avalonia.Metadata;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
public class MenuFlyout : FlyoutBase public class MenuFlyout : FlyoutBase
@ -18,7 +16,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Items"/> property /// Defines the <see cref="Items"/> property
/// </summary> /// </summary>
public static readonly DirectProperty<MenuFlyout, IEnumerable> ItemsProperty = public static readonly DirectProperty<MenuFlyout, IEnumerable?> ItemsProperty =
ItemsControl.ItemsProperty.AddOwner<MenuFlyout>(x => x.Items, ItemsControl.ItemsProperty.AddOwner<MenuFlyout>(x => x.Items,
(x, v) => x.Items = v); (x, v) => x.Items = v);
@ -35,7 +33,7 @@ namespace Avalonia.Controls
/// Gets or sets the items of the MenuFlyout /// Gets or sets the items of the MenuFlyout
/// </summary> /// </summary>
[Content] [Content]
public IEnumerable Items public IEnumerable? Items
{ {
get => _items; get => _items;
set => SetAndRaise(ItemsProperty, ref _items, value); set => SetAndRaise(ItemsProperty, ref _items, value);
@ -51,7 +49,7 @@ namespace Avalonia.Controls
} }
private Classes? _classes; private Classes? _classes;
private IEnumerable _items; private IEnumerable? _items;
private IDataTemplate? _itemTemplate; private IDataTemplate? _itemTemplate;
protected override Control CreatePresenter() protected override Control CreatePresenter()

13
src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs

@ -43,5 +43,18 @@ namespace Avalonia.Controls
{ {
return new MenuItemContainerGenerator(this); return new MenuItemContainerGenerator(this);
} }
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
foreach (var i in LogicalChildren)
{
if (i is MenuItem menuItem)
{
menuItem.IsSubMenuOpen = false;
}
}
}
} }
} }

12
src/Avalonia.Controls/Generators/IItemContainerGenerator.cs

@ -17,27 +17,27 @@ namespace Avalonia.Controls.Generators
/// <summary> /// <summary>
/// Gets or sets the data template used to display the items in the control. /// Gets or sets the data template used to display the items in the control.
/// </summary> /// </summary>
IDataTemplate ItemTemplate { get; set; } IDataTemplate? ItemTemplate { get; set; }
/// <summary> /// <summary>
/// Gets the ContainerType, or null if its an untyped ContainerGenerator. /// Gets the ContainerType, or null if its an untyped ContainerGenerator.
/// </summary> /// </summary>
Type ContainerType { get; } Type? ContainerType { get; }
/// <summary> /// <summary>
/// Signaled whenever new containers are materialized. /// Signaled whenever new containers are materialized.
/// </summary> /// </summary>
event EventHandler<ItemContainerEventArgs> Materialized; event EventHandler<ItemContainerEventArgs>? Materialized;
/// <summary> /// <summary>
/// Event raised whenever containers are dematerialized. /// Event raised whenever containers are dematerialized.
/// </summary> /// </summary>
event EventHandler<ItemContainerEventArgs> Dematerialized; event EventHandler<ItemContainerEventArgs>? Dematerialized;
/// <summary> /// <summary>
/// Event raised whenever containers are recycled. /// Event raised whenever containers are recycled.
/// </summary> /// </summary>
event EventHandler<ItemContainerEventArgs> Recycled; event EventHandler<ItemContainerEventArgs>? Recycled;
/// <summary> /// <summary>
/// Creates a container control for an item. /// Creates a container control for an item.
@ -90,7 +90,7 @@ namespace Avalonia.Controls.Generators
/// </summary> /// </summary>
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
/// <returns>The container, or null if no container created.</returns> /// <returns>The container, or null if no container created.</returns>
IControl ContainerFromIndex(int index); IControl? ContainerFromIndex(int index);
/// <summary> /// <summary>
/// Gets the index of the specified container control. /// Gets the index of the specified container control.

2
src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs

@ -8,7 +8,7 @@ namespace Avalonia.Controls.Generators
/// <summary> /// <summary>
/// Gets the container index for the tree. /// Gets the container index for the tree.
/// </summary> /// </summary>
TreeContainerIndex Index { get; } TreeContainerIndex? Index { get; }
/// <summary> /// <summary>
/// Updates the index based on the parent <see cref="TreeView"/>. /// Updates the index based on the parent <see cref="TreeView"/>.

26
src/Avalonia.Controls/Generators/ItemContainerGenerator.cs

@ -20,27 +20,25 @@ namespace Avalonia.Controls.Generators
/// <param name="owner">The owner control.</param> /// <param name="owner">The owner control.</param>
public ItemContainerGenerator(IControl owner) public ItemContainerGenerator(IControl owner)
{ {
Contract.Requires<ArgumentNullException>(owner != null); Owner = owner ?? throw new ArgumentNullException(nameof(owner));
Owner = owner;
} }
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<ItemContainerInfo> Containers => _containers.Values; public IEnumerable<ItemContainerInfo> Containers => _containers.Values;
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<ItemContainerEventArgs> Materialized; public event EventHandler<ItemContainerEventArgs>? Materialized;
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<ItemContainerEventArgs> Dematerialized; public event EventHandler<ItemContainerEventArgs>? Dematerialized;
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<ItemContainerEventArgs> Recycled; public event EventHandler<ItemContainerEventArgs>? Recycled;
/// <summary> /// <summary>
/// Gets or sets the data template used to display the items in the control. /// Gets or sets the data template used to display the items in the control.
/// </summary> /// </summary>
public IDataTemplate ItemTemplate { get; set; } public IDataTemplate? ItemTemplate { get; set; }
/// <summary> /// <summary>
/// Gets the owner control. /// Gets the owner control.
@ -48,12 +46,12 @@ namespace Avalonia.Controls.Generators
public IControl Owner { get; } public IControl Owner { get; }
/// <inheritdoc/> /// <inheritdoc/>
public virtual Type ContainerType => null; public virtual Type? ContainerType => null;
/// <inheritdoc/> /// <inheritdoc/>
public ItemContainerInfo Materialize(int index, object item) public ItemContainerInfo Materialize(int index, object item)
{ {
var container = new ItemContainerInfo(CreateContainer(item), item, index); var container = new ItemContainerInfo(CreateContainer(item)!, item, index);
_containers.Add(container.Index, container); _containers.Add(container.Index, container);
Materialized?.Invoke(this, new ItemContainerEventArgs(container)); Materialized?.Invoke(this, new ItemContainerEventArgs(container));
@ -104,9 +102,7 @@ namespace Avalonia.Controls.Generators
{ {
for (var i = startingIndex; i < startingIndex + count; ++i) for (var i = startingIndex; i < startingIndex + count; ++i)
{ {
ItemContainerInfo found; if (_containers.TryGetValue(i, out var found))
if (_containers.TryGetValue(i, out found))
{ {
result.Add(found); result.Add(found);
} }
@ -154,9 +150,9 @@ namespace Avalonia.Controls.Generators
} }
/// <inheritdoc/> /// <inheritdoc/>
public IControl ContainerFromIndex(int index) public IControl? ContainerFromIndex(int index)
{ {
ItemContainerInfo result; ItemContainerInfo? result;
_containers.TryGetValue(index, out result); _containers.TryGetValue(index, out result);
return result?.ContainerControl; return result?.ContainerControl;
} }
@ -180,7 +176,7 @@ namespace Avalonia.Controls.Generators
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>The created container control.</returns> /// <returns>The created container control.</returns>
protected virtual IControl CreateContainer(object item) protected virtual IControl? CreateContainer(object item)
{ {
var result = item as IControl; var result = item as IControl;

11
src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs

@ -19,13 +19,10 @@ namespace Avalonia.Controls.Generators
public ItemContainerGenerator( public ItemContainerGenerator(
IControl owner, IControl owner,
AvaloniaProperty contentProperty, AvaloniaProperty contentProperty,
AvaloniaProperty contentTemplateProperty) AvaloniaProperty? contentTemplateProperty)
: base(owner) : base(owner)
{ {
Contract.Requires<ArgumentNullException>(owner != null); ContentProperty = contentProperty ?? throw new ArgumentNullException(nameof(contentProperty));
Contract.Requires<ArgumentNullException>(contentProperty != null);
ContentProperty = contentProperty;
ContentTemplateProperty = contentTemplateProperty; ContentTemplateProperty = contentTemplateProperty;
} }
@ -40,10 +37,10 @@ namespace Avalonia.Controls.Generators
/// <summary> /// <summary>
/// Gets the container's ContentTemplate property. /// Gets the container's ContentTemplate property.
/// </summary> /// </summary>
protected AvaloniaProperty ContentTemplateProperty { get; } protected AvaloniaProperty? ContentTemplateProperty { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override IControl CreateContainer(object item) protected override IControl? CreateContainer(object item)
{ {
var container = item as T; var container = item as T;

2
src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs

@ -12,7 +12,7 @@
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override IControl CreateContainer(object item) protected override IControl? CreateContainer(object item)
{ {
var separator = item as Separator; var separator = item as Separator;
return separator != null ? separator : base.CreateContainer(item); return separator != null ? separator : base.CreateContainer(item);

12
src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs

@ -20,7 +20,7 @@ namespace Avalonia.Controls.Generators
protected override IControl CreateContainer(object item) protected override IControl CreateContainer(object item)
{ {
var tabItem = (TabItem)base.CreateContainer(item); var tabItem = (TabItem)base.CreateContainer(item)!;
tabItem.Bind(TabItem.TabStripPlacementProperty, new OwnerBinding<Dock>( tabItem.Bind(TabItem.TabStripPlacementProperty, new OwnerBinding<Dock>(
tabItem, tabItem,
@ -28,7 +28,7 @@ namespace Avalonia.Controls.Generators
if (tabItem.HeaderTemplate == null) if (tabItem.HeaderTemplate == null)
{ {
tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding<IDataTemplate>( tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding<IDataTemplate?>(
tabItem, tabItem,
TabControl.ItemTemplateProperty)); TabControl.ItemTemplateProperty));
} }
@ -50,7 +50,7 @@ namespace Avalonia.Controls.Generators
if (!(tabItem.Content is IControl)) if (!(tabItem.Content is IControl))
{ {
tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding<IDataTemplate>( tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding<IDataTemplate?>(
tabItem, tabItem,
TabControl.ContentTemplateProperty)); TabControl.ContentTemplateProperty));
} }
@ -62,8 +62,8 @@ namespace Avalonia.Controls.Generators
{ {
private readonly TabItem _item; private readonly TabItem _item;
private readonly StyledProperty<T> _ownerProperty; private readonly StyledProperty<T> _ownerProperty;
private IDisposable _ownerSubscription; private IDisposable? _ownerSubscription;
private IDisposable _propertySubscription; private IDisposable? _propertySubscription;
public OwnerBinding(TabItem item, StyledProperty<T> ownerProperty) public OwnerBinding(TabItem item, StyledProperty<T> ownerProperty)
{ {
@ -82,7 +82,7 @@ namespace Avalonia.Controls.Generators
_ownerSubscription = null; _ownerSubscription = null;
} }
private void OwnerChanged(ILogical c) private void OwnerChanged(ILogical? c)
{ {
_propertySubscription?.Dispose(); _propertySubscription?.Dispose();
_propertySubscription = null; _propertySubscription = null;

8
src/Avalonia.Controls/Generators/TreeContainerIndex.cs

@ -21,12 +21,12 @@ namespace Avalonia.Controls.Generators
/// <summary> /// <summary>
/// Signaled whenever new containers are materialized. /// Signaled whenever new containers are materialized.
/// </summary> /// </summary>
public event EventHandler<ItemContainerEventArgs> Materialized; public event EventHandler<ItemContainerEventArgs>? Materialized;
/// <summary> /// <summary>
/// Event raised whenever containers are dematerialized. /// Event raised whenever containers are dematerialized.
/// </summary> /// </summary>
public event EventHandler<ItemContainerEventArgs> Dematerialized; public event EventHandler<ItemContainerEventArgs>? Dematerialized;
/// <summary> /// <summary>
/// Gets the currently materialized containers. /// Gets the currently materialized containers.
@ -92,7 +92,7 @@ namespace Avalonia.Controls.Generators
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>The container, or null of not found.</returns> /// <returns>The container, or null of not found.</returns>
public IControl ContainerFromItem(object item) public IControl? ContainerFromItem(object item)
{ {
if (item != null) if (item != null)
{ {
@ -108,7 +108,7 @@ namespace Avalonia.Controls.Generators
/// </summary> /// </summary>
/// <param name="container">The container.</param> /// <param name="container">The container.</param>
/// <returns>The item, or null of not found.</returns> /// <returns>The item, or null of not found.</returns>
public object ItemFromContainer(IControl container) public object? ItemFromContainer(IControl? container)
{ {
if (container != null) if (container != null)
{ {

23
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@ -14,7 +14,7 @@ namespace Avalonia.Controls.Generators
public class TreeItemContainerGenerator<T> : ItemContainerGenerator<T>, ITreeItemContainerGenerator public class TreeItemContainerGenerator<T> : ItemContainerGenerator<T>, ITreeItemContainerGenerator
where T : class, IControl, new() where T : class, IControl, new()
{ {
private TreeView _treeView; private TreeView? _treeView;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TreeItemContainerGenerator{T}"/> class. /// Initializes a new instance of the <see cref="TreeItemContainerGenerator{T}"/> class.
@ -32,20 +32,15 @@ namespace Avalonia.Controls.Generators
AvaloniaProperty isExpandedProperty) AvaloniaProperty isExpandedProperty)
: base(owner, contentProperty, contentTemplateProperty) : base(owner, contentProperty, contentTemplateProperty)
{ {
Contract.Requires<ArgumentNullException>(owner != null); ItemsProperty = itemsProperty ?? throw new ArgumentNullException(nameof(itemsProperty));
Contract.Requires<ArgumentNullException>(contentProperty != null); IsExpandedProperty = isExpandedProperty ?? throw new ArgumentNullException(nameof(isExpandedProperty));
Contract.Requires<ArgumentNullException>(itemsProperty != null);
Contract.Requires<ArgumentNullException>(isExpandedProperty != null);
ItemsProperty = itemsProperty;
IsExpandedProperty = isExpandedProperty;
UpdateIndex(); UpdateIndex();
} }
/// <summary> /// <summary>
/// Gets the container index for the tree. /// Gets the container index for the tree.
/// </summary> /// </summary>
public TreeContainerIndex Index { get; private set; } public TreeContainerIndex? Index { get; private set; }
/// <summary> /// <summary>
/// Gets the item container's Items property. /// Gets the item container's Items property.
@ -58,7 +53,7 @@ namespace Avalonia.Controls.Generators
protected AvaloniaProperty IsExpandedProperty { get; } protected AvaloniaProperty IsExpandedProperty { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override IControl CreateContainer(object item) protected override IControl? CreateContainer(object? item)
{ {
var container = item as T; var container = item as T;
@ -141,12 +136,12 @@ namespace Avalonia.Controls.Generators
{ {
private readonly IDataTemplate _inner; private readonly IDataTemplate _inner;
public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner; public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner;
public IControl Build(object param) => _inner.Build(param); public IControl? Build(object? param) => _inner.Build(param);
public bool Match(object data) => _inner.Match(data); public bool Match(object? data) => _inner.Match(data);
public InstancedBinding ItemsSelector(object item) => null; public InstancedBinding? ItemsSelector(object item) => null;
} }
private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate primary) private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate? primary)
{ {
var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default; var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default;
var treeTemplate = template as ITreeDataTemplate ?? new WrapperTreeDataTemplate(template); var treeTemplate = template as ITreeDataTemplate ?? new WrapperTreeDataTemplate(template);

165
src/Avalonia.Controls/Grid.cs

@ -8,6 +8,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading; using System.Threading;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
@ -48,7 +49,7 @@ namespace Avalonia.Controls
/// <param name="value">Column property value.</param> /// <param name="value">Column property value.</param>
public static void SetColumn(Control element, int value) public static void SetColumn(Control element, int value)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(ColumnProperty, value); element.SetValue(ColumnProperty, value);
} }
@ -59,7 +60,7 @@ namespace Avalonia.Controls
/// <returns>Column property value.</returns> /// <returns>Column property value.</returns>
public static int GetColumn(Control element) public static int GetColumn(Control element)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(ColumnProperty); return element.GetValue(ColumnProperty);
} }
@ -70,7 +71,7 @@ namespace Avalonia.Controls
/// <param name="value">Row property value.</param> /// <param name="value">Row property value.</param>
public static void SetRow(Control element, int value) public static void SetRow(Control element, int value)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(RowProperty, value); element.SetValue(RowProperty, value);
} }
@ -81,7 +82,7 @@ namespace Avalonia.Controls
/// <returns>Row property value.</returns> /// <returns>Row property value.</returns>
public static int GetRow(Control element) public static int GetRow(Control element)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(RowProperty); return element.GetValue(RowProperty);
} }
@ -92,7 +93,7 @@ namespace Avalonia.Controls
/// <param name="value">ColumnSpan property value.</param> /// <param name="value">ColumnSpan property value.</param>
public static void SetColumnSpan(Control element, int value) public static void SetColumnSpan(Control element, int value)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(ColumnSpanProperty, value); element.SetValue(ColumnSpanProperty, value);
} }
@ -103,7 +104,7 @@ namespace Avalonia.Controls
/// <returns>ColumnSpan property value.</returns> /// <returns>ColumnSpan property value.</returns>
public static int GetColumnSpan(Control element) public static int GetColumnSpan(Control element)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(ColumnSpanProperty); return element.GetValue(ColumnSpanProperty);
} }
@ -114,7 +115,7 @@ namespace Avalonia.Controls
/// <param name="value">RowSpan property value.</param> /// <param name="value">RowSpan property value.</param>
public static void SetRowSpan(Control element, int value) public static void SetRowSpan(Control element, int value)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(RowSpanProperty, value); element.SetValue(RowSpanProperty, value);
} }
@ -125,7 +126,7 @@ namespace Avalonia.Controls
/// <returns>RowSpan property value.</returns> /// <returns>RowSpan property value.</returns>
public static int GetRowSpan(Control element) public static int GetRowSpan(Control element)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(RowSpanProperty); return element.GetValue(RowSpanProperty);
} }
@ -136,7 +137,7 @@ namespace Avalonia.Controls
/// <param name="value">IsSharedSizeScope property value.</param> /// <param name="value">IsSharedSizeScope property value.</param>
public static void SetIsSharedSizeScope(Control element, bool value) public static void SetIsSharedSizeScope(Control element, bool value)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(IsSharedSizeScopeProperty, value); element.SetValue(IsSharedSizeScopeProperty, value);
} }
@ -147,7 +148,7 @@ namespace Avalonia.Controls
/// <returns>IsSharedSizeScope property value.</returns> /// <returns>IsSharedSizeScope property value.</returns>
public static bool GetIsSharedSizeScope(Control element) public static bool GetIsSharedSizeScope(Control element)
{ {
Contract.Requires<ArgumentNullException>(element != null); _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(IsSharedSizeScopeProperty); return element.GetValue(IsSharedSizeScopeProperty);
} }
@ -573,7 +574,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// <see cref="Panel.ChildrenChanged"/> /// <see cref="Panel.ChildrenChanged"/>
/// </summary> /// </summary>
protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) protected override void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
CellsStructureDirty = true; CellsStructureDirty = true;
base.ChildrenChanged(sender, e); base.ChildrenChanged(sender, e);
@ -598,7 +599,7 @@ namespace Avalonia.Controls
{ {
double value = 0.0; double value = 0.0;
Contract.Requires<NullReferenceException>(_data != null); Debug.Assert(_data != null);
// actual value calculations require structure to be up-to-date // actual value calculations require structure to be up-to-date
if (!ColumnDefinitionsDirty) if (!ColumnDefinitionsDirty)
@ -620,7 +621,7 @@ namespace Avalonia.Controls
{ {
double value = 0.0; double value = 0.0;
Contract.Requires<NullReferenceException>(_data != null); Debug.Assert(_data != null);
// actual value calculations require structure to be up-to-date // actual value calculations require structure to be up-to-date
if (!RowDefinitionsDirty) if (!RowDefinitionsDirty)
@ -994,7 +995,7 @@ namespace Avalonia.Controls
} }
var children = this.Children; var children = this.Children;
Hashtable spanStore = null; Hashtable? spanStore = null;
bool ignoreDesiredSizeV = forceInfinityV; bool ignoreDesiredSizeV = forceInfinityV;
int i = cellsHead; int i = cellsHead;
@ -1048,7 +1049,7 @@ namespace Avalonia.Controls
foreach (DictionaryEntry e in spanStore) foreach (DictionaryEntry e in spanStore)
{ {
SpanKey key = (SpanKey)e.Key; SpanKey key = (SpanKey)e.Key;
double requestedSize = (double)e.Value; double requestedSize = (double)e.Value!;
EnsureMinSizeInDefinitionRange( EnsureMinSizeInDefinitionRange(
key.U ? DefinitionsU : DefinitionsV, key.U ? DefinitionsU : DefinitionsV,
@ -1069,7 +1070,7 @@ namespace Avalonia.Controls
/// <param name="u"><c>true</c> if this is a column span. <c>false</c> if this is a row span.</param> /// <param name="u"><c>true</c> if this is a column span. <c>false</c> if this is a row span.</param>
/// <param name="value">Value to store. If an entry already exists the biggest value is stored.</param> /// <param name="value">Value to store. If an entry already exists the biggest value is stored.</param>
private static void RegisterSpan( private static void RegisterSpan(
ref Hashtable store, ref Hashtable? store,
int start, int start,
int count, int count,
bool u, bool u,
@ -1081,7 +1082,7 @@ namespace Avalonia.Controls
} }
SpanKey key = new SpanKey(start, count, u); SpanKey key = new SpanKey(start, count, u);
object o = store[key]; object? o = store[key];
if (o == null if (o == null
|| value > (double)o) || value > (double)o)
@ -1605,12 +1606,12 @@ namespace Avalonia.Controls
while (minCount > 0 && tempDefinitions[minCount - 1].MeasureSize < 0.0) while (minCount > 0 && tempDefinitions[minCount - 1].MeasureSize < 0.0)
{ {
--minCount; --minCount;
tempDefinitions[minCount] = null; tempDefinitions[minCount] = null!;
} }
while (maxCount > 0 && tempDefinitions[defCount + maxCount - 1].MeasureSize < 0.0) while (maxCount > 0 && tempDefinitions[defCount + maxCount - 1].MeasureSize < 0.0)
{ {
--maxCount; --maxCount;
tempDefinitions[defCount + maxCount] = null; tempDefinitions[defCount + maxCount] = null!;
} }
} }
@ -2321,7 +2322,7 @@ namespace Avalonia.Controls
/// by adding / removing GridLinesRenderer visual. /// by adding / removing GridLinesRenderer visual.
/// Returns a reference to GridLinesRenderer visual or null. /// Returns a reference to GridLinesRenderer visual or null.
/// </summary> /// </summary>
private GridLinesRenderer EnsureGridLinesRenderer() private GridLinesRenderer? EnsureGridLinesRenderer()
{ {
// //
// synchronize the state // synchronize the state
@ -2369,16 +2370,16 @@ namespace Avalonia.Controls
grid.InvalidateVisual(); grid.InvalidateVisual();
} }
grid.SetFlags((bool)e.NewValue, Flags.ShowGridLinesPropertyValue); grid.SetFlags((bool)e.NewValue!, Flags.ShowGridLinesPropertyValue);
} }
private static void OnCellAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) private static void OnCellAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
{ {
Visual child = d as Visual; Visual? child = d as Visual;
if (child != null) if (child != null)
{ {
Grid grid = child.GetVisualParent<Grid>(); Grid? grid = child.GetVisualParent<Grid>();
if (grid != null if (grid != null
&& grid.ExtData != null && grid.ExtData != null
&& grid.ListenToNotifications) && grid.ListenToNotifications)
@ -2395,7 +2396,7 @@ namespace Avalonia.Controls
/// true if one or both of x and y are null, in which case result holds /// true if one or both of x and y are null, in which case result holds
/// the relative sort order. /// the relative sort order.
/// </returns> /// </returns>
private static bool CompareNullRefs(object x, object y, out int result) private static bool CompareNullRefs([NotNullWhen(false)] object? x, [NotNullWhen(false)] object? y, out int result)
{ {
result = 2; result = 2;
@ -2426,7 +2427,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private IReadOnlyList<DefinitionBase> DefinitionsU private IReadOnlyList<DefinitionBase> DefinitionsU
{ {
get { return (ExtData.DefinitionsU); } get { return (ExtData.DefinitionsU!); }
} }
/// <summary> /// <summary>
@ -2434,7 +2435,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private IReadOnlyList<DefinitionBase> DefinitionsV private IReadOnlyList<DefinitionBase> DefinitionsV
{ {
get { return (ExtData.DefinitionsV); } get { return (ExtData.DefinitionsV!); }
} }
/// <summary> /// <summary>
@ -2450,7 +2451,7 @@ namespace Avalonia.Controls
if (extData.TempDefinitions == null if (extData.TempDefinitions == null
|| extData.TempDefinitions.Length < requiredLength) || extData.TempDefinitions.Length < requiredLength)
{ {
WeakReference tempDefinitionsWeakRef = (WeakReference)Thread.GetData(s_tempDefinitionsDataSlot); WeakReference? tempDefinitionsWeakRef = (WeakReference?)Thread.GetData(s_tempDefinitionsDataSlot);
if (tempDefinitionsWeakRef == null) if (tempDefinitionsWeakRef == null)
{ {
extData.TempDefinitions = new DefinitionBase[requiredLength]; extData.TempDefinitions = new DefinitionBase[requiredLength];
@ -2458,7 +2459,7 @@ namespace Avalonia.Controls
} }
else else
{ {
extData.TempDefinitions = (DefinitionBase[])tempDefinitionsWeakRef.Target; extData.TempDefinitions = (DefinitionBase[]?)tempDefinitionsWeakRef.Target;
if (extData.TempDefinitions == null if (extData.TempDefinitions == null
|| extData.TempDefinitions.Length < requiredLength) || extData.TempDefinitions.Length < requiredLength)
{ {
@ -2515,7 +2516,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private CellCache[] PrivateCells private CellCache[] PrivateCells
{ {
get { return (ExtData.CellCachesCollection); } get { return (ExtData.CellCachesCollection!); }
} }
/// <summary> /// <summary>
@ -2586,7 +2587,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private ExtendedData ExtData private ExtendedData ExtData
{ {
get { return (_data); } get { return (_data!); }
} }
/// <summary> /// <summary>
@ -2608,17 +2609,17 @@ namespace Avalonia.Controls
} }
// Extended data instantiated on demand, for non-trivial case handling only // Extended data instantiated on demand, for non-trivial case handling only
private ExtendedData _data; private ExtendedData? _data;
// Grid validity / property caches dirtiness flags // Grid validity / property caches dirtiness flags
private Flags _flags; private Flags _flags;
private GridLinesRenderer _gridLinesRenderer; private GridLinesRenderer? _gridLinesRenderer;
// Keeps track of definition indices. // Keeps track of definition indices.
int[] _definitionIndices; int[]? _definitionIndices;
// Stores unrounded values and rounding errors during layout rounding. // Stores unrounded values and rounding errors during layout rounding.
double[] _roundingErrors; double[]? _roundingErrors;
// 5 is an arbitrary constant chosen to end the measure loop // 5 is an arbitrary constant chosen to end the measure loop
private const int c_layoutLoopMaxCount = 5; private const int c_layoutLoopMaxCount = 5;
@ -2635,16 +2636,16 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private class ExtendedData private class ExtendedData
{ {
internal ColumnDefinitions ColumnDefinitions; // collection of column definitions (logical tree support) internal ColumnDefinitions? ColumnDefinitions; // collection of column definitions (logical tree support)
internal RowDefinitions RowDefinitions; // collection of row definitions (logical tree support) internal RowDefinitions? RowDefinitions; // collection of row definitions (logical tree support)
internal IReadOnlyList<DefinitionBase> DefinitionsU; // collection of column definitions used during calc internal IReadOnlyList<DefinitionBase>? DefinitionsU; // collection of column definitions used during calc
internal IReadOnlyList<DefinitionBase> DefinitionsV; // collection of row definitions used during calc internal IReadOnlyList<DefinitionBase>? DefinitionsV; // collection of row definitions used during calc
internal CellCache[] CellCachesCollection; // backing store for logical children internal CellCache[]? CellCachesCollection; // backing store for logical children
internal int CellGroup1; // index of the first cell in first cell group internal int CellGroup1; // index of the first cell in first cell group
internal int CellGroup2; // index of the first cell in second cell group internal int CellGroup2; // index of the first cell in second cell group
internal int CellGroup3; // index of the first cell in third cell group internal int CellGroup3; // index of the first cell in third cell group
internal int CellGroup4; // index of the first cell in forth cell group internal int CellGroup4; // index of the first cell in forth cell group
internal DefinitionBase[] TempDefinitions; // temporary array used during layout for various purposes internal DefinitionBase[]? TempDefinitions; // temporary array used during layout for various purposes
// TempDefinitions.Length == Max(definitionsU.Length, definitionsV.Length) // TempDefinitions.Length == Max(definitionsU.Length, definitionsV.Length)
} }
@ -2831,9 +2832,9 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// <see cref="object.Equals(object)"/> /// <see cref="object.Equals(object)"/>
/// </summary> /// </summary>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
SpanKey sk = obj as SpanKey; SpanKey? sk = obj as SpanKey;
return (sk != null return (sk != null
&& sk._start == _start && sk._start == _start
&& sk._count == _count && sk._count == _count
@ -2866,10 +2867,10 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private class SpanPreferredDistributionOrderComparer : IComparer private class SpanPreferredDistributionOrderComparer : IComparer
{ {
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
DefinitionBase definitionX = x as DefinitionBase; DefinitionBase? definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase; DefinitionBase? definitionY = y as DefinitionBase;
int result; int result;
@ -2908,10 +2909,10 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private class SpanMaxDistributionOrderComparer : IComparer private class SpanMaxDistributionOrderComparer : IComparer
{ {
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
DefinitionBase definitionX = x as DefinitionBase; DefinitionBase? definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase; DefinitionBase? definitionY = y as DefinitionBase;
int result; int result;
@ -2954,17 +2955,16 @@ namespace Avalonia.Controls
internal StarDistributionOrderIndexComparer(IReadOnlyList<DefinitionBase> definitions) internal StarDistributionOrderIndexComparer(IReadOnlyList<DefinitionBase> definitions)
{ {
Contract.Requires<NullReferenceException>(definitions != null); this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
this.definitions = definitions;
} }
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
int? indexX = x as int?; int? indexX = x as int?;
int? indexY = y as int?; int? indexY = y as int?;
DefinitionBase definitionX = null; DefinitionBase? definitionX = null;
DefinitionBase definitionY = null; DefinitionBase? definitionY = null;
if (indexX != null) if (indexX != null)
{ {
@ -2995,17 +2995,16 @@ namespace Avalonia.Controls
internal DistributionOrderIndexComparer(IReadOnlyList<DefinitionBase> definitions) internal DistributionOrderIndexComparer(IReadOnlyList<DefinitionBase> definitions)
{ {
Contract.Requires<NullReferenceException>(definitions != null); this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
this.definitions = definitions;
} }
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
int? indexX = x as int?; int? indexX = x as int?;
int? indexY = y as int?; int? indexY = y as int?;
DefinitionBase definitionX = null; DefinitionBase? definitionX = null;
DefinitionBase definitionY = null; DefinitionBase? definitionY = null;
if (indexX != null) if (indexX != null)
{ {
@ -3038,11 +3037,10 @@ namespace Avalonia.Controls
internal RoundingErrorIndexComparer(double[] errors) internal RoundingErrorIndexComparer(double[] errors)
{ {
Contract.Requires<NullReferenceException>(errors != null); this.errors = errors ?? throw new ArgumentNullException(nameof(errors));
this.errors = errors;
} }
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
int? indexX = x as int?; int? indexX = x as int?;
int? indexY = y as int?; int? indexY = y as int?;
@ -3067,10 +3065,10 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private class MinRatioComparer : IComparer private class MinRatioComparer : IComparer
{ {
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
DefinitionBase definitionX = x as DefinitionBase; DefinitionBase? definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase; DefinitionBase? definitionY = y as DefinitionBase;
int result; int result;
@ -3090,10 +3088,10 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private class MaxRatioComparer : IComparer private class MaxRatioComparer : IComparer
{ {
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
DefinitionBase definitionX = x as DefinitionBase; DefinitionBase? definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase; DefinitionBase? definitionY = y as DefinitionBase;
int result; int result;
@ -3112,10 +3110,10 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private class StarWeightComparer : IComparer private class StarWeightComparer : IComparer
{ {
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
DefinitionBase definitionX = x as DefinitionBase; DefinitionBase? definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase; DefinitionBase? definitionY = y as DefinitionBase;
int result; int result;
@ -3137,17 +3135,16 @@ namespace Avalonia.Controls
internal MinRatioIndexComparer(IReadOnlyList<DefinitionBase> definitions) internal MinRatioIndexComparer(IReadOnlyList<DefinitionBase> definitions)
{ {
Contract.Requires<NullReferenceException>(definitions != null); this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
this.definitions = definitions;
} }
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
int? indexX = x as int?; int? indexX = x as int?;
int? indexY = y as int?; int? indexY = y as int?;
DefinitionBase definitionX = null; DefinitionBase? definitionX = null;
DefinitionBase definitionY = null; DefinitionBase? definitionY = null;
if (indexX != null) if (indexX != null)
{ {
@ -3178,17 +3175,16 @@ namespace Avalonia.Controls
internal MaxRatioIndexComparer(IReadOnlyList<DefinitionBase> definitions) internal MaxRatioIndexComparer(IReadOnlyList<DefinitionBase> definitions)
{ {
Contract.Requires<NullReferenceException>(definitions != null); this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
this.definitions = definitions;
} }
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
int? indexX = x as int?; int? indexX = x as int?;
int? indexY = y as int?; int? indexY = y as int?;
DefinitionBase definitionX = null; DefinitionBase? definitionX = null;
DefinitionBase definitionY = null; DefinitionBase? definitionY = null;
if (indexX != null) if (indexX != null)
{ {
@ -3219,17 +3215,16 @@ namespace Avalonia.Controls
internal StarWeightIndexComparer(IReadOnlyList<DefinitionBase> definitions) internal StarWeightIndexComparer(IReadOnlyList<DefinitionBase> definitions)
{ {
Contract.Requires<NullReferenceException>(definitions != null); this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
this.definitions = definitions;
} }
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
int? indexX = x as int?; int? indexX = x as int?;
int? indexY = y as int?; int? indexY = y as int?;
DefinitionBase definitionX = null; DefinitionBase? definitionX = null;
DefinitionBase definitionY = null; DefinitionBase? definitionY = null;
if (indexX != null) if (indexX != null)
{ {

2
src/Avalonia.Controls/GridLength.cs

@ -135,7 +135,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="o">The object with which to test equality.</param> /// <param name="o">The object with which to test equality.</param>
/// <returns>True if the objects are equal, otherwise false.</returns> /// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object o) public override bool Equals(object? o)
{ {
if (o == null) if (o == null)
{ {

62
src/Avalonia.Controls/GridSplitter.cs

@ -59,7 +59,7 @@ namespace Avalonia.Controls
private static readonly Cursor s_columnSplitterCursor = new Cursor(StandardCursorType.SizeWestEast); private static readonly Cursor s_columnSplitterCursor = new Cursor(StandardCursorType.SizeWestEast);
private static readonly Cursor s_rowSplitterCursor = new Cursor(StandardCursorType.SizeNorthSouth); private static readonly Cursor s_rowSplitterCursor = new Cursor(StandardCursorType.SizeNorthSouth);
private ResizeData _resizeData; private ResizeData? _resizeData;
/// <summary> /// <summary>
/// Indicates whether the Splitter resizes the Columns, Rows, or Both. /// Indicates whether the Splitter resizes the Columns, Rows, or Both.
@ -197,9 +197,9 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private void RemovePreviewAdorner() private void RemovePreviewAdorner()
{ {
if (_resizeData.Adorner != null) if (_resizeData?.Adorner != null)
{ {
AdornerLayer layer = AdornerLayer.GetAdornerLayer(this); AdornerLayer layer = AdornerLayer.GetAdornerLayer(this)!;
layer.Children.Remove(_resizeData.Adorner); layer.Children.Remove(_resizeData.Adorner);
} }
} }
@ -242,7 +242,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private bool SetupDefinitionsToResize() private bool SetupDefinitionsToResize()
{ {
int gridSpan = GetValue(_resizeData.ResizeDirection == GridResizeDirection.Columns ? int gridSpan = GetValue(_resizeData!.ResizeDirection == GridResizeDirection.Columns ?
Grid.ColumnSpanProperty : Grid.ColumnSpanProperty :
Grid.RowSpanProperty); Grid.RowSpanProperty);
@ -276,8 +276,8 @@ namespace Avalonia.Controls
// Get count of rows/columns in the resize direction. // Get count of rows/columns in the resize direction.
int count = _resizeData.ResizeDirection == GridResizeDirection.Columns ? int count = _resizeData.ResizeDirection == GridResizeDirection.Columns ?
_resizeData.Grid.ColumnDefinitions.Count : _resizeData.Grid!.ColumnDefinitions.Count :
_resizeData.Grid.RowDefinitions.Count; _resizeData.Grid!.RowDefinitions.Count;
if (index1 >= 0 && index2 < count) if (index1 >= 0 && index2 < count)
{ {
@ -322,10 +322,10 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private void SetupPreviewAdorner() private void SetupPreviewAdorner()
{ {
if (_resizeData.ShowsPreview) if (_resizeData!.ShowsPreview)
{ {
// Get the adorner layer and add an adorner to it. // Get the adorner layer and add an adorner to it.
var adornerLayer = AdornerLayer.GetAdornerLayer(_resizeData.Grid); var adornerLayer = AdornerLayer.GetAdornerLayer(_resizeData.Grid!);
var previewContent = PreviewContent; var previewContent = PreviewContent;
@ -335,7 +335,7 @@ namespace Avalonia.Controls
return; return;
} }
IControl builtPreviewContent = previewContent?.Build(); IControl? builtPreviewContent = previewContent?.Build();
_resizeData.Adorner = new PreviewAdorner(builtPreviewContent); _resizeData.Adorner = new PreviewAdorner(builtPreviewContent);
@ -409,13 +409,13 @@ namespace Avalonia.Controls
// Set the Translation of the Adorner to the distance from the thumb. // Set the Translation of the Adorner to the distance from the thumb.
if (_resizeData.ResizeDirection == GridResizeDirection.Columns) if (_resizeData.ResizeDirection == GridResizeDirection.Columns)
{ {
_resizeData.Adorner.OffsetX = Math.Min( _resizeData.Adorner!.OffsetX = Math.Min(
Math.Max(horizontalChange, _resizeData.MinChange), Math.Max(horizontalChange, _resizeData.MinChange),
_resizeData.MaxChange); _resizeData.MaxChange);
} }
else else
{ {
_resizeData.Adorner.OffsetY = Math.Min( _resizeData.Adorner!.OffsetY = Math.Min(
Math.Max(verticalChange, _resizeData.MinChange), Math.Max(verticalChange, _resizeData.MinChange),
_resizeData.MaxChange); _resizeData.MaxChange);
} }
@ -437,7 +437,7 @@ namespace Avalonia.Controls
if (_resizeData.ShowsPreview) if (_resizeData.ShowsPreview)
{ {
// Update the grid. // Update the grid.
MoveSplitter(_resizeData.Adorner.OffsetX, _resizeData.Adorner.OffsetY); MoveSplitter(_resizeData.Adorner!.OffsetX, _resizeData.Adorner.OffsetY);
RemovePreviewAdorner(); RemovePreviewAdorner();
} }
@ -481,14 +481,14 @@ namespace Avalonia.Controls
private void CancelResize() private void CancelResize()
{ {
// Restore original column/row lengths. // Restore original column/row lengths.
if (_resizeData.ShowsPreview) if (_resizeData!.ShowsPreview)
{ {
RemovePreviewAdorner(); RemovePreviewAdorner();
} }
else // Reset the columns/rows lengths to the saved values. else // Reset the columns/rows lengths to the saved values.
{ {
SetDefinitionLength(_resizeData.Definition1, _resizeData.OriginalDefinition1Length); SetDefinitionLength(_resizeData.Definition1!, _resizeData.OriginalDefinition1Length);
SetDefinitionLength(_resizeData.Definition2, _resizeData.OriginalDefinition2Length); SetDefinitionLength(_resizeData.Definition2!, _resizeData.OriginalDefinition2Length);
} }
_resizeData = null; _resizeData = null;
@ -536,12 +536,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private void GetDeltaConstraints(out double minDelta, out double maxDelta) private void GetDeltaConstraints(out double minDelta, out double maxDelta)
{ {
double definition1Len = GetActualLength(_resizeData.Definition1); double definition1Len = GetActualLength(_resizeData!.Definition1!);
double definition1Min = _resizeData.Definition1.UserMinSizeValueCache; double definition1Min = _resizeData.Definition1!.UserMinSizeValueCache;
double definition1Max = _resizeData.Definition1.UserMaxSizeValueCache; double definition1Max = _resizeData.Definition1.UserMaxSizeValueCache;
double definition2Len = GetActualLength(_resizeData.Definition2); double definition2Len = GetActualLength(_resizeData.Definition2!);
double definition2Min = _resizeData.Definition2.UserMinSizeValueCache; double definition2Min = _resizeData.Definition2!.UserMinSizeValueCache;
double definition2Max = _resizeData.Definition2.UserMaxSizeValueCache; double definition2Max = _resizeData.Definition2.UserMaxSizeValueCache;
// Set MinWidths to be greater than width of splitter. // Set MinWidths to be greater than width of splitter.
@ -565,11 +565,11 @@ namespace Avalonia.Controls
private void SetLengths(double definition1Pixels, double definition2Pixels) private void SetLengths(double definition1Pixels, double definition2Pixels)
{ {
// For the case where both definition1 and 2 are stars, update all star values to match their current pixel values. // For the case where both definition1 and 2 are stars, update all star values to match their current pixel values.
if (_resizeData.SplitBehavior == SplitBehavior.Split) if (_resizeData!.SplitBehavior == SplitBehavior.Split)
{ {
var definitions = _resizeData.ResizeDirection == GridResizeDirection.Columns ? var definitions = _resizeData.ResizeDirection == GridResizeDirection.Columns ?
(IAvaloniaReadOnlyList<DefinitionBase>)_resizeData.Grid.ColumnDefinitions : (IAvaloniaReadOnlyList<DefinitionBase>)_resizeData.Grid!.ColumnDefinitions :
(IAvaloniaReadOnlyList<DefinitionBase>)_resizeData.Grid.RowDefinitions; (IAvaloniaReadOnlyList<DefinitionBase>)_resizeData.Grid!.RowDefinitions;
var definitionsCount = definitions.Count; var definitionsCount = definitions.Count;
@ -595,11 +595,11 @@ namespace Avalonia.Controls
} }
else if (_resizeData.SplitBehavior == SplitBehavior.Resize1) else if (_resizeData.SplitBehavior == SplitBehavior.Resize1)
{ {
SetDefinitionLength(_resizeData.Definition1, new GridLength(definition1Pixels)); SetDefinitionLength(_resizeData.Definition1!, new GridLength(definition1Pixels));
} }
else else
{ {
SetDefinitionLength(_resizeData.Definition2, new GridLength(definition2Pixels)); SetDefinitionLength(_resizeData.Definition2!, new GridLength(definition2Pixels));
} }
} }
@ -623,8 +623,8 @@ namespace Avalonia.Controls
delta = LayoutHelper.RoundLayoutValue(delta, LayoutHelper.GetLayoutScale(this)); delta = LayoutHelper.RoundLayoutValue(delta, LayoutHelper.GetLayoutScale(this));
} }
DefinitionBase definition1 = _resizeData.Definition1; DefinitionBase? definition1 = _resizeData.Definition1;
DefinitionBase definition2 = _resizeData.Definition2; DefinitionBase? definition2 = _resizeData.Definition2;
if (definition1 != null && definition2 != null) if (definition1 != null && definition2 != null)
{ {
@ -691,7 +691,7 @@ namespace Avalonia.Controls
private readonly TranslateTransform _translation; private readonly TranslateTransform _translation;
private readonly Decorator _decorator; private readonly Decorator _decorator;
public PreviewAdorner(IControl previewControl) public PreviewAdorner(IControl? previewControl)
{ {
// Add a decorator to perform translations. // Add a decorator to perform translations.
_translation = new TranslateTransform(); _translation = new TranslateTransform();
@ -762,22 +762,22 @@ namespace Avalonia.Controls
private class ResizeData private class ResizeData
{ {
public bool ShowsPreview; public bool ShowsPreview;
public PreviewAdorner Adorner; public PreviewAdorner? Adorner;
// The constraints to keep the Preview within valid ranges. // The constraints to keep the Preview within valid ranges.
public double MinChange; public double MinChange;
public double MaxChange; public double MaxChange;
// The grid to Resize. // The grid to Resize.
public Grid Grid; public Grid? Grid;
// Cache of Resize Direction and Behavior. // Cache of Resize Direction and Behavior.
public GridResizeDirection ResizeDirection; public GridResizeDirection ResizeDirection;
public GridResizeBehavior ResizeBehavior; public GridResizeBehavior ResizeBehavior;
// The columns/rows to resize. // The columns/rows to resize.
public DefinitionBase Definition1; public DefinitionBase? Definition1;
public DefinitionBase Definition2; public DefinitionBase? Definition2;
// Are the columns/rows star lengths. // Are the columns/rows star lengths.
public SplitBehavior SplitBehavior; public SplitBehavior SplitBehavior;

40
src/Avalonia.Controls/HotkeyManager.cs

@ -7,29 +7,29 @@ namespace Avalonia.Controls
{ {
public class HotKeyManager public class HotKeyManager
{ {
public static readonly AttachedProperty<KeyGesture> HotKeyProperty public static readonly AttachedProperty<KeyGesture?> HotKeyProperty
= AvaloniaProperty.RegisterAttached<Control, KeyGesture>("HotKey", typeof(HotKeyManager)); = AvaloniaProperty.RegisterAttached<Control, KeyGesture?>("HotKey", typeof(HotKeyManager));
class HotkeyCommandWrapper : ICommand class HotkeyCommandWrapper : ICommand
{ {
public HotkeyCommandWrapper(ICommandSource control) public HotkeyCommandWrapper(ICommandSource? control)
{ {
CommandSource = control; CommandSource = control;
} }
public readonly ICommandSource CommandSource; public readonly ICommandSource? CommandSource;
private ICommand GetCommand() => CommandSource.Command; private ICommand? GetCommand() => CommandSource?.Command;
public bool CanExecute(object parameter) => public bool CanExecute(object? parameter) =>
CommandSource.Command?.CanExecute(CommandSource.CommandParameter) == true CommandSource?.Command?.CanExecute(CommandSource.CommandParameter) == true
&& CommandSource.IsEffectivelyEnabled; && CommandSource.IsEffectivelyEnabled;
public void Execute(object parameter) => public void Execute(object? parameter) =>
GetCommand()?.Execute(CommandSource.CommandParameter); GetCommand()?.Execute(CommandSource?.CommandParameter);
#pragma warning disable 67 // Event not used #pragma warning disable 67 // Event not used
public event EventHandler CanExecuteChanged; public event EventHandler? CanExecuteChanged;
#pragma warning restore 67 #pragma warning restore 67
} }
@ -37,12 +37,12 @@ namespace Avalonia.Controls
class Manager class Manager
{ {
private readonly IControl _control; private readonly IControl _control;
private TopLevel _root; private TopLevel? _root;
private IDisposable _parentSub; private IDisposable? _parentSub;
private IDisposable _hotkeySub; private IDisposable? _hotkeySub;
private KeyGesture _hotkey; private KeyGesture? _hotkey;
private readonly HotkeyCommandWrapper _wrapper; private readonly HotkeyCommandWrapper _wrapper;
private KeyBinding _binding; private KeyBinding? _binding;
public Manager(IControl control) public Manager(IControl control)
{ {
@ -56,14 +56,14 @@ namespace Avalonia.Controls
_parentSub = AncestorFinder.Create<TopLevel>(_control).Subscribe(OnParentChanged); _parentSub = AncestorFinder.Create<TopLevel>(_control).Subscribe(OnParentChanged);
} }
private void OnParentChanged(TopLevel control) private void OnParentChanged(TopLevel? control)
{ {
Unregister(); Unregister();
_root = control; _root = control;
Register(); Register();
} }
private void OnHotkeyChanged(KeyGesture hotkey) private void OnHotkeyChanged(KeyGesture? hotkey)
{ {
if (hotkey == null) if (hotkey == null)
//Subscription will be recreated by static property watcher //Subscription will be recreated by static property watcher
@ -95,8 +95,8 @@ namespace Avalonia.Controls
void Stop() void Stop()
{ {
Unregister(); Unregister();
_parentSub.Dispose(); _parentSub?.Dispose();
_hotkeySub.Dispose(); _hotkeySub?.Dispose();
} }
} }
@ -118,6 +118,6 @@ namespace Avalonia.Controls
}); });
} }
public static void SetHotKey(AvaloniaObject target, KeyGesture value) => target.SetValue(HotKeyProperty, value); public static void SetHotKey(AvaloniaObject target, KeyGesture value) => target.SetValue(HotKeyProperty, value);
public static KeyGesture GetHotKey(AvaloniaObject target) => target.GetValue(HotKeyProperty); public static KeyGesture? GetHotKey(AvaloniaObject target) => target.GetValue(HotKeyProperty);
} }
} }

6
src/Avalonia.Controls/IContentControl.cs

@ -12,12 +12,12 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the content to display. /// Gets or sets the content to display.
/// </summary> /// </summary>
object Content { get; set; } object? Content { get; set; }
/// <summary> /// <summary>
/// Gets or sets the data template used to display the content of the control. /// Gets or sets the data template used to display the content of the control.
/// </summary> /// </summary>
IDataTemplate ContentTemplate { get; set; } IDataTemplate? ContentTemplate { get; set; }
/// <summary> /// <summary>
/// Gets or sets the horizontal alignment of the content within the control. /// Gets or sets the horizontal alignment of the content within the control.
@ -29,4 +29,4 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
VerticalAlignment VerticalContentAlignment { get; set; } VerticalAlignment VerticalContentAlignment { get; set; }
} }
} }

2
src/Avalonia.Controls/IControl.cs

@ -3,8 +3,6 @@ using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.VisualTree; using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/IHeadered.cs

@ -8,6 +8,6 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or set the header. /// Gets or set the header.
/// </summary> /// </summary>
object Header { get; set; } object? Header { get; set; }
} }
} }

2
src/Avalonia.Controls/IMenuElement.cs

@ -1,8 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Input; using Avalonia.Input;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/IMenuItem.cs

@ -1,6 +1,4 @@
#nullable enable namespace Avalonia.Controls
namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Represents a <see cref="MenuItem"/>. /// Represents a <see cref="MenuItem"/>.

4
src/Avalonia.Controls/IScrollAnchorProvider.cs

@ -1,6 +1,4 @@
#nullable enable namespace Avalonia.Controls
namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Specifies a contract for a scrolling control that supports scroll anchoring. /// Specifies a contract for a scrolling control that supports scroll anchoring.

2
src/Avalonia.Controls/IVirtualizingPanel.cs

@ -16,7 +16,7 @@ namespace Avalonia.Controls
/// Note that this property may remain null if the panel is added to a control that does /// Note that this property may remain null if the panel is added to a control that does
/// not act as a virtualizing controller. /// not act as a virtualizing controller.
/// </remarks> /// </remarks>
IVirtualizingController Controller { get; set; } IVirtualizingController? Controller { get; set; }
/// <summary> /// <summary>
/// Gets a value indicating whether the panel is full. /// Gets a value indicating whether the panel is full.

56
src/Avalonia.Controls/ItemsControl.cs

@ -32,8 +32,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Items"/> property. /// Defines the <see cref="Items"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<ItemsControl, IEnumerable> ItemsProperty = public static readonly DirectProperty<ItemsControl, IEnumerable?> ItemsProperty =
AvaloniaProperty.RegisterDirect<ItemsControl, IEnumerable>(nameof(Items), o => o.Items, (o, v) => o.Items = v); AvaloniaProperty.RegisterDirect<ItemsControl, IEnumerable?>(nameof(Items), o => o.Items, (o, v) => o.Items = v);
/// <summary> /// <summary>
/// Defines the <see cref="ItemCount"/> property. /// Defines the <see cref="ItemCount"/> property.
@ -50,13 +50,13 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="ItemTemplate"/> property. /// Defines the <see cref="ItemTemplate"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty = public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty =
AvaloniaProperty.Register<ItemsControl, IDataTemplate>(nameof(ItemTemplate)); AvaloniaProperty.Register<ItemsControl, IDataTemplate?>(nameof(ItemTemplate));
private IEnumerable _items = new AvaloniaList<object>(); private IEnumerable? _items = new AvaloniaList<object>();
private int _itemCount; private int _itemCount;
private IItemContainerGenerator _itemContainerGenerator; private IItemContainerGenerator? _itemContainerGenerator;
private EventHandler<ChildIndexChangedEventArgs> _childIndexChanged; private EventHandler<ChildIndexChangedEventArgs>? _childIndexChanged;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="ItemsControl"/> class. /// Initializes static members of the <see cref="ItemsControl"/> class.
@ -79,7 +79,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the <see cref="IItemContainerGenerator"/> for the control. /// Gets the <see cref="IItemContainerGenerator"/> for the control.
/// </summary> /// </summary>
public IItemContainerGenerator ItemContainerGenerator public IItemContainerGenerator? ItemContainerGenerator
{ {
get get
{ {
@ -104,7 +104,7 @@ namespace Avalonia.Controls
/// Gets or sets the items to display. /// Gets or sets the items to display.
/// </summary> /// </summary>
[Content] [Content]
public IEnumerable Items public IEnumerable? Items
{ {
get { return _items; } get { return _items; }
set { SetAndRaise(ItemsProperty, ref _items, value); } set { SetAndRaise(ItemsProperty, ref _items, value); }
@ -131,7 +131,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the data template used to display the items in the control. /// Gets or sets the data template used to display the items in the control.
/// </summary> /// </summary>
public IDataTemplate ItemTemplate public IDataTemplate? ItemTemplate
{ {
get { return GetValue(ItemTemplateProperty); } get { return GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); } set { SetValue(ItemTemplateProperty, value); }
@ -140,13 +140,13 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the items presenter control. /// Gets the items presenter control.
/// </summary> /// </summary>
public IItemsPresenter Presenter public IItemsPresenter? Presenter
{ {
get; get;
protected set; protected set;
} }
event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged event EventHandler<ChildIndexChangedEventArgs>? IChildIndexProvider.ChildIndexChanged
{ {
add => _childIndexChanged += value; add => _childIndexChanged += value;
remove => _childIndexChanged -= value; remove => _childIndexChanged -= value;
@ -161,7 +161,7 @@ namespace Avalonia.Controls
} }
Presenter = presenter; Presenter = presenter;
ItemContainerGenerator.Clear(); ItemContainerGenerator?.Clear();
if (Presenter is IChildIndexProvider innerProvider) if (Presenter is IChildIndexProvider innerProvider)
{ {
@ -189,11 +189,11 @@ namespace Avalonia.Controls
/// <param name="items">The collection.</param> /// <param name="items">The collection.</param>
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
/// <returns>The item at the given index or null if the index is out of bounds.</returns> /// <returns>The item at the given index or null if the index is out of bounds.</returns>
protected static object ElementAt(IEnumerable items, int index) protected static object? ElementAt(IEnumerable? items, int index)
{ {
if (index != -1 && index < items.Count()) if (index != -1 && index < items.Count())
{ {
return items.ElementAt(index) ?? null; return items!.ElementAt(index) ?? null;
} }
else else
{ {
@ -207,7 +207,7 @@ namespace Avalonia.Controls
/// <param name="items">The collection.</param> /// <param name="items">The collection.</param>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>The index of the item or -1 if the item was not found.</returns> /// <returns>The index of the item or -1 if the item was not found.</returns>
protected static int IndexOf(IEnumerable items, object item) protected static int IndexOf(IEnumerable? items, object item)
{ {
if (items != null && item != null) if (items != null && item != null)
{ {
@ -282,7 +282,7 @@ namespace Avalonia.Controls
{ {
// If the item is its own container, then it will be removed from the logical tree // If the item is its own container, then it will be removed from the logical tree
// when it is removed from the Items collection. // when it is removed from the Items collection.
if (container?.ContainerControl != container?.Item) if (container.ContainerControl != container.Item)
{ {
LogicalChildren.Remove(container.ContainerControl); LogicalChildren.Remove(container.ContainerControl);
} }
@ -311,20 +311,20 @@ namespace Avalonia.Controls
var container = Presenter?.Panel as INavigableContainer; var container = Presenter?.Panel as INavigableContainer;
if (container == null || if (container == null ||
focus.Current == null || focus?.Current == null ||
direction == null || direction == null ||
direction.Value.IsTab()) direction.Value.IsTab())
{ {
return; return;
} }
IVisual current = focus.Current; IVisual? current = focus.Current;
while (current != null) while (current != null)
{ {
if (current.VisualParent == container && current is IInputElement inputElement) if (current.VisualParent == container && current is IInputElement inputElement)
{ {
IInputElement next = GetNextControl(container, direction.Value, inputElement, false); var next = GetNextControl(container, direction.Value, inputElement, false);
if (next != null) if (next != null)
{ {
@ -406,7 +406,7 @@ namespace Avalonia.Controls
/// Given a collection of items, adds those that are controls to the logical children. /// Given a collection of items, adds those that are controls to the logical children.
/// </summary> /// </summary>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
private void AddControlItemsToLogicalChildren(IEnumerable items) private void AddControlItemsToLogicalChildren(IEnumerable? items)
{ {
var toAdd = new List<ILogical>(); var toAdd = new List<ILogical>();
@ -430,7 +430,7 @@ namespace Avalonia.Controls
/// Given a collection of items, removes those that are controls to from logical children. /// Given a collection of items, removes those that are controls to from logical children.
/// </summary> /// </summary>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
private void RemoveControlItemsFromLogicalChildren(IEnumerable items) private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
{ {
var toRemove = new List<ILogical>(); var toRemove = new List<ILogical>();
@ -454,7 +454,7 @@ namespace Avalonia.Controls
/// Subscribes to an <see cref="Items"/> collection. /// Subscribes to an <see cref="Items"/> collection.
/// </summary> /// </summary>
/// <param name="items">The items collection.</param> /// <param name="items">The items collection.</param>
private void SubscribeToItems(IEnumerable items) private void SubscribeToItems(IEnumerable? items)
{ {
if (items is INotifyCollectionChanged incc) if (items is INotifyCollectionChanged incc)
{ {
@ -470,7 +470,7 @@ namespace Avalonia.Controls
{ {
if (_itemContainerGenerator != null) if (_itemContainerGenerator != null)
{ {
_itemContainerGenerator.ItemTemplate = (IDataTemplate)e.NewValue; _itemContainerGenerator.ItemTemplate = (IDataTemplate?)e.NewValue;
// TODO: Rebuild the item containers. // TODO: Rebuild the item containers.
} }
} }
@ -497,13 +497,13 @@ namespace Avalonia.Controls
PseudoClasses.Set(":singleitem", itemCount == 1); PseudoClasses.Set(":singleitem", itemCount == 1);
} }
protected static IInputElement GetNextControl( protected static IInputElement? GetNextControl(
INavigableContainer container, INavigableContainer container,
NavigationDirection direction, NavigationDirection direction,
IInputElement from, IInputElement? from,
bool wrap) bool wrap)
{ {
IInputElement result; IInputElement? result;
var c = from; var c = from;
do do
@ -525,7 +525,7 @@ namespace Avalonia.Controls
return null; return null;
} }
private void PresenterChildIndexChanged(object sender, ChildIndexChangedEventArgs e) private void PresenterChildIndexChanged(object? sender, ChildIndexChangedEventArgs e)
{ {
_childIndexChanged?.Invoke(this, e); _childIndexChanged?.Invoke(this, e);
} }

8
src/Avalonia.Controls/ItemsSourceView.cs

@ -7,12 +7,9 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
@ -196,7 +193,7 @@ namespace Avalonia.Controls
_collectionChanged?.Invoke(this, args); _collectionChanged?.Invoke(this, args);
} }
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
OnItemsSourceChanged(e); OnItemsSourceChanged(e);
} }
@ -239,8 +236,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
/// <returns>The item.</returns> /// <returns>The item.</returns>
[return: MaybeNull] public new T GetAt(int index) => (T)Inner[index]!;
public new T GetAt(int index) => (T)Inner[index];
public IEnumerator<T> GetEnumerator() => Inner.Cast<T>().GetEnumerator(); public IEnumerator<T> GetEnumerator() => Inner.Cast<T>().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Inner.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => Inner.GetEnumerator();

8
src/Avalonia.Controls/Label.cs

@ -18,19 +18,19 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Target"/> Direct property /// Defines the <see cref="Target"/> Direct property
/// </summary> /// </summary>
public static readonly DirectProperty<Label, IInputElement> TargetProperty = public static readonly DirectProperty<Label, IInputElement?> TargetProperty =
AvaloniaProperty.RegisterDirect<Label, IInputElement>(nameof(Target), lbl => lbl.Target, (lbl, inp) => lbl.Target = inp); AvaloniaProperty.RegisterDirect<Label, IInputElement?>(nameof(Target), lbl => lbl.Target, (lbl, inp) => lbl.Target = inp);
/// <summary> /// <summary>
/// Label focus target storage field /// Label focus target storage field
/// </summary> /// </summary>
private IInputElement _target; private IInputElement? _target;
/// <summary> /// <summary>
/// Label focus Target /// Label focus Target
/// </summary> /// </summary>
[ResolveByName] [ResolveByName]
public IInputElement Target public IInputElement? Target
{ {
get => _target; get => _target;
set => SetAndRaise(TargetProperty, ref _target, value); set => SetAndRaise(TargetProperty, ref _target, value);

14
src/Avalonia.Controls/LayoutTransformControl.cs

@ -14,8 +14,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public class LayoutTransformControl : Decorator public class LayoutTransformControl : Decorator
{ {
public static readonly StyledProperty<ITransform> LayoutTransformProperty = public static readonly StyledProperty<ITransform?> LayoutTransformProperty =
AvaloniaProperty.Register<LayoutTransformControl, ITransform>(nameof(LayoutTransform)); AvaloniaProperty.Register<LayoutTransformControl, ITransform?>(nameof(LayoutTransform));
public static readonly StyledProperty<bool> UseRenderTransformProperty = public static readonly StyledProperty<bool> UseRenderTransformProperty =
AvaloniaProperty.Register<LayoutTransformControl, bool>(nameof(UseRenderTransform)); AvaloniaProperty.Register<LayoutTransformControl, bool>(nameof(UseRenderTransform));
@ -37,7 +37,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets a graphics transformation that should apply to this element when layout is performed. /// Gets or sets a graphics transformation that should apply to this element when layout is performed.
/// </summary> /// </summary>
public ITransform LayoutTransform public ITransform? LayoutTransform
{ {
get { return GetValue(LayoutTransformProperty); } get { return GetValue(LayoutTransformProperty); }
set { SetValue(LayoutTransformProperty, value); } set { SetValue(LayoutTransformProperty, value); }
@ -52,7 +52,7 @@ namespace Avalonia.Controls
set { SetValue(UseRenderTransformProperty, value); } set { SetValue(UseRenderTransformProperty, value); }
} }
public IControl TransformRoot => Child; public IControl? TransformRoot => Child;
/// <summary> /// <summary>
/// Provides the behavior for the "Arrange" pass of layout. /// Provides the behavior for the "Arrange" pass of layout.
@ -146,7 +146,7 @@ namespace Avalonia.Controls
return transformedDesiredSize; return transformedDesiredSize;
} }
IDisposable _renderTransformChangedEvent; IDisposable? _renderTransformChangedEvent;
private void OnUseRenderTransformPropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnUseRenderTransformPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
@ -158,7 +158,7 @@ namespace Avalonia.Controls
// workaround. // workaround.
var target = e.Sender as LayoutTransformControl; var target = e.Sender as LayoutTransformControl;
var shouldUseRenderTransform = (bool)e.NewValue; var shouldUseRenderTransform = (bool)e.NewValue!;
if (target != null) if (target != null)
{ {
if (shouldUseRenderTransform) if (shouldUseRenderTransform)
@ -217,7 +217,7 @@ namespace Avalonia.Controls
/// Transformation matrix corresponding to _matrixTransform. /// Transformation matrix corresponding to _matrixTransform.
/// </summary> /// </summary>
private Matrix _transformation; private Matrix _transformation;
private IDisposable _transformChangedEvent = null; private IDisposable? _transformChangedEvent = null;
/// <summary> /// <summary>
/// Returns true if Size a is smaller than Size b in either dimension. /// Returns true if Size a is smaller than Size b in either dimension.

12
src/Avalonia.Controls/ListBox.cs

@ -23,13 +23,13 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Scroll"/> property. /// Defines the <see cref="Scroll"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<ListBox, IScrollable> ScrollProperty = public static readonly DirectProperty<ListBox, IScrollable?> ScrollProperty =
AvaloniaProperty.RegisterDirect<ListBox, IScrollable>(nameof(Scroll), o => o.Scroll); AvaloniaProperty.RegisterDirect<ListBox, IScrollable?>(nameof(Scroll), o => o.Scroll);
/// <summary> /// <summary>
/// Defines the <see cref="SelectedItems"/> property. /// Defines the <see cref="SelectedItems"/> property.
/// </summary> /// </summary>
public static readonly new DirectProperty<SelectingItemsControl, IList> SelectedItemsProperty = public static readonly new DirectProperty<SelectingItemsControl, IList?> SelectedItemsProperty =
SelectingItemsControl.SelectedItemsProperty; SelectingItemsControl.SelectedItemsProperty;
/// <summary> /// <summary>
@ -50,7 +50,7 @@ namespace Avalonia.Controls
public static readonly StyledProperty<ItemVirtualizationMode> VirtualizationModeProperty = public static readonly StyledProperty<ItemVirtualizationMode> VirtualizationModeProperty =
ItemsPresenter.VirtualizationModeProperty.AddOwner<ListBox>(); ItemsPresenter.VirtualizationModeProperty.AddOwner<ListBox>();
private IScrollable _scroll; private IScrollable? _scroll;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="ItemsControl"/> class. /// Initializes static members of the <see cref="ItemsControl"/> class.
@ -64,14 +64,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the scroll information for the <see cref="ListBox"/>. /// Gets the scroll information for the <see cref="ListBox"/>.
/// </summary> /// </summary>
public IScrollable Scroll public IScrollable? Scroll
{ {
get { return _scroll; } get { return _scroll; }
private set { SetAndRaise(ScrollProperty, ref _scroll, value); } private set { SetAndRaise(ScrollProperty, ref _scroll, value); }
} }
/// <inheritdoc/> /// <inheritdoc/>
public new IList SelectedItems public new IList? SelectedItems
{ {
get => base.SelectedItems; get => base.SelectedItems;
set => base.SelectedItems = value; set => base.SelectedItems = value;

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save