Browse Source

Merge branch 'master' into fixes/5027-avaloniaobject-batching

Dan Walmsley 5 years ago
parent
commit
8f74fb2366
100 changed files with 950 additions and 1409 deletions
  1. 27 0
      Avalonia.sln
  2. 3 8
      Documentation/build.md
  3. 1 1
      build/ApiDiff.props
  4. 2 1
      build/EmbedXaml.props
  5. 1 1
      build/SharedVersion.props
  6. 0 1020
      native/Avalonia.Native/inc/key.h
  7. 8 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  8. 6 1
      native/Avalonia.Native/src/OSX/KeyTransform.h
  9. 151 0
      native/Avalonia.Native/src/OSX/KeyTransform.mm
  10. 1 1
      native/Avalonia.Native/src/OSX/menu.h
  11. 51 14
      native/Avalonia.Native/src/OSX/menu.mm
  12. 1 1
      native/Avalonia.Native/src/OSX/platformthreading.mm
  13. 9 1
      native/Avalonia.Native/src/OSX/rendertarget.mm
  14. 4 4
      native/Avalonia.Native/src/OSX/window.mm
  15. 0 2
      samples/BindingDemo/App.xaml.cs
  16. 1 2
      samples/BindingDemo/BindingDemo.csproj
  17. 2 2
      samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs
  18. 2 2
      samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs
  19. 6 6
      samples/BindingDemo/ViewModels/MainWindowViewModel.cs
  20. 4 4
      samples/BindingDemo/ViewModels/NestedCommandViewModel.cs
  21. 2 2
      samples/BindingDemo/ViewModels/TestItem.cs
  22. 1 3
      samples/ControlCatalog.Desktop/Program.cs
  23. 1 4
      samples/ControlCatalog.NetCore/Program.cs
  24. 2 2
      samples/ControlCatalog/App.xaml.cs
  25. 1 1
      samples/ControlCatalog/ControlCatalog.csproj
  26. 13 17
      samples/ControlCatalog/DecoratedWindow.xaml
  27. 29 37
      samples/ControlCatalog/MainWindow.xaml
  28. 1 1
      samples/ControlCatalog/MainWindow.xaml.cs
  29. 0 1
      samples/ControlCatalog/Pages/LabelsPage.axaml.cs
  30. 1 1
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  31. 0 1
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  32. 2 2
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  33. 2 2
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs
  34. 2 2
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  35. 7 7
      samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs
  36. 3 3
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  37. 13 14
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  38. 14 14
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  39. 7 7
      samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
  40. 5 5
      samples/ControlCatalog/ViewModels/NotificationViewModel.cs
  41. 2 2
      samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs
  42. 8 8
      samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs
  43. 66 0
      samples/MiniMvvm/MiniCommand.cs
  44. 6 0
      samples/MiniMvvm/MiniMvvm.csproj
  45. 108 0
      samples/MiniMvvm/PropertyChangedExtensions.cs
  46. 26 0
      samples/MiniMvvm/ViewModelBase.cs
  47. 0 1
      samples/Previewer/Previewer.csproj
  48. 1 1
      samples/RenderDemo/App.xaml
  49. 0 2
      samples/RenderDemo/App.xaml.cs
  50. 1 1
      samples/RenderDemo/MainWindow.xaml.cs
  51. 1 2
      samples/RenderDemo/RenderDemo.csproj
  52. 2 2
      samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs
  53. 8 9
      samples/RenderDemo/ViewModels/MainWindowViewModel.cs
  54. 1 1
      samples/Sandbox/App.axaml
  55. 0 2
      samples/Sandbox/Program.cs
  56. 0 1
      samples/Sandbox/Sandbox.csproj
  57. 0 2
      samples/VirtualizationDemo/Program.cs
  58. 2 2
      samples/VirtualizationDemo/ViewModels/ItemViewModel.cs
  59. 12 12
      samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
  60. 1 2
      samples/VirtualizationDemo/VirtualizationDemo.csproj
  61. 1 1
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  62. 2 2
      samples/interop/Direct3DInteropSample/MainWindowViewModel.cs
  63. 1 5
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  64. 0 4
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  65. 0 4
      src/Avalonia.Base/ApiCompatBaseline.txt
  66. 12 12
      src/Avalonia.Base/AvaloniaProperty.cs
  67. 4 4
      src/Avalonia.Base/AvaloniaPropertyMetadata.cs
  68. 3 3
      src/Avalonia.Base/AvaloniaProperty`1.cs
  69. 18 0
      src/Avalonia.Base/Collections/AvaloniaList.cs
  70. 3 3
      src/Avalonia.Base/DirectPropertyBase.cs
  71. 2 2
      src/Avalonia.Base/DirectPropertyMetadata`1.cs
  72. 5 0
      src/Avalonia.Base/Logging/LogArea.cs
  73. 2 0
      src/Avalonia.Base/Properties/AssemblyInfo.cs
  74. 2 2
      src/Avalonia.Base/StyledPropertyMetadata`1.cs
  75. 2 23
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  76. 22 5
      src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
  77. 14 0
      src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs
  78. 10 1
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  79. 4 0
      src/Avalonia.Build.Tasks/SpanCompat.cs
  80. 0 5
      src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
  81. 21 0
      src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs
  82. 27 2
      src/Avalonia.Controls.DataGrid/Themes/Default.xaml
  83. 22 4
      src/Avalonia.Controls.DataGrid/Utils/ReflectionHelper.cs
  84. 0 26
      src/Avalonia.Controls/ApiCompatBaseline.txt
  85. 5 2
      src/Avalonia.Controls/AutoCompleteBox.cs
  86. 3 1
      src/Avalonia.Controls/Button.cs
  87. 10 0
      src/Avalonia.Controls/ContextMenu.cs
  88. 41 19
      src/Avalonia.Controls/DefinitionList.cs
  89. 2 2
      src/Avalonia.Controls/Generators/ItemContainerInfo.cs
  90. 8 2
      src/Avalonia.Controls/GridLength.cs
  91. 17 9
      src/Avalonia.Controls/HotkeyManager.cs
  92. 3 1
      src/Avalonia.Controls/MenuItem.cs
  93. 1 2
      src/Avalonia.Controls/NativeMenuBar.cs
  94. 16 4
      src/Avalonia.Controls/NativeMenuItem.cs
  95. 1 1
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  96. 2 2
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  97. 1 1
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  98. 11 0
      src/Avalonia.Controls/Slider.cs
  99. 4 1
      src/Avalonia.Controls/TextBox.cs
  100. 17 3
      src/Avalonia.Controls/Window.cs

+ 27 - 0
Avalonia.sln

@@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroComGenerator", "src\to
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.MicroCom", "src\Avalonia.MicroCom\Avalonia.MicroCom.csproj", "{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}"
+EndProject
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -2116,6 +2118,30 @@ Global
 		{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhone.Build.0 = Release|Any CPU
 		{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -2176,6 +2202,7 @@ Global
 		{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
 		{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{AEC9031E-06EA-4A9E-9E7F-7D7C719404DD} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
+		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

+ 3 - 8
Documentation/build.md

@@ -60,15 +60,10 @@ git submodule update --init --recursive
 
 ### Build native libraries (macOS only)
 
-On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/).  The steps to get this working correctly are:
-- (for revisions after 2 Nov 2020) Run `./build.sh GenerateCppHeaders` to generate `avalonia-native.h` from `avn.idl`
-- Navigate to the Avalonia/native/Avalonia.Native/src/OSX folder and open the `Avalonia.Native.OSX.xcodeproj` project
-- Build the library via the Product->Build menu.  This will generate binaries in your local path under ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-*guid* where "guid" is uniquely generated every time you build.
-- Manually install the native library by copying it from the build artifacts folder into the shared dynamic library path:
+On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). Execute the build script in the root project with the `CompileNative` task. It will build the headers, build the libraries, and place them in the appropriate place to allow .NET to find them at compilation and run time.
 
-```
-cd ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-[guid]/Build/Products/Debug
-cp libAvalonia.Native.OSX.dylib /usr/local/lib/libAvaloniaNative.dylib
+```bash
+./build.sh CompileNative
 ```
 
 ###  Build and Run Avalonia

+ 1 - 1
build/ApiDiff.props

@@ -1,6 +1,6 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
-    <ApiContractPackageVersion>0.10.0-preview6</ApiContractPackageVersion>
+    <ApiContractPackageVersion>0.10.0-rc1</ApiContractPackageVersion>
     <NugetPackageName Condition="'$(PackageId)' != ''">$(PackageId)</NugetPackageName>
     <NugetPackageName Condition="'$(PackageId)' == ''">Avalonia</NugetPackageName>
   </PropertyGroup>

+ 2 - 1
build/EmbedXaml.props

@@ -4,8 +4,9 @@
      <Compile Update="**\*.xaml.cs">
       <DependentUpon>%(Filename)</DependentUpon>
     </Compile>
+    <None Remove="**\*.xaml"/>
     <AvaloniaResource Include="**\*.xaml">
       <SubType>Designer</SubType>
     </AvaloniaResource>
   </ItemGroup>
-</Project>
+</Project>

+ 1 - 1
build/SharedVersion.props

@@ -16,7 +16,7 @@
     <PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes>
     <RepositoryType>git</RepositoryType>
     <AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\avalonia.snk</AssemblyOriginatorKeyFile>
-    <SignAssembly>false</SignAssembly>
+    <SignAssembly>true</SignAssembly>
     <DefineConstants Condition="$(SignAssembly) == true">$(DefineConstants);SIGNED_BUILD</DefineConstants>
   </PropertyGroup>
 

+ 0 - 1020
native/Avalonia.Native/inc/key.h

