Browse Source

Merge branch 'master' into refactor/controltemplate-binding-priority

pull/7679/head
Steven Kirk 4 years ago
parent
commit
2679dab332
  1. 9
      Avalonia.sln
  2. 1
      Directory.Build.props
  3. 9
      azure-pipelines.yml
  4. 16
      build/AndroidWorkarounds.props
  5. 1
      build/CoreLibraries.props
  6. 2
      build/SharedVersion.props
  7. 3
      build/System.Drawing.Common.props
  8. 11
      dirs.proj
  9. 1
      global.json
  10. 5
      nukebuild/Build.cs
  11. 23
      readme.md
  12. 19
      samples/ControlCatalog.Android/Assets/AboutAssets.txt
  13. 183
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  14. 5
      samples/ControlCatalog.Android/MainActivity.cs
  15. 7
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  16. 30
      samples/ControlCatalog.Android/Properties/AssemblyInfo.cs
  17. 101
      samples/ControlCatalog.Android/Resources/Resource.Designer.cs
  18. 2
      samples/ControlCatalog.Android/Resources/values/styles.xml
  19. 2
      samples/ControlCatalog.Android/SplashActivity.cs
  20. 14
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  21. 7
      samples/ControlCatalog.NetCore/rd.xml
  22. 1
      samples/ControlCatalog.Web/ControlCatalog.Web.csproj
  23. 7
      samples/ControlCatalog/MainView.xaml
  24. 45
      samples/ControlCatalog/Pages/ButtonPage.xaml
  25. 224
      samples/ControlCatalog/Pages/ButtonsPage.xaml
  26. 4
      samples/ControlCatalog/Pages/ButtonsPage.xaml.cs
  27. 23
      samples/ControlCatalog/Pages/ClipboardPage.xaml
  28. 77
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  29. 160
      samples/ControlCatalog/Pages/ComboBoxPage.xaml
  30. 2
      samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
  31. 8
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  32. 4
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  33. 2
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  34. 4
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  35. 37
      samples/ControlCatalog/Pages/ScreenPage.cs
  36. 2
      samples/ControlCatalog/Pages/ToggleSwitchPage.xaml
  37. 21
      samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
  38. 7
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  39. 3
      samples/RenderDemo/MainWindow.xaml
  40. 13
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  41. 7
      samples/RenderDemo/Pages/FormattedTextPage.axaml
  42. 60
      samples/RenderDemo/Pages/FormattedTextPage.axaml.cs
  43. 7
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  44. 4
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  45. 9
      src/Android/Avalonia.Android/AndroidPlatform.cs
  46. 14
      src/Android/Avalonia.Android/AppBuilder.cs
  47. 17
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  48. 36
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  49. 11
      src/Android/Avalonia.Android/AvaloniaViewModel.cs
  50. 50
      src/Android/Avalonia.Android/Resources/AboutResources.txt
  51. 6
      src/Android/Avalonia.Android/Resources/Values/Strings.xml
  52. 4
      src/Android/Avalonia.Android/SoftKeyboardListener.cs
  53. 19
      src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt
  54. 172
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  55. 2
      src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
  56. 6
      src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml
  57. 30
      src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs
  58. 79
      src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
  59. 11
      src/Android/Avalonia.AndroidTestApplication/app.config
  60. 33
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  61. 2
      src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs
  62. 7
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  63. 6
      src/Avalonia.Controls/ApiCompatBaseline.txt
  64. 35
      src/Avalonia.Controls/AppBuilderBase.cs
  65. 5
      src/Avalonia.Controls/Application.cs
  66. 26
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  67. 6
      src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
  68. 2
      src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs
  69. 309
      src/Avalonia.Controls/AutoCompleteBox.cs
  70. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  71. 12
      src/Avalonia.Controls/Border.cs
  72. 34
      src/Avalonia.Controls/Button.cs
  73. 14
      src/Avalonia.Controls/ButtonSpinner.cs
  74. 76
      src/Avalonia.Controls/Calendar/Calendar.cs
  75. 6
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  76. 90
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  77. 2
      src/Avalonia.Controls/Calendar/CalendarDateRange.cs
  78. 6
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  79. 100
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  80. 2
      src/Avalonia.Controls/Calendar/DateTimeHelper.cs
  81. 4
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  82. 2
      src/Avalonia.Controls/Canvas.cs
  83. 2
      src/Avalonia.Controls/Chrome/CaptionButtons.cs
  84. 4
      src/Avalonia.Controls/Chrome/TitleBar.cs
  85. 65
      src/Avalonia.Controls/ComboBox.cs
  86. 14
      src/Avalonia.Controls/ContentControl.cs
  87. 14
      src/Avalonia.Controls/ContextMenu.cs
  88. 2
      src/Avalonia.Controls/ContextRequestedEventArgs.cs
  89. 23
      src/Avalonia.Controls/Control.cs
  90. 35
      src/Avalonia.Controls/ControlExtensions.cs
  91. 3
      src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
  92. 4
      src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
  93. 2
      src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
  94. 4
      src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
  95. 29
      src/Avalonia.Controls/DataValidationErrors.cs
  96. 74
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  97. 104
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  98. 28
      src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
  99. 6
      src/Avalonia.Controls/DateTimePickers/PickerPresenterBase.cs
  100. 69
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs

9
Avalonia.sln

