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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
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
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}"
EndProject
@ -95,7 +95,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}"
EndProject
@ -107,7 +107,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RenderDemo", "samples\RenderDemo\RenderDemo.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
EndProject
@ -115,7 +115,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\ApiDiff.props = build\ApiDiff.props
build\Base.props = build\Base.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|iPhoneSimulator.ActiveCfg = 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.Build.0 = Debug|iPhone
{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>
<!-- https://github.com/dotnet/msbuild/issues/2661 -->
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
</PropertyGroup>
</Project>

9
azure-pipelines.yml

@ -1,6 +1,3 @@
variables:
MSBuildEnableWorkloadResolver: 'false'
jobs:
- job: GetPRNumber
@ -146,6 +143,12 @@ jobs:
inputs:
version: 6.0.100
- task: CmdLine@2
displayName: 'Install Workloads'
inputs:
script: |
dotnet workload install android
- task: CmdLine@2
displayName: 'Install Nuke'
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/Avalonia.MicroCom/Avalonia.MicroCom.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj" />
</ItemGroup>
</Project>

2
build/SharedVersion.props

@ -11,7 +11,7 @@
<LangVersion>latest</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<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>
<PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes>
<RepositoryType>git</RepositoryType>

3
build/System.Drawing.Common.props

@ -1,5 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
</Project>

11
dirs.proj

@ -1,5 +1,7 @@
<Project Sdk="Microsoft.Build.Traversal">
<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="samples/**/*.*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.Loader/xamlil.github/**/*.*proj" />
<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="src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')">
<ProjectReference Remove="src/iOS/**/*.*proj" />
@ -23,6 +22,10 @@
<ProjectReference Remove="samples/interop/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</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>
<PackageReference Include="SlnGen" Version="2.0.40" PrivateAssets="all" />

1
global.json

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

5
nukebuild/Build.cs

@ -87,7 +87,8 @@ partial class Build : NukeBuild
Console.WriteLine(preamble);
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(
@ -99,7 +100,7 @@ partial class Build : NukeBuild
// This is required for VS2019 image on Azure Pipelines
.When(Parameters.IsRunningOnWindows &&
Parameters.IsRunningOnAzure, _ => _
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64")))
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64")))
.AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true)
.SetProcessToolPath(MsBuildExe.Value)

23
readme.md

@ -3,13 +3,11 @@
<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)
## 📖 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" />
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png)
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
Examples of UIs built with Avalonia
![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png)
([Synfonia](https://github.com/jmacato/Synfonia))
<video src="https://user-images.githubusercontent.com/4672627/152325602-28df36ec-6444-44a6-aebe-90ad52c8f27a.mp4"></video>
([Lunacy](https://icons8.com/lunacy))
![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
![image](https://user-images.githubusercontent.com/4672627/152325740-261c27a3-e6f0-4662-bff7-4796d4940e04.png)
([PlasticSCM](https://www.plasticscm.com/))
![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
![image](https://user-images.githubusercontent.com/4672627/152326453-14944c4d-33da-4d50-a268-b87f80927adb.png)
([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

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 ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{29132311-1848-4FD6-AE0C-4FF841151BD3}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ControlCatalog.Android</RootNamespace>
<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>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ApplicationId>com.Avalonia.ControlCatalog</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
</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>
<None Include="Resources\AboutResources.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" />
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="..\..\build\Assets\Icon.png">
<Link>Resources\drawable\Icon.png</Link>
</AndroidResource>
</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>
<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>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj">
<Project>{7B92AF71-6287-4693-9DCB-BD5B6E927E23}</Project>
<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>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</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>

5
samples/ControlCatalog.Android/MainActivity.cs

@ -5,10 +5,10 @@ using Avalonia.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
{
protected override void OnCreate(Bundle savedInstanceState)
protected override void OnCreate(Bundle? 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"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ControlCatalog.Android" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:targetSdkVersion="30" />
<application android:label="ControlCatalog.Android"></application>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
</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>
<style name="MyTheme.NoActionBar">
<style name="MyTheme.NoActionBar" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>

2
samples/ControlCatalog.Android/SplashActivity.cs

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

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

@ -6,6 +6,12 @@
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</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>
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
@ -15,6 +21,14 @@
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
</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>
<!-- For Microsoft.CodeAnalysis -->
<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">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
<Nullable>enable</Nullable>
<WasmBuildNative>True</WasmBuildNative>
</PropertyGroup>

7
samples/ControlCatalog/MainView.xaml

@ -22,8 +22,8 @@
<TabItem Header="Border">
<pages:BorderPage />
</TabItem>
<TabItem Header="Button">
<pages:ButtonPage />
<TabItem Header="Buttons">
<pages:ButtonsPage />
</TabItem>
<TabItem Header="ButtonSpinner">
<pages:ButtonSpinnerPage />
@ -40,6 +40,9 @@
<TabItem Header="CheckBox">
<pages:CheckBoxPage />
</TabItem>
<TabItem Header="Clipboard">
<pages:ClipboardPage />
</TabItem>
<TabItem Header="ComboBox">
<pages:ComboBoxPage />
</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
{
public class ButtonPage : UserControl
public class ButtonsPage : UserControl
{
private int repeatButtonClickCount = 0;
public ButtonPage()
public ButtonsPage()
{
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"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ComboBoxPage"
xmlns:sys="using:System"
xmlns:col="using:System.Collections">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A drop-down list.</TextBlock>
<UserControl
x:Class="ControlCatalog.Pages.ComboBoxPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:col="using:System.Collections"
xmlns:sys="using:System">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A drop-down list.</TextBlock>
<WrapPanel HorizontalAlignment="Center" Margin="0 16 0 0"
MaxWidth="660">
<WrapPanel.Styles>
<Style Selector="ComboBox">
<Setter Property="Width" Value="250" />
<Setter Property="Margin" Value="10" />
</Style>
</WrapPanel.Styles>
<ComboBox PlaceholderText="Pick an Item">
<ComboBoxItem>Inline Items</ComboBoxItem>
<ComboBoxItem>Inline Item 2</ComboBoxItem>
<ComboBoxItem>Inline Item 3</ComboBoxItem>
<ComboBoxItem>Inline Item 4</ComboBoxItem>
</ComboBox>
<StackPanel
Margin="0,16,0,0"
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="8">
<WrapPanel
MaxWidth="660"
Margin="0,16,0,0"
HorizontalAlignment="Center">
<WrapPanel.Styles>
<Style Selector="ComboBox">
<Setter Property="Width" Value="250" />
<Setter Property="Margin" Value="10" />
</Style>
</WrapPanel.Styles>
<ComboBox>
<ComboBox.Items>
<col:ArrayList>
<x:Null />
<sys:String>Hello</sys:String>
<sys:String>World</sys:String>
</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 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>
</ComboBox>
<ComboBox SelectedIndex="0">
<ComboBoxItem>
<Panel>
<Rectangle Fill="{DynamicResource SystemAccentColor}"/>
<TextBlock Margin="8">Control Items</TextBlock>
</Panel>
</ComboBoxItem>
<ComboBoxItem>
<Ellipse Width="50" Height="50" Fill="Yellow"/>
</ComboBoxItem>
<ComboBoxItem>
<TextBox Text="TextBox"/>
</ComboBoxItem>
</ComboBox>
<ComboBox WrapSelection="{Binding WrapSelection}">
<ComboBox.Items>
<col:ArrayList>
<x:Null />
<sys:String>Hello</sys:String>
<sys:String>World</sys:String>
</col:ArrayList>
</ComboBox.Items>
<ComboBox.ItemTemplate>
<DataTemplate>
<Panel>
<TextBlock Text="{Binding}" />
<TextBlock IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" Text="Null object" />
</Panel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox x:Name="fontComboBox" SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox PlaceholderText="Pick an Item">
<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>
<ComboBox SelectedIndex="0" WrapSelection="{Binding WrapSelection}">
<ComboBoxItem>
<Panel>
<Rectangle Fill="{DynamicResource SystemAccentColor}" />
<TextBlock Margin="8">Control Items</TextBlock>
</Panel>
</ComboBoxItem>
<ComboBoxItem>
<Ellipse
Width="50"
Height="50"
Fill="Yellow" />
</ComboBoxItem>
<ComboBoxItem>
<TextBox Text="TextBox" />
</ComboBoxItem>
</ComboBox>
</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>

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

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

8
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -11,7 +11,13 @@
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
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 Name="DragMeCustom"
Padding="16"

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

@ -2,11 +2,8 @@
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ControlCatalog.Pages
{
@ -24,6 +21,7 @@ namespace ControlCatalog.Pages
$"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
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)

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

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

4
samples/ControlCatalog/Pages/ListBoxPage.xaml

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

37
samples/ControlCatalog/Pages/ScreenPage.cs

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
@ -49,25 +50,33 @@ namespace ControlCatalog.Pages
context.DrawRectangle(p, boundsRect);
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}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text);
text.Text = $"WorkArea: {screen.WorkingArea.TopLeft} {screen.WorkingArea.Width}:{screen.WorkingArea.Height}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text);
var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height));
text.Text = $"Scaling: {screen.PixelDensity * 100}%";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
text.Text = $"Primary: {screen.Primary}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
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($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20));
formattedText = CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
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));
}
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>
</Border>
<TextBlock Text="headered ToggleSwitch" Classes="header"/>
<TextBlock Text="Headered ToggleSwitch" Classes="header"/>
<Border Classes="Thin">
<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 _alwaysSelected;
private bool _autoScrollToSelectedItem = true;
private bool _wrapSelection;
private int _counter;
private IObservable<SelectionMode> _selectionMode;
@ -85,6 +86,12 @@ namespace ControlCatalog.ViewModels
set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value);
}
public bool WrapSelection
{
get => _wrapSelection;
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
}
public MiniCommand AddItemCommand { get; }
public MiniCommand RemoveItemCommand { get; }
public MiniCommand SelectRandomItemCommand { get; }

3
samples/RenderDemo/MainWindow.xaml

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

13
samples/RenderDemo/Pages/CustomSkiaPage.cs

@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
@ -41,7 +42,10 @@ namespace RenderDemo.Pages
{
var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
if (canvas == null)
context.DrawText(Brushes.Black, new Point(), _noSkia.PlatformImpl);
using (var c = new DrawingContext(context, false))
{
c.DrawText(_noSkia, new Point());
}
else
{
canvas.Save();
@ -108,10 +112,9 @@ namespace RenderDemo.Pages
public override void Render(DrawingContext context)
{
var noSkia = new FormattedText()
{
Text = "Current rendering API is not Skia"
};
var noSkia = new FormattedText("Current rendering API is not Skia", CultureInfo.CurrentCulture,
FlowDirection.LeftToRight, Typeface.Default, 12, Brushes.Black);
context.Custom(new CustomDrawOp(new Rect(0, 0, Bounds.Width, Bounds.Height), noSkia));
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 readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20;
private int _direction = 10;
@ -38,7 +39,7 @@ namespace RenderDemo.Pages
private void UpdateGlyphRun()
{
var c = (uint)_rand.Next(65, 90);
var c = (char)_rand.Next(65, 90);
if (_fontSize + _direction > 200)
{
@ -54,6 +55,8 @@ namespace RenderDemo.Pages
_glyphIndices[0] = _glyphTypeface.GetGlyph(c);
_characters[0] = c;
var scale = (double)_fontSize / _glyphTypeface.DesignEmHeight;
var drawingGroup = new DrawingGroup();
@ -61,7 +64,7 @@ namespace RenderDemo.Pages
var glyphRunDrawing = new GlyphRunDrawing
{
Foreground = Brushes.Black,
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices),
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices)
};
drawingGroup.Children.Add(glyphRunDrawing);

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

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

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

@ -10,7 +10,6 @@ using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.PlatformSupport;
using Avalonia.Skia;
namespace Avalonia
@ -20,9 +19,9 @@ namespace Avalonia
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{
var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
builder.UseWindowingSubsystem(() => AndroidPlatform.Initialize(builder.ApplicationType, options), "Android");
builder.UseSkia();
return builder;
return builder
.UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android")
.UseSkia();
}
}
}
@ -44,7 +43,7 @@ namespace Avalonia.Android
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500);
public static void Initialize(Type appType, AndroidPlatformOptions options)
public static void Initialize(AndroidPlatformOptions 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>
<TargetFramework>monoandroid11.0</TargetFramework>
<TargetFrameworks>net6.0-android;monoandroid11.0</TargetFrameworks>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj">
<SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework>
</ProjectReference>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
</Project>

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

@ -1,35 +1,55 @@
using Android.App;
using Android.OS;
using Android.Views;
using Android.Content.PM;
using AndroidX.AppCompat.App;
using Android.Content.Res;
using AndroidX.Lifecycle;
namespace Avalonia.Android
{
public abstract class AvaloniaActivity : Activity
public abstract class AvaloniaActivity : AppCompatActivity
{
internal AvaloniaView View;
object _content;
internal AvaloniaViewModel _viewModel;
protected override void OnCreate(Bundle savedInstanceState)
{
View = new AvaloniaView(this);
if (_content != null)
View.Content = _content;
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);
}
public object Content
{
get
{
return _content;
return _viewModel.Content;
}
set
{
_content = value;
_viewModel.Content = value;
if (View != null)
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
{
class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
class SoftKeyboardListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private const int DefaultKeyboardHeightDP = 100;
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 bool _wasKeyboard;
public SoftKeyboardListner(View view)
public SoftKeyboardListener(View 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 ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FF69B927-C545-49AE-8E16-3D14D621AA12}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Avalonia.AndroidTestApplication</RootNamespace>
<AssemblyName>Avalonia.AndroidTestApplication</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>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ApplicationId>com.Avalonia.AndroidTestApplication</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<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</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<DevInstrumentationEnabled>True</DevInstrumentationEnabled>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<EnableProguard>False</EnableProguard>
<RunAOTCompilation>True</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
<!--<AndroidEnableProfiledAot>True</AndroidEnableProfiledAot>-->
</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>
<AndroidResource Include="Resources\values\Strings.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\Icon.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj">
<Project>{7b92af71-6287-4693-9dcb-bd5b6e927e23}</Project>
<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>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Android\Avalonia.Android.csproj" />
</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>

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

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

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

@ -1,6 +1,4 @@
<?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">
<uses-sdk android:targetSdkVersion="30" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<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);
if (method != null)
if (method is not null)
{
var parameters = method.GetParameters();
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);
return new Accessor(reference, method);
}
else
{
@ -82,18 +72,20 @@ namespace Avalonia.Data.Core.Plugins
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));
_ = method ?? throw new ArgumentNullException(nameof(method));
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];
for (var i = 0; i < parameters.Length; i++)
{
ParameterInfo parameter = parameters[i];
@ -101,16 +93,9 @@ namespace Avalonia.Data.Core.Plugins
paramTypes[i] = parameter.ParameterType;
}
if (hasReturn)
{
paramTypes[paramTypes.Length - 1] = returnType;
paramTypes[paramTypes.Length - 1] = returnType;
PropertyType = Expression.GetFuncType(paramTypes);
}
else
{
PropertyType = Expression.GetActionType(paramTypes);
}
PropertyType = Expression.GetDelegateType(paramTypes);
if (method.IsStatic)
{

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

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

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

@ -124,6 +124,9 @@ namespace Avalonia.Build.Tasks
var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
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 compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
@ -133,6 +136,7 @@ namespace Avalonia.Build.Tasks
AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)),
new XamlIlTrampolineBuilder(typeSystem.CreateTypeBuilder(trampolineBuilder)),
new DeterministicIdGenerator());
@ -256,7 +260,8 @@ namespace Avalonia.Build.Tasks
true),
(closureName, closureBaseType) =>
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
);

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 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.
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.
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.
@ -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.
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.
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()
{
private static bool s_setupWasAlreadyCalled;
private Action _optionsInitializers;
private Func<Application> _appFactory;
private IApplicationLifetime _lifetime;
private Action? _optionsInitializers;
private Func<Application>? _appFactory;
private IApplicationLifetime? _lifetime;
/// <summary>
/// Gets or sets the <see cref="IRuntimePlatform"/> instance.
@ -31,32 +31,32 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the <see cref="Application"/> instance being initialized.
/// </summary>
public Application Instance { get; private set; }
public Application? Instance { get; private set; }
/// <summary>
/// Gets the type of the Instance (even if it's not created yet)
/// </summary>
public Type ApplicationType { get; private set; }
public Type? ApplicationType { get; private set; }
/// <summary>
/// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary>
public Action WindowingSubsystemInitializer { get; private set; }
public Action? WindowingSubsystemInitializer { get; private set; }
/// <summary>
/// Gets the name of the currently selected windowing subsystem.
/// </summary>
public string WindowingSubsystemName { get; private set; }
public string? WindowingSubsystemName { get; private set; }
/// <summary>
/// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary>
public Action RenderingSubsystemInitializer { get; private set; }
public Action? RenderingSubsystemInitializer { get; private set; }
/// <summary>
/// Gets the name of the currently selected rendering subsystem.
/// </summary>
public string RenderingSubsystemName { get; private set; }
public string? RenderingSubsystemName { get; private set; }
/// <summary>
/// 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>
/// <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")]
public void Start<TMainWindow>(Func<object> dataContextProvider = null)
public void Start<TMainWindow>(Func<object>? dataContextProvider = null)
where TMainWindow : Window, new()
{
AfterSetup(builder =>
@ -134,7 +134,7 @@ namespace Avalonia.Controls
var window = new TMainWindow();
if (dataContextProvider != null)
window.DataContext = dataContextProvider();
((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime)
((IClassicDesktopStyleApplicationLifetime)builder.Instance!.ApplicationLifetime!)
.MainWindow = window;
});
@ -155,7 +155,7 @@ namespace Avalonia.Controls
public void Start(AppMainDelegate main, string[] args)
{
Setup();
main(Instance, args);
main(Instance!, args);
}
/// <summary>
@ -226,8 +226,8 @@ namespace Avalonia.Controls
var platformClassName = assemblyName.Replace("Avalonia.", string.Empty) + "Platform";
var platformClassFullName = assemblyName + "." + platformClassName;
var platformClass = assembly.GetType(platformClassFullName);
var init = platformClass.GetRuntimeMethod("Initialize", Type.EmptyTypes);
init.Invoke(null, null);
var init = platformClass!.GetRuntimeMethod("Initialize", Type.EmptyTypes);
init!.Invoke(null, null);
};
public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules());
@ -251,7 +251,7 @@ namespace Avalonia.Controls
where constructor.GetParameters().Length == 0 && !constructor.IsStatic
select constructor).Single() into constructor
select (Action)(() => constructor.Invoke(Array.Empty<object>()));
Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke();
Delegate.Combine(moduleInitializers.ToArray())!.DynamicInvoke();
}
/// <summary>
@ -292,6 +292,11 @@ namespace Avalonia.Controls
throw new InvalidOperationException("No rendering system configured.");
}
if (_appFactory == null)
{
throw new InvalidOperationException("No Application factory configured.");
}
if (s_setupWasAlreadyCalled && CheckSetup)
{
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.Styling;
using Avalonia.Threading;
#nullable enable
namespace Avalonia
{
@ -177,13 +176,13 @@ namespace Avalonia
/// </summary>
public IApplicationLifetime? ApplicationLifetime { get; set; }
event Action<IReadOnlyList<IStyle>> IGlobalStyles.GlobalStylesAdded
event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesAdded
{
add => _stylesAdded += value;
remove => _stylesAdded -= value;
}
event Action<IReadOnlyList<IStyle>> IGlobalStyles.GlobalStylesRemoved
event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesRemoved
{
add => _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
{
private int _exitCode;
private CancellationTokenSource _cts;
private CancellationTokenSource? _cts;
private bool _isShuttingDown;
private HashSet<Window> _windows = new HashSet<Window>();
private static ClassicDesktopStyleApplicationLifetime _activeLifetime;
private static ClassicDesktopStyleApplicationLifetime? _activeLifetime;
static ClassicDesktopStyleApplicationLifetime()
{
Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened);
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?.HandleWindowClosed((Window)sender);
_activeLifetime?._windows.Remove((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()
@ -46,24 +46,24 @@ namespace Avalonia.Controls.ApplicationLifetimes
}
/// <inheritdoc/>
public event EventHandler<ControlledApplicationLifetimeStartupEventArgs> Startup;
public event EventHandler<ControlledApplicationLifetimeStartupEventArgs>? Startup;
/// <inheritdoc/>
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;
public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;
/// <inheritdoc/>
public event EventHandler<ControlledApplicationLifetimeExitEventArgs> Exit;
public event EventHandler<ControlledApplicationLifetimeExitEventArgs>? Exit;
/// <summary>
/// Gets the arguments passed to the AppBuilder Start method.
/// </summary>
public string[] Args { get; set; }
public string[]? Args { get; set; }
/// <inheritdoc/>
public ShutdownMode ShutdownMode { get; set; }
/// <inheritdoc/>
public Window MainWindow { get; set; }
public Window? MainWindow { get; set; }
public IReadOnlyList<Window> Windows => _windows.ToList();
@ -183,7 +183,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
return true;
}
private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e) => DoShutdown(e);
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e);
}
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)"/>
/// method.
/// </summary>
string[] Args { get; }
string[]? Args { get; }
/// <summary>
/// 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>
/// The main window.
/// </value>
Window MainWindow { get; set; }
Window? MainWindow { get; set; }
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
/// each window to cancel the shutdown of the application. Windows cannot however prevent OS shutdown.
/// </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
{
Control MainView { get; set; }
Control? MainView { get; set; }
}
}