@@ -1,1020 +0,0 @@
-#ifndef _KEY_H_
-#define _KEY_H_
-
-/// <summary>
-/// Defines the keys available on a keyboard.
-/// </summary>
-enum AvnKey
-{
-    /// <summary>
-    /// No key pressed.
-    /// </summary>
-    AvnKeyNone = 0,
-    
-    /// <summary>
-    /// The Cancel key.
-    /// </summary>
-    AvnKeyCancel = 1,
-    
-    /// <summary>
-    /// The Back key.
-    /// </summary>
-    AvnKeyBack = 2,
-    
-    /// <summary>
-    /// The Tab key.
-    /// </summary>
-    AvnKeyTab = 3,
-    
-    /// <summary>
-    /// The Linefeed key.
-    /// </summary>
-    AvnKeyLineFeed = 4,
-    
-    /// <summary>
-    /// The Clear key.
-    /// </summary>
-    AvnKeyClear = 5,
-    
-    /// <summary>
-    /// The Return key.
-    /// </summary>
-    AvnKeyReturn = 6,
-    
-    /// <summary>
-    /// The Enter key.
-    /// </summary>
-    AvnKeyEnter = 6,
-    
-    /// <summary>
-    /// The Pause key.
-    /// </summary>
-    AvnKeyPause = 7,
-    
-    /// <summary>
-    /// The Caps Lock key.
-    /// </summary>
-    AvnKeyCapsLock = 8,
-    
-    /// <summary>
-    /// The Caps Lock key.
-    /// </summary>
-    AvnKeyCapital = 8,
-    
-    /// <summary>
-    /// The IME Hangul mode key.
-    /// </summary>
-    AvnKeyHangulMode = 9,
-    
-    /// <summary>
-    /// The IME Kana mode key.
-    /// </summary>
-    AvnKeyKanaMode = 9,
-    
-    /// <summary>
-    /// The IME Junja mode key.
-    /// </summary>
-    AvnKeyJunjaMode = 10,
-    
-    /// <summary>
-    /// The IME Final mode key.
-    /// </summary>
-    AvnKeyFinalMode = 11,
-    
-    /// <summary>
-    /// The IME Kanji mode key.
-    /// </summary>
-    AvnKeyKanjiMode = 12,
-    
-    /// <summary>
-    /// The IME Hanja mode key.
-    /// </summary>
-    HanjaMode = 12,
-    
-    /// <summary>
-    /// The Escape key.
-    /// </summary>
-    Escape = 13,
-    
-    /// <summary>
-    /// The IME Convert key.
-    /// </summary>
-    ImeConvert = 14,
-    
-    /// <summary>
-    /// The IME NonConvert key.
-    /// </summary>
-    ImeNonConvert = 15,
-    
-    /// <summary>
-    /// The IME Accept key.
-    /// </summary>
-    ImeAccept = 16,
-    
-    /// <summary>
-    /// The IME Mode change key.
-    /// </summary>
-    ImeModeChange = 17,
-    
-    /// <summary>
-    /// The space bar.
-    /// </summary>
-    Space = 18,
-    
-    /// <summary>
-    /// The Page Up key.
-    /// </summary>
-    PageUp = 19,
-    
-    /// <summary>
-    /// The Page Up key.
-    /// </summary>
-    Prior = 19,
-    
-    /// <summary>
-    /// The Page Down key.
-    /// </summary>
-    PageDown = 20,
-    
-    /// <summary>
-    /// The Page Down key.
-    /// </summary>
-    Next = 20,
-    
-    /// <summary>
-    /// The End key.
-    /// </summary>
-    End = 21,
-    
-    /// <summary>
-    /// The Home key.
-    /// </summary>
-    Home = 22,
-    
-    /// <summary>
-    /// The Left arrow key.
-    /// </summary>
-    Left = 23,
-    
-    /// <summary>
-    /// The Up arrow key.
-    /// </summary>
-    Up = 24,
-    
-    /// <summary>
-    /// The Right arrow key.
-    /// </summary>
-    Right = 25,
-    
-    /// <summary>
-    /// The Down arrow key.
-    /// </summary>
-    Down = 26,
-    
-    /// <summary>
-    /// The Select key.
-    /// </summary>
-    Select = 27,
-    
-    /// <summary>
-    /// The Print key.
-    /// </summary>
-    Print = 28,
-    
-    /// <summary>
-    /// The Execute key.
-    /// </summary>
-    Execute = 29,
-    
-    /// <summary>
-    /// The Print Screen key.
-    /// </summary>
-    Snapshot = 30,
-    
-    /// <summary>
-    /// The Print Screen key.
-    /// </summary>
-    PrintScreen = 30,
-    
-    /// <summary>
-    /// The Insert key.
-    /// </summary>
-    Insert = 31,
-    
-    /// <summary>
-    /// The Delete key.
-    /// </summary>
-    Delete = 32,
-    
-    /// <summary>
-    /// The Help key.
-    /// </summary>
-    Help = 33,
-    
-    /// <summary>
-    /// The 0 key.
-    /// </summary>
-    D0 = 34,
-    
-    /// <summary>
-    /// The 1 key.
-    /// </summary>
-    D1 = 35,
-    
-    /// <summary>
-    /// The 2 key.
-    /// </summary>
-    D2 = 36,
-    
-    /// <summary>
-    /// The 3 key.
-    /// </summary>
-    D3 = 37,
-    
-    /// <summary>
-    /// The 4 key.
-    /// </summary>
-    D4 = 38,
-    
-    /// <summary>
-    /// The 5 key.
-    /// </summary>
-    D5 = 39,
-    
-    /// <summary>
-    /// The 6 key.
-    /// </summary>
-    D6 = 40,
-    
-    /// <summary>
-    /// The 7 key.
-    /// </summary>
-    D7 = 41,
-    
-    /// <summary>
-    /// The 8 key.
-    /// </summary>
-    D8 = 42,
-    
-    /// <summary>
-    /// The 9 key.
-    /// </summary>
-    D9 = 43,
-    
-    /// <summary>
-    /// The A key.
-    /// </summary>
-    A = 44,
-    
-    /// <summary>
-    /// The B key.
-    /// </summary>
-    B = 45,
-    
-    /// <summary>
-    /// The C key.
-    /// </summary>
-    C = 46,
-    
-    /// <summary>
-    /// The D key.
-    /// </summary>
-    D = 47,
-    
-    /// <summary>
-    /// The E key.
-    /// </summary>
-    E = 48,
-    
-    /// <summary>
-    /// The F key.
-    /// </summary>
-    F = 49,
-    
-    /// <summary>
-    /// The G key.
-    /// </summary>
-    G = 50,
-    
-    /// <summary>
-    /// The H key.
-    /// </summary>
-    H = 51,
-    
-    /// <summary>
-    /// The I key.
-    /// </summary>
-    I = 52,
-    
-    /// <summary>
-    /// The J key.
-    /// </summary>
-    J = 53,
-    
-    /// <summary>
-    /// The K key.
-    /// </summary>
-    AvnKeyK = 54,
-    
-    /// <summary>
-    /// The L key.
-    /// </summary>
-    L = 55,
-    
-    /// <summary>
-    /// The M key.
-    /// </summary>
-    M = 56,
-    
-    /// <summary>
-    /// The N key.
-    /// </summary>
-    N = 57,
-    
-    /// <summary>
-    /// The O key.
-    /// </summary>
-    O = 58,
-    
-    /// <summary>
-    /// The P key.
-    /// </summary>
-    P = 59,
-    
-    /// <summary>
-    /// The Q key.
-    /// </summary>
-    Q = 60,
-    
-    /// <summary>
-    /// The R key.
-    /// </summary>
-    R = 61,
-    
-    /// <summary>
-    /// The S key.
-    /// </summary>
-    S = 62,
-    
-    /// <summary>
-    /// The T key.
-    /// </summary>
-    T = 63,
-    
-    /// <summary>
-    /// The U key.
-    /// </summary>
-    U = 64,
-    
-    /// <summary>
-    /// The V key.
-    /// </summary>
-    V = 65,
-    
-    /// <summary>
-    /// The W key.
-    /// </summary>
-    W = 66,
-    
-    /// <summary>
-    /// The X key.
-    /// </summary>
-    X = 67,
-    
-    /// <summary>
-    /// The Y key.
-    /// </summary>
-    Y = 68,
-    
-    /// <summary>
-    /// The Z key.
-    /// </summary>
-    Z = 69,
-    
-    /// <summary>
-    /// The left Windows key.
-    /// </summary>
-    LWin = 70,
-    
-    /// <summary>
-    /// The right Windows key.
-    /// </summary>
-    RWin = 71,
-    
-    /// <summary>
-    /// The Application key.
-    /// </summary>
-    Apps = 72,
-    
-    /// <summary>
-    /// The Sleep key.
-    /// </summary>
-    Sleep = 73,
-    
-    /// <summary>
-    /// The 0 key on the numeric keypad.
-    /// </summary>
-    NumPad0 = 74,
-    
-    /// <summary>
-    /// The 1 key on the numeric keypad.
-    /// </summary>
-    NumPad1 = 75,
-    
-    /// <summary>
-    /// The 2 key on the numeric keypad.
-    /// </summary>
-    NumPad2 = 76,
-    
-    /// <summary>
-    /// The 3 key on the numeric keypad.
-    /// </summary>
-    NumPad3 = 77,
-    
-    /// <summary>
-    /// The 4 key on the numeric keypad.
-    /// </summary>
-    NumPad4 = 78,
-    
-    /// <summary>
-    /// The 5 key on the numeric keypad.
-    /// </summary>
-    NumPad5 = 79,
-    
-    /// <summary>
-    /// The 6 key on the numeric keypad.
-    /// </summary>
-    NumPad6 = 80,
-    
-    /// <summary>
-    /// The 7 key on the numeric keypad.
-    /// </summary>
-    NumPad7 = 81,
-    
-    /// <summary>
-    /// The 8 key on the numeric keypad.
-    /// </summary>
-    NumPad8 = 82,
-    
-    /// <summary>
-    /// The 9 key on the numeric keypad.
-    /// </summary>
-    NumPad9 = 83,
-    
-    /// <summary>
-    /// The Multiply key.
-    /// </summary>
-    Multiply = 84,
-    
-    /// <summary>
-    /// The Add key.
-    /// </summary>
-    Add = 85,
-    
-    /// <summary>
-    /// The Separator key.
-    /// </summary>
-    Separator = 86,
-    
-    /// <summary>
-    /// The Subtract key.
-    /// </summary>
-    Subtract = 87,
-    
-    /// <summary>
-    /// The Decimal key.
-    /// </summary>
-    Decimal = 88,
-    
-    /// <summary>
-    /// The Divide key.
-    /// </summary>
-    Divide = 89,
-    
-    /// <summary>
-    /// The F1 key.
-    /// </summary>
-    F1 = 90,
-    
-    /// <summary>
-    /// The F2 key.
-    /// </summary>
-    F2 = 91,
-    
-    /// <summary>
-    /// The F3 key.
-    /// </summary>
-    F3 = 92,
-    
-    /// <summary>
-    /// The F4 key.
-    /// </summary>
-    F4 = 93,
-    
-    /// <summary>
-    /// The F5 key.
-    /// </summary>
-    F5 = 94,
-    
-    /// <summary>
-    /// The F6 key.
-    /// </summary>
-    F6 = 95,
-    
-    /// <summary>
-    /// The F7 key.
-    /// </summary>
-    F7 = 96,
-    
-    /// <summary>
-    /// The F8 key.
-    /// </summary>
-    F8 = 97,
-    
-    /// <summary>
-    /// The F9 key.
-    /// </summary>
-    F9 = 98,
-    
-    /// <summary>
-    /// The F10 key.
-    /// </summary>
-    F10 = 99,
-    
-    /// <summary>
-    /// The F11 key.
-    /// </summary>
-    F11 = 100,
-    
-    /// <summary>
-    /// The F12 key.
-    /// </summary>
-    F12 = 101,
-    
-    /// <summary>
-    /// The F13 key.
-    /// </summary>
-    F13 = 102,
-    
-    /// <summary>
-    /// The F14 key.
-    /// </summary>
-    F14 = 103,
-    
-    /// <summary>
-    /// The F15 key.
-    /// </summary>
-    F15 = 104,
-    
-    /// <summary>
-    /// The F16 key.
-    /// </summary>
-    F16 = 105,
-    
-    /// <summary>
-    /// The F17 key.
-    /// </summary>
-    F17 = 106,
-    
-    /// <summary>
-    /// The F18 key.
-    /// </summary>
-    F18 = 107,
-    
-    /// <summary>
-    /// The F19 key.
-    /// </summary>
-    F19 = 108,
-    
-    /// <summary>
-    /// The F20 key.
-    /// </summary>
-    F20 = 109,
-    
-    /// <summary>
-    /// The F21 key.
-    /// </summary>
-    F21 = 110,
-    
-    /// <summary>
-    /// The F22 key.
-    /// </summary>
-    F22 = 111,
-    
-    /// <summary>
-    /// The F23 key.
-    /// </summary>
-    F23 = 112,
-    
-    /// <summary>
-    /// The F24 key.
-    /// </summary>
-    F24 = 113,
-    
-    /// <summary>
-    /// The Numlock key.
-    /// </summary>
-    NumLock = 114,
-    
-    /// <summary>
-    /// The Scroll key.
-    /// </summary>
-    Scroll = 115,
-    
-    /// <summary>
-    /// The left Shift key.
-    /// </summary>
-    LeftShift = 116,
-    
-    /// <summary>
-    /// The right Shift key.
-    /// </summary>
-    RightShift = 117,
-    
-    /// <summary>
-    /// The left Ctrl key.
-    /// </summary>
-    LeftCtrl = 118,
-    
-    /// <summary>
-    /// The right Ctrl key.
-    /// </summary>
-    RightCtrl = 119,
-    
-    /// <summary>
-    /// The left Alt key.
-    /// </summary>
-    LeftAlt = 120,
-    
-    /// <summary>
-    /// The right Alt key.
-    /// </summary>
-    RightAlt = 121,
-    
-    /// <summary>
-    /// The browser Back key.
-    /// </summary>
-    BrowserBack = 122,
-    
-    /// <summary>
-    /// The browser Forward key.
-    /// </summary>
-    BrowserForward = 123,
-    
-    /// <summary>
-    /// The browser Refresh key.
-    /// </summary>
-    BrowserRefresh = 124,
-    
-    /// <summary>
-    /// The browser Stop key.
-    /// </summary>
-    BrowserStop = 125,
-    
-    /// <summary>
-    /// The browser Search key.
-    /// </summary>
-    BrowserSearch = 126,
-    
-    /// <summary>
-    /// The browser Favorites key.
-    /// </summary>
-    BrowserFavorites = 127,
-    
-    /// <summary>
-    /// The browser Home key.
-    /// </summary>
-    BrowserHome = 128,
-    
-    /// <summary>
-    /// The Volume Mute key.
-    /// </summary>
-    VolumeMute = 129,
-    
-    /// <summary>
-    /// The Volume Down key.
-    /// </summary>
-    VolumeDown = 130,
-    
-    /// <summary>
-    /// The Volume Up key.
-    /// </summary>
-    VolumeUp = 131,
-    
-    /// <summary>
-    /// The media Next Track key.
-    /// </summary>
-    MediaNextTrack = 132,
-    
-    /// <summary>
-    /// The media Previous Track key.
-    /// </summary>
-    MediaPreviousTrack = 133,
-    
-    /// <summary>
-    /// The media Stop key.
-    /// </summary>
-    MediaStop = 134,
-    
-    /// <summary>
-    /// The media Play/Pause key.
-    /// </summary>
-    MediaPlayPause = 135,
-    
-    /// <summary>
-    /// The Launch Mail key.
-    /// </summary>
-    LaunchMail = 136,
-    
-    /// <summary>
-    /// The Select Media key.
-    /// </summary>
-    SelectMedia = 137,
-    
-    /// <summary>
-    /// The Launch Application 1 key.
-    /// </summary>
-    LaunchApplication1 = 138,
-    
-    /// <summary>
-    /// The Launch Application 2 key.
-    /// </summary>
-    LaunchApplication2 = 139,
-    
-    /// <summary>
-    /// The OEM Semicolon key.
-    /// </summary>
-    OemSemicolon = 140,
-    
-    /// <summary>
-    /// The OEM 1 key.
-    /// </summary>
-    Oem1 = 140,
-    
-    /// <summary>
-    /// The OEM Plus key.
-    /// </summary>
-    OemPlus = 141,
-    
-    /// <summary>
-    /// The OEM Comma key.
-    /// </summary>
-    OemComma = 142,
-    
-    /// <summary>
-    /// The OEM Minus key.
-    /// </summary>
-    OemMinus = 143,
-    
-    /// <summary>
-    /// The OEM Period key.
-    /// </summary>
-    OemPeriod = 144,
-    
-    /// <summary>
-    /// The OEM Question Mark key.
-    /// </summary>
-    OemQuestion = 145,
-    
-    /// <summary>
-    /// The OEM 2 key.
-    /// </summary>
-    Oem2 = 145,
-    
-    /// <summary>
-    /// The OEM Tilde key.
-    /// </summary>
-    OemTilde = 146,
-    
-    /// <summary>
-    /// The OEM 3 key.
-    /// </summary>
-    Oem3 = 146,
-    
-    /// <summary>
-    /// The ABNT_C1 (Brazilian) key.
-    /// </summary>
-    AbntC1 = 147,
-    
-    /// <summary>
-    /// The ABNT_C2 (Brazilian) key.
-    /// </summary>
-    AbntC2 = 148,
-    
-    /// <summary>
-    /// The OEM Open Brackets key.
-    /// </summary>
-    OemOpenBrackets = 149,
-    
-    /// <summary>
-    /// The OEM 4 key.
-    /// </summary>
-    Oem4 = 149,
-    
-    /// <summary>
-    /// The OEM Pipe key.
-    /// </summary>
-    OemPipe = 150,
-    
-    /// <summary>
-    /// The OEM 5 key.
-    /// </summary>
-    Oem5 = 150,
-    
-    /// <summary>
-    /// The OEM Close Brackets key.
-    /// </summary>
-    OemCloseBrackets = 151,
-    
-    /// <summary>
-    /// The OEM 6 key.
-    /// </summary>
-    Oem6 = 151,
-    
-    /// <summary>
-    /// The OEM Quotes key.
-    /// </summary>
-    OemQuotes = 152,
-    
-    /// <summary>
-    /// The OEM 7 key.
-    /// </summary>
-    Oem7 = 152,
-    
-    /// <summary>
-    /// The OEM 8 key.
-    /// </summary>
-    Oem8 = 153,
-    
-    /// <summary>
-    /// The OEM Backslash key.
-    /// </summary>
-    OemBackslash = 154,
-    
-    /// <summary>
-    /// The OEM 3 key.
-    /// </summary>
-    Oem102 = 154,
-    
-    /// <summary>
-    /// A special key masking the real key being processed by an IME.
-    /// </summary>
-    ImeProcessed = 155,
-    
-    /// <summary>
-    /// A special key masking the real key being processed as a system key.
-    /// </summary>
-    System = 156,
-    
-    /// <summary>
-    /// The OEM ATTN key.
-    /// </summary>
-    OemAttn = 157,
-    
-    /// <summary>
-    /// The DBE_ALPHANUMERIC key.
-    /// </summary>
-    DbeAlphanumeric = 157,
-    
-    /// <summary>
-    /// The OEM Finish key.
-    /// </summary>
-    OemFinish = 158,
-    
-    /// <summary>
-    /// The DBE_KATAKANA key.
-    /// </summary>
-    DbeKatakana = 158,
-    
-    /// <summary>
-    /// The DBE_HIRAGANA key.
-    /// </summary>
-    DbeHiragana = 159,
-    
-    /// <summary>
-    /// The OEM Copy key.
-    /// </summary>
-    OemCopy = 159,
-    
-    /// <summary>
-    /// The DBE_SBCSCHAR key.
-    /// </summary>
-    DbeSbcsChar = 160,
-    
-    /// <summary>
-    /// The OEM Auto key.
-    /// </summary>
-    OemAuto = 160,
-    
-    /// <summary>
-    /// The DBE_DBCSCHAR key.
-    /// </summary>
-    DbeDbcsChar = 161,
-    
-    /// <summary>
-    /// The OEM ENLW key.
-    /// </summary>
-    OemEnlw = 161,
-    
-    /// <summary>
-    /// The OEM BackTab key.
-    /// </summary>
-    OemBackTab = 162,
-    
-    /// <summary>
-    /// The DBE_ROMAN key.
-    /// </summary>
-    DbeRoman = 162,
-    
-    /// <summary>
-    /// The DBE_NOROMAN key.
-    /// </summary>
-    DbeNoRoman = 163,
-    
-    /// <summary>
-    /// The ATTN key.
-    /// </summary>
-    Attn = 163,
-    
-    /// <summary>
-    /// The CRSEL key.
-    /// </summary>
-    CrSel = 164,
-    
-    /// <summary>
-    /// The DBE_ENTERWORDREGISTERMODE key.
-    /// </summary>
-    DbeEnterWordRegisterMode = 164,
-    
-    /// <summary>
-    /// The EXSEL key.
-    /// </summary>
-    ExSel = 165,
-    
-    /// <summary>
-    /// The DBE_ENTERIMECONFIGMODE key.
-    /// </summary>
-    DbeEnterImeConfigureMode = 165,
-    
-    /// <summary>
-    /// The ERASE EOF Key.
-    /// </summary>
-    EraseEof = 166,
-    
-    /// <summary>
-    /// The DBE_FLUSHSTRING key.
-    /// </summary>
-    DbeFlushString = 166,
-    
-    /// <summary>
-    /// The Play key.
-    /// </summary>
-    Play = 167,
-    
-    /// <summary>
-    /// The DBE_CODEINPUT key.
-    /// </summary>
-    DbeCodeInput = 167,
-    
-    /// <summary>
-    /// The DBE_NOCODEINPUT key.
-    /// </summary>
-    DbeNoCodeInput = 168,
-    
-    /// <summary>
-    /// The Zoom key.
-    /// </summary>
-    Zoom = 168,
-    
-    /// <summary>
-    /// Reserved for future use.
-    /// </summary>
-    NoName = 169,
-    
-    /// <summary>
-    /// The DBE_DETERMINESTRING key.
-    /// </summary>
-    DbeDetermineString = 169,
-    
-    /// <summary>
-    /// The DBE_ENTERDLGCONVERSIONMODE key.
-    /// </summary>
-    DbeEnterDialogConversionMode = 170,
-    
-    /// <summary>
-    /// The PA1 key.
-    /// </summary>
-    Pa1 = 170,
-    
-    /// <summary>
-    /// The OEM Clear key.
-    /// </summary>
-    OemClear = 171,
-    
-    /// <summary>
-    /// The key is used with another key to create a single combined character.
-    /// </summary>
-    DeadCharProcessed = 172,
-};
-
-#endif