@ -77,7 +77,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}"
EndProject EndProject
@ -95,7 +95,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}"
EndProject EndProject
@ -107,7 +107,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RenderDemo", "samples\RenderDemo\RenderDemo.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RenderDemo", "samples\RenderDemo\RenderDemo.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
EndProject EndProject
@ -115,7 +115,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\ApiDiff.props = build\ApiDiff.props build\ApiDiff.props = build\ApiDiff.props
build\Base.props = build\Base.props build\Base.props = build\Base.props
build\Binding.props = build\Binding.props build\Binding.props = build\Binding.props
@ -1106,7 +1105,7 @@ Global
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.Build.0 = AppStore|iPhone {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.Build.0 = AppStore|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|iPhone {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.ActiveCfg = Debug|iPhone {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.ActiveCfg = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.Build.0 = Debug|iPhone {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.Build.0 = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator

1
Directory.Build.props

@ -4,5 +4,6 @@
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> <AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<!-- https://github.com/dotnet/msbuild/issues/2661 --> <!-- https://github.com/dotnet/msbuild/issues/2661 -->
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies> <AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

9
azure-pipelines.yml

@ -1,6 +1,3 @@
variables:
MSBuildEnableWorkloadResolver: 'false'
jobs: jobs:
- job: GetPRNumber - job: GetPRNumber
@ -146,6 +143,12 @@ jobs:
inputs: inputs:
version: 6.0.100 version: 6.0.100
- task: CmdLine@2
displayName: 'Install Workloads'
inputs:
script: |
dotnet workload install android
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Install Nuke' displayName: 'Install Nuke'
inputs: inputs:

16
build/AndroidWorkarounds.props

@ -1,16 +0,0 @@
<Project>
<ItemGroup Condition="'$(AndroidApplication)' == 'true'">
<!-- WORKAROUND: The packages below are transitively referenced by System.Memory,
but Xamarin.Android applications need the newest versions directly referenced for the linker. -->
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
<Target Name="_RemoveNonExistingResgenFile" BeforeTargets="CoreCompile" Condition="'$(_SdkSetAndroidResgenFile)' == 'true' And '$(AndroidResgenFile)' != '' And !Exists('$(AndroidResgenFile)')">
<ItemGroup>
<Compile Remove="$(AndroidResgenFile)"/>
</ItemGroup>
</Target>
<PropertyGroup>
<DesignTimeBuild>false</DesignTimeBuild>
</PropertyGroup>
</Project>

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

@ -11,7 +11,7 @@
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon> <PackageIcon>Icon.png</PackageIcon>
<PackageDescription>Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.</PackageDescription> <PackageDescription>Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOS and with experimental support for Android, iOS and WebAssembly.</PackageDescription>
<PackageTags>avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin</PackageTags> <PackageTags>avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin</PackageTags>
<PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>

3
build/System.Drawing.Common.props

@ -1,5 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="4.5.0" /> <PackageReference Condition="'$(TargetFramework)'!='netstandard2.0'" Include="System.Drawing.Common" Version="6.0.0" />
<PackageReference Condition="'$(TargetFramework)'=='netstandard2.0'" Include="System.Drawing.Common" Version="4.5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

11
dirs.proj

@ -1,5 +1,7 @@
<Project Sdk="Microsoft.Build.Traversal"> <Project Sdk="Microsoft.Build.Traversal">
<ItemGroup> <ItemGroup>
<!-- Build Avalonia.Build.Tasks first because everything depends on it -->
<ProjectReference Include="src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
<ProjectReference Include="src/**/*.*proj" /> <ProjectReference Include="src/**/*.*proj" />
<ProjectReference Include="samples/**/*.*proj" /> <ProjectReference Include="samples/**/*.*proj" />
<ProjectReference Include="tests/**/*.*proj" /> <ProjectReference Include="tests/**/*.*proj" />
@ -8,11 +10,8 @@
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" /> <ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" /> <ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
<ProjectReference Remove="tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj" /> <ProjectReference Remove="tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj" />
</ItemGroup>
<!--<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">-->
<ItemGroup>
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" /> <ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" />
<ProjectReference Remove="src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')"> <ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')">
<ProjectReference Remove="src/iOS/**/*.*proj" /> <ProjectReference Remove="src/iOS/**/*.*proj" />
@ -23,6 +22,10 @@
<ProjectReference Remove="samples/interop/**/*.*proj" /> <ProjectReference Remove="samples/interop/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" /> <ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</ItemGroup> </ItemGroup>
<!-- Build android projects only on Windows, where we have installed android workload -->
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows'))">
<ProjectReference Remove="src/Android/**/*.*proj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="SlnGen" Version="2.0.40" PrivateAssets="all" /> <PackageReference Include="SlnGen" Version="2.0.40" PrivateAssets="all" />

1
global.json

@ -4,6 +4,7 @@
}, },
"msbuild-sdks": { "msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43", "Microsoft.Build.Traversal": "1.0.43",
"Xamarin.Legacy.Sdk": "0.1.2-alpha6",
"MSBuild.Sdk.Extras": "3.0.22", "MSBuild.Sdk.Extras": "3.0.22",
"AggregatePackage.NuGet.Sdk" : "0.1.12" "AggregatePackage.NuGet.Sdk" : "0.1.12"
} }

5
nukebuild/Build.cs

@ -87,7 +87,8 @@ partial class Build : NukeBuild
Console.WriteLine(preamble); Console.WriteLine(preamble);
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit(); Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
} }
ExecWait("dotnet version:", "dotnet", "--version"); ExecWait("dotnet version:", "dotnet", "--info");
ExecWait("dotnet workloads:", "dotnet", "workload list");
} }
IReadOnlyCollection<Output> MsBuildCommon( IReadOnlyCollection<Output> MsBuildCommon(
@ -99,7 +100,7 @@ partial class Build : NukeBuild
// This is required for VS2019 image on Azure Pipelines // This is required for VS2019 image on Azure Pipelines
.When(Parameters.IsRunningOnWindows && .When(Parameters.IsRunningOnWindows &&
Parameters.IsRunningOnAzure, _ => _ Parameters.IsRunningOnAzure, _ => _
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64"))) .AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64")))
.AddProperty("PackageVersion", Parameters.Version) .AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true) .AddProperty("iOSRoslynPathHackRequired", true)
.SetProcessToolPath(MsBuildExe.Value) .SetProcessToolPath(MsBuildExe.Value)

23
readme.md

