瀏覽代碼

Merge branch 'master' into ischeckedchanged-event

Max Katz 2 年之前
父節點
當前提交
96fd5489ee
共有 100 個文件被更改,包括 1031 次插入885 次删除
  1. 6 0
      .editorconfig
  2. 2 10
      Avalonia.sln
  3. 1 0
      Directory.Build.props
  4. 68 9
      NOTICE.md
  5. 8 8
      azure-pipelines-integrationtests.yml
  6. 12 12
      azure-pipelines.yml
  7. 2 1
      build/Base.props
  8. 0 5
      build/JetBrains.Annotations.props
  9. 0 1
      build/SharedVersion.props
  10. 1 1
      build/System.Memory.props
  11. 2 4
      global.json
  12. 0 1
      nukebuild/DotNetConfigHelper.cs
  13. 20 5
      packages/Avalonia/AvaloniaBuildTasks.targets
  14. 1 1
      samples/ControlCatalog.Android/MainActivity.cs
  15. 4 0
      samples/ControlCatalog.Android/Resources/values/styles.xml
  16. 2 0
      samples/ControlCatalog.Android/SplashActivity.cs
  17. 13 0
      samples/ControlCatalog.Browser/Properties/launchSettings.json
  18. 1 1
      samples/ControlCatalog.Desktop/Program.cs
  19. 4 2
      samples/ControlCatalog/MainView.xaml
  20. 16 7
      samples/ControlCatalog/MainView.xaml.cs
  21. 0 1
      samples/ControlCatalog/MainWindow.xaml
  22. 3 3
      samples/ControlCatalog/Models/StateData.cs
  23. 51 38
      samples/ControlCatalog/Pages/CompositionPage.axaml
  24. 172 0
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  25. 1 1
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
  26. 2 2
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  27. 212 0
      samples/ControlCatalog/Pages/GesturePage.cs
  28. 117 0
      samples/ControlCatalog/Pages/GesturePage.xaml
  29. 1 1
      samples/ControlCatalog/Pages/ImagePage.xaml.cs
  30. 0 1
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  31. 1 1
      samples/ControlCatalog/Pages/NotificationsPage.xaml.cs
  32. 4 1
      samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs
  33. 1 1
      samples/ControlCatalog/Pages/PointerCanvas.cs
  34. 0 1
      samples/ControlCatalog/Pages/PointerContactsTab.cs
  35. 29 29
      samples/ControlCatalog/Pages/ScreenPage.cs
  36. 1 1
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  37. 1 1
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  38. 0 7
      samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml
  39. 0 1
      samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
  40. 2 6
      samples/ControlCatalog/ViewModels/ContextPageViewModel.cs
  41. 1 1
      samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
  42. 0 1
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  43. 1 10
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  44. 0 2
      samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
  45. 1 2
      samples/ControlCatalog/ViewModels/NotificationViewModel.cs
  46. 3 1
      samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs
  47. 0 1
      samples/ControlCatalog/ViewModels/RefreshContainerViewModel.cs
  48. 1 1
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  49. 0 1
      samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs
  50. 2 3
      samples/Directory.Build.props
  51. 3 1
      samples/MiniMvvm/MiniMvvm.csproj
  52. 8 6
      samples/MiniMvvm/PropertyChangedExtensions.cs
  53. 0 1
      samples/MiniMvvm/ViewModelBase.cs
  54. 1 0
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj
  55. 2 1
      samples/Previewer/Previewer.csproj
  56. 0 1
      samples/ReactiveUIDemo/ReactiveUIDemo.csproj
  57. 0 5
      samples/interop/Direct3DInteropSample/App.paml
  58. 0 21
      samples/interop/Direct3DInteropSample/App.paml.cs
  59. 0 32
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  60. 0 283
      samples/interop/Direct3DInteropSample/MainWindow.cs
  61. 0 14
      samples/interop/Direct3DInteropSample/MainWindow.paml
  62. 0 45
      samples/interop/Direct3DInteropSample/MainWindowViewModel.cs
  63. 0 47
      samples/interop/Direct3DInteropSample/MiniCube.fx
  64. 0 16
      samples/interop/Direct3DInteropSample/Program.cs
  65. 0 2
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  66. 1 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  67. 1 1
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  68. 10 4
      src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
  69. 2 0
      src/Android/Avalonia.Android/AvaloniaView.cs
  70. 1 1
      src/Android/Avalonia.Android/ChoreographerTimer.cs
  71. 2 0
      src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
  72. 1 0
      src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs
  73. 83 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  74. 1 1
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
  75. 1 2
      src/Avalonia.Base/Animation/Animation.cs
  76. 1 4
      src/Avalonia.Base/Animation/AnimationInstance`1.cs
  77. 1 1
      src/Avalonia.Base/Animation/AnimatorKeyFrame.cs
  78. 0 2
      src/Avalonia.Base/Animation/Animators/Animator`1.cs
  79. 1 1
      src/Avalonia.Base/Animation/Animators/ColorAnimator.cs
  80. 1 1
      src/Avalonia.Base/Animation/Animators/TransformAnimator.cs
  81. 1 0
      src/Avalonia.Base/Animation/Clock.cs
  82. 2 2
      src/Avalonia.Base/Animation/CrossFade.cs
  83. 14 2
      src/Avalonia.Base/Avalonia.Base.csproj
  84. 1 1
      src/Avalonia.Base/AvaloniaObject.cs
  85. 55 121
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  86. 3 3
      src/Avalonia.Base/AvaloniaProperty`1.cs
  87. 1 0
      src/Avalonia.Base/ClassBindingManager.cs
  88. 1 1
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  89. 0 1
      src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs
  90. 26 0
      src/Avalonia.Base/Compatibility/OperatingSystem.cs
  91. 0 34
      src/Avalonia.Base/Contract.cs
  92. 13 8
      src/Avalonia.Base/Controls/NameScopeExtensions.cs
  93. 1 1
      src/Avalonia.Base/Controls/NameScopeLocator.cs
  94. 6 3
      src/Avalonia.Base/CornerRadius.cs
  95. 5 6
      src/Avalonia.Base/Data/BindingOperations.cs
  96. 1 1
      src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs
  97. 0 1
      src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs
  98. 2 4
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  99. 0 2
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  100. 8 11
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs

+ 6 - 0
.editorconfig

@@ -140,6 +140,8 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
 # CS1591: Missing XML comment for publicly visible type or member
 dotnet_diagnostic.CS1591.severity = suggestion
 
+# CS0162: Remove unreachable code
+dotnet_diagnostic.CS0162.severity = error
 # CA1304: Specify CultureInfo
 dotnet_diagnostic.CA1304.severity = warning
 # CA1802: Use literals where appropriate
@@ -152,6 +154,8 @@ dotnet_diagnostic.CA1820.severity = warning
 dotnet_diagnostic.CA1821.severity = warning
 # CA1822: Mark members as static
 dotnet_diagnostic.CA1822.severity = suggestion
+# CA1823: Avoid unused private fields
+dotnet_diagnostic.CA1823.severity = warning
 dotnet_code_quality.CA1822.api_surface = private, internal
 # CA1825: Avoid zero-length array allocations
 dotnet_diagnostic.CA1825.severity = warning
@@ -165,6 +169,8 @@ dotnet_diagnostic.CA1828.severity = warning
 dotnet_diagnostic.CA1829.severity = warning
 #CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
 dotnet_diagnostic.CA1847.severity = warning
+#CACA2211:Non-constant fields should not be visible
+dotnet_diagnostic.CA2211.severity = error
 
 # Wrapping preferences
 csharp_wrap_before_ternary_opsigns = false

+ 2 - 10
Avalonia.sln

@@ -100,7 +100,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
 		build\EmbedXaml.props = build\EmbedXaml.props
 		build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
 		build\ImageSharp.props = build\ImageSharp.props
-		build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
 		build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
 		build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
 		build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
@@ -136,8 +135,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.LinuxFramebuffer", "src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj", "{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.Skia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
@@ -228,9 +225,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser", "src\Bro
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\ReactiveUIDemo\ReactiveUIDemo.csproj", "{75C47156-C5D8-44BC-A5A7-E8657C2248D6}"
 EndProject
@@ -366,10 +363,6 @@ Global
 		{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.Build.0 = Release|Any CPU
-		{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.Build.0 = Release|Any CPU
 		{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -580,7 +573,6 @@ Global
 		{7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
 		{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
-		{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 		{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
 		{E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{E2999E4A-9086-401F-898C-AEB0AD38E676} = {9B9E3891-2366-4253-A952-D08BCEB71098}

+ 1 - 0
Directory.Build.props

@@ -7,5 +7,6 @@
       <AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
       <MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
       <RunApiCompat>False</RunApiCompat>
+      <LangVersion>11</LangVersion>
   </PropertyGroup>
 </Project>

+ 68 - 9
NOTICE.md

@@ -81,14 +81,14 @@ A "contributor" is any person that distributes its contribution under this licen
 
 https://github.com/wayland-project/wayland-protocols
 
-Copyright © 2008-2013 Kristian Høgsberg
-Copyright © 2010-2013 Intel Corporation
-Copyright © 2013      Rafael Antognolli
-Copyright © 2013      Jasper St. Pierre
-Copyright © 2014      Jonas Ådahl
-Copyright © 2014      Jason Ekstrand
-Copyright © 2014-2015 Collabora, Ltd.
-Copyright © 2015      Red Hat Inc.
+Copyright © 2008-2013 Kristian Høgsberg
+Copyright © 2010-2013 Intel Corporation
+Copyright © 2013      Rafael Antognolli
+Copyright © 2013      Jasper St. Pierre
+Copyright © 2014      Jonas Ådahl
+Copyright © 2014      Jason Ekstrand
+Copyright © 2014-2015 Collabora, Ltd.
+Copyright © 2015      Red Hat Inc.
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
@@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 https://github.com/toptensoftware/RichTextKit
 
-Copyright © 2019 Topten Software. All Rights Reserved.
+Copyright © 2019 Topten Software. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License"); you may 
 not use this product except in compliance with the License. You may obtain 
@@ -303,3 +303,62 @@ https://github.com/chromium/chromium
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Flutter
+
+https://github.com/flutter/flutter
+
+//Copyright 2014 The Flutter Authors. All rights reserved.
+
+//Redistribution and use in source and binary forms, with or without modification,
+//are permitted provided that the following conditions are met:
+
+//    * Redistributions of source code must retain the above copyright
+//      notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer in the documentation and/or other materials provided
+//      with the distribution.
+//    * Neither the name of Google Inc. nor the names of its
+//      contributors may be used to endorse or promote products derived
+//      from this software without specific prior written permission.
+
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+//ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+//LOSS OF USE, DATA, OR PROFITS;
+//OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Reactive Extensions
+
+https://github.com/dotnet/reactive
+
+The MIT License (MIT)
+
+Copyright (c) .NET Foundation and Contributors
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 8 - 8
azure-pipelines-integrationtests.yml

@@ -13,14 +13,14 @@ jobs:
 
   steps:
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 6.0.401'
+    displayName: 'Use .NET Core SDK 6.0.404'
     inputs:
-      version: 6.0.401
+      version: 6.0.404
 
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 7.0.100'
+    displayName: 'Use .NET Core SDK 7.0.101'
     inputs:
-      version: 7.0.100
+      version: 7.0.101
       
   - script: system_profiler SPDisplaysDataType |grep Resolution
   
@@ -51,14 +51,14 @@ jobs:
 
   steps:
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 6.0.401'
+    displayName: 'Use .NET Core SDK 6.0.404'
     inputs:
-      version: 6.0.401
+      version: 6.0.404
 
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 7.0.100'
+    displayName: 'Use .NET Core SDK 7.0.101'
     inputs:
-      version: 7.0.100
+      version: 7.0.101
 
   - task: Windows Application Driver@0
     inputs:

+ 12 - 12
azure-pipelines.yml

@@ -30,14 +30,14 @@ jobs:
     vmImage: 'ubuntu-20.04'
   steps:
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 6.0.401'
+    displayName: 'Use .NET Core SDK 6.0.404'
     inputs:
-      version: 6.0.401
+      version: 6.0.404
 
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 7.0'
+    displayName: 'Use .NET Core SDK 7.0.101'
     inputs:
-      version: 7.0.100
+      version: 7.0.101
 
   - task: CmdLine@2
     displayName: 'Install Workloads'
@@ -67,14 +67,14 @@ jobs:
     vmImage: 'macos-12'
   steps:
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 6.0.401'
+    displayName: 'Use .NET Core SDK 6.0.404'
     inputs:
-      version: 6.0.401
+      version: 6.0.404
 
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 7.0.100'
+    displayName: 'Use .NET Core SDK 7.0.101'
     inputs:
-      version: 7.0.100
+      version: 7.0.101
 
   - task: CmdLine@2
     displayName: 'Install Workloads'
@@ -138,14 +138,14 @@ jobs:
     SolutionDir: '$(Build.SourcesDirectory)'
   steps:
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 6.0.401'
+    displayName: 'Use .NET Core SDK 6.0.404'
     inputs:
-      version: 6.0.401
+      version: 6.0.404
 
   - task: UseDotNet@2
-    displayName: 'Use .NET Core SDK 7.0.100'
+    displayName: 'Use .NET Core SDK 7.0.101'
     inputs:
-      version: 7.0.100
+      version: 7.0.101
 
   - task: CmdLine@2
     displayName: 'Install Workloads'

+ 2 - 1
build/Base.props

@@ -1,6 +1,7 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
+  <ItemGroup Condition="'$(TargetFramework)' != 'net6'">
     <PackageReference Include="System.ValueTuple" Version="4.5.0" />
+    <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
     <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
   </ItemGroup>
 </Project>

+ 0 - 5
build/JetBrains.Annotations.props

@@ -1,5 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <PackageReference Include="JetBrains.Annotations" Version="10.3.0" />
-  </ItemGroup>
-</Project>

+ 0 - 1
build/SharedVersion.props

@@ -8,7 +8,6 @@
     <RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <NoWarn>$(NoWarn);CS1591</NoWarn>
-    <LangVersion>preview</LangVersion>
     <PackageLicenseExpression>MIT</PackageLicenseExpression>
     <PackageIcon>Icon.png</PackageIcon>
     <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>

+ 1 - 1
build/System.Memory.props

@@ -1,5 +1,5 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
+  <ItemGroup Condition="'$(TargetFramework)' != 'net6'">
     <PackageReference Include="System.Memory" Version="4.5.3" />
   </ItemGroup>
 </Project>

+ 2 - 4
global.json

@@ -1,11 +1,9 @@
 {
     "sdk": {
-        "version": "7.0.100",
+        "version": "7.0.101",
         "rollForward": "latestFeature"
     },
     "msbuild-sdks": {
-        "Microsoft.Build.Traversal": "1.0.43",
-        "MSBuild.Sdk.Extras": "3.0.22",
-        "AggregatePackage.NuGet.Sdk" : "0.1.12"
+        "Microsoft.Build.Traversal": "3.2.0"
     }
 }

+ 0 - 1
nukebuild/DotNetConfigHelper.cs

@@ -1,5 +1,4 @@
 using System.Globalization;
-using JetBrains.Annotations;
 using Nuke.Common.Tools.DotNet;
 // ReSharper disable ReturnValueOfPureMethodIsNotUsed
 

+ 20 - 5
packages/Avalonia/AvaloniaBuildTasks.targets

@@ -48,8 +48,12 @@
   </PropertyGroup>
 
   <Target Name="_GenerateAvaloniaResourcesDependencyCache" BeforeTargets="GenerateAvaloniaResources">
+    <PropertyGroup>
+      <_AvaloniaResourcesInputsCacheFilePath>$(IntermediateOutputPath)/Avalonia/Resources.Inputs.cache</_AvaloniaResourcesInputsCacheFilePath>
+    </PropertyGroup>
+
     <ItemGroup>
-      <CustomAdditionalGenerateAvaloniaResourcesInputs Include="$(IntermediateOutputPath)/Avalonia/Resources.Inputs.cache" />
+      <CustomAdditionalGenerateAvaloniaResourcesInputs Include="$(_AvaloniaResourcesInputsCacheFilePath)" />
     </ItemGroup>
     
     <Hash ItemsToHash="@(AvaloniaResource);@(AvaloniaXaml);$(MSBuildAllProjects)">
@@ -57,7 +61,11 @@
     </Hash>
 
     <MakeDir Directories="$(IntermediateOutputPath)/Avalonia" />
-    <WriteLinesToFile Overwrite="true" File="$(IntermediateOutputPath)/Avalonia/Resources.Inputs.cache" Lines="$(AvaloniaResourcesDependencyHash)" WriteOnlyWhenDifferent="True" />
+    <WriteLinesToFile Overwrite="true" File="$(_AvaloniaResourcesInputsCacheFilePath)" Lines="$(AvaloniaResourcesDependencyHash)" WriteOnlyWhenDifferent="True" />
+
+    <ItemGroup>
+      <FileWrites Include="$(_AvaloniaResourcesInputsCacheFilePath)" />
+    </ItemGroup>
   </Target>
   
   <Target Name="GenerateAvaloniaResources" 
@@ -68,7 +76,7 @@
           Condition="('@(AvaloniaResource->Count())' &gt; 0) or ('@(AvaloniaXaml->Count())' &gt; 0)"
           >
     <ItemGroup>
-        <AvaloniaResource Include="@(AvaloniaXaml)"/>
+      <AvaloniaResource Include="@(AvaloniaXaml)" />
     </ItemGroup>
     <GenerateAvaloniaResourcesTask
       Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
@@ -76,6 +84,9 @@
       Root="$(MSBuildProjectDirectory)"
       Resources="@(AvaloniaResource)"
       ReportImportance="$(AvaloniaXamlReportImportance)"/>
+    <ItemGroup Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'">
+      <FileWrites Include="$(AvaloniaResourcesTemporaryFilePath)" />
+    </ItemGroup>
     <Exec 
       Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
       Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:BuildProjectReferences=false"/>
@@ -103,6 +114,9 @@
       File="$(AvaloniaXamlReferencesTemporaryFilePath)"
       Lines="@(ReferencePathWithRefAssemblies)"
       Overwrite="true" />
+    <ItemGroup Condition="'$(_AvaloniaForceInternalMSBuild)' != 'true'">
+      <FileWrites Include="$(AvaloniaXamlReferencesTemporaryFilePath)" />
+    </ItemGroup>
     <CompileAvaloniaXamlTask
       Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
       AssemblyFile="@(IntermediateAssembly)"
@@ -117,8 +131,9 @@
       DelaySign="$(DelaySign)"
       SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)"
       DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)"
-      DefaultCompileBindings="$(AvaloniaUseCompiledBindingsByDefault)"
-    />
+      DefaultCompileBindings="$(AvaloniaUseCompiledBindingsByDefault)">
+      <Output TaskParameter="WrittenFilePaths" ItemName="FileWrites" />
+    </CompileAvaloniaXamlTask>
     <Exec
       Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
       Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileAvaloniaXaml /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:BuildProjectReferences=false"/>

+ 1 - 1
samples/ControlCatalog.Android/MainActivity.cs

@@ -5,7 +5,7 @@ using Avalonia.Android;
 
 namespace ControlCatalog.Android
 {
-    [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+    [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
     public class MainActivity : AvaloniaMainActivity
     {
     }

+ 4 - 0
samples/ControlCatalog.Android/Resources/values/styles.xml

@@ -14,4 +14,8 @@
     <item name="android:windowContentOverlay">@null</item>
   </style>
 
+  <style name="MyTheme.Main" parent ="MyTheme.NoActionBar">
+    <item name="android:windowIsTranslucent">true</item>
+  </style>
+
 </resources>

+ 2 - 0
samples/ControlCatalog.Android/SplashActivity.cs

@@ -28,6 +28,8 @@ namespace ControlCatalog.Android
             base.OnResume();
 
             StartActivity(new Intent(Application.Context, typeof(MainActivity)));
+
+            Finish();
         }
     }
 }

+ 13 - 0
samples/ControlCatalog.Browser/Properties/launchSettings.json

@@ -0,0 +1,13 @@
+{
+  "profiles": {
+    "ControlCatalog.Browser": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "applicationUrl": "https://localhost:5001;http://localhost:5000",
+      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/debug?browser={browserInspectUri}"
+    }
+  }
+}

+ 1 - 1
samples/ControlCatalog.Desktop/Program.cs

@@ -23,7 +23,7 @@ namespace ControlCatalog
         private static void ConfigureAssetAssembly(AppBuilder builder)
         {
             AvaloniaLocator.CurrentMutable
-                .GetService<IAssetLoader>()
+                .GetRequiredService<IAssetLoader>()
                 .SetDefaultAssembly(typeof(App).Assembly);
         }
     }

+ 4 - 2
samples/ControlCatalog/MainView.xaml

@@ -92,6 +92,9 @@
       <TabItem Header="Flyouts">
         <pages:FlyoutsPage />
       </TabItem>
+      <TabItem Header="Gestures">
+        <pages:GesturePage />
+      </TabItem>
       <TabItem Header="Image"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                ScrollViewer.VerticalScrollBarVisibility="Disabled">
@@ -206,8 +209,7 @@
               </ComboBox.Items>
             </ComboBox>
             <ComboBox x:Name="TransparencyLevels"
-                      HorizontalAlignment="Stretch"
-                      SelectedIndex="{Binding TransparencyLevel}">
+                      HorizontalAlignment="Stretch">
               <ComboBox.Items>
                 <WindowTransparencyLevel>None</WindowTransparencyLevel>
                 <WindowTransparencyLevel>Transparent</WindowTransparencyLevel>

+ 16 - 7
samples/ControlCatalog/MainView.xaml.cs

@@ -6,6 +6,7 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
 using Avalonia.Media.Immutable;
+using Avalonia.VisualTree;
 using ControlCatalog.Models;
 using ControlCatalog.Pages;
 
@@ -59,17 +60,25 @@ namespace ControlCatalog
             };
 
             var transparencyLevels = this.Get<ComboBox>("TransparencyLevels");
-            IDisposable? backgroundSetter = null, paneBackgroundSetter = null;
+            IDisposable? topLevelBackgroundSideSetter = null, sideBarBackgroundSetter = null, paneBackgroundSetter = null;
             transparencyLevels.SelectionChanged += (sender, e) =>
             {
-                backgroundSetter?.Dispose();
+                topLevelBackgroundSideSetter?.Dispose();
+                sideBarBackgroundSetter?.Dispose();
                 paneBackgroundSetter?.Dispose();
-                if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected
-                    && selected != WindowTransparencyLevel.None)
+                if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected)
                 {
-                    var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.5);
-                    backgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
-                    paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
+                    var topLevel = (TopLevel)this.GetVisualRoot()!;
+                    topLevel.TransparencyLevelHint = selected;
+
+                    if (selected != WindowTransparencyLevel.None)
+                    {
+                        var transparentBrush = new ImmutableSolidColorBrush(Colors.White, 0);
+                        var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.2);
+                        topLevelBackgroundSideSetter = topLevel.SetValue(BackgroundProperty, transparentBrush, Avalonia.Data.BindingPriority.Style);
+                        sideBarBackgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
+                        paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
+                    }
                 }
             };
         }

+ 0 - 1
samples/ControlCatalog/MainWindow.xaml

@@ -10,7 +10,6 @@
         ExtendClientAreaToDecorationsHint="{Binding ExtendClientAreaEnabled}"
         ExtendClientAreaChromeHints="{Binding ChromeHints}"
         ExtendClientAreaTitleBarHeightHint="{Binding TitleBarHeight}"
-        TransparencyLevelHint="{Binding TransparencyLevel}"        
         x:Name="MainWindow"
         Background="Transparent"
         x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"

+ 3 - 3
samples/ControlCatalog/Models/StateData.cs

@@ -6,10 +6,10 @@ public class StateData
     public string Abbreviation { get; private set; }
     public string Capital { get; private set; }
 
-    public StateData(string name, string abbreviatoin, string capital)
+    public StateData(string name, string abbreviation, string capital)
     {
         Name = name;
-        Abbreviation = abbreviatoin;
+        Abbreviation = abbreviation;
         Capital = capital;
     }
 
@@ -17,4 +17,4 @@ public class StateData
     {
         return Name;
     }
-}
+}

+ 51 - 38
samples/ControlCatalog/Pages/CompositionPage.axaml

@@ -2,44 +2,57 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:pages="using:ControlCatalog.Pages"
              x:Class="ControlCatalog.Pages.CompositionPage">
-    <StackPanel>
-        <TextBlock Classes="h1">Implicit animations</TextBlock>
+  <TabControl>
+    <TabItem Header="Implicit animations">
+      <StackPanel>
         <Grid ColumnDefinitions="*,10,40" Margin="0 0 40 0">
-            <ItemsControl x:Name="Items">
-                <ItemsControl.ItemsPanel>
-                    <ItemsPanelTemplate>
-                        <WrapPanel/>
-                    </ItemsPanelTemplate>
-                </ItemsControl.ItemsPanel>
-                <ItemsControl.DataTemplates>
-                    <DataTemplate DataType="pages:CompositionPageColorItem">
-                        <Border 
-                            pages:CompositionPage.EnableAnimations="True"
-                            Padding="10" BorderBrush="Gray" BorderThickness="2"
-                            Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10">
-                            <TextBlock Text="{Binding ColorHexValue}"/>
-                        </Border>
-                    </DataTemplate>
-                </ItemsControl.DataTemplates>
-            </ItemsControl>
-            <GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray" 
-                          Background="#e0e0e0" Grid.Column="1"
-                          ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"
-            />
-            <Border Grid.Column="2">
-                <LayoutTransformControl
-                    HorizontalAlignment="Center"
-            
-                    MinWidth="30">
-                    <LayoutTransformControl.LayoutTransform>
-                        <RotateTransform Angle="90"/>
-                    </LayoutTransformControl.LayoutTransform>
-            
-                    <TextBlock>Resize me</TextBlock>
-                </LayoutTransformControl>
-            </Border>
+          <ItemsControl x:Name="Items">
+            <ItemsControl.ItemsPanel>
+              <ItemsPanelTemplate>
+                <WrapPanel/>
+              </ItemsPanelTemplate>
+            </ItemsControl.ItemsPanel>
+            <ItemsControl.DataTemplates>
+              <DataTemplate DataType="pages:CompositionPageColorItem">
+                <Border 
+                  pages:CompositionPage.EnableAnimations="True"
+                  Padding="10" BorderBrush="Gray" BorderThickness="2"
+                  Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10">
+                    <TextBlock Text="{Binding ColorHexValue}"/>
+                </Border>
+              </DataTemplate>
+            </ItemsControl.DataTemplates>
+          </ItemsControl>
+          <GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray" 
+                        Background="#e0e0e0" Grid.Column="1"
+                        ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"
+          />
+          <Border Grid.Column="2">
+            <LayoutTransformControl HorizontalAlignment="Center" MinWidth="30">
+              <LayoutTransformControl.LayoutTransform>
+                <RotateTransform Angle="90"/>
+              </LayoutTransformControl.LayoutTransform>
+              <TextBlock>Resize me</TextBlock>
+            </LayoutTransformControl>
+          </Border>
         </Grid>
-
-     
-    </StackPanel>
+      </StackPanel>
+    </TabItem>
+    <TabItem Header="Animation">
+      <DockPanel>
+        <Button DockPanel.Dock="Top" Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
+        <Control x:Name="SolidVisualHost" />
+      </DockPanel>
+    </TabItem>
+    <TabItem Header="Custom">
+      <DockPanel>
+        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
+          <Button Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
+          <Button Margin="10" Click="ButtonStartCustomVisual">Start</Button>
+          <Button Margin="10" Click="ButtonStopCustomVisual">Stop</Button>
+        </StackPanel>
+        <Control x:Name="CustomVisualHost" />
+      </DockPanel>
+    </TabItem>
+  </TabControl>
 </UserControl>

+ 172 - 0
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@@ -1,28 +1,39 @@
 using System;
 using System.Collections.Generic;
+using System.Numerics;
+using System.Threading;
 using Avalonia;
+using Avalonia.Animation;
 using Avalonia.Controls;
+using Avalonia.Interactivity;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
+using Avalonia.Media.Immutable;
 using Avalonia.Rendering.Composition;
 using Avalonia.Rendering.Composition.Animations;
 using Avalonia.VisualTree;
+using Math = System.Math;
 
 namespace ControlCatalog.Pages;
 
 public partial class CompositionPage : UserControl
 {
     private ImplicitAnimationCollection? _implicitAnimations;
+    private CompositionCustomVisual? _customVisual;
+    private CompositionSolidColorVisual? _solidVisual;
 
     public CompositionPage()
     {
         AvaloniaXamlLoader.Load(this);
+        AttachAnimatedSolidVisual(this.FindControl<Control>("SolidVisualHost")!);
+        AttachCustomVisual(this.FindControl<Control>("CustomVisualHost")!);
     }
 
     protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
     {
         base.OnAttachedToVisualTree(e);
         this.Get<ItemsControl>("Items").Items = CreateColorItems();
+
     }
 
     private static List<CompositionPageColorItem> CreateColorItems()
@@ -126,6 +137,167 @@ public partial class CompositionPage : UserControl
             compositionVisual.ImplicitAnimations = page._implicitAnimations;
         }
     }
+    
+    void AttachAnimatedSolidVisual(Visual v)
+    {
+        void Update()
+        {
+            if(_solidVisual == null)
+                return;
+            _solidVisual.Size = new Vector2((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3);
+            _solidVisual.Offset = new Vector3((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3, 0);
+        }
+        v.AttachedToVisualTree += delegate
+        {
+            var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+            if(compositor == null || _solidVisual?.Compositor == compositor)
+                return;
+            _solidVisual = compositor.CreateSolidColorVisual();
+            ElementComposition.SetElementChildVisual(v, _solidVisual);
+            _solidVisual.Color = Colors.Red;
+            var animation = _solidVisual.Compositor.CreateColorKeyFrameAnimation();
+            animation.InsertKeyFrame(0, Colors.Red);
+            animation.InsertKeyFrame(0.5f, Colors.Blue);
+            animation.InsertKeyFrame(1, Colors.Green);
+            animation.Duration = TimeSpan.FromSeconds(5);
+            animation.IterationBehavior = AnimationIterationBehavior.Forever;
+            animation.Direction = PlaybackDirection.Alternate;
+            _solidVisual.StartAnimation("Color", animation);
+
+            _solidVisual.AnchorPoint = new Vector2(0, 0);
+
+            var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
+            scale.Duration = TimeSpan.FromSeconds(5);
+            scale.IterationBehavior = AnimationIterationBehavior.Forever;
+            scale.InsertKeyFrame(0, new Vector3(1, 1, 0));
+            scale.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 0));
+            scale.InsertKeyFrame(1, new Vector3(1, 1, 0));
+
+            _solidVisual.StartAnimation("Scale", scale);
+
+            var center =
+                _solidVisual.Compositor.CreateExpressionAnimation(
+                    "Vector3(this.Target.Size.X * 0.5, this.Target.Size.Y * 0.5, 1)");
+            _solidVisual.StartAnimation("CenterPoint", center);
+            Update();
+        };
+        v.PropertyChanged += (_, a) =>
+        {
+            if (a.Property == BoundsProperty)
+                Update();
+        };
+    }
+
+    void AttachCustomVisual(Visual v)
+    {
+        void Update()
+        {
+            if (_customVisual == null)
+                return;
+            var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3);
+            _customVisual.Size = new Vector2((float)v.Bounds.Width, h);
+            _customVisual.Offset = new Vector3(0, (float)(v.Bounds.Height - h) / 2, 0);
+        }
+        v.AttachedToVisualTree += delegate
+        {
+            var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+            if(compositor == null || _customVisual?.Compositor == compositor)
+                return;
+            _customVisual = compositor.CreateCustomVisual(new CustomVisualHandler());
+            ElementComposition.SetElementChildVisual(v, _customVisual);
+            _customVisual.SendHandlerMessage(CustomVisualHandler.StartMessage);
+            Update();
+        };
+        
+        v.PropertyChanged += (_, a) =>
+        {
+            if (a.Property == BoundsProperty)
+                Update();
+        };
+    }
+
+    class CustomVisualHandler : CompositionCustomVisualHandler
+    {
+        private TimeSpan _animationElapsed;
+        private TimeSpan? _lastServerTime;
+        private bool _running;
+
+        public static readonly object StopMessage = new(), StartMessage = new();
+        
+        public override void OnRender(ImmediateDrawingContext drawingContext)
+        {
+            if (_running)
+            {
+                if (_lastServerTime.HasValue) _animationElapsed += (CompositionNow - _lastServerTime.Value);
+                _lastServerTime = CompositionNow;
+            }
+            
+            const int cnt = 20;
+            var maxPointSizeX = EffectiveSize.X / (cnt * 1.6);
+            var maxPointSizeY = EffectiveSize.Y / 4;
+            var pointSize = Math.Min(maxPointSizeX, maxPointSizeY);
+            var animationLength = TimeSpan.FromSeconds(4);
+            var animationStage = _animationElapsed.TotalSeconds / animationLength.TotalSeconds;
+
+            var sinOffset = Math.Cos(_animationElapsed.TotalSeconds) * 1.5;
+            
+            for (var c = 0; c < cnt; c++)
+            {
+                var stage = (animationStage + (double)c / cnt) % 1;
+                var colorStage =
+                    (animationStage + (Math.Sin(_animationElapsed.TotalSeconds * 2) + 1) / 2 + (double)c / cnt) % 1;
+                var posX = (EffectiveSize.X + pointSize * 3) * stage - pointSize;
+                var posY = (EffectiveSize.Y - pointSize) * (1 + Math.Sin(stage * 3.14 * 3 + sinOffset)) / 2 + pointSize / 2;
+                var opacity = Math.Sin(stage * 3.14);
+
+
+                drawingContext.DrawEllipse(new ImmutableSolidColorBrush(Color.FromArgb(
+                        255, 
+                        (byte)(255 - 255 * colorStage),
+                        (byte)(255 * Math.Abs(0.5 - colorStage) * 2), 
+                        (byte)(255 * colorStage)
+                    ), opacity), null,
+                    new Point(posX, posY), pointSize / 2, pointSize / 2);
+            }
+            
+        }
+
+        public override void OnMessage(object message)
+        {
+            if (message == StartMessage)
+            {
+                _running = true;
+                _lastServerTime = null;
+                RegisterForNextAnimationFrameUpdate();
+            }
+            else if (message == StopMessage)
+                _running = false;
+        }
+
+        public override void OnAnimationFrameUpdate()
+        {
+            if (_running)
+            {
+                Invalidate();
+                RegisterForNextAnimationFrameUpdate();
+            }
+        }
+    }
+    
+    private void ButtonThreadSleep(object? sender, RoutedEventArgs e)
+    {
+        Thread.Sleep(10000);
+    }
+
+    private void ButtonStartCustomVisual(object? sender, RoutedEventArgs e)
+    {
+        _customVisual?.SendHandlerMessage(CustomVisualHandler.StartMessage);
+    }
+
+    private void ButtonStopCustomVisual(object? sender, RoutedEventArgs e)
+    {
+        _customVisual?.SendHandlerMessage(CustomVisualHandler.StopMessage);
+    }
 }
 
 public class CompositionPageColorItem

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

@@ -17,7 +17,7 @@ namespace ControlCatalog.Pages
             this.Get<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
                 "to set a reminder. The TimePicker displays three controls for hour, minute, and AM / PM(if necessary).These controls " +
                 "are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
-                "12 - hour or 24 - hour clock and visiblility of AM / PM is dynamically set based on user time settings, or can be overridden.";
+                "12 - hour or 24 - hour clock and visibility of AM / PM is dynamically set based on user time settings, or can be overridden.";
 
 
         }

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

@@ -396,8 +396,8 @@ CanPickFolder: {storageProvider.CanPickFolder}";
             return item.TryGetUri(out var uri) ? uri.ToString() : item.Name;
         }
 
-        Window GetWindow() => this.VisualRoot as Window ?? throw new NullReferenceException("Invalid Owner");
-        TopLevel GetTopLevel() => this.VisualRoot as TopLevel ?? throw new NullReferenceException("Invalid Owner");
+        Window GetWindow() => TopLevel.GetTopLevel(this) as Window ?? throw new NullReferenceException("Invalid Owner");
+        TopLevel GetTopLevel() => TopLevel.GetTopLevel(this) ?? throw new NullReferenceException("Invalid Owner");
 
         private void InitializeComponent()
         {

+ 212 - 0
samples/ControlCatalog/Pages/GesturePage.cs

@@ -0,0 +1,212 @@
+using System;
+using System.Numerics;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.LogicalTree;
+using Avalonia.Markup.Xaml;
+using Avalonia.Rendering.Composition;
+
+namespace ControlCatalog.Pages
+{
+    public class GesturePage : UserControl
+    {
+        private bool _isInit;
+        private float _currentScale;
+
+        public GesturePage()
+        {
+            this.InitializeComponent();
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+
+        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+        {
+            base.OnAttachedToVisualTree(e);
+
+            if(_isInit)
+            {
+                return;
+            }
+
+            _isInit = true;
+
+            SetPullHandlers(this.Find<Border>("TopPullZone"), false);
+            SetPullHandlers(this.Find<Border>("BottomPullZone"), true);
+            SetPullHandlers(this.Find<Border>("RightPullZone"), true);
+            SetPullHandlers(this.Find<Border>("LeftPullZone"), false);
+
+            var image = this.Find<Image>("PinchImage");
+            SetPinchHandlers(image);
+
+            var reset = this.Find<Button>("ResetButton");
+
+            reset!.Click += (s, e) =>
+            {
+                var compositionVisual = ElementComposition.GetElementVisual(image);
+
+                if(compositionVisual!= null)
+                {
+                    _currentScale = 1;
+                    compositionVisual.Scale = new Vector3(1,1,1);
+                    image.InvalidateMeasure();
+                }
+            };
+                
+        }
+
+        private void SetPinchHandlers(Control? control)
+        {
+            if (control == null)
+            {
+                return;
+            }
+
+            _currentScale = 1;
+            Vector3 currentOffset = default;
+            bool isZooming = false;
+
+            CompositionVisual? compositionVisual = null;
+
+            void InitComposition(Control visual)
+            {
+                if (compositionVisual != null)
+                {
+                    return;
+                }
+
+                compositionVisual = ElementComposition.GetElementVisual(visual);
+            }
+
+            control.LayoutUpdated += (s, e) =>
+            {
+                InitComposition(control!);
+                if (compositionVisual != null)
+                {
+                    compositionVisual.Scale = new(_currentScale, _currentScale, 1);
+
+                    if(currentOffset == default)
+                    {
+                        currentOffset = compositionVisual.Offset;
+                    }
+                }
+            };
+
+            control.AddHandler(Gestures.PinchEvent, (s, e) =>
+            {
+                InitComposition(control!);
+
+                isZooming = true;
+
+                if(compositionVisual != null)
+                {
+                    var scale = _currentScale * (float)e.Scale;
+
+                    compositionVisual.Scale = new(scale, scale, 1);
+                }
+            });
+
+            control.AddHandler(Gestures.PinchEndedEvent, (s, e) =>
+            {
+                InitComposition(control!);
+
+                isZooming = false;
+
+                if (compositionVisual != null)
+                {
+                    _currentScale = compositionVisual.Scale.X;
+                }
+            });
+
+            control.AddHandler(Gestures.ScrollGestureEvent, (s, e) =>
+            {
+                InitComposition(control!);
+
+                if (compositionVisual != null && !isZooming)
+                {
+                    currentOffset -= new Vector3((float)e.Delta.X, (float)e.Delta.Y, 0);
+
+                    compositionVisual.Offset = currentOffset;
+                }
+            });
+        }
+
+        private void SetPullHandlers(Control? control, bool inverse)
+        {
+            if (control == null)
+            {
+                return;
+            }
+
+            var ball = control.FindLogicalDescendantOfType<Border>();
+
+            Vector3 defaultOffset = default;
+
+            CompositionVisual? ballCompositionVisual = null;
+
+            if (ball != null)
+            {
+                InitComposition(ball);
+            }
+            else
+            {
+                return;
+            }
+
+            control.LayoutUpdated += (s, e) =>
+            {
+                InitComposition(ball!);
+                if (ballCompositionVisual != null)
+                {
+                    defaultOffset = ballCompositionVisual.Offset;
+                }
+            };
+
+            control.AddHandler(Gestures.PullGestureEvent, (s, e) =>
+            {
+                Vector3 center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0);
+                InitComposition(ball!);
+                if (ballCompositionVisual != null)
+                {
+                    ballCompositionVisual.Offset = defaultOffset + new System.Numerics.Vector3((float)e.Delta.X * 0.4f, (float)e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
+                }
+            });
+
+            control.AddHandler(Gestures.PullGestureEndedEvent, (s, e) =>
+            {
+                InitComposition(ball!);
+                if (ballCompositionVisual != null)
+                {
+                    ballCompositionVisual.Offset = defaultOffset;
+                }
+            });
+
+            void InitComposition(Control control)
+            {
+                if (ballCompositionVisual != null)
+                {
+                    return;
+                }
+
+                ballCompositionVisual = ElementComposition.GetElementVisual(ball);
+
+                if (ballCompositionVisual != null)
+                {
+                    var offsetAnimation = ballCompositionVisual.Compositor.CreateVector3KeyFrameAnimation();
+                    offsetAnimation.Target = "Offset";
+                    offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
+                    offsetAnimation.Duration = TimeSpan.FromMilliseconds(100);
+
+                    var implicitAnimations = ballCompositionVisual.Compositor.CreateImplicitAnimationCollection();
+                    implicitAnimations["Offset"] = offsetAnimation;
+
+                    ballCompositionVisual.ImplicitAnimations = implicitAnimations;
+                }
+            }
+        }
+    }
+}

+ 117 - 0
samples/ControlCatalog/Pages/GesturePage.xaml

@@ -0,0 +1,117 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             d:DesignHeight="800"
+             d:DesignWidth="400"
+             x:Class="ControlCatalog.Pages.GesturePage">
+  <StackPanel Orientation="Vertical"
+              Spacing="4">
+    <TextBlock FontWeight="Bold"
+               FontSize="18"
+               Margin="5">Pull Gexture (Touch / Pen)</TextBlock>
+    <TextBlock Margin="5">Pull from colored rectangles</TextBlock>
+    <Border>
+      <DockPanel HorizontalAlignment="Stretch"
+                 ClipToBounds="True"
+                 Margin="5"
+                 Height="200">
+        <Border DockPanel.Dock="Top"
+                Margin="2"
+                Name="TopPullZone"
+                Background="Transparent"
+                BorderBrush="Red"
+                HorizontalAlignment="Stretch"
+                Height="50"
+                BorderThickness="1">
+          <Border.GestureRecognizers>
+            <PullGestureRecognizer PullDirection="TopToBottom"/>
+          </Border.GestureRecognizers>
+          <Border Width="10"
+                  Height="10"
+                  HorizontalAlignment="Center"
+                  VerticalAlignment="Center"
+                  CornerRadius="5"
+                  Name="TopBall"
+                  Background="Green"/>
+        </Border>
+        <Border DockPanel.Dock="Bottom"
+                BorderBrush="Green"
+                Margin="2"
+                Background="Transparent"
+                Name="BottomPullZone"
+                HorizontalAlignment="Stretch"
+                Height="50"
+                BorderThickness="1">
+          <Border.GestureRecognizers>
+            <PullGestureRecognizer PullDirection="BottomToTop"/>
+          </Border.GestureRecognizers>
+          <Border Width="10"
+                  Name="BottomBall"
+                  HorizontalAlignment="Center"
+                  VerticalAlignment="Center"
+                  Height="10"
+                  CornerRadius="5"
+                  Background="Green"/>
+        </Border>
+        <Border DockPanel.Dock="Right"
+                Margin="2"
+                Background="Transparent"
+                Name="RightPullZone"
+                BorderBrush="Blue"
+                HorizontalAlignment="Right"
+                VerticalAlignment="Stretch"
+                Width="50"
+                BorderThickness="1">
+          <Border.GestureRecognizers>
+            <PullGestureRecognizer PullDirection="RightToLeft"/>
+          </Border.GestureRecognizers>
+          <Border Width="10"
+                  Height="10"
+                  Name="RightBall"
+                  HorizontalAlignment="Center"
+                  VerticalAlignment="Center"
+                  CornerRadius="5"
+                  Background="Green"/>
+
+        </Border>
+        <Border DockPanel.Dock="Left"
+                Margin="2"
+                Background="Transparent"
+                Name="LeftPullZone"
+                BorderBrush="Orange"
+                HorizontalAlignment="Left"
+                VerticalAlignment="Stretch"
+                Width="50"
+                BorderThickness="1">
+          <Border.GestureRecognizers>
+            <PullGestureRecognizer PullDirection="LeftToRight"/>
+          </Border.GestureRecognizers>
+          <Border Width="10"
+                  Height="10"
+                  Name="LeftBall"
+                  HorizontalAlignment="Center"
+                  VerticalAlignment="Center"
+                  CornerRadius="5"
+                  Background="Green"/>
+
+        </Border>
+      </DockPanel>
+    </Border>
+
+    <TextBlock FontWeight="Bold"
+               FontSize="18"
+               Margin="5">Pinch/Zoom Gexture (Multi Touch)</TextBlock>
+    <Border ClipToBounds="True">
+      <Image Stretch="UniformToFill"
+             Margin="5"
+             Name="PinchImage"
+             Source="/Assets/delicate-arch-896885_640.jpg">
+        <Image.GestureRecognizers>
+          <PinchGestureRecognizer/>
+          <ScrollGestureRecognizer CanHorizontallyScroll="True" CanVerticallyScroll="True"/>
+        </Image.GestureRecognizers>
+      </Image>
+    </Border>
+    <Button HorizontalAlignment="Center" Name="ResetButton">Reset</Button>
+  </StackPanel>
+</UserControl>

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

@@ -70,7 +70,7 @@ namespace ControlCatalog.Pages
                 3 => new PixelRect(new PixelPoint(bitmapWidth - cropSize.Width, 0), cropSize),
                 4 => new PixelRect(new PixelPoint(0, bitmapHeight - cropSize.Height), cropSize),
                 5 => new PixelRect(new PixelPoint(bitmapWidth - cropSize.Width, bitmapHeight - cropSize.Height), cropSize),
-                _ => PixelRect.Empty
+                _ => default
             };
             
         }

+ 0 - 1
samples/ControlCatalog/Pages/MenuPage.xaml.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Reactive;
 using System.Threading.Tasks;
 using System.Windows.Input;
 using Avalonia.Controls;

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

@@ -27,7 +27,7 @@ namespace ControlCatalog.Pages
         {
             base.OnAttachedToVisualTree(e);
 
-            _viewModel.NotificationManager = new Avalonia.Controls.Notifications.WindowNotificationManager(VisualRoot as TopLevel);
+            _viewModel.NotificationManager = new Avalonia.Controls.Notifications.WindowNotificationManager(TopLevel.GetTopLevel(this));
         }
     }
 }

+ 4 - 1
samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs

@@ -1,5 +1,8 @@
-using Avalonia.Controls;
+using System;
+using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
+using Avalonia.Markup.Xaml.MarkupExtensions;
+using Avalonia.Media.Immutable;
 using ControlCatalog.ViewModels;
 
 namespace ControlCatalog.Pages

+ 1 - 1
samples/ControlCatalog/Pages/PointerCanvas.cs

@@ -24,7 +24,7 @@ public class PointerCanvas : Control
     {
         struct CanvasPoint
         {
-            public IBrush Brush;
+            public IBrush? Brush;
             public Point Point;
             public double Radius;
             public double? Pressure;

+ 0 - 1
samples/ControlCatalog/Pages/PointerContactsTab.cs

@@ -2,7 +2,6 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Reactive.Linq;
 
 using Avalonia;
 using Avalonia.Controls;

+ 29 - 29
samples/ControlCatalog/Pages/ScreenPage.cs

@@ -36,44 +36,44 @@ namespace ControlCatalog.Pages
 
             var drawBrush = Brushes.Black;
             Pen p = new Pen(drawBrush);
-            if (screens != null)
-                foreach (Screen screen in screens)
+
+            foreach (Screen screen in screens)
+            {
+                if (screen.Bounds.X / 10f < _leftMost)
                 {
-                    if (screen.Bounds.X / 10f < _leftMost)
-                    {
-                        _leftMost = screen.Bounds.X / 10f;
-                        Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
-                        return;
-                    }
+                    _leftMost = screen.Bounds.X / 10f;
+                    Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
+                    return;
+                }
 
-                    Rect boundsRect = new Rect(screen.Bounds.X / 10f + Math.Abs(_leftMost), screen.Bounds.Y / 10f, screen.Bounds.Width / 10f,
-                                      screen.Bounds.Height / 10f);
-                    Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f,
-                                       screen.WorkingArea.Height / 10f);
-                    
-                    context.DrawRectangle(p, boundsRect);
-                    context.DrawRectangle(p, workingAreaRect);
+                Rect boundsRect = new Rect(screen.Bounds.X / 10f + Math.Abs(_leftMost), screen.Bounds.Y / 10f, screen.Bounds.Width / 10f,
+                                  screen.Bounds.Height / 10f);
+                Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f,
+                                   screen.WorkingArea.Height / 10f);
 
+                context.DrawRectangle(p, boundsRect);
+                context.DrawRectangle(p, workingAreaRect);
 
-                    var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}");
-                    context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height));
 
-                    formattedText =
-                        CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
-                    context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20));
+                var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}");
+                context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height));
 
-                    formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%");
-                    context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
+                formattedText =
+                    CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
+                context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20));
 
-                    formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}");
+                formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%");
+                context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
 
-                    context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60));
+                formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}");
 
-                    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.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));
         }

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

@@ -51,7 +51,7 @@ namespace ControlCatalog.Pages
 
         private static IBitmap LoadBitmap(string uri)
         {
-            var assets = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
+            var assets = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
             return new Bitmap(assets.Open(new Uri(uri)));
         }
     }

+ 1 - 1
samples/ControlCatalog/Pages/TextBoxPage.xaml

@@ -38,7 +38,7 @@
                  UseFloatingWatermark="True"
                  PasswordChar="*"
                  Text="Password" />
-        <TextBox Width="200" Text="Left aligned text" TextAlignment="Left" />
+        <TextBox Width="200" Text="Left aligned text" TextAlignment="Left" AcceptsTab="True" />
         <TextBox Width="200" Text="Center aligned text" TextAlignment="Center" />
         <TextBox Width="200" Text="Right aligned text" TextAlignment="Right" />
         <TextBox Width="200" Text="Custom selection brush"

+ 0 - 7
samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml

@@ -11,12 +11,5 @@
     <CheckBox Content="Title Bar" IsChecked="{Binding SystemTitleBarEnabled}" />    
     <CheckBox Content="Prefer System Chrome" IsChecked="{Binding PreferSystemChromeEnabled}" />
     <Slider Minimum="-1" Maximum="200" Value="{Binding TitleBarHeight}" />
-    <ComboBox x:Name="TransparencyLevels" SelectedIndex="{Binding TransparencyLevel}">
-      <ComboBoxItem>None</ComboBoxItem>
-      <ComboBoxItem>Transparent</ComboBoxItem>
-      <ComboBoxItem>Blur</ComboBoxItem>
-      <ComboBoxItem>AcrylicBlur</ComboBoxItem>
-      <ComboBoxItem>Mica</ComboBoxItem>
-    </ComboBox>
   </StackPanel>
 </UserControl>

+ 0 - 1
samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.ObjectModel;
 using System.Linq;
-using System.Reactive;
 using Avalonia.Controls;
 using Avalonia.Controls.Selection;
 using MiniMvvm;

+ 2 - 6
samples/ControlCatalog/ViewModels/ContextPageViewModel.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using System.Reactive;
 using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.VisualTree;
@@ -56,12 +55,9 @@ namespace ControlCatalog.ViewModels
 
             var result = await window.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions() { AllowMultiple = true });
 
-            if (result != null)
+            foreach (var file in result)
             {
-                foreach (var file in result)
-                {
-                    System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}");
-                }
+                System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}");
             }
         }
 

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

@@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels
                 .Select(x => new StandardCursorModel(x))
                 .ToList();
 
-            var loader = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
+            var loader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
             var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png"));
             var bitmap = new Bitmap(s);
             CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16));

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

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.ObjectModel;
 using System.Linq;
-using System.Reactive;
 using Avalonia.Controls;
 using Avalonia.Controls.Selection;
 using ControlCatalog.Pages;

+ 1 - 10
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@@ -1,9 +1,9 @@
-using System.Reactive;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.Notifications;
 using Avalonia.Dialogs;
 using Avalonia.Platform;
+using Avalonia.Reactive;
 using System;
 using System.ComponentModel.DataAnnotations;
 using MiniMvvm;
@@ -12,12 +12,9 @@ namespace ControlCatalog.ViewModels
 {
     class MainWindowViewModel : ViewModelBase
     {
-        private IManagedNotificationManager _notificationManager;
-
         private bool _isMenuItemChecked = true;
         private WindowState _windowState;
         private WindowState[] _windowStates = Array.Empty<WindowState>();
-        private int _transparencyLevel;
         private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome;
         private bool _extendClientAreaEnabled;
         private bool _systemTitleBarEnabled;
@@ -77,12 +74,6 @@ namespace ControlCatalog.ViewModels
             TitleBarHeight = -1;
         }        
 
-        public int TransparencyLevel
-        {
-            get { return _transparencyLevel; }
-            set { this.RaiseAndSetIfChanged(ref _transparencyLevel, value); }
-        }        
-
         public ExtendClientAreaChromeHints ChromeHints
         {
             get { return _chromeHints; }

+ 0 - 2
samples/ControlCatalog/ViewModels/MenuPageViewModel.cs

@@ -1,6 +1,4 @@
 using System.Collections.Generic;
-using System.Reactive;
-using System.Reactive.Linq;
 using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.VisualTree;

+ 1 - 2
samples/ControlCatalog/ViewModels/NotificationViewModel.cs

@@ -1,5 +1,4 @@
-using System.Reactive;
-using Avalonia.Controls.Notifications;
+using Avalonia.Controls.Notifications;
 using MiniMvvm;
 
 namespace ControlCatalog.ViewModels

+ 3 - 1
samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs

@@ -1,3 +1,5 @@
+using System;
+using System.Runtime.InteropServices;
 using Avalonia;
 using Avalonia.Platform;
 using MiniMvvm;
@@ -13,7 +15,7 @@ public class PlatformInformationViewModel : ViewModelBase
 
         if (runtimeInfo is { } info)
         {
-            if (info.IsBrowser)
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")))
             {
                 if (info.IsDesktop)
                 {

+ 0 - 1
samples/ControlCatalog/ViewModels/RefreshContainerViewModel.cs

@@ -1,6 +1,5 @@
 using System.Collections.ObjectModel;
 using System.Linq;
-using System.Reactive;
 using System.Threading.Tasks;
 using Avalonia.Controls.Notifications;
 using ControlCatalog.Pages;

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

@@ -19,7 +19,7 @@ namespace ControlCatalog.ViewModels
     {
         public TransitioningContentControlPageViewModel()
         {
-            var assetLoader = AvaloniaLocator.Current?.GetService<IAssetLoader>()!;
+            var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
 
             var images = new string[] 
             { 

+ 0 - 1
samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.ObjectModel;
 using System.Linq;
-using System.Reactive;
 using Avalonia.Controls;
 using MiniMvvm;
 

+ 2 - 3
samples/Directory.Build.props

@@ -2,9 +2,8 @@
   <PropertyGroup>
       <IsPackable>false</IsPackable>
       <AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
+      <EnableNETAnalyzers>false</EnableNETAnalyzers>
+      <LangVersion>11</LangVersion>
   </PropertyGroup>
   <Import Project="..\build\SharedVersion.props" />
-  <PropertyGroup>
-    <EnableNETAnalyzers>false</EnableNETAnalyzers>
-  </PropertyGroup>  
 </Project>

+ 3 - 1
samples/MiniMvvm/MiniMvvm.csproj

@@ -2,5 +2,7 @@
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
   </PropertyGroup>
-  <Import Project="..\..\build\Rx.props" />
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
+  </ItemGroup>
 </Project>

+ 8 - 6
samples/MiniMvvm/PropertyChangedExtensions.cs

@@ -1,8 +1,8 @@
 using System;
 using System.ComponentModel;
 using System.Linq.Expressions;
-using System.Reactive.Linq;
 using System.Reflection;
+using Avalonia.Reactive;
 
 namespace MiniMvvm
 {
@@ -92,11 +92,13 @@ namespace MiniMvvm
             Expression<Func<TModel, T3>> v3,
             Func<T1, T2, T3, TRes> cb
         ) where TModel : INotifyPropertyChanged =>
-            Observable.CombineLatest(
-                model.WhenAnyValue(v1),
-                model.WhenAnyValue(v2),
-                model.WhenAnyValue(v3),
-                cb);
+            model.WhenAnyValue(v1)
+                .CombineLatest(
+                    model.WhenAnyValue(v2),
+                    (l, r) => (l, r))
+                .CombineLatest(
+                    model.WhenAnyValue(v3),
+                    (t, r) => cb(t.l, t.r, r));
 
         public static IObservable<ValueTuple<T1, T2, T3>> WhenAnyValue<TModel, T1, T2, T3>(this TModel model,
             Expression<Func<TModel, T1>> v1,

+ 0 - 1
samples/MiniMvvm/ViewModelBase.cs

@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.ComponentModel;
-using System.Reactive.Joins;
 using System.Runtime.CompilerServices;
 
 namespace MiniMvvm

+ 1 - 0
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@@ -11,4 +11,5 @@
     <ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
   </ItemGroup>
 
+  <Import Project="..\..\build\Rx.props" />
 </Project>

+ 2 - 1
samples/Previewer/Previewer.csproj

@@ -12,7 +12,8 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
   </ItemGroup>
-  
+
+  <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\SampleApp.props" />
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />
 </Project>

+ 0 - 1
samples/ReactiveUIDemo/ReactiveUIDemo.csproj

@@ -23,6 +23,5 @@
   <Import Project="..\..\build\SampleApp.props" />
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />
   <Import Project="..\..\build\BuildTargets.targets" />
-  <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\ReactiveUI.props" />
 </Project>

+ 0 - 5
samples/interop/Direct3DInteropSample/App.paml

@@ -1,5 +0,0 @@
-<Application xmlns="https://github.com/avaloniaui">
-  <Application.Styles>
-    <SimpleTheme Mode="Light" />
-  </Application.Styles>
-</Application>

+ 0 - 21
samples/interop/Direct3DInteropSample/App.paml.cs

@@ -1,21 +0,0 @@
-using Avalonia;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Markup.Xaml;
-
-namespace Direct3DInteropSample
-{
-    public class App : Application
-    {
-        public override void Initialize()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-
-        public override void OnFrameworkInitializationCompleted()
-        {
-            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-                desktop.MainWindow = new MainWindow();
-            base.OnFrameworkInitializationCompleted();
-        }
-    }
-}

+ 0 - 32
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@@ -1,32 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <OutputType>Exe</OutputType>
-        <TargetFramework>net461</TargetFramework>
-    </PropertyGroup>
-    <ItemGroup>
-        <PackageReference Include="SharpDX.Mathematics" Version="4.0.1" />
-        <PackageReference Include="SharpDX.D3DCompiler" Version="4.0.1" />
-        <Compile Update="**\*.paml.cs">
-            <DependentUpon>%(Filename)</DependentUpon>
-        </Compile>
-        <EmbeddedResource Include="**\*.paml">
-            <SubType>Designer</SubType>
-        </EmbeddedResource>
-    </ItemGroup>
-    <ItemGroup>
-      <None Remove="MiniCube.fx" />
-    </ItemGroup>
-    <ItemGroup>
-      <EmbeddedResource Include="MiniCube.fx">
-        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-      </EmbeddedResource>
-    </ItemGroup>
-    <ItemGroup>
-        <ProjectReference Include="..\..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
-        <ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
-        <ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
-        <ProjectReference Include="..\..\MiniMvvm\MiniMvvm.csproj" />
-    </ItemGroup>
-    <Import Project="..\..\..\build\Rx.props" />
-    <Import Project="..\..\..\build\ReferenceCoreLibraries.props" />
-</Project>

+ 0 - 283
samples/interop/Direct3DInteropSample/MainWindow.cs

@@ -1,283 +0,0 @@
-using System;
-
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Direct2D1;
-using Avalonia.Direct2D1.Media;
-using Avalonia.Markup.Xaml;
-using Avalonia.Platform;
-using Avalonia.Rendering;
-
-using SharpDX;
-using SharpDX.D3DCompiler;
-using SharpDX.Direct2D1;
-using SharpDX.Direct3D;
-using SharpDX.Direct3D11;
-using SharpDX.DXGI;
-
-using AlphaMode = SharpDX.Direct2D1.AlphaMode;
-using Buffer = SharpDX.Direct3D11.Buffer;
-using DeviceContext = SharpDX.Direct2D1.DeviceContext;
-using Factory2 = SharpDX.DXGI.Factory2;
-using InputElement = SharpDX.Direct3D11.InputElement;
-using Matrix = SharpDX.Matrix;
-using PixelFormat = SharpDX.Direct2D1.PixelFormat;
-using Resource = SharpDX.Direct3D11.Resource;
-
-namespace Direct3DInteropSample
-{
-    public class MainWindow : Window
-    {
-        Texture2D _backBuffer;
-        RenderTargetView _renderView;
-        Texture2D _depthBuffer;
-        DepthStencilView _depthView;
-        private readonly SwapChain _swapChain;
-        private SwapChainDescription1 _desc;
-        private Matrix _proj = Matrix.Identity;
-        private readonly Matrix _view;
-        private Buffer _contantBuffer;
-        private DeviceContext _deviceContext;
-        private readonly MainWindowViewModel _model;
-
-        public MainWindow()
-        {
-            DataContext = _model = new MainWindowViewModel();
-
-            _desc = new SwapChainDescription1()
-            {
-                BufferCount = 1,
-                Width = (int)ClientSize.Width,
-                Height = (int)ClientSize.Height,
-                Format = Format.R8G8B8A8_UNorm,                
-                SampleDescription = new SampleDescription(1, 0),
-                SwapEffect = SwapEffect.Discard,
-                Usage = Usage.RenderTargetOutput
-            };
-
-            using (var factory = Direct2D1Platform.DxgiDevice.Adapter.GetParent<Factory2>())
-            {
-                _swapChain = new SwapChain1(factory, Direct2D1Platform.DxgiDevice, PlatformImpl?.Handle.Handle ?? IntPtr.Zero, ref _desc);
-            }              
-
-            _deviceContext = new DeviceContext(Direct2D1Platform.Direct2D1Device, DeviceContextOptions.None)
-            {
-                DotsPerInch = new Size2F(96, 96)
-            };
-
-            CreateMesh();
-
-            _view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY);
-
-            this.GetObservable(ClientSizeProperty).Subscribe(Resize);
-
-            Resize(ClientSize);
-
-            AvaloniaXamlLoader.Load(this);
-
-            Background = Avalonia.Media.Brushes.Transparent;
-        }
-
-
-        protected override void HandlePaint(Rect rect)
-        {
-            var viewProj = Matrix.Multiply(_view, _proj);
-            var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
-
-            // Clear views
-            context.ClearDepthStencilView(_depthView, DepthStencilClearFlags.Depth, 1.0f, 0);
-            context.ClearRenderTargetView(_renderView, Color.White);
-
-            // Update WorldViewProj Matrix
-            var worldViewProj = Matrix.RotationX((float)_model.RotationX) * Matrix.RotationY((float)_model.RotationY)
-                                                                          * Matrix.RotationZ((float)_model.RotationZ)
-                                                                          * Matrix.Scaling((float)_model.Zoom)
-                                                                          * viewProj;
-            worldViewProj.Transpose();
-            context.UpdateSubresource(ref worldViewProj, _contantBuffer);
-
-            // Draw the cube
-            context.Draw(36, 0);
-            base.HandlePaint(rect);
-
-            // Present!
-            _swapChain.Present(0, PresentFlags.None);
-        }
-
-        private void CreateMesh()
-        {
-            var device = Direct2D1Platform.Direct3D11Device;
-
-            // Compile Vertex and Pixel shaders
-            var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0");
-            var vertexShader = new VertexShader(device, vertexShaderByteCode);
-
-            var pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "PS", "ps_4_0");
-            var pixelShader = new PixelShader(device, pixelShaderByteCode);
-
-            var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode);
-
-            var inputElements = new[]
-                                {
-                                    new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0),
-                                    new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0)
-                                };
-
-            // Layout from VertexShader input signature
-            var layout = new InputLayout(
-                device,
-                signature,
-                inputElements);
-
-            // Instantiate Vertex buffer from vertex data
-            var vertices = Buffer.Create(
-                device,
-                BindFlags.VertexBuffer,
-                new[]
-                {
-                    new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front
-                    new Vector4(-1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
-                    new Vector4( 1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
-                    new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
-                    new Vector4( 1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
-                    new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
-
-                    new Vector4(-1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK
-                    new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4(-1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4(-1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4( 1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
-
-                    new Vector4(-1.0f, 1.0f, -1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top
-                    new Vector4(-1.0f, 1.0f,  1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4( 1.0f, 1.0f,  1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4(-1.0f, 1.0f, -1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4( 1.0f, 1.0f,  1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4( 1.0f, 1.0f, -1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
-
-                    new Vector4(-1.0f, -1.0f, -1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom
-                    new Vector4( 1.0f, -1.0f,  1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4(-1.0f, -1.0f,  1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4(-1.0f, -1.0f, -1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4( 1.0f, -1.0f, -1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
-                    new Vector4( 1.0f, -1.0f,  1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
-
-                    new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left
-                    new Vector4(-1.0f, -1.0f,  1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4(-1.0f,  1.0f,  1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4(-1.0f,  1.0f,  1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
-                    new Vector4(-1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
-
-                    new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right
-                    new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
-                    new Vector4( 1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
-                    new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
-                    new Vector4( 1.0f,  1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
-                    new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
-                });
-
-            // Create Constant Buffer
-            _contantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
-
-            var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
-
-            // Prepare All the stages
-            context.InputAssembler.InputLayout = layout;
-            context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
-            context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, Utilities.SizeOf<Vector4>() * 2, 0));
-            context.VertexShader.SetConstantBuffer(0, _contantBuffer);
-            context.VertexShader.Set(vertexShader);
-            context.PixelShader.Set(pixelShader);
-        }
-
-        private void Resize(Size size)
-        {
-            Utilities.Dispose(ref _deviceContext);
-            Utilities.Dispose(ref _backBuffer);
-            Utilities.Dispose(ref _renderView);
-            Utilities.Dispose(ref _depthBuffer);
-            Utilities.Dispose(ref _depthView);
-            var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
-
-            // Resize the backbuffer
-            _swapChain.ResizeBuffers(0, 0, 0, Format.Unknown, SwapChainFlags.None);
-
-            // Get the backbuffer from the swapchain
-            _backBuffer = Resource.FromSwapChain<Texture2D>(_swapChain, 0);
-
-            // Renderview on the backbuffer
-            _renderView = new RenderTargetView(Direct2D1Platform.Direct3D11Device, _backBuffer);
-
-            // Create the depth buffer
-            _depthBuffer = new Texture2D(
-                Direct2D1Platform.Direct3D11Device,
-                new Texture2DDescription()
-                {
-                    Format = Format.D32_Float_S8X24_UInt,
-                    ArraySize = 1,
-                    MipLevels = 1,
-                    Width = (int)size.Width,
-                    Height = (int)size.Height,
-                    SampleDescription = new SampleDescription(1, 0),
-                    Usage = ResourceUsage.Default,
-                    BindFlags = BindFlags.DepthStencil,
-                    CpuAccessFlags = CpuAccessFlags.None,
-                    OptionFlags = ResourceOptionFlags.None
-                });
-
-            // Create the depth buffer view
-            _depthView = new DepthStencilView(Direct2D1Platform.Direct3D11Device, _depthBuffer);
-
-            // Setup targets and viewport for rendering
-            context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f));
-            context.OutputMerger.SetTargets(_depthView, _renderView);
-
-            // Setup new projection matrix with correct aspect ratio
-            _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f);
-
-            using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0))
-            {
-                var renderTarget = new SharpDX.Direct2D1.RenderTarget(
-                    Direct2D1Platform.Direct2D1Factory,
-                    dxgiBackBuffer,
-                    new RenderTargetProperties
-                    {
-                        DpiX = 96,
-                        DpiY = 96,
-                        Type = RenderTargetType.Default,
-                        PixelFormat = new PixelFormat(
-                            Format.Unknown,
-                            AlphaMode.Premultiplied)
-                    });
-
-                _deviceContext = renderTarget.QueryInterface<DeviceContext>();
-
-                renderTarget.Dispose();
-            }
-        }
-
-        private class D3DRenderTarget : IRenderTarget
-        {
-            private readonly MainWindow _window;
-
-            public D3DRenderTarget(MainWindow window)
-            {
-                _window = window;
-            }
-
-            public void Dispose()
-            {
-            }
-
-            public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
-            {
-                return new DrawingContextImpl(visualBrushRenderer, null, _window._deviceContext);
-            }
-        }
-
-
-        protected override IRenderTarget CreateRenderTarget() => new D3DRenderTarget(this);
-    }
-}

+ 0 - 14
samples/interop/Direct3DInteropSample/MainWindow.paml

@@ -1,14 +0,0 @@
-<Window xmlns="https://github.com/avaloniaui" Background="White" Title="Avalonia Direct3D Demo">
-    <Grid ColumnDefinitions="*,Auto" Margin="20">
-        <StackPanel Grid.Column="1" MinWidth="200">
-            <TextBlock>Rotation X</TextBlock>
-            <Slider Value="{Binding RotationX, Mode=TwoWay}" Maximum="10"/>
-            <TextBlock>Rotation Y</TextBlock>
-            <Slider Value="{Binding RotationY, Mode=TwoWay}" Maximum="10"/>
-            <TextBlock>Rotation Z</TextBlock>
-            <Slider Value="{Binding RotationZ, Mode=TwoWay}" Maximum="10"/>
-            <TextBlock>Zoom</TextBlock>
-            <Slider Value="{Binding Zoom, Mode=TwoWay}" Maximum="3" Minimum="0.5"/>
-        </StackPanel>
-    </Grid>
-</Window>

+ 0 - 45
samples/interop/Direct3DInteropSample/MainWindowViewModel.cs

@@ -1,45 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using MiniMvvm;
-
-namespace Direct3DInteropSample
-{
-    public class MainWindowViewModel : ViewModelBase
-    {
-        private double _rotationX;
-
-        public double RotationX
-        {
-            get { return _rotationX; }
-            set { this.RaiseAndSetIfChanged(ref _rotationX, value); }
-        }
-
-        private double _rotationY = 1;
-
-        public double RotationY
-        {
-            get { return _rotationY; }
-            set { this.RaiseAndSetIfChanged(ref _rotationY, value); }
-        }
-
-        private double _rotationZ = 2;
-
-        public double RotationZ
-        {
-            get { return _rotationZ; }
-            set { this.RaiseAndSetIfChanged(ref _rotationZ, value); }
-        }
-
-
-        private double _zoom = 1;
-
-        public double Zoom
-        {
-            get { return _zoom; }
-            set { this.RaiseAndSetIfChanged(ref _zoom, value); }
-        }
-    }
-}

+ 0 - 47
samples/interop/Direct3DInteropSample/MiniCube.fx

@@ -1,47 +0,0 @@
-// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel
-// 
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-// 
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-struct VS_IN
-{
-	float4 pos : POSITION;
-	float4 col : COLOR;
-};
-
-struct PS_IN
-{
-	float4 pos : SV_POSITION;
-	float4 col : COLOR;
-};
-
-float4x4 worldViewProj;
-
-PS_IN VS( VS_IN input )
-{
-	PS_IN output = (PS_IN)0;
-	
-	output.pos = mul(input.pos, worldViewProj);
-	output.col = input.col;
-	
-	return output;
-}
-
-float4 PS( PS_IN input ) : SV_Target
-{
-	return input.col;
-}

+ 0 - 16
samples/interop/Direct3DInteropSample/Program.cs

@@ -1,16 +0,0 @@
-using Avalonia;
-
-namespace Direct3DInteropSample
-{
-    class Program
-    {
-        public static AppBuilder BuildAvaloniaApp()
-            => AppBuilder.Configure<App>()
-                .With(new Win32PlatformOptions { UseDeferredRendering = false })
-                .UseWin32()
-                .UseDirect2D1();
-
-        public static int Main(string[] args)
-            => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
-    }
-}

+ 0 - 2
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@@ -15,6 +15,4 @@
       <Name>ControlCatalog</Name>
     </ProjectReference>
   </ItemGroup>
-
-  <Import Project="..\..\..\build\Rx.props" />
 </Project>

+ 1 - 1
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -15,7 +15,7 @@ namespace Avalonia
 {
     public static class AndroidApplicationExtensions
     {
-        public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
+        public static AppBuilder UseAndroid(this AppBuilder builder)
         {
             return builder
                 .UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android")

+ 1 - 1
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@@ -1,10 +1,10 @@
 using System;
-using System.Reactive.Disposables;
 using System.Threading;
 
 using Android.OS;
 
 using Avalonia.Platform;
+using Avalonia.Reactive;
 using Avalonia.Threading;
 
 using App = Android.App.Application;

+ 10 - 4
src/Android/Avalonia.Android/AvaloniaSplashActivity.cs

@@ -1,6 +1,5 @@
 using Android.OS;
 using AndroidX.AppCompat.App;
-using AndroidX.Lifecycle;
 
 namespace Avalonia.Android
 {
@@ -8,15 +7,22 @@ namespace Avalonia.Android
     {
         protected abstract AppBuilder CreateAppBuilder();
 
+        private static AppBuilder s_appBuilder;
+
         protected override void OnCreate(Bundle? savedInstanceState)
         {
             base.OnCreate(savedInstanceState);
 
-            var builder = CreateAppBuilder();
+            if (s_appBuilder == null)
+            {
+                var builder = CreateAppBuilder();
+
+                var lifetime = new SingleViewLifetime();
 
-            var lifetime = new SingleViewLifetime();
+                builder.SetupWithLifetime(lifetime);
 
-            builder.SetupWithLifetime(lifetime);
+                s_appBuilder = builder;
+            }
         }
     }
 

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

@@ -24,6 +24,8 @@ namespace Avalonia.Android
 
             _root = new EmbeddableControlRoot(_view);
             _root.Prepare();
+
+            this.SetBackgroundColor(global::Android.Graphics.Color.Transparent);
         }
 
         internal TopLevelImpl TopLevelImpl => _view;

+ 1 - 1
src/Android/Avalonia.Android/ChoreographerTimer.cs

@@ -1,11 +1,11 @@
 using System;
 using System.Collections.Generic;
-using System.Reactive.Disposables;
 using System.Threading.Tasks;
 
 using Android.OS;
 using Android.Views;
 
+using Avalonia.Reactive;
 using Avalonia.Rendering;
 
 using Java.Lang;

+ 2 - 0
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@@ -96,12 +96,14 @@ namespace Avalonia.Android.Platform.SkiaPlatform
             public IntPtr bits;
 
             // Do not touch.
+#pragma warning disable CA1823 // Avoid unused private fields
             uint reserved1;
             uint reserved2;
             uint reserved3;
             uint reserved4;
             uint reserved5;
             uint reserved6;
+#pragma warning restore CA1823 // Avoid unused private fields
         }
     }
 }

+ 1 - 0
src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs

@@ -22,6 +22,7 @@ namespace Avalonia.Android
         public InvalidationAwareSurfaceView(Context context) : base(context)
         {
             Holder.AddCallback(this);
+            Holder.SetFormat(global::Android.Graphics.Format.Transparent);
             _handler = new Handler(context.MainLooper);
         }
 

+ 83 - 2
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -26,6 +26,7 @@ using Avalonia.Rendering.Composition;
 using Java.Lang;
 using Math = System.Math;
 using AndroidRect = Android.Graphics.Rect;
+using Android.Graphics.Drawables;
 
 namespace Avalonia.Android.Platform.SkiaPlatform
 {
@@ -283,7 +284,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         public Action LostFocus { get; set; }
         public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
 
-        public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
+        public WindowTransparencyLevel TransparencyLevel { get; private set; }
 
         public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);
 
@@ -301,7 +302,87 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
         public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
         {
-            throw new NotImplementedException();
+            if (TransparencyLevel != transparencyLevel)
+            {
+                bool isBelowR = Build.VERSION.SdkInt < BuildVersionCodes.R;
+                bool isAboveR = Build.VERSION.SdkInt > BuildVersionCodes.R;
+                if (_view.Context is AvaloniaMainActivity activity)
+                {
+                    switch (transparencyLevel)
+                    {
+                        case WindowTransparencyLevel.AcrylicBlur:
+                        case WindowTransparencyLevel.ForceAcrylicBlur:
+                        case WindowTransparencyLevel.Mica:
+                        case WindowTransparencyLevel.None:
+                            if (!isBelowR)
+                            {
+                                activity.SetTranslucent(false);
+                            }
+                            if (isAboveR)
+                            {
+                                activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
+
+                                var attr = activity.Window?.Attributes;
+                                if (attr != null)
+                                {
+                                    attr.BlurBehindRadius = 0;
+
+                                    activity.Window.Attributes = attr;
+                                }
+                            }
+                            activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White));
+
+                            if(transparencyLevel != WindowTransparencyLevel.None)
+                            {
+                                return;
+                            }
+                            break;
+                        case WindowTransparencyLevel.Transparent:
+                            if (!isBelowR)
+                            {
+                                activity.SetTranslucent(true);
+                            }
+                            if (isAboveR)
+                            {
+                                activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
+
+                                var attr = activity.Window?.Attributes;
+                                if (attr != null)
+                                {
+                                    attr.BlurBehindRadius = 0;
+
+                                    activity.Window.Attributes = attr;
+                                }
+                            }
+                            activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
+                            break;
+                        case WindowTransparencyLevel.Blur:
+                            if (isAboveR)
+                            {
+                                activity.SetTranslucent(true);
+                                activity.Window?.AddFlags(WindowManagerFlags.BlurBehind);
+
+                                var attr = activity.Window?.Attributes;
+                                if (attr != null)
+                                {
+                                    attr.BlurBehindRadius = 120;
+
+                                    activity.Window.Attributes = attr;
+                                }
+                                activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
+                            }
+                            else
+                            {
+                                activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
+                                activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White));
+
+                                return;
+                            }
+                            break;
+                    }
+                    TransparencyLevel = transparencyLevel;
+                }
+            }
         }
     }
 

+ 1 - 1
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs

@@ -38,7 +38,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
                 return null;
             }
 
-            var eventTime = (ulong)DateTime.Now.Millisecond;
+            var eventTime = (ulong)e.EventTime;
             var inputRoot = _view.InputRoot;
             var actionMasked = e.ActionMasked;
             var modifiers = GetModifiers(e.MetaState, e.ButtonState);

+ 1 - 2
src/Avalonia.Base/Animation/Animation.cs

@@ -2,8 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
+using Avalonia.Reactive;
 using System.Threading;
 using System.Threading.Tasks;
 

+ 1 - 4
src/Avalonia.Base/Animation/AnimationInstance`1.cs