+ 8 - 4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@@ -8,19 +8,20 @@
 
 /* Begin PBXBuildFile section */
 		1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
+		1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; };
 		1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; };
 		1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; };
-		1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; };
-		1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
 		1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
 		1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
 		1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; };
+		1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
 		37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
 		37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
 		37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
 		37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; };
 		37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; };
 		520624B322973F4100C4DCEF /* menu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 520624B222973F4100C4DCEF /* menu.mm */; };
+		522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 522D5958258159C1006F7F7A /* Carbon.framework */; };
 		5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
 		5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
 		AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
@@ -32,13 +33,13 @@
 
 /* Begin PBXFileReference section */
 		1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; };
+		1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = "<group>"; };
 		1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = "<group>"; };
 		1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; };
-		1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = "<group>"; };
-		1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
 		1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
 		1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
+		1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
 		37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
 		379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
 		37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; };
@@ -49,6 +50,7 @@
 		37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = "<group>"; };
 		37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
 		520624B222973F4100C4DCEF /* menu.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = menu.mm; sourceTree = "<group>"; };
+		522D5958258159C1006F7F7A /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
 		5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; };
 		5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
 		5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
@@ -69,6 +71,7 @@
 				1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */,
 				1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */,
 				AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
+				522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */,
 				AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -79,6 +82,7 @@
 		AB661C1C2148230E00291242 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				522D5958258159C1006F7F7A /* Carbon.framework */,
 				1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */,
 				1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */,
 				AB1E522B217613570091CD71 /* OpenGL.framework */,

+ 6 - 1
native/Avalonia.Native/src/OSX/KeyTransform.h

@@ -1,9 +1,14 @@
 #ifndef keytransform_h
 #define keytransform_h
 #include "common.h"
-#include "key.h"
 #include <map>
 
 extern std::map<int, AvnKey> s_KeyMap;
 
+extern std::map<AvnKey, int> s_AvnKeyMap;
+
+extern std::map<int, const char*> s_QwertyKeyMap;
+
+extern std::map<AvnKey, int> s_UnicodeKeyMap;
+
 #endif

+ 151 - 0
native/Avalonia.Native/src/OSX/KeyTransform.mm

@@ -120,6 +120,138 @@ const int kVK_UpArrow = 0x7E;
 //const int kVK_JIS_Eisu = 0x66;
 const int kVK_JIS_Kana = 0x68;
 
+// converts from AvaloniaKeys to UnicodeSpecial keys.
+std::map<AvnKey, int> s_UnicodeKeyMap =
+{
+    { Up, NSUpArrowFunctionKey },
+    { Down, NSDownArrowFunctionKey },
+    { Left, NSLeftArrowFunctionKey },
+    { Right, NSRightArrowFunctionKey },
+    { F1, NSF1FunctionKey },
+    { F2, NSF2FunctionKey },
+    { F3, NSF3FunctionKey },
+    { F4, NSF4FunctionKey },
+    { F5, NSF5FunctionKey },
+    { F6, NSF6FunctionKey },
+    { F7, NSF7FunctionKey },
+    { F8, NSF8FunctionKey },
+    { F9, NSF9FunctionKey },
+    { F10, NSF10FunctionKey },
+    { F11, NSF11FunctionKey },
+    { F12, NSF12FunctionKey },
+    { F13, NSF13FunctionKey },
+    { F14, NSF14FunctionKey },
+    { F15, NSF15FunctionKey },
+    { F16, NSF16FunctionKey },
+    { F17, NSF17FunctionKey },
+    { F18, NSF18FunctionKey },
+    { F19, NSF19FunctionKey },
+    { F20, NSF20FunctionKey },
+    { F21, NSF21FunctionKey },
+    { F22, NSF22FunctionKey },
+    { F23, NSF23FunctionKey },
+    { F24, NSF24FunctionKey },
+    { Insert, NSInsertFunctionKey },
+    { Delete, NSDeleteFunctionKey },
+    { Home, NSHomeFunctionKey },
+    //{ Begin, NSBeginFunctionKey },
+    { End, NSEndFunctionKey },
+    { PageUp, NSPageUpFunctionKey },
+    { PageDown, NSPageDownFunctionKey },
+    { PrintScreen, NSPrintScreenFunctionKey },
+    { Scroll, NSScrollLockFunctionKey },
+    //{ SysReq, NSSysReqFunctionKey },
+    //{ Break, NSBreakFunctionKey },
+    //{ Reset, NSResetFunctionKey },
+    //{ Stop, NSStopFunctionKey },
+    //{ Menu, NSMenuFunctionKey },
+    //{ UserFunction, NSUserFunctionKey },
+    //{ SystemFunction, NSSystemFunctionKey },
+    { Print, NSPrintFunctionKey },
+    //{ ClearLine, NSClearLineFunctionKey },
+    //{ ClearDisplay, NSClearDisplayFunctionKey },
+};
+
+// Converts from Ansi virtual keys to Qwerty Keyboard map.
+std::map<int, const char*> s_QwertyKeyMap =
+{
+    { 0, "a" },
+    { 1, "s" },
+    { 2, "d" },
+    { 3, "f" },
+    { 4, "h" },
+    { 5, "g" },
+    { 6, "z" },
+    { 7, "x" },
+    { 8, "c" },
+    { 9, "v" },
+    { 10, "§" },
+    { 11, "b" },
+    { 12, "q" },
+    { 13, "w" },
+    { 14, "e" },
+    { 15, "r" },
+    { 16, "y" },
+    { 17, "t" },
+    { 18, "1" },
+    { 19, "2" },
+    { 20, "3" },
+    { 21, "4" },
+    { 22, "6" },
+    { 23, "5" },
+    { 24, "=" },
+    { 25, "9" },
+    { 26, "7" },
+    { 27, "-" },
+    { 28, "8" },
+    { 29, "0" },
+    { 30, "]" },
+    { 31, "o" },
+    { 32, "u" },
+    { 33, "[" },
+    { 34, "i" },
+    { 35, "p" },
+    { 37, "l" },
+    { 38, "j" },
+    { 39, "'" },
+    { 40, "k" },
+    { 41, ";" },
+    { 42, "\\" },
+    { 43, "," },
+    { 44, "/" },
+    { 45, "n" },
+    { 46, "m" },
+    { 47, "." },
+    { 49, " " },
+    { 50, "`" },
+    { 51, "" },
+    { 52, "" },
+    { 53, "" },
+    { 65, "." },
+    { 66, "" },
+    { 67, "*" },
+    { 69, "+" },
+    { 70, "" },
+    { 71, "" },
+    { 72, "" },
+    { 75, "/" },
+    { 76, "" },
+    { 77, "" },
+    { 78, "-" },
+    { 81, "=" },
+    { 82, "0" },
+    { 83, "1" },
+    { 84, "2" },
+    { 85, "3" },
+    { 86, "4" },
+    { 87, "5" },
+    { 88, "6" },
+    { 89, "7" },
+    { 91, "8" },
+    { 92, "9" }
+};
+
+// converts from ansi virtualkeys to AvnKeys.
  std::map<int, AvnKey> s_KeyMap =
  {
     {kVK_ANSI_A, A},
@@ -237,3 +369,22 @@ const int kVK_JIS_Kana = 0x68;
     {kVK_UpArrow, Up},
     {kVK_JIS_Kana, AvnKeyKanaMode},
 };
+
+static std::map<AvnKey, int> BuildAvnKeyMap ()
+{
+    std::map<AvnKey, int> result;
+    
+    for( auto it = s_KeyMap.begin(); it != s_KeyMap.end(); ++it )
+    {
+        int key = it->first;
+        AvnKey value = it->second;
+        
+        result[value] = key;
+    }
+    
+    return result;
+}
+
+// Converts AvnKeys to Ansi VirtualKeys
+std::map<AvnKey, int> s_AvnKeyMap = BuildAvnKeyMap();
+

+ 1 - 1
native/Avalonia.Native/src/OSX/menu.h

@@ -45,7 +45,7 @@ public:
     
     virtual HRESULT SetTitle (char* utf8String) override;
     
-    virtual HRESULT SetGesture (char* key, AvnInputModifiers modifiers) override;
+    virtual HRESULT SetGesture (AvnKey key, AvnInputModifiers modifiers) override;
     
     virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override;
     

+ 51 - 14
native/Avalonia.Native/src/OSX/menu.mm

@@ -2,6 +2,9 @@
 #include "common.h"
 #include "menu.h"
 #include "window.h"
+#include "KeyTransform.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */
 
 @implementation AvnMenu
 {
@@ -122,23 +125,57 @@ HRESULT AvnAppMenuItem::SetTitle (char* utf8String)
     }
 }
 
-HRESULT AvnAppMenuItem::SetGesture (char* key, AvnInputModifiers modifiers)
+
+HRESULT AvnAppMenuItem::SetGesture (AvnKey key, AvnInputModifiers modifiers)
 {
     @autoreleasepool
     {
-        NSEventModifierFlags flags = 0;
-        
-        if (modifiers & Control)
-            flags |= NSEventModifierFlagControl;
-        if (modifiers & Shift)
-            flags |= NSEventModifierFlagShift;
-        if (modifiers & Alt)
-            flags |= NSEventModifierFlagOption;
-        if (modifiers & Windows)
-            flags |= NSEventModifierFlagCommand;
-        
-        [_native setKeyEquivalent:[NSString stringWithUTF8String:(const char*)key]];
-        [_native setKeyEquivalentModifierMask:flags];
+        if(key != AvnKeyNone)
+        {
+            NSEventModifierFlags flags = 0;
+            
+            if (modifiers & Control)
+                flags |= NSEventModifierFlagControl;
+            if (modifiers & Shift)
+                flags |= NSEventModifierFlagShift;
+            if (modifiers & Alt)
+                flags |= NSEventModifierFlagOption;
+            if (modifiers & Windows)
+                flags |= NSEventModifierFlagCommand;
+            
+            auto it = s_UnicodeKeyMap.find(key);
+            
+            if(it != s_UnicodeKeyMap.end())
+            {
+                auto keyString= [NSString stringWithFormat:@"%C", (unsigned short)it->second];
+                
+                [_native setKeyEquivalent: keyString];
+                [_native setKeyEquivalentModifierMask:flags];
+                
+                return S_OK;
+            }
+            else
+            {
+                auto it = s_AvnKeyMap.find(key); // check if a virtual key is mapped.
+                
+                if(it != s_AvnKeyMap.end())
+                {
+                    auto it1 = s_QwertyKeyMap.find(it->second); // convert virtual key to qwerty string.
+                    
+                    if(it1 != s_QwertyKeyMap.end())
+                    {
+                        [_native setKeyEquivalent: [NSString  stringWithUTF8String: it1->second]];
+                        [_native setKeyEquivalentModifierMask:flags];
+                        
+                        return S_OK;
+                    }
+                }
+            }
+        }
+        
+        // Nothing matched... clear.
+        [_native setKeyEquivalent: @""];
+        [_native setKeyEquivalentModifierMask: 0];
         
         return S_OK;
     }

+ 1 - 1
native/Avalonia.Native/src/OSX/platformthreading.mm

@@ -101,7 +101,7 @@ public:
     
     virtual bool GetCurrentThreadIsLoopThread() override
     {
-        return [[NSThread currentThread] isMainThread];
+        return [NSThread isMainThread];
     }
     virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override
     {

+ 9 - 1
native/Avalonia.Native/src/OSX/rendertarget.mm

@@ -2,6 +2,7 @@
 #include "rendertarget.h"
 #import <IOSurface/IOSurface.h>
 #import <IOSurface/IOSurfaceObjC.h>
+#import <QuartzCore/QuartzCore.h>
 
 #include <OpenGL/CGLIOSurface.h>
 #include <OpenGL/OpenGL.h>
@@ -143,13 +144,17 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
     return _layer;
 }
 
-- (void)resize:(AvnPixelSize)size withScale: (float) scale;{
+- (void)resize:(AvnPixelSize)size withScale: (float) scale{
     @synchronized (lock) {
         if(surface == nil
            || surface->size.Width != size.Width
            || surface->size.Height != size.Height
            || surface->scale != scale)
+        {
             surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()];
+            
+            [self updateLayer];
+        }
     }
 }
 
@@ -159,12 +164,15 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
         @synchronized (lock) {
             if(_layer == nil)
                 return;
+            [CATransaction begin];
             [_layer setContents: nil];
             if(surface != nil)
             {
                 [_layer setContentsScale: surface->scale];
                 [_layer setContents: (__bridge IOSurface*) surface->surface];
             }
+            [CATransaction commit];
+            [CATransaction flush];
         }
     }
     else

+ 4 - 4
native/Avalonia.Native/src/OSX/window.mm

@@ -106,13 +106,13 @@ public:
         return Window;
     }
     