@ -3,13 +3,11 @@
<br /> <br />
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) [![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg)
## 📖 About AvaloniaUI ## 📖 About
Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOs. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
<img src="https://user-images.githubusercontent.com/6759207/84751662-7c79da00-afc5-11ea-8780-dda28db70b76.png" width="700" /> ![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia! To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
@ -28,18 +26,15 @@ Install-Package Avalonia.Desktop
## Showcase ## Showcase
Examples of UIs built with Avalonia Examples of UIs built with Avalonia
![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png) <video src="https://user-images.githubusercontent.com/4672627/152325602-28df36ec-6444-44a6-aebe-90ad52c8f27a.mp4"></video>
([Lunacy](https://icons8.com/lunacy))
([Synfonia](https://github.com/jmacato/Synfonia))
![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG) ![image](https://user-images.githubusercontent.com/4672627/152325740-261c27a3-e6f0-4662-bff7-4796d4940e04.png)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) ([PlasticSCM](https://www.plasticscm.com/))
![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG) ![image](https://user-images.githubusercontent.com/4672627/152326453-14944c4d-33da-4d50-a268-b87f80927adb.png)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) ([WasabiWallet](https://www.wasabiwallet.io/))
![image](https://user-images.githubusercontent.com/4672627/84708947-c3b98980-af37-11ea-8c9d-503334615bbf.png)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
## JetBrains Rider ## JetBrains Rider

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

@ -1,19 +0,0 @@
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".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

183
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -1,165 +1,46 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<ProductVersion>8.0.30703</ProductVersion> <OutputType>Exe</OutputType>
<SchemaVersion>2.0</SchemaVersion> <Nullable>enable</Nullable>
<ProjectGuid>{29132311-1848-4FD6-AE0C-4FF841151BD3}</ProjectGuid> <ApplicationId>com.Avalonia.ControlCatalog</ApplicationId>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ApplicationVersion>1</ApplicationVersion>
<OutputType>Library</OutputType> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AppDesignerFolder>Properties</AppDesignerFolder> <AndroidPackageFormat>apk</AndroidPackageFormat>
<RootNamespace>ControlCatalog.Android</RootNamespace> <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<AssemblyName>ControlCatalog.Android</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<EnableProguard>False</EnableProguard>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>Full</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<EnableProguard>False</EnableProguard>
<DebugSymbols>False</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SplashActivity.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Resources\AboutResources.txt" /> <None Remove="Assets\AboutAssets.txt" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\values\styles.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="..\..\build\Assets\Icon.png"> <AndroidResource Include="..\..\build\Assets\Icon.png">
<Link>Resources\drawable\Icon.png</Link> <Link>Resources\drawable\Icon.png</Link>
</AndroidResource> </AndroidResource>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols>
<RunAOTCompilation>False</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
<RunAOTCompilation>False</RunAOTCompilation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Properties\AndroidManifest.xml" /> <PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj"> <ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />
<Project>{7B92AF71-6287-4693-9DCB-BD5B6E927E23}</Project> <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<Name>Avalonia.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj">
<Project>{c42d2fc1-a531-4ed4-84b9-89aec7c962fc}</Project>
<Name>Avalonia.Themes.Fluent</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
<Name>Avalonia.Markup</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj">
<Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>
<Name>ControlCatalog</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\build\AndroidWorkarounds.props" />
<Import Project="..\..\build\LegacyProject.targets" />
</Project> </Project>

5
samples/ControlCatalog.Android/MainActivity.cs

@ -5,10 +5,10 @@ using Avalonia.Android;
namespace ControlCatalog.Android namespace ControlCatalog.Android
{ {
[Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)] [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class MainActivity : AvaloniaActivity public class MainActivity : AvaloniaActivity
{ {
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle? savedInstanceState)
{ {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
@ -16,4 +16,3 @@ namespace ControlCatalog.Android
} }
} }
} }

7
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ControlCatalog.Android" android:versionCode="1" android:versionName="1.0" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
<uses-sdk android:targetSdkVersion="30" /> <application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
<application android:label="ControlCatalog.Android"></application> </manifest>
</manifest>

30
samples/ControlCatalog.Android/Properties/AssemblyInfo.cs

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ControlCatalog.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ControlCatalog.Android")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

101
samples/ControlCatalog.Android/Resources/Resource.Designer.cs

@ -1,101 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("ControlCatalog.Android.Resource", IsApplication=true)]
namespace ControlCatalog.Android
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Color
{
// aapt resource value: 0x7F010000
public const int splash_background = 2130771968;
static Color()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Color()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F020000
public const int Icon = 2130837504;
// aapt resource value: 0x7F020001
public const int splash_screen = 2130837505;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class Style
{
// aapt resource value: 0x7F030000
public const int MyTheme = 2130903040;
// aapt resource value: 0x7F030001
public const int MyTheme_NoActionBar = 2130903041;
// aapt resource value: 0x7F030002
public const int MyTheme_Splash = 2130903042;
static Style()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Style()
{
}
}
}
}
#pragma warning restore 1591

2
samples/ControlCatalog.Android/Resources/values/styles.xml

@ -4,7 +4,7 @@
<style name="MyTheme"> <style name="MyTheme">
</style> </style>
<style name="MyTheme.NoActionBar"> <style name="MyTheme.NoActionBar" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowActionBar">false</item> <item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>
</style> </style>

2
samples/ControlCatalog.Android/SplashActivity.cs

@ -10,7 +10,7 @@ namespace ControlCatalog.Android
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)] [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : Activity public class SplashActivity : Activity
{ {
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle? savedInstanceState)
{ {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
} }

14
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -6,6 +6,12 @@
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(RunAotCompilation)' == 'true'">
<IlcTrimMetadata>true</IlcTrimMetadata>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json</RestoreAdditionalProjectSources>
<NativeAotCompilerVersion>7.0.0-*</NativeAotCompilerVersion>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
@ -15,6 +21,14 @@
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" /> <PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RunAotCompilation)' == 'true'">
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<!-- Cross-compilation for Windows x64-arm64 and Linux x64-arm64 -->
<PackageReference Condition="'$(RuntimeIdentifier)'=='win-arm64'" Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<PackageReference Condition="'$(RuntimeIdentifier)'=='linux-arm64'" Include="runtime.linux-x64.Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<RdXmlFile Include="rd.xml" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<!-- For Microsoft.CodeAnalysis --> <!-- For Microsoft.CodeAnalysis -->
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>

7
samples/ControlCatalog.NetCore/rd.xml

@ -0,0 +1,7 @@
<Directives>
<Application>
<Assembly Name="ControlCatalog" Dynamic="Required All"></Assembly>
<Assembly Name="Avalonia.Themes.Default" Dynamic="Required All"></Assembly>
<Assembly Name="Avalonia.Themes.Fluent" Dynamic="Required All"></Assembly>
</Application>
</Directives>

1
samples/ControlCatalog.Web/ControlCatalog.Web.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<WasmBuildNative>True</WasmBuildNative> <WasmBuildNative>True</WasmBuildNative>
</PropertyGroup> </PropertyGroup>

7
samples/ControlCatalog/MainView.xaml

@ -22,8 +22,8 @@
<TabItem Header="Border"> <TabItem Header="Border">
<pages:BorderPage /> <pages:BorderPage />
</TabItem> </TabItem>
<TabItem Header="Button"> <TabItem Header="Buttons">
<pages:ButtonPage /> <pages:ButtonsPage />
</TabItem> </TabItem>
<TabItem Header="ButtonSpinner"> <TabItem Header="ButtonSpinner">
<pages:ButtonSpinnerPage /> <pages:ButtonSpinnerPage />
@ -40,6 +40,9 @@
<TabItem Header="CheckBox"> <TabItem Header="CheckBox">
<pages:CheckBoxPage /> <pages:CheckBoxPage />
</TabItem> </TabItem>
<TabItem Header="Clipboard">
<pages:ClipboardPage />
</TabItem>
<TabItem Header="ComboBox"> <TabItem Header="ComboBox">
<pages:ComboBoxPage /> <pages:ComboBoxPage />
</TabItem> </TabItem>

45
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -1,45 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A button control</TextBlock>
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8" Width="200">
<Button>Standard _XAML Button</Button>
<Button Foreground="White">Foreground</Button>
<Button Background="{DynamicResource SystemAccentColor}">Background</Button>
<Button IsEnabled="False">Disabled</Button>
<Button Content="Re-themed">
<Button.Styles>
<Style>
<Style.Resources>
<SolidColorBrush x:Key="ThemeBorderMidBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighBrush">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBorderBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackground">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPressed">OrangeRed</SolidColorBrush>
</Style.Resources>
</Style>
</Button.Styles>
</Button>
<RepeatButton Name="RepeatButton">
<TextBlock Name="RepeatButtonTextBlock" Text="Repeat Button: 0" />
</RepeatButton>
<ToggleButton Content="Toggle Button"/>
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8" Width="150">
<Button BorderThickness="0">No Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}">Border Color</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="4">Thick Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="4" IsEnabled="False">Disabled</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}" KeyboardNavigation.IsTabStop="False">IsTabStop=False</Button>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

224
samples/ControlCatalog/Pages/ButtonsPage.xaml

@ -0,0 +1,224 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonsPage">
<UserControl.Resources>
<MenuFlyout x:Key="SharedMenuFlyout"
Placement="Bottom">
<MenuItem Header="Item 1">
<MenuItem Header="Subitem 1" />
<MenuItem Header="Subitem 2" />
<MenuItem Header="Subitem 3" />
</MenuItem>
<MenuItem Header="Item 2"
InputGesture="Ctrl+A" />
<MenuItem Header="Item 3" />
</MenuFlyout>
</UserControl.Resources>
<UserControl.Styles >
<Style Selector="Border.header-border">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Gray" Opacity="0.5" />
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="0.5" />
<Setter Property="CornerRadius" Value="5,5,0,0" />
<Setter Property="MaxWidth" Value="450" />
<Setter Property="Padding" Value="10" />
</Style>
<Style Selector="TextBlock.header">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="Border.thin">
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="0.5" />
<Setter Property="CornerRadius" Value="0,0,5,5" />
<Setter Property="Margin" Value="0,0,0,15" />
</Style>
</UserControl.Styles>
<!-- Styles and overall page design based on AcrylicPage -->
<StackPanel Orientation="Vertical"
Width="450">
<!-- Button -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="Button" Classes="header" />
<TextBlock TextWrapping="Wrap">A standard button control</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="10">
<StackPanel Orientation="Vertical"
Spacing="8"
Width="200">
<Button>Standard _XAML Button</Button>
<Button Foreground="White">Foreground</Button>
<Button Background="{DynamicResource SystemAccentColor}">Background</Button>
<Button IsEnabled="False">Disabled</Button>
<Button Content="Re-themed">
<Button.Styles>
<Style>
<Style.Resources>
<SolidColorBrush x:Key="ThemeBorderMidBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighBrush">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBorderBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackground">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPressed">OrangeRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonForeground">White</SolidColorBrush>
<SolidColorBrush x:Key="ButtonForegroundPointerOver">Black</SolidColorBrush>
<SolidColorBrush x:Key="ButtonForegroundPressed">Black</SolidColorBrush>
</Style.Resources>
</Style>
</Button.Styles>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical"
Spacing="8"
Width="200">
<Button BorderThickness="0">No Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}">Border Color</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="4">Thick Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="4"
IsEnabled="False">Disabled</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}"
KeyboardNavigation.IsTabStop="False">IsTabStop=False</Button>
</StackPanel>
</StackPanel>
</Border>
<!-- ToggleButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="ToggleButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button control with multiple states: checked, unchecked or indeterminate.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<ToggleButton Content="Toggle Button" />
</StackPanel>
</Border>
<!-- RepeatButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="RepeatButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button control that raises its Click event repeatedly when it is pressed and held.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<RepeatButton Name="RepeatButton">
<TextBlock Name="RepeatButtonTextBlock"
Text="Repeat Button: 0" />
</RepeatButton>
</StackPanel>
</Border>
<!-- SplitButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="SplitButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a Button and the secondary part opens a flyout.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<SplitButton Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Content" />
</SplitButton>
<SplitButton IsEnabled="False"
Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Disabled" />
</SplitButton>
<SplitButton Flyout="{StaticResource SharedMenuFlyout}"
Content="Re-themed">
<SplitButton.Styles>
<Style>
<Style.Resources>
<x:Double x:Key="SplitButtonSeparatorWidth">1.5</x:Double>
<Thickness x:Key="SplitButtonBorderThemeThickness">2</Thickness>
<SolidColorBrush x:Key="SplitButtonBorderBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBorderBrushPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBorderBrushPressed">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBackground">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBackgroundPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBackgroundPressed">OrangeRed</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonForeground">White</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonForegroundPointerOver">Black</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonForegroundPressed">Black</SolidColorBrush>
</Style.Resources>
</Style>
</SplitButton.Styles>
</SplitButton>
</StackPanel>
</Border>
<!-- ToggleSplitButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="ToggleSplitButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a ToggleButton with two states and the secondary part opens a flyout.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<ToggleSplitButton Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Content" />
</ToggleSplitButton>
<ToggleSplitButton IsChecked="True"
Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Content" />
</ToggleSplitButton>
<ToggleSplitButton IsChecked="True"
IsEnabled="False"
Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Disabled" />
</ToggleSplitButton>
</StackPanel>
</Border>
</StackPanel>
</UserControl>

4
samples/ControlCatalog/Pages/ButtonPage.xaml.cs → samples/ControlCatalog/Pages/ButtonsPage.xaml.cs

@ -3,11 +3,11 @@ using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
public class ButtonPage : UserControl public class ButtonsPage : UserControl
{ {
private int repeatButtonClickCount = 0; private int repeatButtonClickCount = 0;
public ButtonPage() public ButtonsPage()
{ {
InitializeComponent(); InitializeComponent();

23
samples/ControlCatalog/Pages/ClipboardPage.xaml

@ -0,0 +1,23 @@
<UserControl x:Class="ControlCatalog.Pages.ClipboardPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">Example of ClipboardPage capabilities</TextBlock>
<Button Click="CopyText" Content="Copy text to clipboard" />
<Button Click="PasteText" Content="Paste text from clipboard" />
<Button Click="CopyTextDataObject" Content="Copy text to clipboard (data object)" />
<Button Click="PasteTextDataObject" Content="Paste text from clipboard (data object)" />
<Button Click="CopyFilesDataObject" Content="Copy files to clipboard (data object)" />
<Button Click="PasteFilesDataObject" Content="Paste files from clipboard (data object)" />
<Button Click="GetFormats" Content="Get clipboard formats" />
<Button Click="Clear" Content="Clear clipboard" />
<TextBox x:Name="ClipboardContent"
MinHeight="100"
AcceptsReturn="True"
Watermark="Text to copy of file names per line" />
</StackPanel>
</UserControl>

77
samples/ControlCatalog/Pages/ClipboardPage.xaml.cs

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public partial class ClipboardPage : UserControl
{
public ClipboardPage()
{
InitializeComponent();
}
private TextBox ClipboardContent => this.Get<TextBox>("ClipboardContent");
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private async void CopyText(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.SetTextAsync(ClipboardContent.Text);
}
private async void PasteText(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetTextAsync();
}
private async void CopyTextDataObject(object sender, RoutedEventArgs args)
{
var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteTextDataObject(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
}
private async void CopyFilesDataObject(object sender, RoutedEventArgs args)
{
var files = ClipboardContent.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
if (files.Length == 0)
{
return;
}
var dataObject = new DataObject();
dataObject.Set(DataFormats.FileNames, files);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteFilesDataObject(object sender, RoutedEventArgs args)
{
var fiels = await Application.Current.Clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>;
ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
}
private async void GetFormats(object sender, RoutedEventArgs args)
{
var formats = await Application.Current.Clipboard.GetFormatsAsync();
ClipboardContent.Text = string.Join(Environment.NewLine, formats);
}
private async void Clear(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.ClearAsync();
}
}
}

160
samples/ControlCatalog/Pages/ComboBoxPage.xaml

@ -1,77 +1,95 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.ComboBoxPage"
x:Class="ControlCatalog.Pages.ComboBoxPage" xmlns="https://github.com/avaloniaui"
xmlns:sys="using:System" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:col="using:System.Collections"> xmlns:col="using:System.Collections"
<StackPanel Orientation="Vertical" Spacing="4"> xmlns:sys="using:System">
<TextBlock Classes="h2">A drop-down list.</TextBlock> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A drop-down list.</TextBlock>
<WrapPanel HorizontalAlignment="Center" Margin="0 16 0 0" <StackPanel
MaxWidth="660"> Margin="0,16,0,0"
<WrapPanel.Styles> HorizontalAlignment="Center"
<Style Selector="ComboBox"> Orientation="Horizontal"
<Setter Property="Width" Value="250" /> Spacing="8">
<Setter Property="Margin" Value="10" /> <WrapPanel
</Style> MaxWidth="660"
</WrapPanel.Styles> Margin="0,16,0,0"
<ComboBox PlaceholderText="Pick an Item"> HorizontalAlignment="Center">
<ComboBoxItem>Inline Items</ComboBoxItem> <WrapPanel.Styles>
<ComboBoxItem>Inline Item 2</ComboBoxItem> <Style Selector="ComboBox">
<ComboBoxItem>Inline Item 3</ComboBoxItem> <Setter Property="Width" Value="250" />
<ComboBoxItem>Inline Item 4</ComboBoxItem> <Setter Property="Margin" Value="10" />
</ComboBox> </Style>
</WrapPanel.Styles>
<ComboBox> <ComboBox PlaceholderText="Pick an Item" WrapSelection="{Binding WrapSelection}">
<ComboBox.Items> <ComboBoxItem>Inline Items</ComboBoxItem>
<col:ArrayList> <ComboBoxItem>Inline Item 2</ComboBoxItem>
<x:Null /> <ComboBoxItem>Inline Item 3</ComboBoxItem>
<sys:String>Hello</sys:String> <ComboBoxItem>Inline Item 4</ComboBoxItem>
<sys:String>World</sys:String> </ComboBox>
</col:ArrayList>
</ComboBox.Items>
<ComboBox.ItemTemplate>
<DataTemplate>
<Panel>
<TextBlock Text="{Binding}" />
<TextBlock Text="Null object" IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" />
</Panel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox SelectedIndex="0"> <ComboBox WrapSelection="{Binding WrapSelection}">
<ComboBoxItem> <ComboBox.Items>
<Panel> <col:ArrayList>
<Rectangle Fill="{DynamicResource SystemAccentColor}"/> <x:Null />
<TextBlock Margin="8">Control Items</TextBlock> <sys:String>Hello</sys:String>
</Panel> <sys:String>World</sys:String>
</ComboBoxItem> </col:ArrayList>
<ComboBoxItem> </ComboBox.Items>
<Ellipse Width="50" Height="50" Fill="Yellow"/> <ComboBox.ItemTemplate>
</ComboBoxItem> <DataTemplate>
<ComboBoxItem> <Panel>
<TextBox Text="TextBox"/> <TextBlock Text="{Binding}" />
</ComboBoxItem> <TextBlock IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" Text="Null object" />
</ComboBox> </Panel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox x:Name="fontComboBox" SelectedIndex="0"> <ComboBox SelectedIndex="0" WrapSelection="{Binding WrapSelection}">
<ComboBox.ItemTemplate> <ComboBoxItem>
<DataTemplate> <Panel>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" /> <Rectangle Fill="{DynamicResource SystemAccentColor}" />
</DataTemplate> <TextBlock Margin="8">Control Items</TextBlock>
</ComboBox.ItemTemplate> </Panel>
</ComboBox> </ComboBoxItem>
<ComboBoxItem>
<ComboBox PlaceholderText="Pick an Item"> <Ellipse
<ComboBoxItem>Inline Items</ComboBoxItem> Width="50"
<ComboBoxItem>Inline Item 2</ComboBoxItem> Height="50"
<ComboBoxItem>Inline Item 3</ComboBoxItem> Fill="Yellow" />
<ComboBoxItem>Inline Item 4</ComboBoxItem> </ComboBoxItem>
<DataValidationErrors.Error> <ComboBoxItem>
<sys:Exception /> <TextBox Text="TextBox" />
</DataValidationErrors.Error> </ComboBoxItem>
</ComboBox> </ComboBox>
</WrapPanel>
</StackPanel> <ComboBox
x:Name="fontComboBox"
SelectedIndex="0"
WrapSelection="{Binding WrapSelection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock FontFamily="{Binding}" Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox PlaceholderText="Pick an Item" WrapSelection="{Binding WrapSelection}">
<ComboBoxItem>Inline Items</ComboBoxItem>
<ComboBoxItem>Inline Item 2</ComboBoxItem>
<ComboBoxItem>Inline Item 3</ComboBoxItem>
<ComboBoxItem>Inline Item 4</ComboBoxItem>
<DataValidationErrors.Error>
<sys:Exception />
</DataValidationErrors.Error>
</ComboBox>
</WrapPanel>
<CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox>
</StackPanel>
</StackPanel>
</UserControl> </UserControl>

2
samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs

@ -2,6 +2,7 @@ using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -10,6 +11,7 @@ namespace ControlCatalog.Pages
public ComboBoxPage() public ComboBoxPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
DataContext = new ComboBoxPageViewModel();
} }
private void InitializeComponent() private void InitializeComponent()

8
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -11,7 +11,13 @@
Padding="16" Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}" BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2"> BorderThickness="2">
<TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me</TextBlock> <TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me (text)</TextBlock>
</Border>
<Border Name="DragMeFiles"
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
<TextBlock Name="DragStateFiles" TextWrapping="Wrap">Drag Me (files)</TextBlock>
</Border> </Border>
<Border Name="DragMeCustom" <Border Name="DragMeCustom"
Padding="16" Padding="16"

4
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -2,11 +2,8 @@
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -24,6 +21,7 @@ namespace ControlCatalog.Pages
$"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link); $"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move); SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
SetupDnd("Files", d => d.Set(DataFormats.FileNames, new[] { Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }), DragDropEffects.Copy);
} }
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects) void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)