@@ -1,11 +1,8 @@
 using System;
 using System.Linq;
-using System.Reactive.Linq;
+using Avalonia.Reactive;
 using Avalonia.Animation.Animators;
-using Avalonia.Animation.Utils;
 using Avalonia.Data;
-using Avalonia.Reactive;
-using JetBrains.Annotations;
 
 namespace Avalonia.Animation
 {

+ 1 - 1
src/Avalonia.Base/Animation/AnimatorKeyFrame.cs

@@ -63,7 +63,7 @@ namespace Avalonia.Animation
             }
             else
             {
-                return this.Bind(ValueProperty, ObservableEx.SingleValue(value).ToBinding(), targetControl);
+                return this.Bind(ValueProperty, Observable.SingleValue(value).ToBinding(), targetControl);
             }
         }
 

+ 0 - 2
src/Avalonia.Base/Animation/Animators/Animator`1.cs

@@ -1,8 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
 using Avalonia.Animation.Utils;
 using Avalonia.Collections;
 using Avalonia.Data;

+ 1 - 1
src/Avalonia.Base/Animation/Animators/ColorAnimator.cs

@@ -2,7 +2,7 @@
 // and adopted from LottieSharp Project (https://github.com/ascora/LottieSharp).
 
 using System;
-using System.Reactive.Disposables;
+using Avalonia.Reactive;
 using Avalonia.Logging;
 using Avalonia.Media;
 

+ 1 - 1
src/Avalonia.Base/Animation/Animators/TransformAnimator.cs

@@ -1,5 +1,5 @@
 using System;
-using System.Reactive.Disposables;
+using Avalonia.Reactive;
 using Avalonia.Logging;
 using Avalonia.Media;
 using Avalonia.Media.Transformation;

+ 1 - 0
src/Avalonia.Base/Animation/Clock.cs

@@ -1,4 +1,5 @@
 using System;
+using Avalonia.Reactive;
 
 namespace Avalonia.Animation
 {

+ 2 - 2
src/Avalonia.Base/Animation/CrossFade.cs

@@ -1,6 +1,6 @@
 using System;
 using System.Collections.Generic;
-using System.Reactive.Disposables;
+using Avalonia.Reactive;
 using System.Threading;
 using System.Threading.Tasks;
 using Avalonia.Animation.Easings;
@@ -108,7 +108,7 @@ namespace Avalonia.Animation
             }
 
             var tasks = new List<Task>();
-            using (var disposables = new CompositeDisposable())
+            using (var disposables = new CompositeDisposable(1))
             {
                 if (to != null)
                 {

+ 14 - 2
src/Avalonia.Base/Avalonia.Base.csproj

@@ -14,8 +14,6 @@
   </ItemGroup>
   <Import Project="..\..\build\Base.props" />
   <Import Project="..\..\build\Binding.props" />
-  <Import Project="..\..\build\Rx.props" />
-  <Import Project="..\..\build\JetBrains.Annotations.props" />
   <Import Project="..\..\build\System.Memory.props" />
   <Import Project="..\..\build\ApiDiff.props" />
   <Import Project="..\..\build\NullableEnable.props" />
@@ -29,12 +27,22 @@
 
   <ItemGroup Label="InternalsVisibleTo">
     <InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Desktop, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Controls, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Markup, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.OpenGL, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Skia, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Controls.ColorPicker, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Controls.DataGrid, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Headless, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Native, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.FreeDesktop, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Browser, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.OpenGL, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Skia, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
@@ -46,8 +54,12 @@
     <InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Win32, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Android, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.iOS, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="MiniMvvm, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="ControlCatalog, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />
   </ItemGroup>
   

+ 1 - 1
src/Avalonia.Base/AvaloniaObject.cs

@@ -621,7 +621,7 @@ namespace Avalonia
         /// <param name="oldValue">The old property value.</param>
         /// <param name="newValue">The new property value.</param>
         /// <param name="priority">The priority of the binding that produced the value.</param>
-        private protected void RaisePropertyChanged<T>(
+        protected void RaisePropertyChanged<T>(
             DirectPropertyBase<T> property,
             Optional<T> oldValue,
             BindingValue<T> newValue,

+ 55 - 121
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@@ -1,10 +1,6 @@
 using System;
-using System.Reactive;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-using System.Reactive.Subjects;
-using Avalonia.Data;
 using Avalonia.Reactive;
+using Avalonia.Data;
 
 namespace Avalonia
 {
@@ -127,108 +123,6 @@ namespace Avalonia
                 property ?? throw new ArgumentNullException(nameof(property)));
         }
 
-        /// <summary>
-        /// Gets a subject for an <see cref="AvaloniaProperty"/>.
-        /// </summary>
-        /// <param name="o">The object.</param>
-        /// <param name="property">The property.</param>
-        /// <param name="priority">
-        /// The priority with which binding values are written to the object.
-        /// </param>
-        /// <returns>
-        /// An <see cref="ISubject{Object}"/> which can be used for two-way binding to/from the 
-        /// property.
-        /// </returns>
-        public static ISubject<object?> GetSubject(
-            this AvaloniaObject o,
-            AvaloniaProperty property,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            return Subject.Create<object?>(
-                Observer.Create<object?>(x => o.SetValue(property, x, priority)),
-                o.GetObservable(property));
-        }
-
-        /// <summary>
-        /// Gets a subject for an <see cref="AvaloniaProperty"/>.
-        /// </summary>
-        /// <typeparam name="T">The property type.</typeparam>
-        /// <param name="o">The object.</param>
-        /// <param name="property">The property.</param>
-        /// <param name="priority">
-        /// The priority with which binding values are written to the object.
-        /// </param>
-        /// <returns>
-        /// An <see cref="ISubject{T}"/> which can be used for two-way binding to/from the 
-        /// property.
-        /// </returns>
-        public static ISubject<T> GetSubject<T>(
-            this AvaloniaObject o,
-            AvaloniaProperty<T> property,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            return Subject.Create<T>(
-                Observer.Create<T>(x => o.SetValue(property, x, priority)),
-                o.GetObservable(property));
-        }
-
-        /// <summary>
-        /// Gets a subject for a <see cref="AvaloniaProperty"/>.
-        /// </summary>
-        /// <param name="o">The object.</param>
-        /// <param name="property">The property.</param>
-        /// <param name="priority">
-        /// The priority with which binding values are written to the object.
-        /// </param>
-        /// <returns>
-        /// An <see cref="ISubject{Object}"/> which can be used for two-way binding to/from the 
-        /// property.
-        /// </returns>
-        public static ISubject<BindingValue<object?>> GetBindingSubject(
-            this AvaloniaObject o,
-            AvaloniaProperty property,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            return Subject.Create<BindingValue<object?>>(
-                Observer.Create<BindingValue<object?>>(x =>
-                {
-                    if (x.HasValue)
-                    {
-                        o.SetValue(property, x.Value, priority);
-                    }
-                }),
-                o.GetBindingObservable(property));
-        }
-
-        /// <summary>
-        /// Gets a subject for a <see cref="AvaloniaProperty"/>.
-        /// </summary>
-        /// <typeparam name="T">The property type.</typeparam>
-        /// <param name="o">The object.</param>
-        /// <param name="property">The property.</param>
-        /// <param name="priority">
-        /// The priority with which binding values are written to the object.
-        /// </param>
-        /// <returns>
-        /// An <see cref="ISubject{T}"/> which can be used for two-way binding to/from the 
-        /// property.
-        /// </returns>
-        public static ISubject<BindingValue<T>> GetBindingSubject<T>(
-            this AvaloniaObject o,
-            AvaloniaProperty<T> property,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            return Subject.Create<BindingValue<T>>(
-                Observer.Create<BindingValue<T>>(x =>
-                {
-                    if (x.HasValue)
-                    {
-                        o.SetValue(property, x.Value, priority);
-                    }
-                }),
-                o.GetBindingObservable(property));
-        }
-
         /// <summary>
         /// Binds an <see cref="AvaloniaProperty"/> to an observable.
         /// </summary>
@@ -407,13 +301,7 @@ namespace Avalonia
             Action<TTarget, AvaloniaPropertyChangedEventArgs> action)
             where TTarget : AvaloniaObject
         {
-            return observable.Subscribe(e =>
-            {
-                if (e.Sender is TTarget target)
-                {
-                    action(target, e);
-                }
-            });
+            return observable.Subscribe(new ClassHandlerObserver<TTarget>(action));
         }
 
         /// <summary>
@@ -431,13 +319,7 @@ namespace Avalonia
             this IObservable<AvaloniaPropertyChangedEventArgs<TValue>> observable,
             Action<TTarget, AvaloniaPropertyChangedEventArgs<TValue>> action) where TTarget : AvaloniaObject
         {
-            return observable.Subscribe(e =>
-            {
-                if (e.Sender is TTarget target)
-                {
-                    action(target, e);
-                }
-            });
+            return observable.Subscribe(new ClassHandlerObserver<TTarget, TValue>(action));
         }
 
         private class BindingAdaptor : IBinding
@@ -458,5 +340,57 @@ namespace Avalonia
                 return InstancedBinding.OneWay(_source);
             }
         }
+        
+        private class ClassHandlerObserver<TTarget, TValue> : IObserver<AvaloniaPropertyChangedEventArgs<TValue>>
+        {
+            private readonly Action<TTarget, AvaloniaPropertyChangedEventArgs<TValue>> _action;
+
+            public ClassHandlerObserver(Action<TTarget, AvaloniaPropertyChangedEventArgs<TValue>> action)
+            {
+                _action = action;
+            }
+
+            public void OnCompleted()
+            {
+            }
+
+            public void OnError(Exception error)
+            {
+            }
+
+            public void OnNext(AvaloniaPropertyChangedEventArgs<TValue> value)
+            {
+                if (value.Sender is TTarget target)
+                {
+                    _action(target, value);
+                }
+            }
+        }
+
+        private class ClassHandlerObserver<TTarget> : IObserver<AvaloniaPropertyChangedEventArgs>
+        {
+            private readonly Action<TTarget, AvaloniaPropertyChangedEventArgs> _action;
+
+            public ClassHandlerObserver(Action<TTarget, AvaloniaPropertyChangedEventArgs> action)
+            {
+                _action = action;
+            }
+
+            public void OnCompleted()
+            {
+            }
+
+            public void OnError(Exception error)
+            {
+            }
+
+            public void OnNext(AvaloniaPropertyChangedEventArgs value)
+            {
+                if (value.Sender is TTarget target)
+                {
+                    _action(target, value);
+                }
+            }
+        }
     }
 }

+ 3 - 3
src/Avalonia.Base/AvaloniaProperty`1.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
-using System.Reactive.Subjects;
 using Avalonia.Data;