-    virtual HRESULT Show() override
+    virtual HRESULT Show(bool activate) override
     {
         @autoreleasepool
         {
             SetPosition(lastPositionSet);
             UpdateStyle();
-            if(ShouldTakeFocusOnShow())
+            if(ShouldTakeFocusOnShow() && activate)
             {
                 [Window makeKeyAndOrderFront:Window];
                 [NSApp activateIgnoringOtherApps:YES];
@@ -561,11 +561,11 @@ private:
         }
     }
     
-    virtual HRESULT Show () override
+    virtual HRESULT Show (bool activate) override
     {
         @autoreleasepool
         {            
-            WindowBaseImpl::Show();
+            WindowBaseImpl::Show(activate);
             
             HideOrShowTrafficLights();
             

+ 0 - 2
samples/BindingDemo/App.xaml.cs

@@ -1,7 +1,6 @@
 using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
-using Avalonia.ReactiveUI;
 
 namespace BindingDemo
 {
@@ -25,7 +24,6 @@ namespace BindingDemo
         public static AppBuilder BuildAvaloniaApp()
           => AppBuilder.Configure<App>()
                 .UsePlatformDetect()
-                .UseReactiveUI()
                 .LogToTrace();
     }
 }

+ 1 - 2
samples/BindingDemo/BindingDemo.csproj

@@ -6,12 +6,11 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
+    <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>
   <Import Project="..\..\build\SampleApp.props" />
   <Import Project="..\..\build\EmbedXaml.props" />
   <Import Project="..\..\build\Rx.props" />
-  <Import Project="..\..\build\ReactiveUI.props" />
   <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />
   <Import Project="..\..\build\BuildTargets.targets" />

+ 2 - 2
samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs

@@ -1,9 +1,9 @@
-using ReactiveUI;
+using MiniMvvm;
 using System;
 
 namespace BindingDemo.ViewModels
 {
-    public class ExceptionErrorViewModel : ReactiveObject
+    public class ExceptionErrorViewModel : ViewModelBase
     {
         private int _lessThan10;
 

+ 2 - 2
samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs

@@ -1,11 +1,11 @@
-using ReactiveUI;
+using MiniMvvm;
 using System;
 using System.ComponentModel;
 using System.Collections;
 
 namespace BindingDemo.ViewModels
 {
-    public class IndeiErrorViewModel : ReactiveObject, INotifyDataErrorInfo
+    public class IndeiErrorViewModel : ViewModelBase, INotifyDataErrorInfo
     {
         private int _maximum = 10;
         private int _value;

+ 6 - 6
samples/BindingDemo/ViewModels/MainWindowViewModel.cs

@@ -5,14 +5,14 @@ using System.Reactive;
 using System.Reactive.Linq;
 using System.Threading.Tasks;
 using System.Threading;
-using ReactiveUI;
+using MiniMvvm;
 using Avalonia.Controls;
 using Avalonia.Metadata;
 using Avalonia.Controls.Selection;
 
 namespace BindingDemo.ViewModels
 {
-    public class MainWindowViewModel : ReactiveObject
+    public class MainWindowViewModel : ViewModelBase
     {
         private string _booleanString = "True";
         private double _doubleValue = 5.0;
@@ -32,13 +32,13 @@ namespace BindingDemo.ViewModels
 
             Selection = new SelectionModel<TestItem> { SingleSelect = false };
 
-            ShuffleItems = ReactiveCommand.Create(() =>
+            ShuffleItems = MiniCommand.Create(() =>
             {
                 var r = new Random();
                 Items.Move(r.Next(Items.Count), 1);
             });
 
-            StringValueCommand = ReactiveCommand.Create<object>(param =>
+            StringValueCommand = MiniCommand.Create<object>(param =>
             {
                 BooleanFlag = !BooleanFlag;
                 StringValue = param.ToString();
@@ -60,7 +60,7 @@ namespace BindingDemo.ViewModels
 
         public ObservableCollection<TestItem> Items { get; }
         public SelectionModel<TestItem> Selection { get; }
-        public ReactiveCommand<Unit, Unit> ShuffleItems { get; }
+        public MiniCommand ShuffleItems { get; }
 
         public string BooleanString
         {
@@ -93,7 +93,7 @@ namespace BindingDemo.ViewModels
         }
 
         public IObservable<DateTimeOffset> CurrentTimeObservable { get; }
-        public ReactiveCommand<object, Unit> StringValueCommand { get; }
+        public MiniCommand StringValueCommand { get; }
 
         public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel();
         public ExceptionErrorViewModel ExceptionDataValidation { get; } = new ExceptionErrorViewModel();

+ 4 - 4
samples/BindingDemo/ViewModels/NestedCommandViewModel.cs

@@ -1,18 +1,18 @@
-using ReactiveUI;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Input;
+using MiniMvvm;
 
 namespace BindingDemo.ViewModels
 {
-    public class NestedCommandViewModel : ReactiveObject
+    public class NestedCommandViewModel : ViewModelBase
     {
         public NestedCommandViewModel()
         {
-            Command = ReactiveCommand.Create(() => { });
+            Command = MiniCommand.Create(() => { });
         }
 
         public ICommand Command { get; }

+ 2 - 2
samples/BindingDemo/ViewModels/TestItem.cs

@@ -1,8 +1,8 @@
-using ReactiveUI;
+using MiniMvvm;
 
 namespace BindingDemo.ViewModels
 {
-    public class TestItem : ReactiveObject
+    public class TestItem : ViewModelBase
     {
         private string _stringValue = "String Value";
         private string _detail;

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

@@ -3,7 +3,6 @@ using System.Linq;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Platform;
-using Avalonia.ReactiveUI;
 
 namespace ControlCatalog
 {
@@ -19,8 +18,7 @@ namespace ControlCatalog
         public static AppBuilder BuildAvaloniaApp()
             => AppBuilder.Configure<App>()
                 .LogToTrace()
-                .UsePlatformDetect()
-                .UseReactiveUI();
+                .UsePlatformDetect();
 
         private static void ConfigureAssetAssembly(AppBuilder builder)
         {

+ 1 - 4
samples/ControlCatalog.NetCore/Program.cs

@@ -10,7 +10,6 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Dialogs;
 using Avalonia.Headless;
 using Avalonia.LogicalTree;
-using Avalonia.ReactiveUI;
 using Avalonia.Threading;
 
 namespace ControlCatalog.NetCore
@@ -114,11 +113,9 @@ namespace ControlCatalog.NetCore
                 })
                 .With(new Win32PlatformOptions
                 {
-                    EnableMultitouch = true,
-                    AllowEglInitialization = true
+                    EnableMultitouch = true
                 })
                 .UseSkia()
-                .UseReactiveUI()
                 .UseManagedSystemDialogs()
                 .LogToTrace();
 

+ 2 - 2
samples/ControlCatalog/App.xaml.cs

@@ -23,7 +23,7 @@ namespace ControlCatalog
         {
             new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
             {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentDark.xaml")
+                Source = new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml")
             },
             DataGridFluent
         };
@@ -32,7 +32,7 @@ namespace ControlCatalog
         {
             new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
             {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml")
+                Source = new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml")
             },
             DataGridFluent
         };

+ 1 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -24,8 +24,8 @@
   <ItemGroup>
     <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
+    <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>
   
   <Import Project="..\..\build\BuildTargets.targets" />

+ 13 - 17
samples/ControlCatalog/DecoratedWindow.xaml

@@ -6,25 +6,21 @@
         <NativeMenu.Menu>
     <NativeMenu>
       <NativeMenuItem Header="Decorated">
-        <NativeMenuItem.Menu>
-          <NativeMenu>
-            <NativeMenuItem Header="Open"/>
-            <NativeMenuItem Header="Recent">
-              <NativeMenuItem.Menu>
-                <NativeMenu/>
-              </NativeMenuItem.Menu>
-            </NativeMenuItem>
-            <NativeMenuItem Header="Quit Avalonia" Gesture="CMD+Q"/>
-          </NativeMenu>
-        </NativeMenuItem.Menu>
+        <NativeMenu>
+          <NativeMenuItem Header="Open"/>
+          <NativeMenuItem Header="Recent">
+            <NativeMenuItem.Menu>
+              <NativeMenu/>
+            </NativeMenuItem.Menu>
+          </NativeMenuItem>
+          <NativeMenuItem Header="Quit Avalonia" Gesture="CMD+Q"/>
+        </NativeMenu>
       </NativeMenuItem>
       <NativeMenuItem Header="Edit">
-        <NativeMenuItem.Menu>
-          <NativeMenu>
-            <NativeMenuItem Header="Copy"/>
-            <NativeMenuItem Header="Paste"/>
-          </NativeMenu>
-        </NativeMenuItem.Menu>
+        <NativeMenu>
+          <NativeMenuItem Header="Copy"/>
+          <NativeMenuItem Header="Paste"/>
+        </NativeMenu>
       </NativeMenuItem>
     </NativeMenu>
   </NativeMenu.Menu>

+ 29 - 37
samples/ControlCatalog/MainWindow.xaml

@@ -16,47 +16,39 @@
   <NativeMenu.Menu>
     <NativeMenu>
       <NativeMenuItem Header="File">
-        <NativeMenuItem.Menu>
-          <NativeMenu>
-            <NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Clicked="OnOpenClicked" Gesture="Ctrl+O"/>
-            <NativeMenuItemSeperator/>
-            <NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent">
-              <NativeMenuItem.Menu>
-                <NativeMenu/>
-              </NativeMenuItem.Menu>
-            </NativeMenuItem>
-            <NativeMenuItemSeperator/>
-            <NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}"
-                            Gesture="{x:Static local:MainWindow.MenuQuitGesture}"
-                            Clicked="OnCloseClicked" />
-          </NativeMenu>
-        </NativeMenuItem.Menu>
+        <NativeMenu>
+          <NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Clicked="OnOpenClicked" Gesture="Ctrl+O"/>
+          <NativeMenuItemSeperator/>
+          <NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent">
+            <NativeMenu/>
+          </NativeMenuItem>
+          <NativeMenuItemSeperator/>
+          <NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}"
+                          Gesture="{x:Static local:MainWindow.MenuQuitGesture}"
+                          Clicked="OnCloseClicked" />
+        </NativeMenu>
       </NativeMenuItem>
       <NativeMenuItem Header="Edit">
-        <NativeMenuItem.Menu>
-          <NativeMenu>
-            <NativeMenuItem Header="Copy"/>
-            <NativeMenuItem Header="Paste"/>
-          </NativeMenu>
-        </NativeMenuItem.Menu>
+        <NativeMenu>
+          <NativeMenuItem Header="Copy"/>
+          <NativeMenuItem Header="Paste"/>
+        </NativeMenu>
       </NativeMenuItem>
       <NativeMenuItem Header="Options">
-        <NativeMenuItem.Menu>
-          <NativeMenu>
-           <NativeMenuItem Header="Check Me (None)" 
-                            Command="{Binding ToggleMenuItemCheckedCommand}"
-                            ToggleType="None"
-                            IsChecked="{Binding IsMenuItemChecked}"  />
-            <NativeMenuItem Header="Check Me (CheckBox)" 
-                            Command="{Binding ToggleMenuItemCheckedCommand}"
-                            ToggleType="CheckBox"
-                            IsChecked="{Binding IsMenuItemChecked}"  />
-            <NativeMenuItem Header="Check Me (Radio)" 
-                            Command="{Binding ToggleMenuItemCheckedCommand}"
-                            ToggleType="Radio"
-                            IsChecked="{Binding IsMenuItemChecked}"  />
-          </NativeMenu>
-        </NativeMenuItem.Menu>
+        <NativeMenu>
+          <NativeMenuItem Header="Check Me (None)" 
+                          Command="{Binding ToggleMenuItemCheckedCommand}"
+                          ToggleType="None"
+                          IsChecked="{Binding IsMenuItemChecked}"  />
+          <NativeMenuItem Header="Check Me (CheckBox)" 
+                          Command="{Binding ToggleMenuItemCheckedCommand}"
+                          ToggleType="CheckBox"
+                          IsChecked="{Binding IsMenuItemChecked}"  />
+          <NativeMenuItem Header="Check Me (Radio)" 
+                          Command="{Binding ToggleMenuItemCheckedCommand}"
+                          ToggleType="Radio"
+                          IsChecked="{Binding IsMenuItemChecked}"  />
+        </NativeMenu>
       </NativeMenuItem>
     </NativeMenu>
   </NativeMenu.Menu>

+ 1 - 1
samples/ControlCatalog/MainWindow.xaml.cs

@@ -67,7 +67,7 @@ namespace ControlCatalog
             if (Application.Current.Styles.Contains(App.FluentDark)
                 || Application.Current.Styles.Contains(App.FluentLight))
             {
-                var theme = new Avalonia.Themes.Fluent.FluentTheme();
+                var theme = new Avalonia.Themes.Fluent.Controls.FluentControls();
                 theme.TryGetResource("Button", out _);
             }
             else

+ 0 - 1
samples/ControlCatalog/Pages/LabelsPage.axaml.cs

@@ -2,7 +2,6 @@
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using ControlCatalog.Models;
-using ReactiveUI;
 
 namespace ControlCatalog.Pages
 {

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

@@ -20,6 +20,6 @@
     <ListBox Items="{Binding Items}"
              Selection="{Binding Selection}"
              AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}"
-             SelectionMode="{Binding SelectionMode}"/>
+             SelectionMode="{Binding SelectionMode^}"/>
   </DockPanel>
 </UserControl>

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

@@ -6,7 +6,6 @@ using System.Windows.Input;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using ControlCatalog.ViewModels;
-using ReactiveUI;
 
 namespace ControlCatalog.Pages
 {

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

@@ -6,7 +6,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.Primitives;
 using Avalonia.Markup.Xaml;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.Pages
 {
@@ -26,7 +26,7 @@ namespace ControlCatalog.Pages
 
     }
 
-    public class NumbersPageViewModel : ReactiveObject
+    public class NumbersPageViewModel : ViewModelBase
     {
         private IList<FormatObject> _formats;
         private FormatObject _selectedFormat;

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

@@ -2,11 +2,11 @@ using System.Collections.Generic;
 using Avalonia.Controls;
 using Avalonia.Controls.Primitives;
 using Avalonia.Markup.Xaml;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.Pages
 {
-    public class ScrollViewerPageViewModel : ReactiveObject
+    public class ScrollViewerPageViewModel : ViewModelBase
     {
         private bool _allowAutoHide;
         private ScrollBarVisibility _horizontalScrollVisibility;

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

@@ -6,7 +6,7 @@ using Avalonia.Markup.Xaml;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.Pages
 {
@@ -56,7 +56,7 @@ namespace ControlCatalog.Pages
             return new Bitmap(assets.Open(new Uri(uri)));
         }
 
-        private class PageViewModel : ReactiveObject
+        private class PageViewModel : ViewModelBase
         {
             private Dock _tabPlacement;
 

+ 7 - 7
samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs

@@ -3,7 +3,7 @@ using System.Reactive;
 using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.VisualTree;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
@@ -12,9 +12,9 @@ namespace ControlCatalog.ViewModels
         public Control View { get; set; }
         public ContextMenuPageViewModel()
         {
-            OpenCommand = ReactiveCommand.CreateFromTask(Open);
-            SaveCommand = ReactiveCommand.Create(Save);
-            OpenRecentCommand = ReactiveCommand.Create<string>(OpenRecent);
+            OpenCommand = MiniCommand.CreateFromTask(Open);
+            SaveCommand = MiniCommand.Create(Save);
+            OpenRecentCommand = MiniCommand.Create<string>(OpenRecent);
 
             MenuItems = new[]
             {
@@ -44,9 +44,9 @@ namespace ControlCatalog.ViewModels
         }
 
         public IReadOnlyList<MenuItemViewModel> MenuItems { get; set; }
-        public ReactiveCommand<Unit, Unit> OpenCommand { get; }
-        public ReactiveCommand<Unit, Unit> SaveCommand { get; }
-        public ReactiveCommand<string, Unit> OpenRecentCommand { get; }
+        public MiniCommand OpenCommand { get; }
+        public MiniCommand SaveCommand { get; }
+        public MiniCommand OpenRecentCommand { get; }
 
         public async Task Open()
         {

+ 3 - 3
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@@ -2,11 +2,11 @@
 using System.Collections.ObjectModel;
 using System.Linq;
 using Avalonia.Media;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
-    public class ItemsRepeaterPageViewModel : ReactiveObject
+    public class ItemsRepeaterPageViewModel : ViewModelBase
     {
         private int _newItemIndex = 1;
         private int _newGenerationIndex = 0;
@@ -59,7 +59,7 @@ namespace ControlCatalog.ViewModels
                 }));
         }
 
-        public class Item : ReactiveObject
+        public class Item : ViewModelBase
         {
             private double _height = double.NaN;
 

+ 13 - 14
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@@ -4,18 +4,18 @@ using System.Linq;
 using System.Reactive;
 using Avalonia.Controls;
 using Avalonia.Controls.Selection;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
-    public class ListBoxPageViewModel : ReactiveObject
+    public class ListBoxPageViewModel : ViewModelBase
     {
         private bool _multiple;
         private bool _toggle;
         private bool _alwaysSelected;
         private bool _autoScrollToSelectedItem = true;
         private int _counter;
-        private ObservableAsPropertyHelper<SelectionMode> _selectionMode;
+        private IObservable<SelectionMode> _selectionMode;
 
         public ListBoxPageViewModel()
         {
@@ -29,14 +29,13 @@ namespace ControlCatalog.ViewModels
                 x => x.Toggle,
                 x => x.AlwaysSelected,
                 (m, t, a) =>
-                    (m ? SelectionMode.Multiple : 0) |
-                    (t ? SelectionMode.Toggle : 0) |
-                    (a ? SelectionMode.AlwaysSelected : 0))
-                .ToProperty(this, x => x.SelectionMode);
+                    (m ? Avalonia.Controls.SelectionMode.Multiple : 0) |
+                    (t ? Avalonia.Controls.SelectionMode.Toggle : 0) |
+                    (a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0));
 
-            AddItemCommand = ReactiveCommand.Create(() => Items.Add(GenerateItem()));
+            AddItemCommand = MiniCommand.Create(() => Items.Add(GenerateItem()));
 
-            RemoveItemCommand = ReactiveCommand.Create(() =>
+            RemoveItemCommand = MiniCommand.Create(() =>
             {
                 var items = Selection.SelectedItems.ToList();
 
@@ -46,7 +45,7 @@ namespace ControlCatalog.ViewModels
                 }
             });
 
-            SelectRandomItemCommand = ReactiveCommand.Create(() =>
+            SelectRandomItemCommand = MiniCommand.Create(() =>
             {
                 var random = new Random();
 
@@ -60,7 +59,7 @@ namespace ControlCatalog.ViewModels
 
         public ObservableCollection<string> Items { get; }
         public SelectionModel<string> Selection { get; }
-        public SelectionMode SelectionMode => _selectionMode.Value;
+        public IObservable<SelectionMode> SelectionMode => _selectionMode;
 
         public bool Multiple
         {
@@ -86,9 +85,9 @@ namespace ControlCatalog.ViewModels
             set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value);
         }
 
-        public ReactiveCommand<Unit, Unit> AddItemCommand { get; }
-        public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; }
-        public ReactiveCommand<Unit, Unit> SelectRandomItemCommand { get; }
+        public MiniCommand AddItemCommand { get; }
+        public MiniCommand RemoveItemCommand { get; }
+        public MiniCommand SelectRandomItemCommand { get; }
 
         private string GenerateItem() => $"Item {_counter++.ToString()}";
     }

+ 14 - 14
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@@ -5,11 +5,11 @@ using Avalonia.Controls.Notifications;
 using Avalonia.Dialogs;
 using Avalonia.Platform;
 using System;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
-    class MainWindowViewModel : ReactiveObject
+    class MainWindowViewModel : ViewModelBase
     {
         private IManagedNotificationManager _notificationManager;
 
@@ -27,22 +27,22 @@ namespace ControlCatalog.ViewModels
         {
             _notificationManager = notificationManager;
 
-            ShowCustomManagedNotificationCommand = ReactiveCommand.Create(() =>
+            ShowCustomManagedNotificationCommand = MiniCommand.Create(() =>
             {
                 NotificationManager.Show(new NotificationViewModel(NotificationManager) { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" });
             });
 
-            ShowManagedNotificationCommand = ReactiveCommand.Create(() =>
+            ShowManagedNotificationCommand = MiniCommand.Create(() =>
             {
                 NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information));
             });
 
-            ShowNativeNotificationCommand = ReactiveCommand.Create(() =>
+            ShowNativeNotificationCommand = MiniCommand.Create(() =>
             {
                 NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error));
             });
 
-            AboutCommand = ReactiveCommand.CreateFromTask(async () =>
+            AboutCommand = MiniCommand.CreateFromTask(async () =>
             {
                 var dialog = new AboutAvaloniaDialog();
 
@@ -51,12 +51,12 @@ namespace ControlCatalog.ViewModels
                 await dialog.ShowDialog(mainWindow);
             });
 
-            ExitCommand = ReactiveCommand.Create(() =>
+            ExitCommand = MiniCommand.Create(() =>
             {
                 (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown();
             });
 
-            ToggleMenuItemCheckedCommand = ReactiveCommand.Create(() =>
+            ToggleMenuItemCheckedCommand = MiniCommand.Create(() =>
             {
                 IsMenuItemChecked = !IsMenuItemChecked;
             });
@@ -153,16 +153,16 @@ namespace ControlCatalog.ViewModels
             set { this.RaiseAndSetIfChanged(ref _isMenuItemChecked, value); }
         }
 
-        public ReactiveCommand<Unit, Unit> ShowCustomManagedNotificationCommand { get; }
+        public MiniCommand ShowCustomManagedNotificationCommand { get; }
 
-        public ReactiveCommand<Unit, Unit> ShowManagedNotificationCommand { get; }
+        public MiniCommand ShowManagedNotificationCommand { get; }
 
-        public ReactiveCommand<Unit, Unit> ShowNativeNotificationCommand { get; }
+        public MiniCommand ShowNativeNotificationCommand { get; }
 
-        public ReactiveCommand<Unit, Unit> AboutCommand { get; }
+        public MiniCommand AboutCommand { get; }
 
-        public ReactiveCommand<Unit, Unit> ExitCommand { get; }
+        public MiniCommand ExitCommand { get; }
 
-        public ReactiveCommand<Unit, Unit> ToggleMenuItemCheckedCommand { get; }
+        public MiniCommand ToggleMenuItemCheckedCommand { get; }
     }
 }

+ 7 - 7
samples/ControlCatalog/ViewModels/MenuPageViewModel.cs

@@ -4,7 +4,7 @@ using System.Reactive.Linq;
 using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.VisualTree;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
@@ -13,9 +13,9 @@ namespace ControlCatalog.ViewModels
         public Control View { get; set; }
         public MenuPageViewModel()
         {
-            OpenCommand = ReactiveCommand.CreateFromTask(Open);
-            SaveCommand = ReactiveCommand.Create(Save, Observable.Return(false));
-            OpenRecentCommand = ReactiveCommand.Create<string>(OpenRecent);
+            OpenCommand = MiniCommand.CreateFromTask(Open);
+            SaveCommand = MiniCommand.Create(Save);
+            OpenRecentCommand = MiniCommand.Create<string>(OpenRecent);
 
             var recentItems = new[]
             {
@@ -65,9 +65,9 @@ namespace ControlCatalog.ViewModels
 
         public IReadOnlyList<MenuItemViewModel> MenuItems { get; set; }
         public IReadOnlyList<MenuItemViewModel> RecentItems { get; set; }
-        public ReactiveCommand<Unit, Unit> OpenCommand { get; }
-        public ReactiveCommand<Unit, Unit> SaveCommand { get; }
-        public ReactiveCommand<string, Unit> OpenRecentCommand { get; }
+        public MiniCommand OpenCommand { get; }
+        public MiniCommand SaveCommand { get; }
+        public MiniCommand OpenRecentCommand { get; }
 
         public async Task Open()
         {

+ 5 - 5
samples/ControlCatalog/ViewModels/NotificationViewModel.cs

@@ -1,6 +1,6 @@
 using System.Reactive;
 using Avalonia.Controls.Notifications;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
@@ -8,12 +8,12 @@ namespace ControlCatalog.ViewModels
     {
         public NotificationViewModel(INotificationManager manager)
         {
-            YesCommand = ReactiveCommand.Create(() =>
+            YesCommand = MiniCommand.Create(() =>
             {
                 manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today."));
             });
 
-            NoCommand = ReactiveCommand.Create(() =>
+            NoCommand = MiniCommand.Create(() =>
             {
                 manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit..."));
             });
@@ -22,9 +22,9 @@ namespace ControlCatalog.ViewModels
         public string Title { get; set; }
         public string Message { get; set; }
 
-        public ReactiveCommand<Unit, Unit> YesCommand { get; }
+        public MiniCommand YesCommand { get; }
 
-        public ReactiveCommand<Unit, Unit> NoCommand { get; }
+        public MiniCommand NoCommand { get; }
 
     }
 }

+ 2 - 2
samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs

@@ -1,10 +1,10 @@
 using System;
 using Avalonia.Controls;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
-    public class SplitViewPageViewModel : ReactiveObject
+    public class SplitViewPageViewModel : ViewModelBase
     {
         private bool _isLeft = true;
         private int _displayMode = 3; //CompactOverlay

+ 8 - 8
samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs

@@ -3,11 +3,11 @@ using System.Collections.ObjectModel;
 using System.Linq;
 using System.Reactive;
 using Avalonia.Controls;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
 {
-    public class TreeViewPageViewModel : ReactiveObject
+    public class TreeViewPageViewModel : ViewModelBase
     {
         private readonly Node _root;
         private SelectionMode _selectionMode;
@@ -19,16 +19,16 @@ namespace ControlCatalog.ViewModels
             Items = _root.Children;
             SelectedItems = new ObservableCollection<Node>();
 
-            AddItemCommand = ReactiveCommand.Create(AddItem);
-            RemoveItemCommand = ReactiveCommand.Create(RemoveItem);
-            SelectRandomItemCommand = ReactiveCommand.Create(SelectRandomItem);
+            AddItemCommand = MiniCommand.Create(AddItem);
+            RemoveItemCommand = MiniCommand.Create(RemoveItem);
+            SelectRandomItemCommand = MiniCommand.Create(SelectRandomItem);
         }
 
         public ObservableCollection<Node> Items { get; }
         public ObservableCollection<Node> SelectedItems { get; }
-        public ReactiveCommand<Unit, Unit> AddItemCommand { get; }
-        public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; }
-        public ReactiveCommand<Unit, Unit> SelectRandomItemCommand { get; }
+        public MiniCommand AddItemCommand { get; }
+        public MiniCommand RemoveItemCommand { get; }
+        public MiniCommand SelectRandomItemCommand { get; }
 
         public SelectionMode SelectionMode
         {

+ 66 - 0
samples/MiniMvvm/MiniCommand.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace MiniMvvm
+{
+    public sealed class MiniCommand<T> : MiniCommand, ICommand
+    {
+        private readonly Action<T> _cb;
+        private bool _busy;
+        private Func<T, Task> _acb;
+        
+        public MiniCommand(Action<T> cb)
+        {
+            _cb = cb;
+        }
+
+        public MiniCommand(Func<T, Task> cb)
+        {
+            _acb = cb;
+        }
+
+        private bool Busy
+        {
+            get => _busy;
+            set
+            {
+                _busy = value;
+                CanExecuteChanged?.Invoke(this, EventArgs.Empty);
+            }
+        }
+
+        
+        public override event EventHandler CanExecuteChanged;
+        public override bool CanExecute(object parameter) => !_busy;
+
+        public override async void Execute(object parameter)
+        {
+            if(Busy)
+                return;
+            try
+            {
+                Busy = true;
+                if (_cb != null)
+                    _cb((T)parameter);
+                else
+                    await _acb((T)parameter);
+            }
+            finally
+            {
+                Busy = false;
+            }
+        }
+    }
+    
+    public abstract class MiniCommand : ICommand
+    {
+        public static MiniCommand Create(Action cb) => new MiniCommand<object>(_ => cb());
+        public static MiniCommand Create<TArg>(Action<TArg> cb) => new MiniCommand<TArg>(cb);
+        public static MiniCommand CreateFromTask(Func<Task> cb) => new MiniCommand<object>(_ => cb());
+        
+        public abstract bool CanExecute(object parameter);
+        public abstract void Execute(object parameter);
+        public abstract event EventHandler CanExecuteChanged;
+    }
+}

+ 6 - 0
samples/MiniMvvm/MiniMvvm.csproj

@@ -0,0 +1,6 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+  </PropertyGroup>
+  <Import Project="..\..\build\Rx.props" />
+</Project>

+ 108 - 0
samples/MiniMvvm/PropertyChangedExtensions.cs

@@ -0,0 +1,108 @@
+using System;
+using System.ComponentModel;
+using System.Linq.Expressions;
+using System.Reactive.Linq;
+using System.Reflection;
+
+namespace MiniMvvm
+{
+    public static class PropertyChangedExtensions
+    {
+        class PropertyObservable<T> : IObservable<T>
+        {
+            private readonly INotifyPropertyChanged _target;
+            private readonly PropertyInfo _info;
+
+            public PropertyObservable(INotifyPropertyChanged target, PropertyInfo info)
+            {
+                _target = target;
+                _info = info;
+            }
+
+            class Subscription : IDisposable
+            {
+                private readonly INotifyPropertyChanged _target;
+                private readonly PropertyInfo _info;
+                private readonly IObserver<T> _observer;
+
+                public Subscription(INotifyPropertyChanged target, PropertyInfo info, IObserver<T> observer)
+                {
+                    _target = target;
+                    _info = info;
+                    _observer = observer;
+                    _target.PropertyChanged += OnPropertyChanged;
+                    _observer.OnNext((T)_info.GetValue(_target));
+                }
+
+                private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+                {
+                    if (e.PropertyName == _info.Name)
+                        _observer.OnNext((T)_info.GetValue(_target));
+                }
+
+                public void Dispose()
+                {
+                    _target.PropertyChanged -= OnPropertyChanged;
+                    _observer.OnCompleted();
+                }
+            }
+
+            public IDisposable Subscribe(IObserver<T> observer)
+            {
+                return new Subscription(_target, _info, observer);
+            }
+        }
+
+        public static IObservable<TRes> WhenAnyValue<TModel, TRes>(this TModel model,
+            Expression<Func<TModel, TRes>> expr) where TModel : INotifyPropertyChanged
+        {
+            var l = (LambdaExpression)expr;
+            var ma = (MemberExpression)l.Body;
+            var prop = (PropertyInfo)ma.Member;
+            return new PropertyObservable<TRes>(model, prop);
+        }
+        
+        public static IObservable<TRes> WhenAnyValue<TModel, T1, TRes>(this TModel model,
+            Expression<Func<TModel, T1>> v1,
+            Func<T1,  TRes> cb
+        ) where TModel : INotifyPropertyChanged
+        {
+            return model.WhenAnyValue(v1).Select(cb);
+        }
+        
+        public static IObservable<TRes> WhenAnyValue<TModel, T1, T2, TRes>(this TModel model,
+            Expression<Func<TModel, T1>> v1,
+            Expression<Func<TModel, T2>> v2,
+            Func<T1, T2, TRes> cb
+        ) where TModel : INotifyPropertyChanged =>
+            Observable.CombineLatest(
+                model.WhenAnyValue(v1),
+                model.WhenAnyValue(v2),
+                cb);
+
+        public static IObservable<ValueTuple<T1, T2>> WhenAnyValue<TModel, T1, T2>(this TModel model,
+            Expression<Func<TModel, T1>> v1,
+            Expression<Func<TModel, T2>> v2
+        ) where TModel : INotifyPropertyChanged =>
+            model.WhenAnyValue(v1, v2, (a1, a2) => (a1, a2));
+
+        public static IObservable<TRes> WhenAnyValue<TModel, T1, T2, T3, TRes>(this TModel model,
+            Expression<Func<TModel, T1>> v1,
+            Expression<Func<TModel, T2>> v2,
+            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);
+
+        public static IObservable<ValueTuple<T1, T2, T3>> WhenAnyValue<TModel, T1, T2, T3>(this TModel model,
+            Expression<Func<TModel, T1>> v1,
+            Expression<Func<TModel, T2>> v2,
+            Expression<Func<TModel, T3>> v3
+        ) where TModel : INotifyPropertyChanged =>
+            model.WhenAnyValue(v1, v2, v3, (a1, a2, a3) => (a1, a2, a3));
+    }
+}

+ 26 - 0
samples/MiniMvvm/ViewModelBase.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Reactive.Joins;
+using System.Runtime.CompilerServices;
+
+namespace MiniMvvm
+{
+    public class ViewModelBase : INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+        protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
+        {
+            if (!EqualityComparer<T>.Default.Equals(field, value))
+            {
+                field = value;
+                RaisePropertyChanged(propertyName);
+                return true;
+            }
+            return false;
+        }
+        
+        
+        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+    }
+}

+ 0 - 1
samples/Previewer/Previewer.csproj

@@ -8,7 +8,6 @@
       <DependentUpon>%(Filename)</DependentUpon>
     </Compile>
     <EmbeddedResource Include="**\*.xaml" />
-    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
   </ItemGroup>
   
   <Import Project="..\..\build\SampleApp.props" />

+ 1 - 1
samples/RenderDemo/App.xaml

@@ -3,7 +3,7 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="RenderDemo.App">
     <Application.Styles>
-        <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml"/>
+        <FluentTheme/>
         <StyleInclude Source="avares://RenderDemo/SideBar.xaml"/>
     </Application.Styles>
 </Application>

+ 0 - 2
samples/RenderDemo/App.xaml.cs

@@ -1,7 +1,6 @@
 using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
-using Avalonia.ReactiveUI;
 
 namespace RenderDemo
 {
@@ -32,7 +31,6 @@ namespace RenderDemo
                    OverlayPopups = true,
                })
                 .UsePlatformDetect()
-                .UseReactiveUI()
                 .LogToTrace();
     }
 }

+ 1 - 1
samples/RenderDemo/MainWindow.xaml.cs

@@ -3,7 +3,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using RenderDemo.ViewModels;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace RenderDemo
 {

+ 1 - 2
samples/RenderDemo/RenderDemo.csproj

@@ -9,12 +9,11 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
+    <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>
   <Import Project="..\..\build\SampleApp.props" />
   <Import Project="..\..\build\EmbedXaml.props" />
   <Import Project="..\..\build\Rx.props" />
-  <Import Project="..\..\build\ReactiveUI.props" />
   <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />
   <Import Project="..\..\build\BuildTargets.targets" />

+ 2 - 2
samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs

@@ -1,10 +1,10 @@
 using System;
-using ReactiveUI;
+using MiniMvvm;
 using Avalonia.Animation;
 
 namespace RenderDemo.ViewModels
 {
-    public class AnimationsPageViewModel : ReactiveObject
+    public class AnimationsPageViewModel : ViewModelBase
     {
         private bool _isPlaying = true;
 

+ 8 - 9
samples/RenderDemo/ViewModels/MainWindowViewModel.cs

@@ -1,11 +1,10 @@
 using System.Reactive;
 using System.Threading.Tasks;
-
-using ReactiveUI;
+using MiniMvvm;
 
 namespace RenderDemo.ViewModels
 {
-    public class MainWindowViewModel : ReactiveObject
+    public class MainWindowViewModel : ViewModelBase
     {
         private bool drawDirtyRects = false;
         private bool drawFps = true;
@@ -14,9 +13,9 @@ namespace RenderDemo.ViewModels
 
         public MainWindowViewModel()
         {
-            ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects);
-            ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps);
-            ResizeWindow = ReactiveCommand.CreateFromTask(ResizeWindowAsync);
+            ToggleDrawDirtyRects = MiniCommand.Create(() => DrawDirtyRects = !DrawDirtyRects);
+            ToggleDrawFps = MiniCommand.Create(() => DrawFps = !DrawFps);
+            ResizeWindow = MiniCommand.CreateFromTask(ResizeWindowAsync);
         }
 
         public bool DrawDirtyRects
@@ -43,9 +42,9 @@ namespace RenderDemo.ViewModels
             set => this.RaiseAndSetIfChanged(ref height, value);
         }
 
-        public ReactiveCommand<Unit, bool> ToggleDrawDirtyRects { get; }
-        public ReactiveCommand<Unit, bool> ToggleDrawFps { get; }
-        public ReactiveCommand<Unit, Unit> ResizeWindow { get; }
+        public MiniCommand ToggleDrawDirtyRects { get; }
+        public MiniCommand ToggleDrawFps { get; }
+        public MiniCommand ResizeWindow { get; }
 
         private async Task ResizeWindowAsync()
         {

+ 1 - 1
samples/Sandbox/App.axaml

@@ -3,6 +3,6 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="Sandbox.App">
     <Application.Styles>
-        <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/FluentDark.xaml"/>
+        <FluentTheme Mode="Dark"/>
     </Application.Styles>
 </Application>

+ 0 - 2
samples/Sandbox/Program.cs

@@ -1,5 +1,4 @@
 using Avalonia;
-using Avalonia.ReactiveUI;
 
 namespace Sandbox
 {
@@ -9,7 +8,6 @@ namespace Sandbox
         {
             AppBuilder.Configure<App>()
                 .UsePlatformDetect()
-                .UseReactiveUI()
                 .LogToTrace()
                 .StartWithClassicDesktopLifetime(args);
         }

+ 0 - 1
samples/Sandbox/Sandbox.csproj

@@ -8,7 +8,6 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
   </ItemGroup>
   

+ 0 - 2
samples/VirtualizationDemo/Program.cs

@@ -1,5 +1,4 @@
 using Avalonia;
-using Avalonia.ReactiveUI;
 
 namespace VirtualizationDemo
 {
@@ -8,7 +7,6 @@ namespace VirtualizationDemo
         public static AppBuilder BuildAvaloniaApp()
             => AppBuilder.Configure<App>()
                 .UsePlatformDetect()
-                .UseReactiveUI()
                 .LogToTrace();
 
         public static int Main(string[] args)

+ 2 - 2
samples/VirtualizationDemo/ViewModels/ItemViewModel.cs

@@ -1,9 +1,9 @@
 using System;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace VirtualizationDemo.ViewModels
 {
-    internal class ItemViewModel : ReactiveObject
+    internal class ItemViewModel : ViewModelBase
     {
         private string _prefix;
         private int _index;

+ 12 - 12
samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs

@@ -5,13 +5,13 @@ using System.Reactive;
 using Avalonia.Collections;
 using Avalonia.Controls;
 using Avalonia.Controls.Primitives;
-using ReactiveUI;
 using Avalonia.Layout;
 using Avalonia.Controls.Selection;
+using MiniMvvm;
 
 namespace VirtualizationDemo.ViewModels
 {
-    internal class MainWindowViewModel : ReactiveObject
+    internal class MainWindowViewModel : ViewModelBase
     {
         private int _itemCount = 200;
         private string _newItemString = "New Item";
@@ -26,15 +26,15 @@ namespace VirtualizationDemo.ViewModels
         public MainWindowViewModel()
         {
             this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems);
-            RecreateCommand = ReactiveCommand.Create(() => Recreate());
+            RecreateCommand = MiniCommand.Create(() => Recreate());
 
-            AddItemCommand = ReactiveCommand.Create(() => AddItem());
+            AddItemCommand = MiniCommand.Create(() => AddItem());
 
-            RemoveItemCommand = ReactiveCommand.Create(() => Remove());
+            RemoveItemCommand = MiniCommand.Create(() => Remove());
 
-            SelectFirstCommand = ReactiveCommand.Create(() => SelectItem(0));
+            SelectFirstCommand = MiniCommand.Create(() => SelectItem(0));
 
-            SelectLastCommand = ReactiveCommand.Create(() => SelectItem(Items.Count - 1));
+            SelectLastCommand = MiniCommand.Create(() => SelectItem(Items.Count - 1));
         }
 
         public string NewItemString
@@ -90,11 +90,11 @@ namespace VirtualizationDemo.ViewModels
         public IEnumerable<ItemVirtualizationMode> VirtualizationModes => 
             Enum.GetValues(typeof(ItemVirtualizationMode)).Cast<ItemVirtualizationMode>();
 
-        public ReactiveCommand<Unit, Unit> AddItemCommand { get; private set; }
-        public ReactiveCommand<Unit, Unit> RecreateCommand { get; private set; }
-        public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; private set; }
-        public ReactiveCommand<Unit, Unit> SelectFirstCommand { get; private set; }
-        public ReactiveCommand<Unit, Unit> SelectLastCommand { get; private set; }
+        public MiniCommand AddItemCommand { get; private set; }
+        public MiniCommand RecreateCommand { get; private set; }
+        public MiniCommand RemoveItemCommand { get; private set; }
+        public MiniCommand SelectFirstCommand { get; private set; }
+        public MiniCommand SelectLastCommand { get; private set; }
 
         public void RandomizeSize()
         {

+ 1 - 2
samples/VirtualizationDemo/VirtualizationDemo.csproj

@@ -6,12 +6,11 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
+    <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>
   <Import Project="..\..\build\SampleApp.props" />
   <Import Project="..\..\build\EmbedXaml.props" />
   <Import Project="..\..\build\Rx.props" />
-  <Import Project="..\..\build\ReactiveUI.props" />
   <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />
   <Import Project="..\..\build\BuildTargets.targets" />

+ 1 - 1
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@@ -22,9 +22,9 @@
       </EmbeddedResource>
     </ItemGroup>
     <ItemGroup>
-        <ProjectReference Include="..\..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.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" />

+ 2 - 2
samples/interop/Direct3DInteropSample/MainWindowViewModel.cs

@@ -3,11 +3,11 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using ReactiveUI;
+using MiniMvvm;
 
 namespace Direct3DInteropSample
 {
-    public class MainWindowViewModel : ReactiveObject
+    public class MainWindowViewModel : ViewModelBase
     {
         private double _rotationX;
 

+ 1 - 5
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@@ -136,10 +136,6 @@
       <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
       <Name>Avalonia.Layout</Name>
     </ProjectReference>
-    <ProjectReference Include="..\..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj">
-      <Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project>
-      <Name>Avalonia.ReactiveUI</Name>
-    </ProjectReference>
     <ProjectReference Include="..\..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj">
       <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
       <Name>Avalonia.Visuals</Name>
@@ -190,4 +186,4 @@
   <Import Project="..\..\..\build\Rx.props" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\..\build\LegacyProject.targets" />
-</Project>
+</Project>

+ 0 - 4
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@@ -127,10 +127,6 @@
       <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
       <Name>Avalonia.Layout</Name>
     </ProjectReference>
-    <ProjectReference Include="..\..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj">
-      <Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project>
-      <Name>Avalonia.ReactiveUI</Name>
-    </ProjectReference>
     <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
       <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
       <Name>Avalonia.Visuals</Name>

+ 0 - 4
src/Avalonia.Base/ApiCompatBaseline.txt

@@ -1,4 +0,0 @@
-Compat issues with assembly Avalonia.Base:
-CannotAddAbstractMembers : Member 'protected System.IObservable<Avalonia.AvaloniaPropertyChangedEventArgs> Avalonia.AvaloniaProperty.GetChanged()' is abstract in the implementation but is missing in the contract.
-TypesMustExist : Type 'Avalonia.Logging.DebugLogSink' does not exist in the implementation but it does exist in the contract.
-Total Issues: 2

+ 12 - 12
src/Avalonia.Base/AvaloniaProperty.cs

@@ -17,9 +17,9 @@ namespace Avalonia
         public static readonly object UnsetValue = new UnsetValueType();
 
         private static int s_nextId;
-        private readonly PropertyMetadata _defaultMetadata;
-        private readonly Dictionary<Type, PropertyMetadata> _metadata;
-        private readonly Dictionary<Type, PropertyMetadata> _metadataCache = new Dictionary<Type, PropertyMetadata>();
+        private readonly AvaloniaPropertyMetadata _defaultMetadata;
+        private readonly Dictionary<Type, AvaloniaPropertyMetadata> _metadata;
+        private readonly Dictionary<Type, AvaloniaPropertyMetadata> _metadataCache = new Dictionary<Type, AvaloniaPropertyMetadata>();
 
         private bool _hasMetadataOverrides;
 
@@ -35,7 +35,7 @@ namespace Avalonia
             string name,
             Type valueType,
             Type ownerType,
-            PropertyMetadata metadata,
+            AvaloniaPropertyMetadata metadata,
             Action<IAvaloniaObject, bool> notifying = null)
         {
             Contract.Requires<ArgumentNullException>(name != null);
@@ -48,7 +48,7 @@ namespace Avalonia
                 throw new ArgumentException("'name' may not contain periods.");
             }
 
-            _metadata = new Dictionary<Type, PropertyMetadata>();
+            _metadata = new Dictionary<Type, AvaloniaPropertyMetadata>();
 
             Name = name;
             PropertyType = valueType;
@@ -69,12 +69,12 @@ namespace Avalonia
         protected AvaloniaProperty(
             AvaloniaProperty source,
             Type ownerType,
-            PropertyMetadata metadata)
+            AvaloniaPropertyMetadata metadata)
         {
             Contract.Requires<ArgumentNullException>(source != null);
             Contract.Requires<ArgumentNullException>(ownerType != null);
 
-            _metadata = new Dictionary<Type, PropertyMetadata>();
+            _metadata = new Dictionary<Type, AvaloniaPropertyMetadata>();
 
             Name = source.Name;
             PropertyType = source.PropertyType;
@@ -419,7 +419,7 @@ namespace Avalonia
         /// <returns>
         /// The property metadata.
         /// </returns>
-        public PropertyMetadata GetMetadata<T>() where T : IAvaloniaObject
+        public AvaloniaPropertyMetadata GetMetadata<T>() where T : IAvaloniaObject
         {
             return GetMetadata(typeof(T));
         }
@@ -432,7 +432,7 @@ namespace Avalonia
         /// The property metadata.
         /// </returns>
         ///
-        public PropertyMetadata GetMetadata(Type type)
+        public AvaloniaPropertyMetadata GetMetadata(Type type)
         {
             if (!_hasMetadataOverrides)
             {
@@ -521,7 +521,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="type">The type.</param>
         /// <param name="metadata">The metadata.</param>
-        protected void OverrideMetadata(Type type, PropertyMetadata metadata)
+        protected void OverrideMetadata(Type type, AvaloniaPropertyMetadata metadata)
         {
             Contract.Requires<ArgumentNullException>(type != null);
             Contract.Requires<ArgumentNullException>(metadata != null);
@@ -542,14 +542,14 @@ namespace Avalonia
 
         protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
 
-        private PropertyMetadata GetMetadataWithOverrides(Type type)
+        private AvaloniaPropertyMetadata GetMetadataWithOverrides(Type type)
         {
             if (type is null)
             {
                 throw new ArgumentNullException(nameof(type));
             }
 
-            if (_metadataCache.TryGetValue(type, out PropertyMetadata result))
+            if (_metadataCache.TryGetValue(type, out AvaloniaPropertyMetadata result))
             {
                 return result;
             }

+ 4 - 4
src/Avalonia.Base/PropertyMetadata.cs → src/Avalonia.Base/AvaloniaPropertyMetadata.cs

@@ -5,15 +5,15 @@ namespace Avalonia
     /// <summary>
     /// Base class for avalonia property metadata.
     /// </summary>
-    public class PropertyMetadata
+    public class AvaloniaPropertyMetadata
     {
         private BindingMode _defaultBindingMode;
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="PropertyMetadata"/> class.
+        /// Initializes a new instance of the <see cref="AvaloniaPropertyMetadata"/> class.
         /// </summary>
         /// <param name="defaultBindingMode">The default binding mode.</param>
-        public PropertyMetadata(
+        public AvaloniaPropertyMetadata(
             BindingMode defaultBindingMode = BindingMode.Default)
         {
             _defaultBindingMode = defaultBindingMode;
@@ -37,7 +37,7 @@ namespace Avalonia
         /// <param name="baseMetadata">The base metadata to merge.</param>
         /// <param name="property">The property to which the metadata is being applied.</param>
         public virtual void Merge(
-            PropertyMetadata baseMetadata, 
+            AvaloniaPropertyMetadata baseMetadata, 
             AvaloniaProperty property)
         {
             if (_defaultBindingMode == BindingMode.Default)

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

@@ -23,7 +23,7 @@ namespace Avalonia
         protected AvaloniaProperty(
             string name,
             Type ownerType,
-            PropertyMetadata metadata,
+            AvaloniaPropertyMetadata metadata,
             Action<IAvaloniaObject, bool> notifying = null)
             : base(name, typeof(TValue), ownerType, metadata, notifying)
         {
@@ -40,7 +40,7 @@ namespace Avalonia
         protected AvaloniaProperty(
             AvaloniaProperty source,
             Type ownerType,
-            PropertyMetadata metadata)
+            AvaloniaPropertyMetadata metadata)
             : this(source as AvaloniaProperty<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
         {
         }
@@ -54,7 +54,7 @@ namespace Avalonia
         protected AvaloniaProperty(
             AvaloniaProperty<TValue> source,
             Type ownerType,
-            PropertyMetadata metadata)
+            AvaloniaPropertyMetadata metadata)
             : base(source, ownerType, metadata)
         {
             _changed = source._changed;

+ 18 - 0
src/Avalonia.Base/Collections/AvaloniaList.cs

@@ -63,6 +63,15 @@ namespace Avalonia.Collections
             _inner = new List<T>();
         }
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AvaloniaList{T}"/>.
+        /// </summary>
+        /// <param name="capacity">Initial list capacity.</param>
+        public AvaloniaList(int capacity)
+        {
+            _inner = new List<T>(capacity);
+        }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="AvaloniaList{T}"/> class.
         /// </summary>
@@ -175,6 +184,15 @@ namespace Avalonia.Collections
             set { this[index] = (T)value; }
         }
 
+        /// <summary>
+        /// Gets or sets the total number of elements the internal data structure can hold without resizing.
+        /// </summary>
+        public int Capacity
+        {
+            get => _inner.Capacity;
+            set => _inner.Capacity = value;
+        }
+
         /// <summary>
         /// Adds an item to the collection.
         /// </summary>

+ 3 - 3
src/Avalonia.Base/DirectPropertyBase.cs

@@ -26,7 +26,7 @@ namespace Avalonia
         protected DirectPropertyBase(
             string name,
             Type ownerType,
-            PropertyMetadata metadata)
+            AvaloniaPropertyMetadata metadata)
             : base(name, ownerType, metadata)
         {
         }
@@ -41,7 +41,7 @@ namespace Avalonia
         protected DirectPropertyBase(
             AvaloniaProperty source,
             Type ownerType,
-            PropertyMetadata metadata)
+            AvaloniaPropertyMetadata metadata)
             : this(source as DirectPropertyBase<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
         {
         }
@@ -55,7 +55,7 @@ namespace Avalonia
         protected DirectPropertyBase(
             DirectPropertyBase<TValue> source,
             Type ownerType,
-            PropertyMetadata metadata)
+            AvaloniaPropertyMetadata metadata)
             : base(source, ownerType, metadata)
         {
         }

+ 2 - 2
src/Avalonia.Base/DirectPropertyMetadata`1.cs

@@ -5,7 +5,7 @@ namespace Avalonia
     /// <summary>
     /// Metadata for direct avalonia properties.
     /// </summary>
-    public class DirectPropertyMetadata<TValue> : PropertyMetadata, IDirectPropertyMetadata
+    public class DirectPropertyMetadata<TValue> : AvaloniaPropertyMetadata, IDirectPropertyMetadata
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="StyledPropertyMetadata{TValue}"/> class.
@@ -47,7 +47,7 @@ namespace Avalonia
         object IDirectPropertyMetadata.UnsetValue => UnsetValue;
 
         /// <inheritdoc/>
-        public override void Merge(PropertyMetadata baseMetadata, AvaloniaProperty property)
+        public override void Merge(AvaloniaPropertyMetadata baseMetadata, AvaloniaProperty property)
         {
             base.Merge(baseMetadata, property);
 

+ 5 - 0
src/Avalonia.Base/Logging/LogArea.cs

@@ -34,5 +34,10 @@ namespace Avalonia.Logging
         /// The log event comes from the control system.
         /// </summary>
         public const string Control = "Control";
+
+        /// <summary>
+        /// The log event comes from Win32Platform.
+        /// </summary>
+        public const string Win32Platform = nameof(Win32Platform);
     }
 }

+ 2 - 0
src/Avalonia.Base/Properties/AssemblyInfo.cs

@@ -11,10 +11,12 @@ using Avalonia.Metadata;
 [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] 
 [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
 [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.Visuals, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
 #else
 [assembly: InternalsVisibleTo("Avalonia.Base.UnitTests")]
 [assembly: InternalsVisibleTo("Avalonia.UnitTests")]
 [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
 [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid")]
 [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Visuals")]
 #endif

+ 2 - 2
src/Avalonia.Base/StyledPropertyMetadata`1.cs

@@ -6,7 +6,7 @@ namespace Avalonia
     /// <summary>
     /// Metadata for styled avalonia properties.
     /// </summary>
-    public class StyledPropertyMetadata<TValue> : PropertyMetadata, IStyledPropertyMetadata
+    public class StyledPropertyMetadata<TValue> : AvaloniaPropertyMetadata, IStyledPropertyMetadata
     {
         private Optional<TValue> _defaultValue;
 
@@ -39,7 +39,7 @@ namespace Avalonia
         object IStyledPropertyMetadata.DefaultValue => DefaultValue;
 
         /// <inheritdoc/>
-        public override void Merge(PropertyMetadata baseMetadata, AvaloniaProperty property)
+        public override void Merge(AvaloniaPropertyMetadata baseMetadata, AvaloniaProperty property)
         {
             base.Merge(baseMetadata, property);
 

+ 2 - 23
src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs

@@ -9,20 +9,6 @@ namespace Avalonia.Threading
     /// </summary>
     public class AvaloniaSynchronizationContext : SynchronizationContext
     {
-        public interface INonPumpingPlatformWaitProvider
-        {
-            int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
-        }
-
-        private readonly INonPumpingPlatformWaitProvider _waitProvider;
-
-        public AvaloniaSynchronizationContext(INonPumpingPlatformWaitProvider waitProvider)
-        {
-            _waitProvider = waitProvider;
-            if (_waitProvider != null)
-                SetWaitNotificationRequired();
-        }
-
         /// <summary>
         /// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer.
         /// </summary>
@@ -38,8 +24,7 @@ namespace Avalonia.Threading
                 return;
             }
 
-            SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current
-                .GetService<INonPumpingPlatformWaitProvider>()));
+            SetSynchronizationContext(new AvaloniaSynchronizationContext());
         }
 
         /// <inheritdoc/>
@@ -57,12 +42,6 @@ namespace Avalonia.Threading
                 Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
         }
 
-        [PrePrepareMethod]
-        public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
-        {
-            if (_waitProvider != null)
-                return _waitProvider.Wait(waitHandles, waitAll, millisecondsTimeout);
-            return base.Wait(waitHandles, waitAll, millisecondsTimeout);
-        }
+
     }
 }

+ 22 - 5
src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs

@@ -1,4 +1,8 @@
-using System;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+#nullable enable
 
 namespace Avalonia.Utilities
 {
@@ -8,12 +12,14 @@ namespace Avalonia.Utilities
     /// <typeparam name="TValue">Stored value type.</typeparam>
     internal sealed class AvaloniaPropertyValueStore<TValue>
     {
+        // The last item in the list is always int.MaxValue.
+        private static readonly Entry[] s_emptyEntries = { new Entry { PropertyId = int.MaxValue, Value = default! } };
+        
         private Entry[] _entries;
 
         public AvaloniaPropertyValueStore()
         {
-            // The last item in the list is always int.MaxValue
-            _entries = new[] { new Entry { PropertyId = int.MaxValue, Value = default } };
+            _entries = s_emptyEntries;
         }
 
         public int Count => _entries.Length - 1;
@@ -88,7 +94,7 @@ namespace Avalonia.Utilities
             return (0, false);
         }
 
-        public bool TryGetValue(AvaloniaProperty property, out TValue value)
+        public bool TryGetValue(AvaloniaProperty property, [MaybeNull] out TValue value)
         {
             (int index, bool found) = TryFindEntry(property.Id);
             if (!found)
@@ -134,7 +140,18 @@ namespace Avalonia.Utilities
 
             if (found)
             {
-                Entry[] entries = new Entry[_entries.Length - 1];
+                var newLength = _entries.Length - 1;
+                
+                // Special case - one element left means that value store is empty so we can just reuse our "empty" array.
+                if (newLength == 1)
+                {
+                    _entries = s_emptyEntries;
+                    
+                    return;
+                }
+                
+                var entries = new Entry[newLength];
+
                 int ix = 0;
 
                 for (int i = 0; i < _entries.Length; ++i)

+ 14 - 0
src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Avalonia.Utilities
+{
+    public class NonPumpingLockHelper
+    {
+        public interface IHelperImpl
+        {
+            IDisposable Use();
+        }
+
+        public static IDisposable Use() => AvaloniaLocator.Current.GetService<IHelperImpl>()?.Use();
+    }
+}

+ 10 - 1
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <TargetFrameworks>netstandard2.0</TargetFrameworks>
-        <TargetFrameworks Condition="$(Configuration) == 'Debug'">netstandard2.0;netcoreapp2.0</TargetFrameworks>
+        <TargetFrameworks Condition="$(Configuration) == 'Debug'">netstandard2.0;netcoreapp3.1</TargetFrameworks>
         <OutputType>exe</OutputType>
         <GenerateDocumentationFile>false</GenerateDocumentationFile>
         <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
@@ -81,6 +81,15 @@
       <Compile Include="../Avalonia.Visuals/CornerRadius.cs">
         <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
       </Compile>
+      <Compile Include="../Avalonia.Visuals/Media/Color.cs">
+        <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
+      </Compile>
+      <Compile Include="../Avalonia.Visuals/Media/KnownColors.cs">
+        <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
+      </Compile>
+      <Compile Include="../Avalonia.Controls/GridLength.cs">
+        <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
+      </Compile>
       <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
       <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
       <PackageReference Include="Mono.Cecil" Version="0.11.2" />

+ 4 - 0
src/Avalonia.Build.Tasks/SpanCompat.cs

@@ -1,3 +1,4 @@
+#if !NETCOREAPP3_1
 namespace System
 {
     // This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory
@@ -63,6 +64,8 @@ namespace System
         }
 
         public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
+
+        public static implicit operator ReadOnlySpan<T>(char[] arr) => new ReadOnlySpan<T>(new string(arr));
     }
 
     static class SpanCompatExtensions
@@ -71,3 +74,4 @@ namespace System
     }
 
 }
+#endif

+ 0 - 5
src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt

@@ -1,5 +0,0 @@
-Compat issues with assembly Avalonia.Controls.DataGrid:
-MembersMustExist : Member 'public Avalonia.StyledProperty<System.String> Avalonia.StyledProperty<System.String> Avalonia.Controls.DataGridTextColumn.FontFamilyProperty' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.String Avalonia.Controls.DataGridTextColumn.FontFamily.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Controls.DataGridTextColumn.FontFamily.set(System.String)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 3

+ 21 - 0
src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs

@@ -22,6 +22,22 @@ namespace Avalonia.Controls.Primitives
         }
 
         private double _measureHeightOffset = 0;
+        private double _effectiveViewPortHeight = 0;
+
+        public DataGridRowsPresenter()
+        {
+            EffectiveViewportChanged += OnEffectiveViewportChanged;
+        }
+
+        private void OnEffectiveViewportChanged(object sender, Layout.EffectiveViewportChangedEventArgs e)
+        {
+            if (_effectiveViewPortHeight != e.EffectiveViewport.Height)
+            {
+                _effectiveViewPortHeight = e.EffectiveViewport.Height;
+                InvalidateMeasure();
+            }
+        }
+
         private double CalculateEstimatedAvailableHeight(Size availableSize)
         {
             if(!Double.IsPositiveInfinity(availableSize.Height))
@@ -108,6 +124,11 @@ namespace Avalonia.Controls.Primitives
         /// </returns>
         protected override Size MeasureOverride(Size availableSize)
         {
+            if (double.IsInfinity(availableSize.Height))
+            {
+                availableSize = availableSize.WithHeight(_effectiveViewPortHeight);
+            }
+
             if (availableSize.Height == 0 || OwningGrid == null)
             {
                 return base.MeasureOverride(availableSize);

+ 27 - 2
src/Avalonia.Controls.DataGrid/Themes/Default.xaml

@@ -137,12 +137,37 @@
   </Style>
 
   <Style Selector="DataGridRowHeader">
+    <Setter Property="Focusable" Value="False" />
     <Setter Property="Template">
       <ControlTemplate>
         <Grid x:Name="PART_Root"
-        RowDefinitions="*,*,Auto"
-        ColumnDefinitions="Auto,*">
+              RowDefinitions="*,*,Auto"
+              ColumnDefinitions="Auto,*">
+          <Border Grid.RowSpan="3"
+                  Grid.ColumnSpan="2"
+                  BorderBrush="{TemplateBinding SeparatorBrush}"
+                  BorderThickness="0,0,1,0">
+            <Grid Background="{TemplateBinding Background}">
+              <Rectangle x:Name="RowInvalidVisualElement"
+                         Stretch="Fill" />
+              <Rectangle x:Name="BackgroundRectangle"
+                         Stretch="Fill" />
+            </Grid>
+          </Border>
+          <Rectangle x:Name="HorizontalSeparator"
+                     Grid.Row="2"
+                     Grid.ColumnSpan="2"
+                     Height="1"
+                     Margin="1,0,1,0"
+                     HorizontalAlignment="Stretch"
+                     Fill="{TemplateBinding SeparatorBrush}"
+                     IsVisible="{TemplateBinding AreSeparatorsVisible}" />
 
+          <ContentPresenter Grid.RowSpan="2"
+                            Grid.Column="1"
+                            HorizontalAlignment="Center"
+                            VerticalAlignment="Center"
+                            Content="{TemplateBinding Content}" />
         </Grid>
       </ControlTemplate>
     </Setter>

+ 22 - 4
src/Avalonia.Controls.DataGrid/Utils/ReflectionHelper.cs

@@ -352,19 +352,37 @@ namespace Avalonia.Controls.Utils
                 return null;
             }
 
-            PropertyInfo indexer = null;
             string stringIndex = propertyPath.Substring(1, propertyPath.Length - 2);
-            indexer = FindIndexerInMembers(type.GetDefaultMembers(), stringIndex, out index);
+            var indexer = FindIndexerInMembers(type.GetDefaultMembers(), stringIndex, out index);
             if (indexer != null)
             {
                 // We found the indexer, so return it.
                 return indexer;
             }
 
-            if (typeof(IList).IsAssignableFrom(type))
+            var elementType = type.GetElementType();
+            if (elementType == null)
+            {
+                var genericArguments = type.GetGenericArguments();
+                if (genericArguments.Length == 1)
+                {
+                    elementType = genericArguments[0];
+                }
+            }
+
+            if (elementType != null)
             {
                 // If the object is of type IList, try to use its default indexer.
-                indexer = FindIndexerInMembers(typeof(IList).GetDefaultMembers(), stringIndex, out index);
+                if (typeof(IList<>).MakeGenericType(elementType) is Type genericList
+                    && genericList.IsAssignableFrom(type))
+                {
+                    indexer = FindIndexerInMembers(genericList.GetDefaultMembers(), stringIndex, out index);
+                }
+                if (typeof(IReadOnlyList<>).MakeGenericType(elementType) is Type genericReadOnlyList
+                   && genericReadOnlyList.IsAssignableFrom(type))
+                {
+                    indexer = FindIndexerInMembers(genericReadOnlyList.GetDefaultMembers(), stringIndex, out index);
+                }
             }
 
             return indexer;

+ 0 - 26
src/Avalonia.Controls/ApiCompatBaseline.txt

@@ -1,26 +0,0 @@
-Compat issues with assembly Avalonia.Controls:
-TypesMustExist : Type 'Avalonia.Controls.IndexPath' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Controls.ISelectedItemInfo' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Controls.ISelectionModel' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.Controls.ListBox.SelectionProperty' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.ListBox.Selection.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Controls.ListBox.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Controls.SelectionModel' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Controls.SelectionModelChildrenRequestedEventArgs' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Controls.SelectionModelSelectionChangedEventArgs' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Controls.IControl> Avalonia.StyledProperty<Avalonia.Controls.IControl> Avalonia.Controls.SplitView.ContentProperty' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Controls.IControl> Avalonia.StyledProperty<Avalonia.Controls.IControl> Avalonia.Controls.SplitView.PaneProperty' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Content.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Content.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Pane.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Pane.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.TreeView, Avalonia.Controls.ISelectionModel> Avalonia.DirectProperty<Avalonia.Controls.TreeView, Avalonia.Controls.ISelectionModel> Avalonia.Controls.TreeView.SelectionProperty' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Controls.SelectionChangedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Controls.SelectionChangedEventArgs> Avalonia.Controls.TreeView.SelectionChangedEvent' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.TreeView.Selection.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Controls.TreeView.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.Args' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.Args.get()' is present in the implementation but not in the contract.
-MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.Controls.Primitives.SelectingItemsControl.SelectionProperty' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'protected Avalonia.Controls.ISelectionModel Avalonia.Controls.Primitives.SelectingItemsControl.Selection.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 24

+ 5 - 2
src/Avalonia.Controls/AutoCompleteBox.cs

@@ -1405,8 +1405,11 @@ namespace Avalonia.Controls
                     break;
 
                 case Key.Enter:
-                    OnAdapterSelectionComplete(this, new RoutedEventArgs());
-                    e.Handled = true;
+                    if (IsDropDownOpen)
+                    {
+                        OnAdapterSelectionComplete(this, new RoutedEventArgs());
+                        e.Handled = true;
+                    }
                     break;
 
                 default:

+ 3 - 1
src/Avalonia.Controls/Button.cs

@@ -30,7 +30,7 @@ namespace Avalonia.Controls
     /// A button control.
     /// </summary>
     [PseudoClasses(":pressed")]
-    public class Button : ContentControl
+    public class Button : ContentControl, ICommandSource
     {
         /// <summary>
         /// Defines the <see cref="ClickMode"/> property.
@@ -492,5 +492,7 @@ namespace Avalonia.Controls
         {
             PseudoClasses.Set(":pressed", isPressed);
         }
+
+        void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e);
     }
 }

+ 10 - 0
src/Avalonia.Controls/ContextMenu.cs

@@ -233,6 +233,16 @@ namespace Avalonia.Controls
             }
         }
 
+        protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
+        {
+            base.OnPropertyChanged(change);
+
+            if (change.Property == WindowManagerAddShadowHintProperty && _popup != null)
+            {
+                _popup.WindowManagerAddShadowHint = change.NewValue.GetValueOrDefault<bool>();
+            }
+        }
+
         /// <summary>
         /// Opens the menu.
         /// </summary>

+ 41 - 19
src/Avalonia.Controls/DefinitionList.cs

@@ -1,8 +1,9 @@
-using System;
+using System.Collections;
 using System.Collections.Specialized;
-using System.Linq;
 using Avalonia.Collections;
 
+#nullable enable
+
 namespace Avalonia.Controls
 {
     public abstract class DefinitionList<T> : AvaloniaList<T> where T : DefinitionBase
@@ -14,44 +15,65 @@ namespace Avalonia.Controls
         }
 
         internal bool IsDirty = true;
-        private Grid _parent;
+        private Grid? _parent;
 
-        internal Grid Parent
+        internal Grid? Parent
         {
             get => _parent;
             set => SetParent(value);
         }
 
-        private void SetParent(Grid value)
+        private void SetParent(Grid? value)
         {
             _parent = value;
 
-            foreach (var pair in this.Select((definitions, index) => (definitions, index)))
+            var idx = 0;
+
+            foreach (T definition in this)
             {
-                pair.definitions.Parent = value;
-                pair.definitions.Index = pair.index;
+                definition.Parent = value;
+                definition.Index = idx++;
             }
         }
 
         internal void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
         {
-            foreach (var nI in this.Select((d, i) => (d, i)))
-                nI.d._parentIndex = nI.i;
+            var idx = 0;
 
-            foreach (var nD in e.NewItems?.Cast<DefinitionBase>()
-                            ?? Enumerable.Empty<DefinitionBase>())
+            foreach (T definition in this)
             {
-                nD.Parent = this.Parent;
-                nD.OnEnterParentTree();
+                definition.Index = idx++;
             }
+            
+            UpdateDefinitionParent(e.NewItems, false);
+            UpdateDefinitionParent(e.OldItems, true);
+            
+            IsDirty = true;
+        }
 
-            foreach (var oD in e.OldItems?.Cast<DefinitionBase>()
-                            ?? Enumerable.Empty<DefinitionBase>())
+        private void UpdateDefinitionParent(IList? items, bool wasRemoved)
+        {
+            if (items is null)
             {
-                oD.OnExitParentTree();
+                return;
             }
+            
+            var count = items.Count;
 
-            IsDirty = true;
+            for (var i = 0; i < count; i++)
+            {
+                var definition = (DefinitionBase) items[i];
+
+                if (wasRemoved)
+                {
+                    definition.OnExitParentTree();
+                }
+                else
+                {
+                    definition.Parent = Parent;
+                    definition.OnEnterParentTree();                    
+                }
+            }
         }
     }
-}
+}

+ 2 - 2
src/Avalonia.Controls/Generators/ItemContainerInfo.cs

@@ -37,6 +37,6 @@ namespace Avalonia.Controls.Generators
         /// <summary>
         /// Gets the index of the item in the <see cref="ItemsControl.Items"/> collection.
         /// </summary>
-        public int Index { get; internal set; }
+        public int Index { get; set; }
     }
-}
+}

+ 8 - 2
src/Avalonia.Controls/GridLength.cs

@@ -8,7 +8,10 @@ namespace Avalonia.Controls
     /// <summary>
     /// Defines the valid units for a <see cref="GridLength"/>.
     /// </summary>
-    public enum GridUnitType
+#if !BUILDTASK
+    public
+#endif
+    enum GridUnitType
     {
         /// <summary>
         /// The row or column is auto-sized to fit its content.
@@ -29,7 +32,10 @@ namespace Avalonia.Controls
     /// <summary>
     /// Holds the width or height of a <see cref="Grid"/>'s column and row definitions.
     /// </summary>
-    public struct GridLength : IEquatable<GridLength>
+#if !BUILDTASK
+    public
+#endif
+    struct GridLength : IEquatable<GridLength>
     {
         private readonly GridUnitType _type;
 

+ 17 - 9
src/Avalonia.Controls/HotkeyManager.cs

@@ -12,18 +12,21 @@ namespace Avalonia.Controls
 
         class HotkeyCommandWrapper : ICommand
         {
-            public HotkeyCommandWrapper(IControl control)
+            public HotkeyCommandWrapper(ICommandSource control)
             {
-                Control = control;
+                CommandSource = control;
             }
 
-            public readonly IControl Control;
+            public readonly ICommandSource CommandSource;
 
-            private ICommand GetCommand() => Control.GetValue(Button.CommandProperty);
+            private ICommand GetCommand() => CommandSource.Command;
 
-            public bool CanExecute(object parameter) => GetCommand()?.CanExecute(parameter) ?? false;
+            public bool CanExecute(object parameter) =>
+                CommandSource.Command?.CanExecute(CommandSource.CommandParameter) == true
+                && CommandSource.IsEffectivelyEnabled;
 
-            public void Execute(object parameter) => GetCommand()?.Execute(parameter);
+            public void Execute(object parameter) =>
+                GetCommand()?.Execute(CommandSource.CommandParameter);
 
 #pragma warning disable 67 // Event not used
             public event EventHandler CanExecuteChanged;
@@ -44,7 +47,7 @@ namespace Avalonia.Controls
             public Manager(IControl control)
             {
                 _control = control;
-                _wrapper = new HotkeyCommandWrapper(_control);
+                _wrapper = new HotkeyCommandWrapper(_control as ICommandSource);
             }
 
             public void Init()
@@ -84,7 +87,7 @@ namespace Avalonia.Controls
             {
                 if (_root != null && _hotkey != null)
                 {
-                    _binding = new KeyBinding() {Gesture = _hotkey, Command = _wrapper};
+                    _binding = new KeyBinding() { Gesture = _hotkey, Command = _wrapper };
                     _root.KeyBindings.Add(_binding);
                 }
             }
@@ -102,8 +105,13 @@ namespace Avalonia.Controls
             HotKeyProperty.Changed.Subscribe(args =>
             {
                 var control = args.Sender as IControl;
-                if (args.OldValue != null|| control == null)
+                if (args.OldValue != null || control == null || !(control is ICommandSource))
+                {
+                    Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?.
+                        Log(control, $"The element {args.Sender.GetType().Name} does not support binding a HotKey ({args.NewValue}).");
                     return;
+                }
+
                 new Manager(control).Init();
             });
         }

+ 3 - 1
src/Avalonia.Controls/MenuItem.cs

@@ -22,7 +22,7 @@ namespace Avalonia.Controls
     /// A menu item control.
     /// </summary>
     [PseudoClasses(":separator", ":icon", ":open", ":pressed", ":selected")]
-    public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable
+    public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, ICommandSource
     {
         /// <summary>
         /// Defines the <see cref="Command"/> property.
@@ -609,6 +609,8 @@ namespace Avalonia.Controls
             SelectedItem = null;
         }
 
+        void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e);
+
         /// <summary>
         /// A dependency resolver which returns a <see cref="MenuItemAccessKeyHandler"/>.
         /// </summary>

+ 1 - 2
src/Avalonia.Controls/NativeMenuBar.cs

@@ -1,7 +1,6 @@
 using System;
 using Avalonia.Controls.Primitives;
 using Avalonia.Interactivity;
-using Avalonia.Styling;
 
 namespace Avalonia.Controls
 {
@@ -16,7 +15,7 @@ namespace Avalonia.Controls
             EnableMenuItemClickForwardingProperty.Changed.Subscribe(args =>
             {
                 var item = (MenuItem)args.Sender;
-                if (args.NewValue.Equals(true))
+                if (args.NewValue.GetValueOrDefault<bool>())
                     item.Click += OnMenuItemClick;
                 else
                     item.Click -= OnMenuItemClick;

+ 16 - 4
src/Avalonia.Controls/NativeMenuItem.cs

@@ -2,6 +2,7 @@ using System;
 using System.Windows.Input;
 using Avalonia.Input;
 using Avalonia.Media.Imaging;
+using Avalonia.Metadata;
 using Avalonia.Utilities;
 
 namespace Avalonia.Controls
@@ -62,6 +63,7 @@ namespace Avalonia.Controls
         public static readonly DirectProperty<NativeMenuItem, NativeMenu> MenuProperty =
             AvaloniaProperty.RegisterDirect<NativeMenuItem, NativeMenu>(nameof(Menu), o => o.Menu, (o, v) => o.Menu = v);
 
+        [Content]
         public NativeMenu Menu
         {
             get => _menu;
@@ -148,10 +150,10 @@ namespace Avalonia.Controls
 
         void CanExecuteChanged()
         {
-            IsEnabled = _command?.CanExecute(null) ?? true;
+            IsEnabled = _command?.CanExecute(CommandParameter) ?? true;
         }
 
-        public bool HasClickHandlers => Clicked != null;
+        public bool HasClickHandlers => Click != null;
 
         public ICommand Command
         {
@@ -182,11 +184,21 @@ namespace Avalonia.Controls
             set { SetValue(CommandParameterProperty, value); }
         }
 
-        public event EventHandler Clicked;
+        /// <summary>
+        /// Occurs when a <see cref="NativeMenuItem"/> is clicked.
+        /// </summary>
+        public event EventHandler Click;
+
+        [Obsolete("Use Click event.")]
+        public event EventHandler Clicked
+        {
+            add => Click += value;
+            remove => Click -= value;
+        }
 
         void INativeMenuItemExporterEventsImplBridge.RaiseClicked()
         {
-            Clicked?.Invoke(this, new EventArgs());
+            Click?.Invoke(this, new EventArgs());
 
             if (Command?.CanExecute(CommandParameter) == true)
             {

+ 1 - 1
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@@ -231,7 +231,7 @@ namespace Avalonia.Controls.Platform
                 {
                     var direction = e.Key.ToNavigationDirection();
 
-                    if (direction.HasValue)
+                    if (direction?.IsDirectional() == true)
                     {
                         if (item == null && _isContextMenu)
                         {

+ 2 - 2
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@@ -5,9 +5,9 @@ namespace Avalonia.Platform
     public interface IWindowBaseImpl : ITopLevelImpl
     {
         /// <summary>
-        /// Shows the top level.
+        /// Shows the window.
         /// </summary>
-        void Show();
+        void Show(bool activate);
 
         /// <summary>
         /// Hides the window.

+ 1 - 1
src/Avalonia.Controls/Primitives/ScrollBar.cs

@@ -35,7 +35,7 @@ namespace Avalonia.Controls.Primitives
         /// Defines the <see cref="Visibility"/> property.
         /// </summary>
         public static readonly StyledProperty<ScrollBarVisibility> VisibilityProperty =
-            AvaloniaProperty.Register<ScrollBar, ScrollBarVisibility>(nameof(Visibility));
+            AvaloniaProperty.Register<ScrollBar, ScrollBarVisibility>(nameof(Visibility), ScrollBarVisibility.Visible);
 
         /// <summary>
         /// Defines the <see cref="Orientation"/> property.

+ 11 - 0
src/Avalonia.Controls/Slider.cs

@@ -3,6 +3,7 @@ using Avalonia.Collections;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Mixins;
 using Avalonia.Controls.Primitives;
+using Avalonia.Data;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Layout;
@@ -94,6 +95,8 @@ namespace Avalonia.Controls
             Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
             Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
                 RoutingStrategies.Bubble);
+            
+            ValueProperty.OverrideMetadata<Slider>(new DirectPropertyMetadata<double>(enableDataValidation: true));
         }
 
         /// <summary>
@@ -225,6 +228,14 @@ namespace Avalonia.Controls
             Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue;
         }
 
+        protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
+        {
+            if (property == ValueProperty)
+            {
+                DataValidationErrors.SetError(this, value.Error);
+            }
+        }
+
         protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
         {
             base.OnPropertyChanged(change);

+ 4 - 1
src/Avalonia.Controls/TextBox.cs

@@ -867,7 +867,10 @@ namespace Avalonia.Controls
             {
                 var point = e.GetPosition(_presenter);
 
-                point = new Point(MathUtilities.Clamp(point.X, 0, _presenter.Bounds.Width - 1), MathUtilities.Clamp(point.Y, 0, _presenter.Bounds.Height - 1));
+                point = new Point(
+                    MathUtilities.Clamp(point.X, 0, Math.Max(_presenter.Bounds.Width - 1, 0)),
+                    MathUtilities.Clamp(point.Y, 0, Math.Max(_presenter.Bounds.Height - 1, 0)));
+
                 CaretIndex = SelectionEnd = _presenter.GetCaretIndex(point);
             }
         }

+ 17 - 3
src/Avalonia.Controls/Window.cs

@@ -129,6 +129,12 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<SystemDecorations> SystemDecorationsProperty =
             AvaloniaProperty.Register<Window, SystemDecorations>(nameof(SystemDecorations), SystemDecorations.Full);
 
+        /// <summary>
+        /// Defines the <see cref="ShowActivated"/> property.
+        /// </summary>
+        public static readonly StyledProperty<bool> ShowActivatedProperty =
+            AvaloniaProperty.Register<Window, bool>(nameof(ShowActivated), true);
+
         /// <summary>
         /// Enables or disables the taskbar icon
         /// </summary>
@@ -352,13 +358,21 @@ namespace Avalonia.Controls
         /// <summary>
         /// Sets the system decorations (title bar, border, etc)
         /// </summary>
-        /// 
         public SystemDecorations SystemDecorations
         {
             get { return GetValue(SystemDecorationsProperty); }
             set { SetValue(SystemDecorationsProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets a value that indicates whether a window is activated when first shown. 
+        /// </summary>
+        public bool ShowActivated
+        {
+            get { return GetValue(ShowActivatedProperty); }
+            set { SetValue(ShowActivatedProperty, value); }
+        }
+
         /// <summary>
         /// Enables or disables the taskbar icon
         /// </summary>
@@ -650,7 +664,7 @@ namespace Avalonia.Controls
                 Owner = parent;
                 parent?.AddChild(this, false);
 
-                PlatformImpl?.Show();
+                PlatformImpl?.Show(ShowActivated);
                 Renderer?.Start();
                 SetWindowStartupLocation(Owner?.PlatformImpl);
             }
@@ -720,7 +734,7 @@ namespace Avalonia.Controls
                 PlatformImpl?.SetParent(owner.PlatformImpl);
                 Owner = owner;
                 owner.AddChild(this, true);
-                PlatformImpl?.Show();
+                PlatformImpl?.Show(ShowActivated);
 
                 Renderer?.Start();
 

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