2
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -111,7 +111,7 @@ namespace ControlCatalog.Pages
private void ScrollTo(int index) private void ScrollTo(int index)
{ {
System.Diagnostics.Debug.WriteLine("Scroll to " + index); System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((Window)this.GetVisualRoot()).LayoutManager; var layoutManager = ((TopLevel)VisualRoot).LayoutManager;
var element = _repeater.GetOrCreateElement(index); var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass(); layoutManager.ExecuteLayoutPass();
element.BringIntoView(); element.BringIntoView();

4
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -21,6 +21,7 @@
<CheckBox IsChecked="{Binding Toggle}">Toggle</CheckBox> <CheckBox IsChecked="{Binding Toggle}">Toggle</CheckBox>
<CheckBox IsChecked="{Binding AlwaysSelected}">AlwaysSelected</CheckBox> <CheckBox IsChecked="{Binding AlwaysSelected}">AlwaysSelected</CheckBox>
<CheckBox IsChecked="{Binding AutoScrollToSelectedItem}">AutoScrollToSelectedItem</CheckBox> <CheckBox IsChecked="{Binding AutoScrollToSelectedItem}">AutoScrollToSelectedItem</CheckBox>
<CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox>
</StackPanel> </StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Margin="4"> <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Margin="4">
<Button Command="{Binding AddItemCommand}">Add</Button> <Button Command="{Binding AddItemCommand}">Add</Button>
@ -30,6 +31,7 @@
<ListBox Items="{Binding Items}" <ListBox Items="{Binding Items}"
Selection="{Binding Selection}" Selection="{Binding Selection}"
AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}" AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}"
SelectionMode="{Binding SelectionMode^}"/> SelectionMode="{Binding SelectionMode^}"
WrapSelection="{Binding WrapSelection}"/>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

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);
}
} }
} }