+using Avalonia.Reactive;
 using Avalonia.Utilities;
 
 namespace Avalonia
@@ -12,7 +12,7 @@ namespace Avalonia
     /// <typeparam name="TValue">The value type of the property.</typeparam>
     public abstract class AvaloniaProperty<TValue> : AvaloniaProperty
     {
-        private readonly Subject<AvaloniaPropertyChangedEventArgs<TValue>> _changed;
+        private readonly LightweightSubject<AvaloniaPropertyChangedEventArgs<TValue>> _changed;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
@@ -28,7 +28,7 @@ namespace Avalonia
             Action<AvaloniaObject, bool>? notifying = null)
             : base(name, typeof(TValue), ownerType, metadata, notifying)
         {
-            _changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();
+            _changed = new LightweightSubject<AvaloniaPropertyChangedEventArgs<TValue>>();
         }
 
         /// <summary>

+ 1 - 0
src/Avalonia.Base/ClassBindingManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using Avalonia.Data;
+using Avalonia.Reactive;
 
 namespace Avalonia
 {

+ 1 - 1
src/Avalonia.Base/Collections/AvaloniaListExtensions.cs

@@ -3,7 +3,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.ComponentModel;
-using System.Reactive.Disposables;
+using Avalonia.Reactive;
 
 namespace Avalonia.Collections
 {

+ 0 - 1
src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Specialized;
-using System.Reactive.Linq;
 using Avalonia.Reactive;
 using Avalonia.Utilities;
 

+ 26 - 0
src/Avalonia.Base/Compatibility/OperatingSystem.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Avalonia.Compatibility
+{
+    internal sealed class OperatingSystemEx
+    {
+#if NET6_0_OR_GREATER
+        public static bool IsWindows() => OperatingSystem.IsWindows();
+        public static bool IsMacOS() => OperatingSystem.IsMacOS();
+        public static bool IsLinux() => OperatingSystem.IsLinux();
+        public static bool IsAndroid() => OperatingSystem.IsAndroid();
+        public static bool IsIOS() => OperatingSystem.IsIOS();
+        public static bool IsBrowser() => OperatingSystem.IsBrowser();
+        public static bool IsOSPlatform(string platform) => OperatingSystem.IsOSPlatform(platform);
+#else
+        public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+        public static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+        public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+        public static bool IsAndroid() => IsOSPlatform("ANDROID");
+        public static bool IsIOS() => IsOSPlatform("IOS");
+        public static bool IsBrowser() => IsOSPlatform("BROWSER");
+        public static bool IsOSPlatform(string platform) => RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform));
+#endif
+    }
+}