309
src/Avalonia.Controls/AutoCompleteBox.cs

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

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

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

12
src/Avalonia.Controls/Border.cs

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

34
src/Avalonia.Controls/Button.cs

@ -42,21 +42,21 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Command"/> property.
/// </summary>
public static readonly DirectProperty<Button, ICommand> CommandProperty =
AvaloniaProperty.RegisterDirect<Button, ICommand>(nameof(Command),
public static readonly DirectProperty<Button, ICommand?> CommandProperty =
AvaloniaProperty.RegisterDirect<Button, ICommand?>(nameof(Command),
button => button.Command, (button, command) => button.Command = command, enableDataValidation: true);
/// <summary>
/// Defines the <see cref="HotKey"/> property.
/// </summary>
public static readonly StyledProperty<KeyGesture> HotKeyProperty =
public static readonly StyledProperty<KeyGesture?> HotKeyProperty =
HotKeyManager.HotKeyProperty.AddOwner<Button>();
/// <summary>
/// Defines the <see cref="CommandParameter"/> property.
/// </summary>
public static readonly StyledProperty<object> CommandParameterProperty =
AvaloniaProperty.Register<Button, object>(nameof(CommandParameter));
public static readonly StyledProperty<object?> CommandParameterProperty =
AvaloniaProperty.Register<Button, object?>(nameof(CommandParameter));
/// <summary>
/// Defines the <see cref="IsDefault"/> property.
@ -85,12 +85,12 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Flyout"/> property
/// </summary>
public static readonly StyledProperty<FlyoutBase> FlyoutProperty =
AvaloniaProperty.Register<Button, FlyoutBase>(nameof(Flyout));
public static readonly StyledProperty<FlyoutBase?> FlyoutProperty =
AvaloniaProperty.Register<Button, FlyoutBase?>(nameof(Flyout));
private ICommand _command;
private ICommand? _command;
private bool _commandCanExecute = true;
private KeyGesture _hotkey;
private KeyGesture? _hotkey;
/// <summary>
/// Initializes static members of the <see cref="Button"/> class.
@ -112,7 +112,7 @@ namespace Avalonia.Controls
/// <summary>
/// Raised when the user clicks the button.
/// </summary>
public event EventHandler<RoutedEventArgs> Click
public event EventHandler<RoutedEventArgs>? Click
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
@ -130,7 +130,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets an <see cref="ICommand"/> to be invoked when the button is clicked.
/// </summary>
public ICommand Command
public ICommand? Command
{
get => _command;
set => SetAndRaise(CommandProperty, ref _command, value);
@ -139,7 +139,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets an <see cref="KeyGesture"/> associated with this control
/// </summary>
public KeyGesture HotKey
public KeyGesture? HotKey
{
get => GetValue(HotKeyProperty);
set => SetValue(HotKeyProperty, value);
@ -148,7 +148,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets a parameter to be passed to the <see cref="Command"/>.
/// </summary>
public object CommandParameter
public object? CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
@ -186,7 +186,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the Flyout that should be shown with this button.
/// </summary>
public FlyoutBase Flyout
public FlyoutBase? Flyout
{
get => GetValue(FlyoutProperty);
set => SetValue(FlyoutProperty, value);
@ -477,7 +477,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="sender">The event sender.</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);
@ -529,7 +529,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="sender">The event sender.</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)
{
@ -542,7 +542,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="sender">The event sender.</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)
{

14
src/Avalonia.Controls/ButtonSpinner.cs

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

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

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

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

@ -45,7 +45,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Gets or sets the Calendar associated with this button.
/// </summary>
internal Calendar Owner { get; set; }
internal Calendar? Owner { get; set; }
/// <summary>
/// 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
/// UIElement.
/// </summary>
public event EventHandler<PointerPressedEventArgs> CalendarLeftMouseButtonDown;
public event EventHandler<PointerPressedEventArgs>? CalendarLeftMouseButtonDown;
/// <summary>
/// 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
/// capture).
/// </summary>
public event EventHandler<PointerReleasedEventArgs> CalendarLeftMouseButtonUp;
public event EventHandler<PointerReleasedEventArgs>? CalendarLeftMouseButtonUp;
/// <summary>
/// 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 ElementCalendar = "PART_Calendar";
private Calendar _calendar;
private Calendar? _calendar;
private string _defaultText;
private Button _dropDownButton;
private Button? _dropDownButton;
//private Canvas _outsideCanvas;
//private Canvas _outsidePopupCanvas;
private Popup _popUp;
private TextBox _textBox;
private IDisposable _textBoxTextChangedSubscription;
private IDisposable _buttonPointerPressedSubscription;
private Popup? _popUp;
private TextBox? _textBox;
private IDisposable? _textBoxTextChangedSubscription;
private IDisposable? _buttonPointerPressedSubscription;
private DateTime? _onOpenSelectedDate;
private bool _settingSelectedDate;
@ -141,7 +141,7 @@ namespace Avalonia.Controls
private DateTime? _displayDateEnd;
private bool _isDropDownOpen;
private DateTime? _selectedDate;
private string _text;
private string? _text;
private bool _suspendTextChangeHandler = false;
private bool _isPopupClosing = false;
private bool _ignoreButtonClick = false;
@ -153,7 +153,7 @@ namespace Avalonia.Controls
/// A collection of dates that cannot be selected. The default value is
/// an empty collection.
/// </value>
public CalendarBlackoutDatesCollection BlackoutDates { get; private set; }
public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; }
public static readonly DirectProperty<CalendarDatePicker, DateTime> DisplayDateProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, DateTime>(
@ -186,7 +186,8 @@ namespace Avalonia.Controls
nameof(SelectedDate),
o => o.SelectedDate,
(o, v) => o.SelectedDate = v,
enableDataValidation: true);
enableDataValidation: true,
defaultBindingMode:BindingMode.TwoWay);
public static readonly StyledProperty<CalendarDatePickerFormat> SelectedDateFormatProperty =
AvaloniaProperty.Register<CalendarDatePicker, CalendarDatePickerFormat>(
@ -200,12 +201,12 @@ namespace Avalonia.Controls
defaultValue: "d",
validate: IsValidDateFormatString);
public static readonly DirectProperty<CalendarDatePicker, string> TextProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, string>(
public static readonly DirectProperty<CalendarDatePicker, string?> TextProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, string?>(
nameof(Text),
o => o.Text,
(o, v) => o.Text = v);
public static readonly StyledProperty<string> WatermarkProperty =
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<CalendarDatePicker>();
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
TextBox.UseFloatingWatermarkProperty.AddOwner<CalendarDatePicker>();
@ -361,13 +362,13 @@ namespace Avalonia.Controls
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// The text entered parses to a date that is not selectable.
/// </exception>
public string Text
public string? Text
{
get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); }
}
public string Watermark
public string? Watermark
{
get { return GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
@ -401,26 +402,26 @@ namespace Avalonia.Controls
/// Occurs when the drop-down
/// <see cref="T:Avalonia.Controls.Calendar" /> is closed.
/// </summary>
public event EventHandler CalendarClosed;
public event EventHandler? CalendarClosed;
/// <summary>
/// Occurs when the drop-down
/// <see cref="T:Avalonia.Controls.Calendar" /> is opened.
/// </summary>
public event EventHandler CalendarOpened;
public event EventHandler? CalendarOpened;
/// <summary>
/// Occurs when <see cref="P:Avalonia.Controls.DatePicker.Text" />
/// is assigned a value that cannot be interpreted as a date.
/// </summary>
public event EventHandler<CalendarDatePickerDateValidationErrorEventArgs> DateValidationError;
public event EventHandler<CalendarDatePickerDateValidationErrorEventArgs>? DateValidationError;
/// <summary>
/// Occurs when the
/// <see cref="P:Avalonia.Controls.CalendarDatePicker.SelectedDate" />
/// property is changed.
/// </summary>
public event EventHandler<SelectionChangedEventArgs> SelectedDateChanged;
public event EventHandler<SelectionChangedEventArgs>? SelectedDateChanged;
static CalendarDatePicker()
{
@ -579,14 +580,14 @@ namespace Avalonia.Controls
private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e)
{
var oldValue = (bool)e.OldValue;
var value = (bool)e.NewValue;
var oldValue = (bool)e.OldValue!;
var value = (bool)e.NewValue!;
if (_popUp != null && _popUp.Child != null)
{
if (value != oldValue)
{
if (_calendar.DisplayMode != CalendarMode.Month)
if (_calendar!.DisplayMode != CalendarMode.Month)
{
_calendar.DisplayMode = CalendarMode.Month;
}
@ -660,7 +661,7 @@ namespace Avalonia.Controls
if (date != null)
{
string s = DateTimeToString((DateTime)date);
string? s = DateTimeToString((DateTime)date);
Text = s;
}
}
@ -679,8 +680,8 @@ namespace Avalonia.Controls
}
private void OnTextChanged(AvaloniaPropertyChangedEventArgs e)
{
var oldValue = (string)e.OldValue;
var value = (string)e.NewValue;
var oldValue = (string?)e.OldValue;
var value = (string?)e.NewValue;
if (!_suspendTextChangeHandler)
{
@ -731,7 +732,7 @@ namespace Avalonia.Controls
}
private void OnDateSelected(DateTime? addedDate, DateTime? removedDate)
{
EventHandler<SelectionChangedEventArgs> handler = this.SelectedDateChanged;
EventHandler<SelectionChangedEventArgs>? handler = this.SelectedDateChanged;
if (null != handler)
{
Collection<DateTime> addedItems = new Collection<DateTime>();
@ -759,23 +760,23 @@ namespace Avalonia.Controls
CalendarOpened?.Invoke(this, e);
}
private void Calendar_DayButtonMouseUp(object sender, PointerReleasedEventArgs e)
private void Calendar_DayButtonMouseUp(object? sender, PointerReleasedEventArgs e)
{
Focus();
IsDropDownOpen = false;
}
private void Calendar_DisplayDateChanged(object sender, CalendarDateChangedEventArgs e)
private void Calendar_DisplayDateChanged(object? sender, CalendarDateChangedEventArgs e)
{
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!");
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];
}
@ -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)
@ -804,10 +805,9 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
private void Calendar_KeyDown(object sender, KeyEventArgs e)
private void Calendar_KeyDown(object? sender, KeyEventArgs e)
{
Calendar c = sender as Calendar;
Contract.Requires<ArgumentNullException>(c != null);
Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender));
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;
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
private void TextBox_KeyDown(object? sender, KeyEventArgs e)
{
if (!e.Handled)
{
@ -840,11 +840,11 @@ namespace Avalonia.Controls
_suspendTextChangeHandler = false;
}
}
private void DropDownButton_PointerPressed(object sender, PointerPressedEventArgs e)
private void DropDownButton_PointerPressed(object? sender, PointerPressedEventArgs e)
{
_ignoreButtonClick = _isPopupClosing;
}
private void DropDownButton_Click(object sender, RoutedEventArgs e)
private void DropDownButton_Click(object? sender, RoutedEventArgs e)
{
if (!_ignoreButtonClick)
{
@ -855,7 +855,7 @@ namespace Avalonia.Controls
_ignoreButtonClick = false;
}
}
private void PopUp_Closed(object sender, EventArgs e)
private void PopUp_Closed(object? sender, EventArgs e)
{
IsDropDownOpen = false;
@ -891,7 +891,7 @@ namespace Avalonia.Controls
private void OpenPopUp()
{
_onOpenSelectedDate = SelectedDate;
_popUp.IsOpen = true;
_popUp!.IsOpen = true;
}
/// <summary>
@ -914,7 +914,7 @@ namespace Avalonia.Controls
{
newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());
if (Calendar.IsValidDateSelection(this._calendar, newSelectedDate))
if (Calendar.IsValidDateSelection(this._calendar!, newSelectedDate))
{
return newSelectedDate;
}
@ -941,7 +941,7 @@ namespace Avalonia.Controls
}
return null;
}
private string DateTimeToString(DateTime d)
private string? DateTimeToString(DateTime d)
{
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
@ -982,7 +982,7 @@ namespace Avalonia.Controls
{
SetSelectedDate();
IsDropDownOpen = true;
_calendar.Focus();
_calendar!.Focus();
}
private void SetSelectedDate()
{
@ -1001,7 +1001,7 @@ namespace Avalonia.Controls
// ex: SelectedDate = DateTime(1008,12,19) but when
// "12/19/08" is parsed it is interpreted as
// DateTime(2008,12,19)
string selectedDate = DateTimeToString(SelectedDate.Value);
string? selectedDate = DateTimeToString(SelectedDate.Value);
if (selectedDate == s)
{
return;
@ -1053,7 +1053,7 @@ namespace Avalonia.Controls
// SelectedDate value:
if (SelectedDate != null)
{
string newtext = this.DateTimeToString(SelectedDate.Value);
string? newtext = this.DateTimeToString(SelectedDate.Value);
SetValue(TextProperty, newtext);
return SelectedDate;
}

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

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

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

@ -40,7 +40,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Gets or sets the Calendar associated with this button.
/// </summary>
internal Calendar Owner { get; set; }
internal Calendar? Owner { get; set; }
internal int Index { get; set; }
/// <summary>
@ -177,7 +177,7 @@ namespace Avalonia.Controls.Primitives
/// stylus touches the tablet PC) while the mouse pointer is over a
/// UIElement.
/// </summary>
public event EventHandler<PointerPressedEventArgs> CalendarDayButtonMouseDown;
public event EventHandler<PointerPressedEventArgs>? CalendarDayButtonMouseDown;
/// <summary>
/// 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
/// capture).
/// </summary>
public event EventHandler<PointerReleasedEventArgs> CalendarDayButtonMouseUp;
public event EventHandler<PointerReleasedEventArgs>? CalendarDayButtonMouseUp;
/// <summary>
/// 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_ElementYearView = "YearView";
private Button _headerButton;
private Button _nextButton;
private Button _previousButton;
private ITemplate<IControl> _dayTitleTemplate;
private Button? _headerButton;
private Button? _nextButton;
private Button? _previousButton;
private ITemplate<IControl>? _dayTitleTemplate;
private DateTime _currentMonth;
private bool _isMouseLeftButtonDown = false;
@ -45,11 +45,11 @@ namespace Avalonia.Controls.Primitives
private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar();
private PointerPressedEventArgs _downEventArg;
private PointerPressedEventArgs _downEventArgYearView;
private PointerPressedEventArgs? _downEventArg;
private PointerPressedEventArgs? _downEventArgYearView;
internal Calendar Owner { get; set; }
internal CalendarDayButton CurrentButton { get; set; }
internal Calendar? Owner { get; set; }
internal CalendarDayButton? CurrentButton { get; set; }
public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
public IBrush HeaderBackground
@ -57,13 +57,13 @@ namespace Avalonia.Controls.Primitives
get { return GetValue(HeaderBackgroundProperty); }
set { SetValue(HeaderBackgroundProperty, value); }
}
public static readonly DirectProperty<CalendarItem, ITemplate<IControl>> DayTitleTemplateProperty =
AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<IControl>>(
public static readonly DirectProperty<CalendarItem, ITemplate<IControl>?> DayTitleTemplateProperty =
AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<IControl>?>(
nameof(DayTitleTemplate),
o => o.DayTitleTemplate,
(o,v) => o.DayTitleTemplate = v,
defaultBindingMode: BindingMode.OneTime);
public ITemplate<IControl> DayTitleTemplate
public ITemplate<IControl>? DayTitleTemplate
{
get { return _dayTitleTemplate; }
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,
/// and decade mode.
/// </summary>
internal Button HeaderButton
internal Button? HeaderButton
{
get { return _headerButton; }
private set
@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that displays the next page of the calendar when it
/// is clicked.
/// </summary>
internal Button NextButton
internal Button? NextButton
{
get { return _nextButton; }
private set
@ -125,7 +125,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that displays the previous page of the calendar when
/// it is clicked.
/// </summary>
internal Button PreviousButton
internal Button? PreviousButton
{
get { return _previousButton; }
private set
@ -156,11 +156,11 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Gets the Grid that hosts the content when in month mode.
/// </summary>
internal Grid MonthView { get; set; }
internal Grid? MonthView { get; set; }
/// <summary>
/// Gets the Grid that hosts the content when in year or decade mode.
/// </summary>
internal Grid YearView { get; set; }
internal Grid? YearView { get; set; }
private void PopulateGrids()
{
@ -294,7 +294,7 @@ namespace Avalonia.Controls.Primitives
{
for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++)
{
var daytitle = MonthView.Children[childIndex];
var daytitle = MonthView!.Children[childIndex];
if (daytitle != null)
{
if (Owner != null)
@ -495,8 +495,7 @@ namespace Avalonia.Controls.Primitives
for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++)
{
CalendarDayButton childButton = MonthView.Children[childIndex] as CalendarDayButton;
Contract.Requires<ArgumentNullException>(childButton != null);
CalendarDayButton childButton = (CalendarDayButton)MonthView!.Children[childIndex];
childButton.Index = childIndex;
SetButtonState(childButton, dateToAdd);
@ -532,8 +531,7 @@ namespace Avalonia.Controls.Primitives
childIndex++;
for (int i = childIndex; i < count; i++)
{
childButton = MonthView.Children[i] as CalendarDayButton;
Contract.Requires<ArgumentNullException>(childButton != null);
childButton = (CalendarDayButton)MonthView.Children[i];
// button needs a content to occupy the necessary space
// for the content presenter
childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat());
@ -626,10 +624,9 @@ namespace Avalonia.Controls.Primitives
private void SetMonthButtonsForYearMode()
{
int count = 0;
foreach (object child in YearView.Children)
foreach (object child in YearView!.Children)
{
CalendarButton childButton = child as CalendarButton;
Contract.Requires<ArgumentNullException>(childButton != null);
CalendarButton childButton = (CalendarButton)child;
// There should be no time component. Time is 12:00 AM
DateTime day = new DateTime(_currentMonth.Year, count + 1, 1);
childButton.DataContext = day;
@ -703,7 +700,7 @@ namespace Avalonia.Controls.Primitives
{
if (Owner != null && calendarButton != null && calendarButton.DataContext != null)
{
Owner.FocusCalendarButton.IsCalendarButtonFocused = false;
Owner.FocusCalendarButton!.IsCalendarButtonFocused = false;
Owner.FocusCalendarButton = calendarButton;
calendarButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
@ -722,10 +719,9 @@ namespace Avalonia.Controls.Primitives
{
int year;
int count = -1;
foreach (object child in YearView.Children)
foreach (object child in YearView!.Children)
{
CalendarButton childButton = child as CalendarButton;
Contract.Requires<ArgumentNullException>(childButton != null);
CalendarButton childButton = (CalendarButton)child;
year = decade + count;
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)
{
@ -805,7 +801,7 @@ namespace Avalonia.Controls.Primitives
{
Owner.Focus();
}
Button b = (Button)sender;
Button b = (Button)sender!;
DateTime d;
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)
{
@ -842,14 +838,14 @@ namespace Avalonia.Controls.Primitives
Owner.Focus();
}
Button b = (Button)sender;
Button b = (Button)sender!;
if (b.IsEnabled)
{
Owner.OnPreviousClick();
}
}
}
internal void NextButton_Click(object sender, RoutedEventArgs e)
internal void NextButton_Click(object? sender, RoutedEventArgs e)
{
if (Owner != null)
{
@ -857,7 +853,7 @@ namespace Avalonia.Controls.Primitives
{
Owner.Focus();
}
Button b = (Button)sender;
Button b = (Button)sender!;
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)
{
@ -878,7 +874,7 @@ namespace Avalonia.Controls.Primitives
{
case CalendarSelectionMode.SingleDate:
{
DateTime selectedDate = (DateTime)b.DataContext;
DateTime selectedDate = (DateTime)b.DataContext!;
Owner.CalendarDatePickerDisplayDateFlag = true;
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)
{
@ -917,15 +913,14 @@ namespace Avalonia.Controls.Primitives
bool ctrl, shift;
CalendarExtensions.GetMetaKeyState(e.KeyModifiers, out ctrl, out shift);
CalendarDayButton b = sender as CalendarDayButton;
CalendarDayButton b = (CalendarDayButton)sender!;
if (b != null)
{
_isControlPressed = ctrl;
if (b.IsEnabled && !b.IsBlackout)
{
DateTime selectedDate = (DateTime)b.DataContext;
Contract.Requires<ArgumentNullException>(selectedDate != null);
DateTime selectedDate = (DateTime)b.DataContext!;
_isMouseLeftButtonDown = true;
// null check is added for unit tests
if (e != null)
@ -1027,7 +1022,7 @@ namespace Avalonia.Controls.Primitives
if (Owner != null)
{
Owner.HoverEndIndex = b.Index;
Owner.HoverEnd = (DateTime)b.DataContext;
Owner.HoverEnd = (DateTime)b.DataContext!;
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)
{
CalendarDayButton b = sender as CalendarDayButton;
CalendarDayButton? b = sender as CalendarDayButton;
if (b != null && !b.IsBlackout)
{
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 (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange)
{
CalendarDayButton b = sender as CalendarDayButton;
Contract.Requires<ArgumentNullException>(b != null);
CalendarDayButton b = (CalendarDayButton)sender!;
if (b.IsSelected)
{
@ -1118,10 +1112,9 @@ namespace Avalonia.Controls.Primitives
_isControlPressed = false;
}
private void Month_CalendarButtonMouseDown(object sender, PointerPressedEventArgs e)
private void Month_CalendarButtonMouseDown(object? sender, PointerPressedEventArgs e)
{
CalendarButton b = sender as CalendarButton;
Contract.Requires<ArgumentNullException>(b != null);
CalendarButton b = (CalendarButton)sender!;
_isMouseLeftButtonDownYearView = true;
@ -1133,13 +1126,13 @@ namespace Avalonia.Controls.Primitives
UpdateYearViewSelection(b);
}
internal void Month_CalendarButtonMouseUp(object sender, PointerReleasedEventArgs e)
internal void Month_CalendarButtonMouseUp(object? sender, PointerReleasedEventArgs e)
{
_isMouseLeftButtonDownYearView = false;
if (Owner != null)
{
DateTime newmonth = (DateTime)((CalendarButton)sender).DataContext;
DateTime newmonth = (DateTime)((CalendarButton)sender!).DataContext!;
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)
{
CalendarButton b = sender as CalendarButton;
Contract.Requires<ArgumentNullException>(b != null);
CalendarButton b = (CalendarButton)sender!;
UpdateYearViewSelection(b);
}
}

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

@ -5,6 +5,7 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace Avalonia.Controls
@ -73,6 +74,7 @@ namespace Avalonia.Controls
return newD;
}
[return: NotNullIfNotNull("d")]
public static DateTime? DiscardTime(DateTime? d)
{
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)
{
_owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, removedItems, addedItems));
_owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems));
}
/// <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.UpdateMonths();
_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="wrap">Whether to wrap around when the first or last item is reached.</param>
/// <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
return null;

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

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

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

@ -3,8 +3,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
#nullable enable
namespace Avalonia.Controls.Chrome
{
/// <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.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -46,8 +44,8 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="SelectionBoxItem"/> property.
/// </summary>
public static readonly DirectProperty<ComboBox, object> SelectionBoxItemProperty =
AvaloniaProperty.RegisterDirect<ComboBox, object>(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
public static readonly DirectProperty<ComboBox, object?> SelectionBoxItemProperty =
AvaloniaProperty.RegisterDirect<ComboBox, object?>(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
/// <summary>
/// Defines the <see cref="VirtualizationMode"/> property.
@ -58,14 +56,14 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="PlaceholderText"/> property.
/// </summary>
public static readonly StyledProperty<string> PlaceholderTextProperty =
AvaloniaProperty.Register<ComboBox, string>(nameof(PlaceholderText));
public static readonly StyledProperty<string?> PlaceholderTextProperty =
AvaloniaProperty.Register<ComboBox, string?>(nameof(PlaceholderText));
/// <summary>
/// Defines the <see cref="PlaceholderForeground"/> property.
/// </summary>
public static readonly StyledProperty<IBrush> PlaceholderForegroundProperty =
AvaloniaProperty.Register<ComboBox, IBrush>(nameof(PlaceholderForeground));
public static readonly StyledProperty<IBrush?> PlaceholderForegroundProperty =
AvaloniaProperty.Register<ComboBox, IBrush?>(nameof(PlaceholderForeground));
/// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property.
@ -80,8 +78,8 @@ namespace Avalonia.Controls
ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>();
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
private Popup? _popup;
private object? _selectionBoxItem;
private readonly CompositeDisposable _subscriptionsOnOpen = new CompositeDisposable();
/// <summary>
@ -91,7 +89,7 @@ namespace Avalonia.Controls
{
ItemsPanelProperty.OverrideDefaultValue<ComboBox>(DefaultPanel);
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);
IsTextSearchEnabledProperty.OverrideDefaultValue<ComboBox>(true);
}
@ -117,7 +115,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the item to display as the control's content.
/// </summary>
protected object SelectionBoxItem
protected object? SelectionBoxItem
{
get { return _selectionBoxItem; }
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); }
@ -126,7 +124,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the PlaceHolder text.
/// </summary>
public string PlaceholderText
public string? PlaceholderText
{
get { return GetValue(PlaceholderTextProperty); }
set { SetValue(PlaceholderTextProperty, value); }
@ -135,7 +133,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the Brush that renders the placeholder text.
/// </summary>
public IBrush PlaceholderForeground
public IBrush? PlaceholderForeground
{
get { return GetValue(PlaceholderForegroundProperty); }
set { SetValue(PlaceholderForegroundProperty, value); }
@ -221,8 +219,9 @@ namespace Avalonia.Controls
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 &&
(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));
if (firstChild != null)
@ -262,9 +261,9 @@ namespace Avalonia.Controls
/// <inheritdoc/>
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))
{
@ -304,7 +303,7 @@ namespace Avalonia.Controls
}
}
private void PopupClosed(object sender, EventArgs e)
private void PopupClosed(object? sender, EventArgs e)
{
_subscriptionsOnOpen.Clear();
@ -314,7 +313,7 @@ namespace Avalonia.Controls
}
}
private void PopupOpened(object sender, EventArgs e)
private void PopupOpened(object? sender, EventArgs e)
{
TryFocusSelectedItem();
@ -326,7 +325,7 @@ namespace Avalonia.Controls
toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) =>
{
//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;
}
@ -377,7 +376,7 @@ namespace Avalonia.Controls
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;
@ -430,7 +429,18 @@ namespace Avalonia.Controls
int next = SelectedIndex + 1;
if (next >= ItemCount)
next = 0;
{
if (WrapSelection == true)
{
next = 0;
}
else
{
return;
}
}
SelectedIndex = next;
}
@ -440,7 +450,16 @@ namespace Avalonia.Controls
int prev = SelectedIndex - 1;
if (prev < 0)
prev = ItemCount - 1;
{
if (WrapSelection == true)
{
prev = ItemCount - 1;
}
else
{
return;
}
}
SelectedIndex = prev;
}

14
src/Avalonia.Controls/ContentControl.cs

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

14
src/Avalonia.Controls/ContextMenu.cs

@ -14,8 +14,6 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Styling;
#nullable enable
namespace Avalonia.Controls
{
/// <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;
Focus();
@ -376,12 +374,12 @@ namespace Avalonia.Controls
_popupHostChangedHandler?.Invoke(_popup!.Host);
}
private void PopupClosing(object sender, CancelEventArgs e)
private void PopupClosing(object? sender, CancelEventArgs e)
{
e.Cancel = CancelClosing();
}
private void PopupClosed(object sender, EventArgs e)
private void PopupClosed(object? sender, EventArgs e)
{
foreach (var i in LogicalChildren)
{
@ -411,7 +409,7 @@ namespace Avalonia.Controls
_popupHostChangedHandler?.Invoke(null);
}
private void PopupKeyUp(object sender, KeyEventArgs e)
private void PopupKeyUp(object? sender, KeyEventArgs e)
{
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
&& 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
&& control.ContextMenu is ContextMenu contextMenu)

2
src/Avalonia.Controls/ContextRequestedEventArgs.cs

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

23
src/Avalonia.Controls/Control.cs

@ -1,16 +1,16 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
@ -60,7 +60,13 @@ namespace Avalonia.Controls
public static readonly RoutedEvent<ContextRequestedEventArgs> ContextRequestedEvent =
RoutedEvent.Register<Control, ContextRequestedEventArgs>(nameof(ContextRequested),
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 IControl? _focusAdorner;
@ -108,11 +114,20 @@ namespace Avalonia.Controls
get => GetValue(TagProperty);
set => SetValue(TagProperty, value);
}
/// <summary>
/// Gets or sets the text flow direction.
/// </summary>
public FlowDirection FlowDirection
{
get => GetValue(FlowDirectionProperty);
set => SetValue(FlowDirectionProperty, value);
}
/// <summary>
/// Occurs when the user has completed a context input gesture, such as a right-click.
/// </summary>
public event EventHandler<ContextRequestedEventArgs> ContextRequested
public event EventHandler<ContextRequestedEventArgs>? ContextRequested
{
add => AddHandler(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>
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));
}
@ -29,7 +29,7 @@ namespace Avalonia.Controls
/// <param name="rect">The area of the control to being into view.</param>
public static void BringIntoView(this IControl control, Rect rect)
{
Contract.Requires<ArgumentNullException>(control != null);
_ = control ?? throw new ArgumentNullException(nameof(control));
if (control.IsEffectivelyVisible)
{
@ -51,10 +51,10 @@ namespace Avalonia.Controls
/// <param name="control">The control to look in.</param>
/// <param name="name">The name of the control to find.</param>
/// <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);
Contract.Requires<ArgumentNullException>(name != null);
_ = control ?? throw new ArgumentNullException(nameof(control));
_ = name ?? throw new ArgumentNullException(nameof(name));
var nameScope = control.FindNameScope();
@ -66,6 +66,29 @@ namespace Avalonia.Controls
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>
/// Sets a pseudoclass depending on an observable trigger.
/// </summary>
@ -75,7 +98,7 @@ namespace Avalonia.Controls
/// <returns>A disposable used to cancel the subscription.</returns>
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));
}

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

@ -1,5 +1,4 @@
#nullable enable
using System;
using System;
using System.Globalization;
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 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)
{
@ -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();
}

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

@ -11,7 +11,7 @@ namespace Avalonia.Controls.Converters
{
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 ||
values == null ||

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

@ -13,7 +13,7 @@ namespace Avalonia.Controls.Converters
/// </summary>
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)
{
@ -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();
}

29
src/Avalonia.Controls/DataValidationErrors.cs

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

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

@ -4,6 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Layout;
using System;
using System.Collections.Generic;
using System.Globalization;
@ -93,15 +94,15 @@ namespace Avalonia.Controls
defaultBindingMode: BindingMode.TwoWay);
// Template Items
private Button _flyoutButton;
private TextBlock _dayText;
private TextBlock _monthText;
private TextBlock _yearText;
private Grid _container;
private Rectangle _spacer1;
private Rectangle _spacer2;
private Popup _popup;
private DatePickerPresenter _presenter;
private Button? _flyoutButton;
private TextBlock? _dayText;
private TextBlock? _monthText;
private TextBlock? _yearText;
private Grid? _container;
private Rectangle? _spacer1;
private Rectangle? _spacer2;
private Popup? _popup;
private DatePickerPresenter? _presenter;
private bool _areControlsAvailable;
@ -256,7 +257,7 @@ namespace Avalonia.Controls
/// <summary>
/// Raised when the <see cref="SelectedDate"/> changes
/// </summary>
public event EventHandler<DatePickerSelectedValueChangedEventArgs> SelectedDateChanged;
public event EventHandler<DatePickerSelectedValueChangedEventArgs>? SelectedDateChanged;
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();
}
private void OnConfirmed(object sender, EventArgs e)
private void OnConfirmed(object? sender, EventArgs e)
{
_popup.Close();
SelectedDate = _presenter.Date;
_popup!.Close();
SelectedDate = _presenter!.Date;
}
private void SetGrid()
@ -325,7 +326,7 @@ namespace Avalonia.Controls
return;
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),
(_yearText, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1),
@ -366,11 +367,11 @@ namespace Avalonia.Controls
var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated
Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
Grid.SetColumn(_spacer1!, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2!, isSpacer2Visible ? 3 : 0);
_spacer1.IsVisible = isSpacer1Visible;
_spacer2.IsVisible = isSpacer2Visible;
_spacer1!.IsVisible = isSpacer1Visible;
_spacer2!.IsVisible = isSpacer2Visible;
}
private void SetSelectedDateText()
@ -382,37 +383,46 @@ namespace Avalonia.Controls
{
PseudoClasses.Set(":hasnodate", false);
var selDate = SelectedDate.Value;
_monthText.Text = selDate.ToString(MonthFormat);
_yearText.Text = selDate.ToString(YearFormat);
_dayText.Text = selDate.ToString(DayFormat);
_monthText!.Text = selDate.ToString(MonthFormat);
_yearText!.Text = selDate.ToString(YearFormat);
_dayText!.Text = selDate.ToString(DayFormat);
}
else
{
PseudoClasses.Set(":hasnodate", true);
_monthText.Text = "month";
_yearText.Text = "year";
_dayText.Text = "day";
_monthText!.Text = "month";
_yearText!.Text = "year";
_dayText!.Text = "day";
}
}
private void OnFlyoutButtonClicked(object sender, RoutedEventArgs e)
private void OnFlyoutButtonClicked(object? sender, RoutedEventArgs e)
{
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;
_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;
// 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();
// 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),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
_popup.VerticalOffset = deltaY + 5;
}
protected virtual void OnSelectedDateChanged(object sender, DatePickerSelectedValueChangedEventArgs e)
protected virtual void OnSelectedDateChanged(object? sender, DatePickerSelectedValueChangedEventArgs 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);
// Template Items
private Grid _pickerContainer;
private Button _acceptButton;
private Button _dismissButton;
private Rectangle _spacer1;
private Rectangle _spacer2;
private Panel _monthHost;
private Panel _yearHost;
private Panel _dayHost;
private DateTimePickerPanel _monthSelector;
private DateTimePickerPanel _yearSelector;
private DateTimePickerPanel _daySelector;
private Button _monthUpButton;
private Button _dayUpButton;
private Button _yearUpButton;
private Button _monthDownButton;
private Button _dayDownButton;
private Button _yearDownButton;
private Grid? _pickerContainer;
private Button? _acceptButton;
private Button? _dismissButton;
private Rectangle? _spacer1;
private Rectangle? _spacer2;
private Panel? _monthHost;
private Panel? _yearHost;
private Panel? _dayHost;
private DateTimePickerPanel? _monthSelector;
private DateTimePickerPanel? _yearSelector;
private DateTimePickerPanel? _daySelector;
private Button? _monthUpButton;
private Button? _dayUpButton;
private Button? _yearUpButton;
private Button? _monthDownButton;
private Button? _dayDownButton;
private Button? _yearDownButton;
private DateTimeOffset _date;
private string _dayFormat = "%d";
@ -308,9 +308,12 @@ namespace Avalonia.Controls
e.Handled = true;
break;
case Key.Tab:
var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next);
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
e.Handled = true;
if (FocusManager.Instance?.Current is IInputElement focus)
{
var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
e.Handled = true;
}
break;
case Key.Enter:
Date = _syncDate;
@ -332,13 +335,13 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true;
_monthSelector.MaximumValue = 12;
_monthSelector!.MaximumValue = 12;
_monthSelector.MinimumValue = 1;
_monthSelector.ItemFormat = MonthFormat;
_daySelector.ItemFormat = DayFormat;
_daySelector!.ItemFormat = DayFormat;
_yearSelector.MaximumValue = MaxYear.Year;
_yearSelector!.MaximumValue = MaxYear.Year;
_yearSelector.MinimumValue = MinYear.Year;
_yearSelector.ItemFormat = YearFormat;
@ -375,7 +378,7 @@ namespace Avalonia.Controls
private void SetGrid()
{
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),
(_yearHost, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1),
@ -383,7 +386,7 @@ namespace Avalonia.Controls
};
columns.Sort((x, y) => x.Item2 - y.Item2);
_pickerContainer.ColumnDefinitions.Clear();
_pickerContainer!.ColumnDefinitions.Clear();
var columnIndex = 0;
@ -416,18 +419,18 @@ namespace Avalonia.Controls
var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated
Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
Grid.SetColumn(_spacer1!, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2!, isSpacer2Visible ? 3 : 0);
_spacer1.IsVisible = isSpacer1Visible;
_spacer2.IsVisible = isSpacer2Visible;
_spacer1!.IsVisible = isSpacer1Visible;
_spacer2!.IsVisible = isSpacer2Visible;
}
private void SetInitialFocus()
{
int monthCol = MonthVisible ? Grid.GetColumn(_monthHost) : int.MaxValue;
int dayCol = DayVisible ? Grid.GetColumn(_dayHost) : int.MaxValue;
int yearCol = YearVisible ? Grid.GetColumn(_yearHost) : int.MaxValue;
int monthCol = MonthVisible ? Grid.GetColumn(_monthHost!) : int.MaxValue;
int dayCol = DayVisible ? Grid.GetColumn(_dayHost!) : int.MaxValue;
int yearCol = YearVisible ? Grid.GetColumn(_yearHost!) : int.MaxValue;
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();
}
private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
private void OnAcceptButtonClicked(object? sender, RoutedEventArgs e)
{
Date = _syncDate;
OnConfirmed();
}
private void OnSelectorButtonClick(object sender, RoutedEventArgs e)
private void OnSelectorButtonClick(object? sender, RoutedEventArgs e)
{
if (sender == _monthUpButton)
_monthSelector.ScrollUp();
_monthSelector!.ScrollUp();
else if (sender == _monthDownButton)
_monthSelector.ScrollDown();
_monthSelector!.ScrollDown();
else if (sender == _yearUpButton)
_yearSelector.ScrollUp();
_yearSelector!.ScrollUp();
else if (sender == _yearDownButton)
_yearSelector.ScrollDown();
_yearSelector!.ScrollDown();
else if (sender == _dayUpButton)
_daySelector.ScrollUp();
_daySelector!.ScrollUp();
else if (sender == _dayDownButton)
_daySelector.ScrollDown();
_daySelector!.ScrollDown();
}
private void OnYearChanged(object sender, EventArgs e)
private void OnYearChanged(object? sender, EventArgs e)
{
if (_suppressUpdateSelection)
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,
_syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset);
@ -487,7 +490,7 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true;
_daySelector.FormatDate = newDate.Date;
_daySelector!.FormatDate = newDate.Date;
if (_daySelector.MaximumValue != maxDays)
_daySelector.MaximumValue = maxDays;
@ -497,19 +500,19 @@ namespace Avalonia.Controls
_suppressUpdateSelection = false;
}
private void OnDayChanged(object sender, EventArgs e)
private void OnDayChanged(object? sender, EventArgs e)
{
if (_suppressUpdateSelection)
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)
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,
_syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset);
@ -521,7 +524,7 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true;
_daySelector.FormatDate = newDate.Date;
_daySelector!.FormatDate = newDate.Date;
_syncDate = newDate;
if (_daySelector.MaximumValue != maxDays)
@ -534,6 +537,9 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
if (_monthSelector is null)
return 0;
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
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 bool _hasInit;
private bool _suppressUpdateOffset;
private ListBoxItem _pressedItem;
private ListBoxItem? _pressedItem;
public DateTimePickerPanel()
{
@ -271,9 +271,9 @@ namespace Avalonia.Controls.Primitives
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)
{
@ -523,40 +523,44 @@ namespace Avalonia.Controls.Primitives
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;
}
}
private void OnItemPointerUp(object sender, PointerReleasedEventArgs e)
private void OnItemPointerUp(object? sender, PointerReleasedEventArgs e)
{
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;
e.Handled = true;
}
}
//Helper to get ListBoxItem from pointerevent source
private ListBoxItem GetItemFromSource(IVisual src)
private ListBoxItem? GetItemFromSource(IVisual src)
{
var item = src;
while (item != null && !(item is ListBoxItem))
{
item = item.VisualParent;
}
return (ListBoxItem)item;
return (ListBoxItem?)item;
}
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)
{

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

@ -16,10 +16,10 @@ namespace Avalonia.Controls.Primitives
protected virtual void OnDismiss()
{
Dismissed.Invoke(this, EventArgs.Empty);
Dismissed?.Invoke(this, EventArgs.Empty);
}
public event EventHandler Confirmed;
public event EventHandler Dismissed;
public event EventHandler? Confirmed;
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.Templates;
using Avalonia.Data;
using Avalonia.Layout;
using System;
using System.Globalization;
@ -49,18 +50,18 @@ namespace Avalonia.Controls
defaultBindingMode: BindingMode.TwoWay);
// Template Items
private TimePickerPresenter _presenter;
private Button _flyoutButton;
private Border _firstPickerHost;
private Border _secondPickerHost;
private Border _thirdPickerHost;
private TextBlock _hourText;
private TextBlock _minuteText;
private TextBlock _periodText;
private Rectangle _firstSplitter;
private Rectangle _secondSplitter;
private Grid _contentGrid;
private Popup _popup;
private TimePickerPresenter? _presenter;
private Button? _flyoutButton;
private Border? _firstPickerHost;
private Border? _secondPickerHost;
private Border? _thirdPickerHost;
private TextBlock? _hourText;
private TextBlock? _minuteText;
private TextBlock? _periodText;
private Rectangle? _firstSplitter;
private Rectangle? _secondSplitter;
private Grid? _contentGrid;
private Popup? _popup;
private TimeSpan? _selectedTime;
private int _minuteIncrement = 1;
@ -142,7 +143,7 @@ namespace Avalonia.Controls
/// <summary>
/// Raised when the <see cref="SelectedTime"/> property changes
/// </summary>
public event EventHandler<TimePickerSelectedValueChangedEventArgs> SelectedTimeChanged;
public event EventHandler<TimePickerSelectedValueChangedEventArgs>? SelectedTimeChanged;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
@ -200,15 +201,15 @@ namespace Avalonia.Controls
var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
_contentGrid.ColumnDefinitions = new ColumnDefinitions(columnsD);
_thirdPickerHost.IsVisible = !use24HourClock;
_secondSplitter.IsVisible = !use24HourClock;
_thirdPickerHost!.IsVisible = !use24HourClock;
_secondSplitter!.IsVisible = !use24HourClock;
Grid.SetColumn(_firstPickerHost, 0);
Grid.SetColumn(_secondPickerHost, 2);
Grid.SetColumn(_firstPickerHost!, 0);
Grid.SetColumn(_secondPickerHost!, 2);
Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4);
Grid.SetColumn(_firstSplitter, 1);
Grid.SetColumn(_firstSplitter!, 1);
Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3);
}
@ -220,7 +221,7 @@ namespace Avalonia.Controls
var time = SelectedTime;
if (time.HasValue)
{
var newTime = SelectedTime.Value;
var newTime = SelectedTime!.Value;
if (ClockIdentifier == "12HourClock")
{
@ -252,30 +253,42 @@ namespace Avalonia.Controls
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;
_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;
// 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();
// 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),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
_popup.VerticalOffset = deltaY + 5;
}
private void OnDismissPicker(object sender, EventArgs e)
private void OnDismissPicker(object? sender, EventArgs e)
{
_popup.Close();
_popup!.Close();
Focus();
}
private void OnConfirmed(object sender, EventArgs e)
private void OnConfirmed(object? sender, EventArgs e)
{
_popup.Close();
SelectedTime = _presenter.Time;
_popup!.Close();
SelectedTime = _presenter!.Time;
}
}
}

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

Loading…
Cancel
Save