2
samples/ControlCatalog/Pages/ToggleSwitchPage.xaml

@ -11,7 +11,7 @@
</StackPanel> </StackPanel>
</Border> </Border>
<TextBlock Text="headered ToggleSwitch" Classes="header"/> <TextBlock Text="Headered ToggleSwitch" Classes="header"/>
<Border Classes="Thin"> <Border Classes="Thin">
<StackPanel> <StackPanel>

21
samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Avalonia.Controls;
using Avalonia.Controls.Selection;
using MiniMvvm;
namespace ControlCatalog.ViewModels
{
public class ComboBoxPageViewModel : ViewModelBase
{
private bool _wrapSelection;
public bool WrapSelection
{
get => _wrapSelection;
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
}
}
}

7
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@ -14,6 +14,7 @@ namespace ControlCatalog.ViewModels
private bool _toggle; private bool _toggle;
private bool _alwaysSelected; private bool _alwaysSelected;
private bool _autoScrollToSelectedItem = true; private bool _autoScrollToSelectedItem = true;
private bool _wrapSelection;
private int _counter; private int _counter;
private IObservable<SelectionMode> _selectionMode; private IObservable<SelectionMode> _selectionMode;
@ -85,6 +86,12 @@ namespace ControlCatalog.ViewModels
set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value); set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value);
} }
public bool WrapSelection
{
get => _wrapSelection;
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
}
public MiniCommand AddItemCommand { get; } public MiniCommand AddItemCommand { get; }
public MiniCommand RemoveItemCommand { get; } public MiniCommand RemoveItemCommand { get; }
public MiniCommand SelectRandomItemCommand { get; } public MiniCommand SelectRandomItemCommand { get; }

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>

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);