+ 0 - 34
src/Avalonia.Base/Contract.cs

@@ -1,34 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using JetBrains.Annotations;
-
-namespace Avalonia
-{
-    /// <summary>
-    /// A stub of Code Contract's Contract class.
-    /// </summary>
-    /// <remarks>
-    /// It would be nice to use Code Contracts on Avalonia but last time I tried it slowed things
-    /// to a crawl and often crashed. Instead use the same signature for checking preconditions
-    /// in the hope that it might become usable at some point.
-    /// </remarks>
-    public static class Contract
-    {
-        /// <summary>
-        /// Specifies a precondition.
-        /// </summary>
-        /// <typeparam name="TException">
-        /// The exception to throw if <paramref name="condition"/> is false.
-        /// </typeparam>
-        /// <param name="condition">The precondition.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        [ContractAnnotation("condition:false=>stop")]
-        public static void Requires<TException>(bool condition) where TException : Exception, new()
-        {
-            if (!condition)
-            {
-                throw new TException();
-            }
-        }
-    }
-}

+ 13 - 8
src/Avalonia.Base/Controls/NameScopeExtensions.cs

@@ -25,13 +25,18 @@ namespace Avalonia.Controls
 
             var result = nameScope.Find(name);
 
-            if (result != null && !(result is T))
+            if (result == null)
             {
-                throw new InvalidOperationException(
-                    $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
+                return null;
             }
 
-            return (T?)result;
+            if (result is T typed)
+            {
+                return typed;
+            }
+
+            throw new InvalidOperationException(
+                $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
         }
 
         /// <summary>
@@ -74,13 +79,13 @@ namespace Avalonia.Controls
                 throw new KeyNotFoundException($"Could not find control '{name}'.");
             }
 
-            if (!(result is T))
+            if (result is T typed)
             {
-                throw new InvalidOperationException(
-                    $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
+                return typed;
             }
 
-            return (T)result;
+            throw new InvalidOperationException(
+                $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
         }
 
         /// <summary>

+ 1 - 1
src/Avalonia.Base/Controls/NameScopeLocator.cs

@@ -1,5 +1,5 @@
 using System;
-using System.Reactive.Disposables;
+using Avalonia.Reactive;
 using Avalonia.Utilities;
 
 namespace Avalonia.Controls

+ 6 - 3
src/Avalonia.Base/CornerRadius.cs

@@ -61,9 +61,13 @@ namespace Avalonia
         public double BottomLeft { get; }
 
         /// <summary>
-        /// Gets a value indicating whether all corner radii are set to 0.
+        /// Gets a value indicating whether the instance has default values (all corner radii are set to 0).
         /// </summary>
-        public bool IsEmpty => TopLeft.Equals(0) && IsUniform;
+        public bool IsDefault => TopLeft == 0 && TopRight == 0 && BottomLeft == 0 && BottomRight == 0;
+
+        /// <inheritdoc cref="IsDefault"/>
+        [Obsolete("Use IsDefault instead.")]
+        public bool IsEmpty => IsDefault;
 
         /// <summary>
         /// Gets a value indicating whether all corner radii are equal.
@@ -79,7 +83,6 @@ namespace Avalonia
         {
             // ReSharper disable CompareOfFloatsByEqualityOperator
             return TopLeft == other.TopLeft &&
-                   
                    TopRight == other.TopRight &&
                    BottomRight == other.BottomRight &&
                    BottomLeft == other.BottomLeft;

+ 5 - 6
src/Avalonia.Base/Data/BindingOperations.cs

@@ -1,6 +1,5 @@
 using System;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
+using Avalonia.Reactive;
 
 namespace Avalonia.Data
 {
@@ -46,15 +45,15 @@ namespace Avalonia.Data
                         throw new InvalidOperationException("InstancedBinding does not contain an observable.");
                     return target.Bind(property, binding.Observable, binding.Priority);
                 case BindingMode.TwoWay:
+                    if (binding.Observable is null)
+                        throw new InvalidOperationException("InstancedBinding does not contain an observable.");
                     if (binding.Subject is null)
                         throw new InvalidOperationException("InstancedBinding does not contain a subject.");
                     return new TwoWayBindingDisposable(
-                        target.Bind(property, binding.Subject, binding.Priority),
+                        target.Bind(property, binding.Observable, binding.Priority),
                         target.GetObservable(property).Subscribe(binding.Subject));
                 case BindingMode.OneTime:
-                    var source = binding.Subject ?? binding.Observable;
-
-                    if (source != null)
+                    if (binding.Observable is {} source)
                     {
                         // Perf: Avoid allocating closure in the outer scope.
                         var targetCopy = target;

+ 1 - 1
src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs

@@ -38,7 +38,7 @@ namespace Avalonia.Data.Converters
                     }
                     else if (Equals(obj, default(TIn)))
                     {
-                        yield return default(TIn);
+                        yield return default;
                     }
                 }
             }

+ 0 - 1
src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Reactive.Linq;
 using Avalonia.Reactive;
 
 namespace Avalonia.Data.Core

+ 2 - 4
src/Avalonia.Base/Data/Core/BindingExpression.cs

@@ -1,11 +1,9 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
-using System.Reactive.Linq;
-using System.Reactive.Subjects;
+using Avalonia.Reactive;
 using Avalonia.Data.Converters;
 using Avalonia.Logging;
-using Avalonia.Reactive;
 using Avalonia.Utilities;
 
 namespace Avalonia.Data.Core
@@ -15,7 +13,7 @@ namespace Avalonia.Data.Core
     /// that are sent and received.
     /// </summary>
     [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
-    public class BindingExpression : LightweightObservableBase<object?>, ISubject<object?>, IDescription
+    public class BindingExpression : LightweightObservableBase<object?>, IAvaloniaSubject<object?>, IDescription
     {
         private readonly ExpressionObserver _inner;
         private readonly Type _targetType;

+ 0 - 2
src/Avalonia.Base/Data/Core/ExpressionNode.cs

@@ -4,8 +4,6 @@ namespace Avalonia.Data.Core
 {
     public abstract class ExpressionNode
     {
-        private static readonly object CacheInvalid = new object();
-
         protected static readonly WeakReference<object?> UnsetReference = 
             new WeakReference<object?>(AvaloniaProperty.UnsetValue);
 

+ 8 - 11
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@@ -2,8 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq.Expressions;
-using System.Reactive;
-using System.Reactive.Linq;
 using Avalonia.Data.Core.Parsers;
 using Avalonia.Data.Core.Plugins;
 using Avalonia.Reactive;
@@ -49,8 +47,6 @@ namespace Avalonia.Data.Core
                 new TaskStreamPlugin(),
                 new ObservableStreamPlugin(),
             };
-
-        private static readonly object UninitializedValue = new object();
         private readonly ExpressionNode _node;
         private object? _root;
         private Func<object?>? _rootGetter;
@@ -101,14 +97,14 @@ namespace Avalonia.Data.Core
         /// </summary>
         /// <param name="rootGetter">A function which gets the root object.</param>
         /// <param name="node">The expression.</param>
-        /// <param name="update">An observable which triggers a re-read of the getter.</param>
+        /// <param name="update">An observable which triggers a re-read of the getter. Generic argument value is not used.</param>
         /// <param name="description">
         /// A description of the expression.
         /// </param>
         public ExpressionObserver(
             Func<object?> rootGetter,
             ExpressionNode node,
-            IObservable<Unit> update,
+            IObservable<ValueTuple> update,
             string? description)
         {
             Description = description;
@@ -166,7 +162,7 @@ namespace Avalonia.Data.Core
         /// </summary>
         /// <param name="rootGetter">A function which gets the root object.</param>
         /// <param name="expression">The expression.</param>
-        /// <param name="update">An observable which triggers a re-read of the getter.</param>
+        /// <param name="update">An observable which triggers a re-read of the getter. Generic argument value is not used.</param>
         /// <param name="enableDataValidation">Whether or not to track data validation</param>
         /// <param name="description">
         /// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used.
@@ -175,7 +171,7 @@ namespace Avalonia.Data.Core
         public static ExpressionObserver Create<T, U>(
             Func<T> rootGetter,
             Expression<Func<T, U>> expression,
-            IObservable<Unit> update,
+            IObservable<ValueTuple> update,
             bool enableDataValidation = false,
             string? description = null)
         {
@@ -298,9 +294,10 @@ namespace Avalonia.Data.Core
             if (_root is IObservable<object> observable)
             {
                 _rootSubscription = observable.Subscribe(
-                    x => _node.Target = new WeakReference<object?>(x != AvaloniaProperty.UnsetValue ? x : null),
-                    x => PublishCompleted(),
-                    () => PublishCompleted());
+                    new AnonymousObserver<object>(
+                        x => _node.Target = new WeakReference<object?>(x != AvaloniaProperty.UnsetValue ? x : null),
+                        x => PublishCompleted(),
+                        PublishCompleted));
             }
             else
             {

部分文件因文件數量過多而無法顯示