4
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -25,7 +25,7 @@ namespace Avalonia.Android
_host.Focusable = true; _host.Focusable = true;
_host.FocusableInTouchMode = true; _host.FocusableInTouchMode = true;
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListner(_host)); _host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListener(_host));
} }
public void Reset() public void Reset()
@ -83,6 +83,8 @@ namespace Avalonia.Android
if (options.Multiline) if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine; outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi;
}); });
//_inputElement.PointerReleased += RestoreSoftKeyboard; //_inputElement.PointerReleased += RestoreSoftKeyboard;

9
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -10,7 +10,6 @@ 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.PlatformSupport;
using Avalonia.Skia; using Avalonia.Skia;
namespace Avalonia namespace Avalonia
@ -20,9 +19,9 @@ namespace Avalonia
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new() public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{ {
var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions(); var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
builder.UseWindowingSubsystem(() => AndroidPlatform.Initialize(builder.ApplicationType, options), "Android"); return builder
builder.UseSkia(); .UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android")
return builder; .UseSkia();
} }
} }
} }
@ -44,7 +43,7 @@ namespace Avalonia.Android
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500); public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500);
public static void Initialize(Type appType, AndroidPlatformOptions options) public static void Initialize(AndroidPlatformOptions options)
{ {
Options = options; Options = options;

14
src/Android/Avalonia.Android/AppBuilder.cs

@ -1,14 +0,0 @@
using Avalonia.Controls;
using Avalonia.PlatformSupport;
namespace Avalonia
{
public sealed class AppBuilder : AppBuilderBase<AppBuilder>
{
public AppBuilder() : base(new StandardRuntimePlatform(),
builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType()?.Assembly))
{
}
}
}

17
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -1,15 +1,18 @@
<Project Sdk="MSBuild.Sdk.Extras"> <Project Sdk="Xamarin.Legacy.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>monoandroid11.0</TargetFramework> <TargetFrameworks>net6.0-android;monoandroid11.0</TargetFrameworks>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj"> <PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework> <PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ProjectReference> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> <ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
</Project> </Project>

36
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -1,35 +1,55 @@
using Android.App; using Android.App;
using Android.OS; using Android.OS;
using Android.Views; using Android.Views;
using Android.Content.PM;
using AndroidX.AppCompat.App;
using Android.Content.Res;
using AndroidX.Lifecycle;
namespace Avalonia.Android namespace Avalonia.Android
{ {
public abstract class AvaloniaActivity : Activity public abstract class AvaloniaActivity : AppCompatActivity
{ {
internal AvaloniaView View; internal AvaloniaView View;
object _content; internal AvaloniaViewModel _viewModel;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
View = new AvaloniaView(this); View = new AvaloniaView(this);
if (_content != null)
View.Content = _content;
SetContentView(View); SetContentView(View);
_viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
if (_viewModel.Content != null)
{
View.Content = _viewModel.Content;
}
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
} }
public object Content public object Content
{ {
get get
{ {
return _content; return _viewModel.Content;
} }
set set
{ {
_content = value; _viewModel.Content = value;
if (View != null) if (View != null)
View.Content = value; View.Content = value;
} }
} }
public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
}
protected override void OnDestroy()
{
View.Content = null;
base.OnDestroy();
}
} }
} }

11
src/Android/Avalonia.Android/AvaloniaViewModel.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Android
{
internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel
{
public object Content { get; set; }
}
}

50
src/Android/Avalonia.Android/Resources/AboutResources.txt

@ -1,50 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable-hdpi/
icon.png
drawable-ldpi/
icon.png
drawable-mdpi/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called
"Resource" that contains the tokens for each one of the resources included. For example,
for the above Resources layout, this is what the Resource class would expose:
public class Resource {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

6
src/Android/Avalonia.Android/Resources/Values/Strings.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Hello">Hello World, Click Me!</string>
<string name="ApplicationName">$projectname$</string>
</resources>

4
src/Android/Avalonia.Android/SoftKeyboardListner.cs → src/Android/Avalonia.Android/SoftKeyboardListener.cs

@ -9,7 +9,7 @@ using Avalonia.Input;
namespace Avalonia.Android namespace Avalonia.Android
{ {
class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener class SoftKeyboardListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{ {
private const int DefaultKeyboardHeightDP = 100; private const int DefaultKeyboardHeightDP = 100;
private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0); private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0);
@ -17,7 +17,7 @@ namespace Avalonia.Android
private readonly View _host; private readonly View _host;
private bool _wasKeyboard; private bool _wasKeyboard;
public SoftKeyboardListner(View view) public SoftKeyboardListener(View view)
{ {
_host = view; _host = view;
} }

19
src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt

@ -1,19 +0,0 @@
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".
These files will be deployed with you package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

172
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -1,153 +1,37 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<ProductVersion>8.0.30703</ProductVersion> <OutputType>Exe</OutputType>
<SchemaVersion>2.0</SchemaVersion> <Nullable>enable</Nullable>
<ProjectGuid>{FF69B927-C545-49AE-8E16-3D14D621AA12}</ProjectGuid> <ApplicationId>com.Avalonia.AndroidTestApplication</ApplicationId>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ApplicationVersion>1</ApplicationVersion>
<OutputType>Library</OutputType> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AppDesignerFolder>Properties</AppDesignerFolder> <AndroidPackageFormat>apk</AndroidPackageFormat>
<RootNamespace>Avalonia.AndroidTestApplication</RootNamespace> <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<AssemblyName>Avalonia.AndroidTestApplication</AssemblyName> <DebugType>portable</DebugType>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType> <RunAOTCompilation>True</RunAOTCompilation>
<Optimize>false</Optimize> <EnableLLVM>True</EnableLLVM>
<OutputPath>bin\Debug\</OutputPath> <!--<AndroidEnableProfiledAot>True</AndroidEnableProfiledAot>-->
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a;x86</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<DevInstrumentationEnabled>True</DevInstrumentationEnabled>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<EnableProguard>False</EnableProguard>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>Full</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<EnableProguard>False</EnableProguard>
<DevInstrumentationEnabled>False</DevInstrumentationEnabled>
<DebugSymbols>False</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\values\Strings.xml"> <None Remove="Assets\AboutAssets.txt" />
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\Icon.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj"> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<Project>{7b92af71-6287-4693-9dcb-bd5b6e927e23}</Project> <ProjectReference Include="..\Avalonia.Android\Avalonia.Android.csproj" />
<Name>Avalonia.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\..\build\Base.props" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\System.Memory.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
<Import Project="..\..\..\build\LegacyProject.targets" />
</Project> </Project>

2
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

@ -14,6 +14,8 @@ namespace Avalonia.AndroidTestApplication
[Activity(Label = "Main", [Activity(Label = "Main",
MainLauncher = true, MainLauncher = true,
Icon = "@drawable/icon", Icon = "@drawable/icon",
Theme = "@style/Theme.AppCompat.NoActionBar",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,
LaunchMode = LaunchMode.SingleInstance/*, LaunchMode = LaunchMode.SingleInstance/*,
ScreenOrientation = ScreenOrientation.Landscape*/)] ScreenOrientation = ScreenOrientation.Landscape*/)]
public class MainBaseActivity : AvaloniaActivity public class MainBaseActivity : AvaloniaActivity

6
src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="Avalonia.AndroidTestApplication" android:versionCode="1" android:versionName="1.0" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:targetSdkVersion="30" />
<application android:label="Avalonia.AndroidTestApplication" android:icon="@drawable/Icon" android:hardwareAccelerated="true"></application> <application android:label="Avalonia.AndroidTestApplication" android:icon="@drawable/Icon" android:hardwareAccelerated="true"></application>
<uses-permission android:name="android.permission.INTERNET" /> </manifest>
</manifest>

30
src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.AndroidTestApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Avalonia.AndroidTestApplication")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

79
src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs

@ -1,79 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Avalonia.AndroidTestApplication.Resource", IsApplication=true)]
namespace Avalonia.AndroidTestApplication
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F010000
public const int Icon = 2130771968;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class String
{
// aapt resource value: 0x7F020000
public const int ApplicationName = 2130837504;
// aapt resource value: 0x7F020001
public const int Hello = 2130837505;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
}
}
#pragma warning restore 1591

11
src/Android/Avalonia.AndroidTestApplication/app.config

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices.WindowsRuntime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

33
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -22,19 +22,9 @@ namespace Avalonia.Data.Core.Plugins
var method = GetFirstMethodWithName(instance.GetType(), methodName); var method = GetFirstMethodWithName(instance.GetType(), methodName);
if (method != null) if (method is not null)
{ {
var parameters = method.GetParameters(); return new Accessor(reference, method);
if (parameters.Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8)
{
var exception = new ArgumentException(
"Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.",
nameof(methodName));
return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
}
return new Accessor(reference, method, parameters);
} }
else else
{ {
@ -82,18 +72,20 @@ namespace Avalonia.Data.Core.Plugins
private sealed class Accessor : PropertyAccessorBase private sealed class Accessor : PropertyAccessorBase
{ {
public Accessor(WeakReference<object?> reference, MethodInfo method, ParameterInfo[] parameters) public Accessor(WeakReference<object?> reference, MethodInfo method)
{ {
_ = reference ?? throw new ArgumentNullException(nameof(reference)); _ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = method ?? throw new ArgumentNullException(nameof(method)); _ = method ?? throw new ArgumentNullException(nameof(method));
var returnType = method.ReturnType; var returnType = method.ReturnType;
bool hasReturn = returnType != typeof(void);
var signatureTypeCount = (hasReturn ? 1 : 0) + parameters.Length; var parameters = method.GetParameters();
var signatureTypeCount = parameters.Length + 1;
var paramTypes = new Type[signatureTypeCount]; var paramTypes = new Type[signatureTypeCount];
for (var i = 0; i < parameters.Length; i++) for (var i = 0; i < parameters.Length; i++)
{ {
ParameterInfo parameter = parameters[i]; ParameterInfo parameter = parameters[i];
@ -101,16 +93,9 @@ namespace Avalonia.Data.Core.Plugins
paramTypes[i] = parameter.ParameterType; paramTypes[i] = parameter.ParameterType;
} }
if (hasReturn) paramTypes[paramTypes.Length - 1] = returnType;
{
paramTypes[paramTypes.Length - 1] = returnType;
PropertyType = Expression.GetFuncType(paramTypes); PropertyType = Expression.GetDelegateType(paramTypes);
}
else
{
PropertyType = Expression.GetActionType(paramTypes);
}
if (method.IsStatic) if (method.IsStatic)
{ {

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;

7
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -124,6 +124,9 @@ namespace Avalonia.Build.Tasks
var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure", var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object); TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(indexerAccessorClosure); asm.MainModule.Types.Add(indexerAccessorClosure);
var trampolineBuilder = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlTrampolines",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(trampolineBuilder);
var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem); var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem);
var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem, var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
@ -133,6 +136,7 @@ namespace Avalonia.Build.Tasks
AvaloniaXamlIlLanguage.CustomValueConverter, AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)), new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)), new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)),
new XamlIlTrampolineBuilder(typeSystem.CreateTypeBuilder(trampolineBuilder)),
new DeterministicIdGenerator()); new DeterministicIdGenerator());
@ -256,7 +260,8 @@ namespace Avalonia.Build.Tasks
true), true),
(closureName, closureBaseType) => (closureName, closureBaseType) =>
populateBuilder.DefineSubType(closureBaseType, closureName, false), populateBuilder.DefineSubType(closureBaseType, closureName, false),
(s, returnType, parameters) => builder.DefineDelegateSubType(s, false, returnType, parameters), (closureName, returnType, parameterTypes) =>
populateBuilder.DefineDelegateSubType(closureName, false, returnType, parameterTypes),
res.Uri, res res.Uri, res
); );

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); }

34
src/Avalonia.Controls/Button.cs

@ -42,21 +42,21 @@ 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="IsDefault"/> property. /// Defines the <see cref="IsDefault"/> property.
@ -85,12 +85,12 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Flyout"/> property /// Defines the <see cref="Flyout"/> property
/// </summary> /// </summary>
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.
@ -112,7 +112,7 @@ 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);
@ -130,7 +130,7 @@ namespace Avalonia.Controls
/// <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 => _command; get => _command;
set => SetAndRaise(CommandProperty, ref _command, value); set => SetAndRaise(CommandProperty, ref _command, value);
@ -139,7 +139,7 @@ namespace Avalonia.Controls
/// <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 => GetValue(HotKeyProperty); get => GetValue(HotKeyProperty);
set => SetValue(HotKeyProperty, value); set => SetValue(HotKeyProperty, value);
@ -148,7 +148,7 @@ namespace Avalonia.Controls
/// <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 => GetValue(CommandParameterProperty); get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value); set => SetValue(CommandParameterProperty, value);
@ -186,7 +186,7 @@ namespace Avalonia.Controls
/// <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
{ {
get => GetValue(FlyoutProperty); get => GetValue(FlyoutProperty);
set => SetValue(FlyoutProperty, value); set => SetValue(FlyoutProperty, value);
@ -477,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);
@ -529,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)
{ {
@ -542,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)
{ {

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

90
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>(
@ -186,7 +186,8 @@ namespace Avalonia.Controls
nameof(SelectedDate), nameof(SelectedDate),
o => o.SelectedDate, o => o.SelectedDate,
(o, v) => o.SelectedDate = v, (o, v) => o.SelectedDate = v,
enableDataValidation: true); enableDataValidation: true,
defaultBindingMode:BindingMode.TwoWay);
public static readonly StyledProperty<CalendarDatePickerFormat> SelectedDateFormatProperty = public static readonly StyledProperty<CalendarDatePickerFormat> SelectedDateFormatProperty =
AvaloniaProperty.Register<CalendarDatePicker, CalendarDatePickerFormat>( AvaloniaProperty.Register<CalendarDatePicker, CalendarDatePickerFormat>(
@ -200,12 +201,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 +362,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 +402,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 +580,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 +661,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 +680,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 +732,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 +760,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 +797,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 +805,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 +820,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 +840,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 +855,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 +891,7 @@ namespace Avalonia.Controls
private void OpenPopUp() private void OpenPopUp()
{ {
_onOpenSelectedDate = SelectedDate; _onOpenSelectedDate = SelectedDate;
_popUp.IsOpen = true; _popUp!.IsOpen = true;
} }
/// <summary> /// <summary>
@ -914,7 +914,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 +941,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 +982,7 @@ namespace Avalonia.Controls
{ {
SetSelectedDate(); SetSelectedDate();
IsDropDownOpen = true; IsDropDownOpen = true;
_calendar.Focus(); _calendar!.Focus();
} }
private void SetSelectedDate() private void SetSelectedDate()
{ {
@ -1001,7 +1001,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 +1053,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;
} }
} }

65
src/Avalonia.Controls/ComboBox.cs

@ -10,9 +10,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Controls namespace Avalonia.Controls
@ -46,8 +44,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 +56,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 +78,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>
@ -91,7 +89,7 @@ namespace Avalonia.Controls
{ {
ItemsPanelProperty.OverrideDefaultValue<ComboBox>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<ComboBox>(DefaultPanel);
FocusableProperty.OverrideDefaultValue<ComboBox>(true); FocusableProperty.OverrideDefaultValue<ComboBox>(true);
SelectedItemProperty.Changed.AddClassHandler<ComboBox>((x,e) => x.SelectedItemChanged(e)); SelectedItemProperty.Changed.AddClassHandler<ComboBox>((x, e) => x.SelectedItemChanged(e));
KeyDownEvent.AddClassHandler<ComboBox>((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel); KeyDownEvent.AddClassHandler<ComboBox>((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel);
IsTextSearchEnabledProperty.OverrideDefaultValue<ComboBox>(true); IsTextSearchEnabledProperty.OverrideDefaultValue<ComboBox>(true);
} }
@ -117,7 +115,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 +124,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 +133,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); }
@ -221,8 +219,9 @@ namespace Avalonia.Controls
e.Handled = true; e.Handled = true;
} }
} }
// This part of code is needed just to acquire initial focus, subsequent focus navigation will be done by ItemsControl.
else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 && else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 &&
(e.Key == Key.Up || e.Key == Key.Down)) (e.Key == Key.Up || e.Key == Key.Down) && IsFocused == true)
{ {
var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c)); var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c));
if (firstChild != null) if (firstChild != null)
@ -262,9 +261,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 +303,7 @@ namespace Avalonia.Controls
} }
} }
private void PopupClosed(object sender, EventArgs e) private void PopupClosed(object? sender, EventArgs e)
{ {
_subscriptionsOnOpen.Clear(); _subscriptionsOnOpen.Clear();
@ -314,7 +313,7 @@ namespace Avalonia.Controls
} }
} }
private void PopupOpened(object sender, EventArgs e) private void PopupOpened(object? sender, EventArgs e)
{ {
TryFocusSelectedItem(); TryFocusSelectedItem();
@ -326,7 +325,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;
} }
@ -377,7 +376,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;
@ -430,7 +429,18 @@ namespace Avalonia.Controls
int next = SelectedIndex + 1; int next = SelectedIndex + 1;
if (next >= ItemCount) if (next >= ItemCount)
next = 0; {
if (WrapSelection == true)
{
next = 0;
}
else
{
return;
}
}
SelectedIndex = next; SelectedIndex = next;
} }
@ -440,7 +450,16 @@ namespace Avalonia.Controls
int prev = SelectedIndex - 1; int prev = SelectedIndex - 1;
if (prev < 0) if (prev < 0)
prev = ItemCount - 1; {
if (WrapSelection == true)
{
prev = ItemCount - 1;
}
else
{
return;
}
}
SelectedIndex = prev; SelectedIndex = prev;
} }

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;
} }

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

@ -4,6 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -93,15 +94,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 +257,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 +308,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 +326,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 +367,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 +383,46 @@ 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.");
if (_popup == null)
throw new InvalidOperationException("No Popup found.");
_presenter.Date = SelectedDate ?? DateTimeOffset.Now; _presenter.Date = SelectedDate ?? DateTimeOffset.Now;
_popup.PlacementMode = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true; _popup.IsOpen = true;
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
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.VerticalOffset = deltaY + 5;
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
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);
} }

104
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)
@ -534,6 +537,9 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup() internal double GetOffsetForPopup()
{ {
if (_monthSelector is null)
return 0;
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;
} }
} }

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

@ -3,6 +3,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Layout;
using System; using System;
using System.Globalization; using System.Globalization;
@ -49,18 +50,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 +143,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 +201,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 +221,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 +253,42 @@ 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)
{ {
if (_presenter == null)
throw new InvalidOperationException("No DatePickerPresenter found.");
if (_popup == null)
throw new InvalidOperationException("No Popup found.");
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay; _presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
_popup.PlacementMode = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true; _popup.IsOpen = true;
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
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.VerticalOffset = deltaY + 5;
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
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;
} }
} }
} }

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

Loading…
Cancel
Save