Преглед изворни кода

Merge branch 'master' into fixes/8092-uselayoutrounding-fixes

Max Katz пре 3 година
родитељ
комит
a77dff0b85
100 измењених фајлова са 1253 додато и 2024 уклоњено
  1. 1 0
      .editorconfig
  2. 13 1501
      Avalonia.sln
  3. 1 0
      Directory.Build.props
  4. 2 0
      build/ApiCompatAttributeExcludeList.txt
  5. 5 0
      build/AvaloniaPublicKey.props
  6. 0 3
      build/CoreLibraries.props
  7. 3 3
      build/HarfBuzzSharp.props
  8. 1 1
      build/JetBrains.dotMemoryUnit.props
  9. 0 1
      build/NetFX.props
  10. 3 3
      build/SkiaSharp.props
  11. 1 1
      build/SourceLink.props
  12. 1 1
      native/Avalonia.Native/src/OSX/AutoFitContentView.mm
  13. 0 2
      native/Avalonia.Native/src/OSX/AvnPanelWindow.mm
  14. 8 3
      native/Avalonia.Native/src/OSX/AvnView.mm
  15. 82 37
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  16. 1 1
      native/Avalonia.Native/src/OSX/INSWindowHolder.h
  17. 6 17
      native/Avalonia.Native/src/OSX/PopupImpl.mm
  18. 26 21
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  19. 70 55
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  20. 12 0
      native/Avalonia.Native/src/OSX/WindowImpl.h
  21. 95 50
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  22. 3 2
      native/Avalonia.Native/src/OSX/WindowProtocol.h
  23. 11 0
      native/Avalonia.Native/src/OSX/app.mm
  24. 0 1
      nukebuild/Build.cs
  25. 1 2
      nukebuild/_build.csproj
  26. 12 3
      readme.md
  27. 1 0
      samples/BindingDemo/BindingDemo.csproj
  28. 14 16
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  29. 35 0
      samples/ControlCatalog.Android/EmbedSample.Android.cs
  30. 5 1
      samples/ControlCatalog.Android/MainActivity.cs
  31. 2 1
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  32. 1 0
      samples/ControlCatalog.Android/environment.device.txt
  33. 1 0
      samples/ControlCatalog.Android/environment.emulator.txt
  34. 14 0
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  35. 35 0
      samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs
  36. 58 0
      samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs
  37. 0 0
      samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md
  38. 0 0
      samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4
  39. 29 0
      samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs
  40. 38 0
      samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs
  41. 45 0
      samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs
  42. 73 0
      samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs
  43. 7 1
      samples/ControlCatalog.NetCore/Program.cs
  44. 28 0
      samples/ControlCatalog.NetCore/app.manifest
  45. 4 0
      samples/ControlCatalog.Web/App.razor.cs
  46. 34 0
      samples/ControlCatalog.Web/EmbedSample.Browser.cs
  47. 0 70
      samples/ControlCatalog.Web/Shared/MainLayout.razor.css
  48. 5 39
      samples/ControlCatalog.Web/wwwroot/css/app.css
  49. 10 1
      samples/ControlCatalog.Web/wwwroot/js/app.js
  50. 8 1
      samples/ControlCatalog.iOS/AppDelegate.cs
  51. 38 0
      samples/ControlCatalog.iOS/EmbedSample.iOS.cs
  52. 19 1
      samples/ControlCatalog/ControlCatalog.csproj
  53. 7 3
      samples/ControlCatalog/Converter/MathSubtractConverter.cs
  54. 5 5
      samples/ControlCatalog/DecoratedWindow.xaml.cs
  55. 5 2
      samples/ControlCatalog/MainView.xaml
  56. 8 8
      samples/ControlCatalog/MainView.xaml.cs
  57. 3 5
      samples/ControlCatalog/MainWindow.xaml.cs
  58. 2 2
      samples/ControlCatalog/Models/Countries.cs
  59. 2 2
      samples/ControlCatalog/Models/GDPValueConverter.cs
  60. 8 8
      samples/ControlCatalog/Models/Person.cs
  61. 19 14
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  62. 1 1
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs
  63. 2 2
      samples/ControlCatalog/Pages/ButtonsPage.xaml.cs
  64. 1 2
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
  65. 6 6
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
  66. 2 2
      samples/ControlCatalog/Pages/CalendarPage.xaml.cs
  67. 1 0
      samples/ControlCatalog/Pages/CarouselPage.xaml
  68. 9 5
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  69. 11 11
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
  70. 3 3
      samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
  71. 4 4
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  72. 2 2
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
  73. 1 1
      samples/ControlCatalog/Pages/DialogsPage.xaml
  74. 43 22
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  75. 3 3
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  76. 4 4
      samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs
  77. 8 5
      samples/ControlCatalog/Pages/ImagePage.xaml.cs
  78. 15 11
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  79. 1 1
      samples/ControlCatalog/Pages/LabelsPage.axaml.cs
  80. 1 1
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  81. 68 0
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml
  82. 86 0
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
  83. 1 1
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  84. 5 5
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  85. 1 1
      samples/ControlCatalog/Pages/ScreenPage.cs
  86. 6 6
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  87. 1 1
      samples/ControlCatalog/Pages/TabStripPage.xaml.cs
  88. 6 0
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  89. 1 1
      samples/ControlCatalog/ViewModels/ApplicationViewModel.cs
  90. 1 1
      samples/ControlCatalog/ViewModels/ContextPageViewModel.cs
  91. 1 1
      samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
  92. 10 9
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  93. 7 6
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  94. 4 4
      samples/ControlCatalog/ViewModels/MenuItemViewModel.cs
  95. 1 1
      samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
  96. 2 2
      samples/ControlCatalog/ViewModels/NotificationViewModel.cs
  97. 8 8
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  98. 2 2
      samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs
  99. 2 1
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  100. 1 0
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj

+ 1 - 0
.editorconfig

@@ -21,6 +21,7 @@ csharp_new_line_before_finally = true
 csharp_new_line_before_members_in_object_initializers = true
 csharp_new_line_before_members_in_anonymous_types = true
 csharp_new_line_between_query_expression_clauses = true
+trim_trailing_whitespace = true
 
 # Indentation preferences
 csharp_indent_block_contents = true

Разлика између датотеке није приказан због своје велике величине
+ 13 - 1501
Avalonia.sln


+ 1 - 0
Directory.Build.props

@@ -1,4 +1,5 @@
 <Project>
+  <Import Project="$(MSBuildThisFileDirectory)/build/AvaloniaPublicKey.props"/>
   <PropertyGroup>
       <PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
       <AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>

+ 2 - 0
build/ApiCompatAttributeExcludeList.txt

@@ -0,0 +1,2 @@
+T:Avalonia.Metadata.NotClientImplementableAttribute
+T:Avalonia.Metadata.UnstableAttribute

+ 5 - 0
build/AvaloniaPublicKey.props

@@ -0,0 +1,5 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <AvaloniaPublicKey>0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87</AvaloniaPublicKey>
+  </PropertyGroup>
+</Project>

+ 0 - 3
build/CoreLibraries.props

@@ -3,13 +3,10 @@
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Base/Avalonia.Base.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Controls/Avalonia.Controls.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj" />
-      <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj" />
-      <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.OpenGL/Avalonia.OpenGL.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Dialogs/Avalonia.Dialogs.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.MicroCom/Avalonia.MicroCom.csproj" />
-      <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj" />
   </ItemGroup>
 </Project>

+ 3 - 3
build/HarfBuzzSharp.props

@@ -1,7 +1,7 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.254" />
-    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.254" />
-    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.254"/>
+    <PackageReference Include="HarfBuzzSharp" Version="2.8.2" />
+    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2" />
+    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2" />
   </ItemGroup>
 </Project>

+ 1 - 1
build/JetBrains.dotMemoryUnit.props

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

+ 0 - 1
build/NetFX.props

@@ -1,7 +1,6 @@
 <Project>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
   </ItemGroup>
 
 </Project>

+ 3 - 3
build/SkiaSharp.props

@@ -1,7 +1,7 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <PackageReference Include="SkiaSharp" Version="2.88.0-preview.254" />
-    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.254" />
-    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.254"/>
+    <PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" />
+    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.1" />
+    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.1" />
   </ItemGroup>
 </Project>

+ 1 - 1
build/SourceLink.props

@@ -19,7 +19,7 @@
   </PropertyGroup>
   
   <ItemGroup>
-    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
+    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
   </ItemGroup>
 
   <!-- Workaround for https://github.com/dotnet/sdk/issues/11105 -->

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

@@ -85,7 +85,7 @@
     _settingSize = true;
     [super setFrameSize:newSize];
 
-    auto window = static_cast<id <AvnWindowProtocol>>([self window]);
+    auto window = (id <AvnWindowProtocol>) [self window];
 
     // TODO get actual titlebar size
 

+ 0 - 2
native/Avalonia.Native/src/OSX/AvnPanelWindow.mm

@@ -3,8 +3,6 @@
 // Copyright (c) 2022 Avalonia. All rights reserved.
 //
 
-#pragma once
-
 #define IS_NSPANEL
 
 #include "AvnWindow.mm"

+ 8 - 3
native/Avalonia.Native/src/OSX/AvnView.mm

@@ -222,7 +222,7 @@
 
 - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
 {
-    bool triggerInputWhenDisabled = type != Move;
+    bool triggerInputWhenDisabled = type != Move && type != LeaveWindow;
 
     if([self ignoreUserInput: triggerInputWhenDisabled])
     {
@@ -439,7 +439,12 @@
 
     if(_parent != nullptr)
     {
-        _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
+        auto handled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
+        if (key != LeftCtrl && key != RightCtrl) {
+          _lastKeyHandled = handled;
+        } else {
+          _lastKeyHandled = false;
+        }
     }
 }
 
@@ -709,4 +714,4 @@
     return [[self accessibilityChild] accessibilityFocusedUIElement];
 }
 
-@end
+@end

+ 82 - 37
native/Avalonia.Native/src/OSX/AvnWindow.mm

@@ -31,7 +31,9 @@
     ComPtr<WindowBaseImpl> _parent;
     bool _closed;
     bool _isEnabled;
+    bool _canBecomeKeyWindow;
     bool _isExtended;
+    bool _isTransitioningToFullScreen;
     AvnMenu* _menu;
 }
 
@@ -67,7 +69,7 @@
     }
 }
 
-- (void)performClose:(id)sender
+- (void)performClose:(id _Nullable )sender
 {
     if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
     {
@@ -146,7 +148,7 @@
     }
 }
 
--(void) applyMenu:(AvnMenu *)menu
+-(void) applyMenu:(AvnMenu *_Nullable)menu
 {
     if(menu == nullptr)
     {
@@ -156,7 +158,7 @@
     _menu = menu;
 }
 
--(CLASS_NAME*)  initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+-(CLASS_NAME*_Nonnull)  initWithParent: (WindowBaseImpl*_Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
 {
     // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/
     // create nswindow with specific contentRect, otherwise we wont be able to resize the window
@@ -174,10 +176,17 @@
     [self setBackgroundColor: [NSColor clearColor]];
 
     _isExtended = false;
+    _isTransitioningToFullScreen = false;
+
+    if(self.isDialog)
+    {
+        [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary];
+    }
+
     return self;
 }
 
-- (BOOL)windowShouldClose:(NSWindow *)sender
+- (BOOL)windowShouldClose:(NSWindow *_Nonnull)sender
 {
     auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
 
@@ -189,19 +198,28 @@
     return true;
 }
 
-- (void)windowDidChangeBackingProperties:(NSNotification *)notification
+- (void)windowDidChangeBackingProperties:(NSNotification *_Nonnull)notification
 {
     [self backingScaleFactor];
 }
 
-- (void)windowWillClose:(NSNotification *)notification
+
+
+- (void)windowWillClose:(NSNotification *_Nonnull)notification
 {
     _closed = true;
     if(_parent)
     {
         ComPtr<WindowBaseImpl> parent = _parent;
         _parent = NULL;
-        [self restoreParentWindow];
+        
+        auto window = dynamic_cast<WindowImpl*>(parent.getRaw());
+        
+        if(window != nullptr)
+        {
+            window->SetParent(nullptr);
+        }
+        
         parent->BaseEvents->Closed();
         [parent->View onClosed];
     }
@@ -209,26 +227,32 @@
 
 -(BOOL)canBecomeKeyWindow
 {
-    // If the window has a child window being shown as a dialog then don't allow it to become the key window.
-    for(NSWindow* uch in [self childWindows])
+    if(_canBecomeKeyWindow)
     {
-        auto ch = static_cast<id <AvnWindowProtocol>>(uch);
-        if(ch == nil)
-            continue;
-        if (ch.isDialog)
-            return false;
-    }
+        // If the window has a child window being shown as a dialog then don't allow it to become the key window.
+        auto parent = dynamic_cast<WindowImpl*>(_parent.getRaw());
+        
+        if(parent != nullptr)
+        {
+            return parent->CanBecomeKeyWindow();
+        }
 
-    return true;
+        return true;
+    }
+    
+    return false;
 }
 
+#ifndef IS_NSPANEL
 -(BOOL)canBecomeMainWindow
 {
-#ifdef IS_NSPANEL
-    return false;
-#else
     return true;
+}
 #endif
+
+-(void)setCanBecomeKeyWindow:(bool)value
+{
+    _canBecomeKeyWindow = value;
 }
 
 -(bool)shouldTryToHandleEvents
@@ -239,6 +263,10 @@
 -(void) setEnabled:(bool)enable
 {
     _isEnabled = enable;
+    
+    [[self standardWindowButton:NSWindowCloseButton] setEnabled:enable];
+    [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:enable];
+    [[self standardWindowButton:NSWindowZoomButton] setEnabled:enable];
 }
 
 -(void)becomeKeyWindow
@@ -253,17 +281,20 @@
     [super becomeKeyWindow];
 }
 
--(void) restoreParentWindow;
+- (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification
 {
-    auto parent = [self parentWindow];
-
-    if(parent != nil)
-    {
-        [parent removeChildWindow:self];
-    }
+    _parent->BringToFront();
+    
+    dispatch_async(dispatch_get_main_queue(), ^{
+        @try {
+        [self invalidateShadow];
+        }
+        @finally{
+        }
+    });
 }
 
-- (void)windowDidMiniaturize:(NSNotification *)notification
+- (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification
 {
     auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
 
@@ -273,7 +304,7 @@
     }
 }
 
-- (void)windowDidDeminiaturize:(NSNotification *)notification
+- (void)windowDidDeminiaturize:(NSNotification *_Nonnull)notification
 {
     auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
 
@@ -283,7 +314,7 @@
     }
 }
 
-- (void)windowDidResize:(NSNotification *)notification
+- (void)windowDidResize:(NSNotification *_Nonnull)notification
 {
     auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
 
@@ -293,7 +324,7 @@
     }
 }
 
-- (void)windowWillExitFullScreen:(NSNotification *)notification
+- (void)windowWillExitFullScreen:(NSNotification *_Nonnull)notification
 {
     auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
 
@@ -303,7 +334,7 @@
     }
 }
 
-- (void)windowDidExitFullScreen:(NSNotification *)notification
+- (void)windowDidExitFullScreen:(NSNotification *_Nonnull)notification
 {
     auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
 
@@ -326,8 +357,9 @@
     }
 }
 
-- (void)windowWillEnterFullScreen:(NSNotification *)notification
+- (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification
 {
+    _isTransitioningToFullScreen = true;
     auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
 
     if(parent != nullptr)
@@ -336,8 +368,9 @@
     }
 }
 
-- (void)windowDidEnterFullScreen:(NSNotification *)notification
+- (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification
 {
+    _isTransitioningToFullScreen = false;
     auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
 
     if(parent != nullptr)
@@ -347,7 +380,7 @@
     }
 }
 
-- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+- (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame
 {
     return true;
 }
@@ -358,11 +391,13 @@
         _parent->BaseEvents->Deactivated();
 
     [self showAppMenuOnly];
+    
+    [self invalidateShadow];
 
     [super resignKeyWindow];
 }
 
-- (void)windowDidMove:(NSNotification *)notification
+- (void)windowDidMove:(NSNotification *_Nonnull)notification
 {
     AvnPoint position;
 
@@ -372,6 +407,11 @@
 
         if(cparent != nullptr)
         {
+            if(!cparent->IsShown())
+            {
+                return;
+            }
+
             if(cparent->WindowState() == Maximized)
             {
                 cparent->SetWindowState(Normal);
@@ -389,7 +429,7 @@
     return pt;
 }
 
-- (void)sendEvent:(NSEvent *)event
+- (void)sendEvent:(NSEvent *_Nonnull)event
 {
     [super sendEvent:event];
 
@@ -412,8 +452,13 @@
 
                     _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint32>([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
                 }
+                
+                if(!_isTransitioningToFullScreen)
+                {
+                    _parent->BringToFront();
+                }
             }
-                break;
+            break;
 
             case NSEventTypeMouseEntered:
             {

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

@@ -11,7 +11,7 @@
 struct INSWindowHolder
 {
     virtual NSWindow* _Nonnull GetNSWindow () = 0;
-    virtual NSView* _Nonnull GetNSView () = 0;
+    virtual AvnView* _Nonnull GetNSView () = 0;
 };
 
 #endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H

+ 6 - 17
native/Avalonia.Native/src/OSX/PopupImpl.mm

@@ -34,27 +34,16 @@ protected:
         return NSWindowStyleMaskBorderless;
     }
 
-    virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
-    {
-        START_COM_CALL;
-
-        @autoreleasepool
-        {
-            if (Window != nullptr)
-            {
-                [Window setContentSize:NSSize{x, y}];
-
-                [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
-            }
-
-            return S_OK;
-        }
-    }
 public:
     virtual bool ShouldTakeFocusOnShow() override
     {
         return false;
     }
+
+    virtual HRESULT Show(bool activate, bool isDialog) override
+    {
+        return WindowBaseImpl::Show(activate, true);
+    }
 };
 
 
@@ -65,4 +54,4 @@ extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
         IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events, gl));
         return ptr;
     }
-}
+}

+ 26 - 21
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@@ -16,8 +16,6 @@
 class WindowBaseImpl : public virtual ComObject,
                        public virtual IAvnWindowBase,
                        public INSWindowHolder {
-private:
-    NSCursor *cursor;
 
 public:
     FORWARD_IUNKNOWN()
@@ -28,23 +26,7 @@ BEGIN_INTERFACE_MAP()
 
     virtual ~WindowBaseImpl();
 
-    AutoFitContentView *StandardContainer;
-    AvnView *View;
-    NSWindow * Window;
-    ComPtr<IAvnWindowBaseEvents> BaseEvents;
-    ComPtr<IAvnGlContext> _glContext;
-    NSObject <IRenderTarget> *renderTarget;
-    AvnPoint lastPositionSet;
-    NSSize lastSize;
-    NSSize lastMinSize;
-    NSSize lastMaxSize;
-    AvnMenu* lastMenu;
-    NSString *_lastTitle;
-
-    bool _shown;
-    bool _inResize;
-
-    WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl);
+    WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel = false);
 
     virtual HRESULT ObtainNSWindowHandle(void **ret) override;
 
@@ -56,10 +38,12 @@ BEGIN_INTERFACE_MAP()
 
     virtual NSWindow *GetNSWindow() override;
 
-    virtual NSView *GetNSView() override;
+    virtual AvnView *GetNSView() override;
 
     virtual HRESULT Show(bool activate, bool isDialog) override;
 
+    virtual bool IsShown ();
+
     virtual bool ShouldTakeFocusOnShow();
 
     virtual HRESULT Hide() override;
@@ -115,6 +99,8 @@ BEGIN_INTERFACE_MAP()
     virtual bool IsDialog();
 
     id<AvnWindowProtocol> GetWindowProtocol ();
+                           
+    virtual void BringToFront ();
 
 protected:
     virtual NSWindowStyleMask GetStyle();
@@ -124,7 +110,26 @@ protected:
 private:
     void CreateNSWindow (bool isDialog);
     void CleanNSWindow ();
-    void InitialiseNSWindow ();
+
+    NSCursor *cursor;
+    ComPtr<IAvnGlContext> _glContext;
+    bool hasPosition;
+    NSSize lastSize;
+    NSSize lastMinSize;
+    NSSize lastMaxSize;
+    AvnMenu* lastMenu;
+    bool _inResize;
+
+protected:
+    AvnPoint lastPositionSet;
+    AutoFitContentView *StandardContainer;
+    bool _shown;
+
+public:
+    NSObject <IRenderTarget> *renderTarget;
+    NSWindow * Window;
+    ComPtr<IAvnWindowBaseEvents> BaseEvents;
+    AvnView *View;
 };
 
 #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H

+ 70 - 55
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@@ -21,7 +21,7 @@ WindowBaseImpl::~WindowBaseImpl() {
     Window = nullptr;
 }
 
-WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) {
+WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel) {
     _shown = false;
     _inResize = false;
     BaseEvents = events;
@@ -30,15 +30,25 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl)
     View = [[AvnView alloc] initWithParent:this];
     StandardContainer = [[AutoFitContentView new] initWithContent:View];
 
-    lastPositionSet.X = 100;
-    lastPositionSet.Y = 100;
+    lastPositionSet = { 0, 0 };
+    hasPosition = false;
     lastSize = NSSize { 100, 100 };
     lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX};
     lastMinSize = NSSize { 0, 0 };
-    _lastTitle = @"";
 
-    Window = nullptr;
     lastMenu = nullptr;
+    
+    CreateNSWindow(usePanel);
+    
+    [Window setContentView:StandardContainer];
+    [Window setStyleMask:NSWindowStyleMaskBorderless];
+    [Window setBackingType:NSBackingStoreBuffered];
+
+    [Window setContentMinSize:lastMinSize];
+    [Window setContentMaxSize:lastMaxSize];
+
+    [Window setOpaque:false];
+    [Window setHasShadow:true];
 }
 
 HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
@@ -69,7 +79,7 @@ NSWindow *WindowBaseImpl::GetNSWindow() {
     return Window;
 }
 
-NSView *WindowBaseImpl::GetNSView() {
+AvnView *WindowBaseImpl::GetNSView() {
     return View;
 }
 
@@ -89,13 +99,19 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
     START_COM_CALL;
 
     @autoreleasepool {
-        CreateNSWindow(isDialog);
-        InitialiseNSWindow();
+        [Window setContentSize:lastSize];
+        
+        if(hasPosition)
+        {
+            SetPosition(lastPositionSet);
+        } else
+        {
+            [Window center];
+        }
 
-        SetPosition(lastPositionSet);
         UpdateStyle();
-
-        [Window setTitle:_lastTitle];
+        
+        [Window invalidateShadow];
 
         if (ShouldTakeFocusOnShow() && activate) {
             [Window orderFront:Window];
@@ -112,6 +128,11 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
     }
 }
 
+bool WindowBaseImpl::IsShown ()
+{
+    return _shown;
+}
+
 bool WindowBaseImpl::ShouldTakeFocusOnShow() {
     return true;
 }
@@ -134,8 +155,6 @@ HRESULT WindowBaseImpl::Hide() {
     @autoreleasepool {
         if (Window != nullptr) {
             [Window orderOut:Window];
-
-            [GetWindowProtocol() restoreParentWindow];
         }
 
         return S_OK;
@@ -191,9 +210,8 @@ HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) {
         if (ret == nullptr)
             return E_POINTER;
 
-        auto frame = [View frame];
-        ret->Width = frame.size.width;
-        ret->Height = frame.size.height;
+        ret->Width = lastSize.width;
+        ret->Height = lastSize.height;
 
         return S_OK;
     }
@@ -206,9 +224,11 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
         if (ret == nullptr)
             return E_POINTER;
 
-        auto frame = [Window frame];
-        ret->Width = frame.size.width;
-        ret->Height = frame.size.height;
+        if(Window != nullptr){
+            auto frame = [Window frame];
+            ret->Width = frame.size.width;
+            ret->Height = frame.size.height;
+        }
 
         return S_OK;
     }
@@ -278,14 +298,15 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
         }
 
         @try {
-            if (!_shown) {
-                BaseEvents->Resized(AvnSize{x, y}, reason);
-            }
-
-            lastSize = NSSize {x, y};
-
-            if(Window != nullptr) {
-                [Window setContentSize:lastSize];
+            if(x != lastSize.width || y != lastSize.height) {
+                lastSize = NSSize{x, y};
+
+                if (!_shown) {
+                    BaseEvents->Resized(AvnSize{x, y}, reason);
+                } else if (Window != nullptr) {
+                    [Window setContentSize:lastSize];
+                    [Window invalidateShadow];
+                }
             }
         }
         @finally {
@@ -354,12 +375,17 @@ HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) {
             return E_POINTER;
         }
 
-        auto frame = [Window frame];
+        if(Window != nullptr) {
+            auto frame = [Window frame];
 
-        ret->X = frame.origin.x;
-        ret->Y = frame.origin.y + frame.size.height;
+            ret->X = frame.origin.x;
+            ret->Y = frame.origin.y + frame.size.height;
 
-        *ret = ConvertPointY(*ret);
+            *ret = ConvertPointY(*ret);
+        } else
+        {
+            *ret = lastPositionSet;
+        }
 
         return S_OK;
     }
@@ -370,7 +396,11 @@ HRESULT WindowBaseImpl::SetPosition(AvnPoint point) {
 
     @autoreleasepool {
         lastPositionSet = point;
-        [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+        hasPosition = true;
+
+        if(Window != nullptr) {
+            [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+        }
 
         return S_OK;
     }
@@ -538,6 +568,8 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) {
             CleanNSWindow();
 
             Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
+            
+            [Window setHidesOnDeactivate:false];
         }
     } else {
         if (![Window isKindOfClass:[AvnWindow class]]) {
@@ -548,35 +580,18 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) {
     }
 }
 
-void WindowBaseImpl::InitialiseNSWindow() {
-    if(Window != nullptr) {
-        [Window setContentView:StandardContainer];
-        [Window setStyleMask:NSWindowStyleMaskBorderless];
-        [Window setBackingType:NSBackingStoreBuffered];
-
-        [Window setContentSize:lastSize];
-        [Window setContentMinSize:lastMinSize];
-        [Window setContentMaxSize:lastMaxSize];
-
-        [Window setOpaque:false];
-
-        if (lastMenu != nullptr) {
-            [GetWindowProtocol() applyMenu:lastMenu];
-
-            if ([Window isKeyWindow]) {
-                [GetWindowProtocol() showWindowMenuWithAppMenu];
-            }
-        }
-    }
-}
-
 id <AvnWindowProtocol> WindowBaseImpl::GetWindowProtocol() {
     if(Window == nullptr)
     {
         return nullptr;
     }
 
-    return static_cast<id <AvnWindowProtocol>>(Window);
+    return (id <AvnWindowProtocol>) Window;
+}
+
+void WindowBaseImpl::BringToFront()
+{
+    // do nothing.
 }
 
 extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)

+ 12 - 0
native/Avalonia.Native/src/OSX/WindowImpl.h

@@ -8,10 +8,12 @@
 
 #import "WindowBaseImpl.h"
 #include "IWindowStateChanged.h"
+#include <list>
 
 class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
 {
 private:
+    bool _isEnabled;
     bool _canResize;
     bool _fullScreenActive;
     SystemDecorations _decorations;
@@ -22,6 +24,8 @@ private:
     bool _transitioningWindowState;
     bool _isClientAreaExtended;
     bool _isDialog;
+    WindowImpl* _parent;
+    std::list<WindowImpl*> _children;
     AvnExtendClientAreaChromeHints _extendClientHints;
 
     FORWARD_IUNKNOWN()
@@ -88,9 +92,17 @@ BEGIN_INTERFACE_MAP()
     virtual HRESULT SetWindowState (AvnWindowState state) override;
 
     virtual bool IsDialog() override;
+    
+    virtual void BringToFront () override;
+    
+    bool CanBecomeKeyWindow ();
 
 protected:
     virtual NSWindowStyleMask GetStyle() override;
+
+private:
+    void OnInitialiseNSWindow();
+    NSString *_lastTitle;
 };
 
 #endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H

+ 95 - 50
native/Avalonia.Native/src/OSX/WindowImpl.mm

@@ -10,6 +10,8 @@
 #include "WindowProtocol.h"
 
 WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) {
+    _isEnabled = true;
+    _children = std::list<WindowImpl*>();
     _isClientAreaExtended = false;
     _extendClientHints = AvnDefaultChrome;
     _fullScreenActive = false;
@@ -19,10 +21,11 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase
     _inSetWindowState = false;
     _lastWindowState = Normal;
     _actualWindowState = Normal;
+    _lastTitle = @"";
+    _parent = nullptr;
     WindowEvents = events;
-    [Window disableCursorRects];
-    [Window setTabbingMode:NSWindowTabbingModeDisallowed];
-    [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+    
+    OnInitialiseNSWindow();
 }
 
 void WindowImpl::HideOrShowTrafficLights() {
@@ -30,23 +33,27 @@ void WindowImpl::HideOrShowTrafficLights() {
         return;
     }
 
-    for (id subview in Window.contentView.superview.subviews) {
-        if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
-            NSView *titlebarView = [subview subviews][0];
-            for (id button in titlebarView.subviews) {
-                if ([button isKindOfClass:[NSButton class]]) {
-                    if (_isClientAreaExtended) {
-                        auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
+    bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
+    bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
+    
+    [[Window standardWindowButton:NSWindowCloseButton] setHidden:!hasTrafficLights];
+    [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!hasTrafficLights];
+    [[Window standardWindowButton:NSWindowZoomButton] setHidden:!hasTrafficLights];
+}
 
-                        [button setHidden:!wantsChrome];
-                    } else {
-                        [button setHidden:(_decorations != SystemDecorationsFull)];
-                    }
+void WindowImpl::OnInitialiseNSWindow(){
+    [GetWindowProtocol() setCanBecomeKeyWindow:true];
+    
+    [Window disableCursorRects];
+    [Window setTabbingMode:NSWindowTabbingModeDisallowed];
+    [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
 
-                    [button setWantsLayer:true];
-                }
-            }
-        }
+    [Window setTitle:_lastTitle];
+    
+    if(_isClientAreaExtended)
+    {
+        [GetWindowProtocol() setIsExtended:true];
+        SetExtendClientArea(true);
     }
 }
 
@@ -56,19 +63,8 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) {
     @autoreleasepool {
         _isDialog = isDialog;
 
-        bool created = Window == nullptr;
-
         WindowBaseImpl::Show(activate, isDialog);
 
-        if(created)
-        {
-            if(_isClientAreaExtended)
-            {
-                [GetWindowProtocol() setIsExtended:true];
-                SetExtendClientArea(true);
-            }
-        }
-
         HideOrShowTrafficLights();
 
         return SetWindowState(_lastWindowState);
@@ -79,7 +75,9 @@ HRESULT WindowImpl::SetEnabled(bool enable) {
     START_COM_CALL;
 
     @autoreleasepool {
+        _isEnabled = enable;
         [GetWindowProtocol() setEnabled:enable];
+        UpdateStyle();
         return S_OK;
     }
 }
@@ -88,26 +86,68 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) {
     START_COM_CALL;
 
     @autoreleasepool {
-        if (parent == nullptr)
-            return E_POINTER;
+        if(_parent != nullptr)
+        {
+            _parent->_children.remove(this);
+            
+            _parent->BringToFront();
+        }
 
         auto cparent = dynamic_cast<WindowImpl *>(parent);
-        if (cparent == nullptr)
-            return E_INVALIDARG;
-
-        // If one tries to show a child window with a minimized parent window, then the parent window will be
-        // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
-        // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
-        if (cparent->WindowState() == Minimized)
-            cparent->SetWindowState(Normal);
+        
+        _parent = cparent;
+        
+        if(_parent != nullptr && Window != nullptr){
+            // If one tries to show a child window with a minimized parent window, then the parent window will be
+            // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
+            // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
+            if (cparent->WindowState() == Minimized)
+                cparent->SetWindowState(Normal);
+
+            [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
+                
+            cparent->_children.push_back(this);
+                
+            UpdateStyle();
+        }
 
-        [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
-        [cparent->Window addChildWindow:Window ordered:NSWindowAbove];
+        return S_OK;
+    }
+}
 
-        UpdateStyle();
+void WindowImpl::BringToFront()
+{
+    if(Window != nullptr)
+    {
+        if(IsDialog())
+        {
+            Activate();
+        }
+        else
+        {
+            [Window orderFront:nullptr];
+        }
+        
+        [Window invalidateShadow];
+        
+        for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
+        {
+            (*iterator)->BringToFront();
+        }
+    }
+}
 
-        return S_OK;
+bool WindowImpl::CanBecomeKeyWindow()
+{
+    for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
+    {
+        if((*iterator)->IsDialog())
+        {
+            return false;
+        }
     }
+    
+    return true;
 }
 
 void WindowImpl::StartStateTransition() {
@@ -430,6 +470,9 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
     START_COM_CALL;
 
     @autoreleasepool {
+        auto currentState = _actualWindowState;
+        _lastWindowState = state;
+
         if (Window == nullptr) {
             return S_OK;
         }
@@ -440,9 +483,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
 
         _inSetWindowState = true;
 
-        auto currentState = _actualWindowState;
-        _lastWindowState = state;
-
         if (currentState == Normal) {
             _preZoomSize = [Window frame];
         }
@@ -521,7 +561,12 @@ bool WindowImpl::IsDialog() {
 }
 
 NSWindowStyleMask WindowImpl::GetStyle() {
-    unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless;
+    unsigned long s = NSWindowStyleMaskBorderless;
+    
+    if(_actualWindowState == FullScreen)
+    {
+        s |= NSWindowStyleMaskFullScreen;
+    }
 
     switch (_decorations) {
         case SystemDecorationsNone:
@@ -533,15 +578,15 @@ NSWindowStyleMask WindowImpl::GetStyle() {
             break;
 
         case SystemDecorationsFull:
-            s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless;
+            s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
 
-            if (_canResize) {
+            if (_canResize && _isEnabled) {
                 s = s | NSWindowStyleMaskResizable;
             }
             break;
     }
 
-    if ([Window parentWindow] == nullptr) {
+    if (!IsDialog()) {
         s |= NSWindowStyleMaskMiniaturizable;
     }
 

+ 3 - 2
native/Avalonia.Native/src/OSX/WindowProtocol.h

@@ -11,7 +11,6 @@
 
 @protocol AvnWindowProtocol
 -(void) pollModalSession: (NSModalSession _Nonnull) session;
--(void) restoreParentWindow;
 -(bool) shouldTryToHandleEvents;
 -(void) setEnabled: (bool) enable;
 -(void) showAppMenuOnly;
@@ -22,4 +21,6 @@
 -(void) setIsExtended:(bool)value;
 -(void) disconnectParent;
 -(bool) isDialog;
-@end
+
+-(void) setCanBecomeKeyWindow:(bool)value;
+@end

+ 11 - 0
native/Avalonia.Native/src/OSX/app.mm

@@ -82,6 +82,17 @@ ComPtr<IAvnApplicationEvents> _events;
         _isHandlingSendEvent = oldHandling;
     }
 }
+
+// This is needed for certain embedded controls DO NOT REMOVE..
+- (BOOL) isHandlingSendEvent
+{
+    return _isHandlingSendEvent;
+}
+
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent
+{
+    _isHandlingSendEvent = handlingSendEvent;
+}
 @end
 
 extern void InitializeAvnApp(IAvnApplicationEvents* events)

+ 0 - 1
nukebuild/Build.cs

@@ -221,7 +221,6 @@ partial class Build : NukeBuild
             RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
             RunCoreTest("Avalonia.Skia.UnitTests");
             RunCoreTest("Avalonia.ReactiveUI.UnitTests");
-            RunCoreTest("Avalonia.PlatformSupport.UnitTests");
         });
 
     Target RunRenderTests => _ => _

+ 1 - 2
nukebuild/_build.csproj

@@ -8,11 +8,10 @@
     <IsPackable>False</IsPackable>
     <NoWarn>CS0649;CS0169</NoWarn>
   </PropertyGroup>
-
+ <Import Project="..\build\JetBrains.dotMemoryUnit.props" />
   <ItemGroup>
     <PackageReference Include="Nuke.Common" Version="5.0.0" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
-    <PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" />
     <PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />
     <PackageReference Include="ILRepack.NETStandard" Version="2.0.4" />
     <PackageReference Include="MicroCom.CodeGenerator" Version="0.10.4" />

+ 12 - 3
readme.md

@@ -70,11 +70,15 @@ For more information see the [.NET Foundation Code of Conduct](https://dotnetfou
 
 Avalonia is licenced under the [MIT licence](licence.md).
 
-## Support Avalonia
+## Donate
 
-**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx
+Donating to the project is a fantastic way to thank our valued contributors for their hard work. Your donations are shared among our community and awarded for significant contributions.  
+
+If you need support see Commercial Support section below.
+
+Donate with BTC or use [Open Collective](https://opencollective.com/avalonia).
 
-This will be shared with the community and awarded for significant contributions.
+**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx
 
 ### Backers
 
@@ -98,6 +102,11 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
 <a href="https://opencollective.com/Avalonia/sponsor/9/website" target="_blank"><img src="https://opencollective.com/Avalonia/sponsor/9/avatar.svg"></a>
 <a href="https://baseheadinc.com/" target="_blank"><img height="50" src="https://baseheadinc.com/wp-content/uploads/2020/09/BH-Logo-for-Site-Header-New.png"></a>
 
+## Commercial Support 
+
+We have a range of [support plans available](https://avaloniaui.net/support.html) for those looking to partner with the creators of Avalonia, enabling access to the best support at every step of the development process.
+
+*Please note that donations are not considered payment for commercial support agreements. Please contact us to discuss your needs first. [[email protected]](mailto://[email protected])*
 ## .NET Foundation
 
 This project is supported by the [.NET Foundation](https://dotnetfoundation.org).

+ 1 - 0
samples/BindingDemo/BindingDemo.csproj

@@ -5,6 +5,7 @@
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>

+ 14 - 16
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@@ -9,39 +9,37 @@
     <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
     <AndroidPackageFormat>apk</AndroidPackageFormat>
     <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
+    <RuntimeIdentifiers>android-arm64;android-x64</RuntimeIdentifiers>
   </PropertyGroup>
-  <ItemGroup>
-    <None Remove="Assets\AboutAssets.txt" />
-  </ItemGroup>
   <ItemGroup>
     <AndroidResource Include="..\..\build\Assets\Icon.png">
       <Link>Resources\drawable\Icon.png</Link>
     </AndroidResource>
   </ItemGroup>
 
-  <PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
-    <DebugSymbols>True</DebugSymbols>
+  <PropertyGroup Condition="'$(RunAOTCompilation)'=='' and '$(Configuration)'=='Release' and '$(TF_BUILD)'==''">
     <RunAOTCompilation>True</RunAOTCompilation>
-    <EnableLLVM>True</EnableLLVM>
-    <AndroidEnableProfiledAot>True</AndroidEnableProfiledAot>
   </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-    <EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
-    <RunAOTCompilation>False</RunAOTCompilation>
+  
+  <PropertyGroup Condition="'$(RunAOTCompilation)'=='True'">
+    <EnableLLVM>True</EnableLLVM>
+    <AndroidAotAdditionalArguments>no-write-symbols,nodebug</AndroidAotAdditionalArguments>
+    <AndroidAotMode>Hybrid</AndroidAotMode>
+    <AndroidGenerateJniMarshalMethods>True</AndroidGenerateJniMarshalMethods>
   </PropertyGroup>
 
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
+  <PropertyGroup Condition="'$(AndroidEnableProfiler)'=='True'">
+    <IsEmulator Condition="'$(IsEmulator)' == ''">True</IsEmulator>
+    <DebugSymbols>True</DebugSymbols>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
-    <PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
+    <AndroidEnvironment Condition="'$(IsEmulator)'=='True'" Include="environment.emulator.txt" />
+    <AndroidEnvironment Condition="'$(IsEmulator)'!='True'" Include="environment.device.txt" />
   </ItemGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />
     <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
   </ItemGroup>
-</Project>
+</Project>

+ 35 - 0
samples/ControlCatalog.Android/EmbedSample.Android.cs

@@ -0,0 +1,35 @@
+using System;
+using Avalonia.Platform;
+using Avalonia.Android;
+using ControlCatalog.Pages;
+
+namespace ControlCatalog.Android;
+
+public class EmbedSampleAndroid : INativeDemoControl
+{
+    public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
+    {
+        var parentContext = (parent as AndroidViewControlHandle)?.View.Context
+            ?? global::Android.App.Application.Context;
+
+        if (isSecond)
+        {
+            var webView = new global::Android.Webkit.WebView(parentContext);
+            webView.LoadUrl("https://www.android.com/");
+
+            return new AndroidViewControlHandle(webView);
+        }
+        else
+        {
+            var button = new global::Android.Widget.Button(parentContext) { Text = "Hello world" };
+            var clickCount = 0;
+            button.Click += (sender, args) =>
+            {
+                clickCount++;
+                button.Text = $"Click count {clickCount}";
+            };
+
+            return new AndroidViewControlHandle(button);
+        }
+    }
+}

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

@@ -10,7 +10,11 @@ namespace ControlCatalog.Android
     {
         protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
         {
-            return base.CustomizeAppBuilder(builder);
+            return base.CustomizeAppBuilder(builder)
+                .AfterSetup(_ =>
+                {
+                    Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
+                });
         }
     }
 }

+ 2 - 1
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

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

+ 1 - 0
samples/ControlCatalog.Android/environment.device.txt

@@ -0,0 +1 @@
+DOTNET_DiagnosticPorts=127.0.0.1:9000,suspend

+ 1 - 0
samples/ControlCatalog.Android/environment.emulator.txt

@@ -0,0 +1 @@
+DOTNET_DiagnosticPorts=10.0.2.2:9001,suspend

+ 14 - 0
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@@ -4,6 +4,7 @@
     <OutputType>WinExe</OutputType>
     <TargetFramework>net6.0</TargetFramework>
     <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(RunNativeAotCompilation)' == 'true'">
@@ -12,6 +13,16 @@
     <NativeAotCompilerVersion>7.0.0-*</NativeAotCompilerVersion>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="..\..\src\Avalonia.X11\NativeDialogs\Gtk.cs" Link="NativeControls\Gtk\Gtk.cs" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="NativeControls\Gtk\nodes.mp4">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
@@ -20,6 +31,8 @@
     <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
     <PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
+    <!-- For native controls test -->
+    <PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
   </ItemGroup>
 
   <ItemGroup Condition="'$(RunNativeAotCompilation)' == 'true'">
@@ -32,6 +45,7 @@
   <PropertyGroup>
     <!-- For Microsoft.CodeAnalysis -->
     <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
   </PropertyGroup>
 
   <Import Project="..\..\build\SampleApp.props" />

+ 35 - 0
samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs

@@ -0,0 +1,35 @@
+using System.IO;
+using System.Diagnostics;
+using Avalonia.Platform;
+using Avalonia.Controls.Platform;
+using System;
+using ControlCatalog.Pages;
+
+namespace ControlCatalog.NetCore;
+
+public class EmbedSampleGtk : INativeDemoControl
+{
+    private Process _mplayer;
+
+    public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
+    {
+        if (isSecond)
+        {
+            var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle);
+            if (chooser != null)
+                return chooser;
+        }
+
+        var control = createDefault();
+        var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName,
+            "..",
+            "nodes.mp4"));
+        _mplayer = Process.Start(new ProcessStartInfo("mplayer",
+            $"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"")
+        {
+            UseShellExecute = false,
+
+        });
+        return control;
+    }
+}

+ 58 - 0
samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Threading.Tasks;
+using Avalonia.Controls.Platform;
+using Avalonia.Platform.Interop;
+using Avalonia.X11.NativeDialogs;
+using static Avalonia.X11.NativeDialogs.Gtk;
+using static Avalonia.X11.NativeDialogs.Glib;
+
+namespace ControlCatalog.NetCore;
+
+internal class GtkHelper
+{
+    private static Task<bool> s_gtkTask;
+
+    class FileChooser : INativeControlHostDestroyableControlHandle
+    {
+        private readonly IntPtr _widget;
+
+        public FileChooser(IntPtr widget, IntPtr xid)
+        {
+            _widget = widget;
+            Handle = xid;
+        }
+
+        public IntPtr Handle { get; }
+        public string HandleDescriptor => "XID";
+
+        public void Destroy()
+        {
+            RunOnGlibThread(() =>
+            {
+                gtk_widget_destroy(_widget);
+                return 0;
+            }).Wait();
+        }
+    }
+
+
+    public static INativeControlHostDestroyableControlHandle CreateGtkFileChooser(IntPtr parentXid)
+    {
+        if (s_gtkTask == null)
+            s_gtkTask = StartGtk();
+        if (!s_gtkTask.Result)
+            return null;
+        return RunOnGlibThread(() =>
+        {
+            using (var title = new Utf8Buffer("Embedded"))
+            {
+                var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder,
+                    IntPtr.Zero);
+                gtk_widget_realize(widget);
+                var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget));
+                gtk_window_present(widget);
+                return new FileChooser(widget, xid);
+            }
+        }).Result;
+    }
+}

+ 0 - 0
samples/interop/NativeEmbedSample/nodes-license.md → samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md


+ 0 - 0
samples/interop/NativeEmbedSample/nodes.mp4 → samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4


+ 29 - 0
samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs

@@ -0,0 +1,29 @@
+using System;
+
+using Avalonia.Platform;
+using Avalonia.Threading;
+
+using ControlCatalog.Pages;
+
+using MonoMac.Foundation;
+using MonoMac.WebKit;
+
+namespace ControlCatalog.NetCore;
+
+public class EmbedSampleMac : INativeDemoControl
+{
+    public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
+    {
+        // Note: We are using MonoMac for example purposes
+        // It shouldn't be used in production apps
+        MacHelper.EnsureInitialized();
+
+        var webView = new WebView();
+        Dispatcher.UIThread.Post(() =>
+        {
+            webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl(
+                isSecond ? "https://bing.com" : "https://google.com/")));
+        });
+        return new MacOSViewHandle(webView);
+    }
+}

+ 38 - 0
samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs

@@ -0,0 +1,38 @@
+using System;
+
+using Avalonia.Controls.Platform;
+using MonoMac.AppKit;
+
+namespace ControlCatalog.NetCore;
+
+internal class MacHelper
+{
+    private static bool _isInitialized;
+
+    public static void EnsureInitialized()
+    {
+        if (_isInitialized)
+            return;
+        _isInitialized = true;
+        NSApplication.Init();
+    }
+}
+
+internal class MacOSViewHandle : INativeControlHostDestroyableControlHandle
+{
+    private NSView _view;
+
+    public MacOSViewHandle(NSView view)
+    {
+        _view = view;
+    }
+
+    public IntPtr Handle => _view?.Handle ?? IntPtr.Zero;
+    public string HandleDescriptor => "NSView";
+
+    public void Destroy()
+    {
+        _view.Dispose();
+        _view = null;
+    }
+}

+ 45 - 0
samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Text;
+
+using Avalonia.Controls.Platform;
+using Avalonia.Platform;
+
+using ControlCatalog.Pages;
+
+namespace ControlCatalog.NetCore;
+
+public class EmbedSampleWin : INativeDemoControl
+{
+    private const string RichText =
+        @"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
+{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;}
+{\*\generator Riched20 6.3.9600}\viewkind4\uc1 
+\pard\sa200\sl276\slmult1\f0\fs22\lang9 <PREFIX>I \i am\i0  a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par
+}";
+
+    public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
+    {
+        WinApi.LoadLibrary("Msftedit.dll");
+        var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W",
+            @"Rich Edit",
+            0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle,
+            IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero);
+        var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 };
+        var text = RichText.Replace("<PREFIX>", isSecond ? "\\qr " : "");
+        var bytes = Encoding.UTF8.GetBytes(text);
+        WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes);
+        return new Win32WindowControlHandle(handle, "HWND");
+    }
+}
+
+internal class Win32WindowControlHandle : PlatformHandle, INativeControlHostDestroyableControlHandle
+{
+    public Win32WindowControlHandle(IntPtr handle, string descriptor) : base(handle, descriptor)
+    {
+    }
+
+    public void Destroy()
+    {
+        _ = WinApi.DestroyWindow(Handle);
+    }
+}

+ 73 - 0
samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs

@@ -0,0 +1,73 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ControlCatalog.NetCore;
+
+internal unsafe class WinApi
+{
+    public enum CommonControls : uint
+    {
+        ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header
+        ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips
+        ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips
+        ICC_TAB_CLASSES = 0x00000008, // tab, tooltips
+        ICC_UPDOWN_CLASS = 0x00000010, // updown
+        ICC_PROGRESS_CLASS = 0x00000020, // progress
+        ICC_HOTKEY_CLASS = 0x00000040, // hotkey
+        ICC_ANIMATE_CLASS = 0x00000080, // animate
+        ICC_WIN95_CLASSES = 0x000000FF,
+        ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown
+        ICC_USEREX_CLASSES = 0x00000200, // comboex
+        ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control
+        ICC_INTERNET_CLASSES = 0x00000800,
+        ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller
+        ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control
+        ICC_STANDARD_CLASSES = 0x00004000,
+        ICC_LINK_CLASS = 0x00008000
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct INITCOMMONCONTROLSEX
+    {
+        public int dwSize;
+        public uint dwICC;
+    }
+
+    [DllImport("Comctl32.dll")]
+    public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init);
+
+    [DllImport("user32.dll", SetLastError = true)]
+    public static extern bool DestroyWindow(IntPtr hwnd);
+
+    [DllImport("kernel32.dll")]
+    public static extern IntPtr LoadLibrary(string lib);
+
+
+    [DllImport("kernel32.dll")]
+    public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+    [DllImport("user32.dll", SetLastError = true)]
+    public static extern IntPtr CreateWindowEx(
+        int dwExStyle,
+        string lpClassName,
+        string lpWindowName,
+        uint dwStyle,
+        int x,
+        int y,
+        int nWidth,
+        int nHeight,
+        IntPtr hWndParent,
+        IntPtr hMenu,
+        IntPtr hInstance,
+        IntPtr lpParam);
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct SETTEXTEX
+    {
+        public uint Flags;
+        public uint Codepage;
+    }
+
+    [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")]
+    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam);
+}

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

@@ -7,11 +7,12 @@ using System.Threading.Tasks;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Dialogs;
 using Avalonia.Headless;
 using Avalonia.LogicalTree;
 using Avalonia.Threading;
 
+using ControlCatalog.Pages;
+
 namespace ControlCatalog.NetCore
 {
     static class Program
@@ -123,6 +124,11 @@ namespace ControlCatalog.NetCore
                     {
                         StartupScreenIndex = 1,
                     });
+
+                    EmbedSample.Implementation = OperatingSystem.IsWindows() ? (INativeDemoControl)new EmbedSampleWin()
+                        : OperatingSystem.IsMacOS() ? new EmbedSampleMac()
+                        : OperatingSystem.IsLinux() ? new EmbedSampleGtk()
+                        : null;
                 })
                 .LogToTrace();
 

+ 28 - 0
samples/ControlCatalog.NetCore/app.manifest

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="ControlCatalog.app"/>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on
+           and is designed to work with. Uncomment the appropriate elements
+           and Windows will automatically select the most compatible environment. -->
+
+      <!-- Windows Vista -->
+      <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
+
+      <!-- Windows 7 -->
+      <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
+
+      <!-- Windows 8 -->
+      <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
+
+      <!-- Windows 8.1 -->
+      <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+    </application>
+  </compatibility>
+</assembly>

+ 4 - 0
samples/ControlCatalog.Web/App.razor.cs

@@ -7,6 +7,10 @@ public partial class App
     protected override void OnParametersSet()
     {
         WebAppBuilder.Configure<ControlCatalog.App>()
+            .AfterSetup(_ =>
+            {
+                ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb();
+            })
             .SetupWithSingleViewLifetime();
 
         base.OnParametersSet();

+ 34 - 0
samples/ControlCatalog.Web/EmbedSample.Browser.cs

@@ -0,0 +1,34 @@
+using System;
+
+using Avalonia;
+using Avalonia.Platform;
+using Avalonia.Web.Blazor;
+
+using ControlCatalog.Pages;
+
+using Microsoft.JSInterop;
+
+namespace ControlCatalog.Web;
+
+public class EmbedSampleWeb : INativeDemoControl
+{
+    public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
+    {
+        var runtime = AvaloniaLocator.Current.GetRequiredService<IJSInProcessRuntime>();
+
+        if (isSecond)
+        {
+            var iframe = runtime.Invoke<IJSInProcessObjectReference>("document.createElement", "iframe");
+            iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70");
+
+            return new JSObjectControlHandle(iframe);
+        }
+        else
+        {
+            // window.createAppButton source is defined in "app.js" file.
+            var button = runtime.Invoke<IJSInProcessObjectReference>("window.createAppButton");
+
+            return new JSObjectControlHandle(button);
+        }
+    }
+}

+ 0 - 70
samples/ControlCatalog.Web/Shared/MainLayout.razor.css

@@ -1,70 +0,0 @@
-.page {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-}
-
-.main {
-    flex: 1;
-}
-
-.sidebar {
-    background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
-}
-
-.top-row {
-    background-color: #f7f7f7;
-    border-bottom: 1px solid #d6d5d5;
-    justify-content: flex-end;
-    height: 3.5rem;
-    display: flex;
-    align-items: center;
-}
-
-    .top-row ::deep a, .top-row .btn-link {
-        white-space: nowrap;
-        margin-left: 1.5rem;
-    }
-
-    .top-row a:first-child {
-        overflow: hidden;
-        text-overflow: ellipsis;
-    }
-
-@media (max-width: 640.98px) {
-    .top-row:not(.auth) {
-        display: none;
-    }
-
-    .top-row.auth {
-        justify-content: space-between;
-    }
-
-    .top-row a, .top-row .btn-link {
-        margin-left: 0;
-    }
-}
-
-@media (min-width: 641px) {
-    .page {
-        flex-direction: row;
-    }
-
-    .sidebar {
-        width: 250px;
-        height: 100vh;
-        position: sticky;
-        top: 0;
-    }
-
-    .top-row {
-        position: sticky;
-        top: 0;
-        z-index: 1;
-    }
-
-    .main > div {
-        padding-left: 2rem !important;
-        padding-right: 1.5rem !important;
-    }
-}

+ 5 - 39
samples/ControlCatalog.Web/wwwroot/css/app.css

@@ -44,47 +44,13 @@ a, .btn-link {
     z-index: 1000;
 }
 
-    #blazor-error-ui .dismiss {
-        cursor: pointer;
-        position: absolute;
-        right: 0.75rem;
-        top: 0.5rem;
-    }
-
-.canvas-container {
-    opacity:1;
-    background-color:#ccc;
-    position:fixed;
-    width:100%;
-    height:100%;
-    top:0px;
-    left:0px;
-    z-index:500;
-}
-
-canvas
-{
-    opacity:1;
-    background-color:#ccc;
-    position:fixed;
-    width:100%;
-    height:100%;
-    top:0px;
-    left:0px;
-    z-index:500;
+#blazor-error-ui .dismiss {
+    cursor: pointer;
+    position: absolute;
+    right: 0.75rem;
+    top: 0.5rem;
 }
 
 #app, .page {
     height: 100%;
 }
-
-.overlay{
-    opacity:0.0;
-    background-color:#ccc;
-    position:fixed;
-    width:100vw;
-    height:100vh;
-    top:0px;
-    left:0px;
-    z-index:1000;
-}

+ 10 - 1
samples/ControlCatalog.Web/wwwroot/js/app.js

@@ -1 +1,10 @@
-
+window.createAppButton = function () {
+    var button = document.createElement('button');
+    button.innerText = 'Hello world';
+    var clickCount = 0;
+    button.onclick = () => {
+        clickCount++;
+        button.innerText = 'Click count ' + clickCount;
+    };
+    return button;
+}

+ 8 - 1
samples/ControlCatalog.iOS/AppDelegate.cs

@@ -13,6 +13,13 @@ namespace ControlCatalog
     [Register("AppDelegate")]
     public partial class AppDelegate : AvaloniaAppDelegate<App>
     {
-        
+        protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
+        {
+            return base.CustomizeAppBuilder(builder)
+                .AfterSetup(_ =>
+                {
+                    Pages.EmbedSample.Implementation = new EmbedSampleIOS();
+                });
+        }
     }
 }

+ 38 - 0
samples/ControlCatalog.iOS/EmbedSample.iOS.cs

@@ -0,0 +1,38 @@
+using System;
+using Avalonia.Platform;
+using CoreGraphics;
+using Foundation;
+using UIKit;
+using WebKit;
+using Avalonia.iOS;
+using ControlCatalog.Pages;
+
+namespace ControlCatalog;
+
+public class EmbedSampleIOS : INativeDemoControl
+{
+    public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
+    {
+        if (isSecond)
+        {
+            var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration());
+            webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/")));
+
+            return new UIViewControlHandle(webView);
+        }
+        else
+        {
+            var button = new UIButton();
+            var clickCount = 0;
+            button.SetTitle("Hello world", UIControlState.Normal);
+            button.BackgroundColor = UIColor.Blue;
+            button.AddTarget((_, _) =>
+            {
+                clickCount++;
+                button.SetTitle($"Click count {clickCount}", UIControlState.Normal);
+            }, UIControlEvent.TouchDown);
+
+            return new UIViewControlHandle(button);
+        }
+    }
+}

+ 19 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -1,7 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>    
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <Nullable>enable</Nullable>    
   </PropertyGroup>
   <ItemGroup>
     <Compile Update="**\*.xaml.cs">
@@ -13,6 +14,9 @@
     <AvaloniaResource Include="Assets\*" />
     <AvaloniaResource Include="Assets\Fonts\*" />
   </ItemGroup>
+  <ItemGroup>
+    <None Remove="Pages\NativeEmbedPage.xaml" />
+  </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Assets\Fonts\SourceSansPro-Bold.ttf" />
     <EmbeddedResource Include="Assets\Fonts\SourceSansPro-BoldItalic.ttf" />
@@ -25,9 +29,23 @@
     <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
     <ProjectReference Include="..\SampleControls\ControlSamples.csproj" />
   </ItemGroup>
 
+  <ItemGroup>
+    <AvaloniaResource Update="Pages\NativeEmbedPage.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </AvaloniaResource>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="Pages\NativeEmbedPage.xaml.cs">
+      <DependentUpon>%(Filename)</DependentUpon>
+    </Compile>
+  </ItemGroup>
+
   <Import Project="..\..\build\BuildTargets.targets" />
 </Project>

+ 7 - 3
samples/ControlCatalog/Converter/MathSubtractConverter.cs

@@ -6,12 +6,16 @@ namespace ControlCatalog.Converter;
 
 public class MathSubtractConverter : IValueConverter
 {
-    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
     {
-        return (double)value - (double)parameter;
+        if (value is double dv && parameter is double dp)
+        {
+            return dv - dp;
+        }
+        return double.NaN;
     }
 
-    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+    public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
     {
         throw new NotSupportedException();
     }

+ 5 - 5
samples/ControlCatalog/DecoratedWindow.xaml.cs

@@ -15,7 +15,7 @@ namespace ControlCatalog
 
         void SetupSide(string name, StandardCursorType cursor, WindowEdge edge)
         {
-            var ctl = this.FindControl<Control>(name);
+            var ctl = this.Get<Control>(name);
             ctl.Cursor = new Cursor(cursor);
             ctl.PointerPressed += (i, e) =>
             {
@@ -26,7 +26,7 @@ namespace ControlCatalog
         private void InitializeComponent()
         {
             AvaloniaXamlLoader.Load(this);
-            this.FindControl<Control>("TitleBar").PointerPressed += (i, e) =>
+            this.Get<Control>("TitleBar").PointerPressed += (i, e) =>
             {
                 PlatformImpl?.BeginMoveDrag(e);
             };
@@ -38,12 +38,12 @@ namespace ControlCatalog
             SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast);
             SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest);
             SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast);
-            this.FindControl<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
-            this.FindControl<Button>("MaximizeButton").Click += delegate
+            this.Get<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
+            this.Get<Button>("MaximizeButton").Click += delegate
             {
                 WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
             };
-            this.FindControl<Button>("CloseButton").Click += delegate
+            this.Get<Button>("CloseButton").Click += delegate
             {
                 Close();
             };

+ 5 - 2
samples/ControlCatalog/MainView.xaml

@@ -2,8 +2,8 @@
              xmlns="https://github.com/avaloniaui"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:controls="clr-namespace:ControlSamples;assembly=ControlSamples"
-             xmlns:pages="clr-namespace:ControlCatalog.Pages"
-             xmlns:models="clr-namespace:ControlCatalog.Models">
+             xmlns:models="clr-namespace:ControlCatalog.Models"
+             xmlns:pages="clr-namespace:ControlCatalog.Pages">
   <Grid>
     <Grid.Styles>
       <Style Selector="TextBlock.h2">
@@ -157,6 +157,9 @@
       <TabItem Header="Viewbox">
         <pages:ViewboxPage />
       </TabItem>
+      <TabItem Header="Native Embed">
+        <pages:NativeEmbedPage />
+      </TabItem>
       <TabItem Header="Window Customizations">
         <pages:WindowCustomizationsPage />
       </TabItem>

+ 8 - 8
samples/ControlCatalog/MainView.xaml.cs

@@ -18,9 +18,9 @@ namespace ControlCatalog
         {
             AvaloniaXamlLoader.Load(this);
 
-            var sideBar = this.FindControl<TabControl>("Sidebar");
+            var sideBar = this.Get<TabControl>("Sidebar");
 
-            if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().IsDesktop)
+            if (AvaloniaLocator.Current?.GetService<IRuntimePlatform>()?.GetRuntimeInfo().IsDesktop == true)
             {
                 IList tabItems = ((IList)sideBar.Items);
                 tabItems.Add(new TabItem()
@@ -36,7 +36,7 @@ namespace ControlCatalog
 
             }
 
-            var themes = this.Find<ComboBox>("Themes");
+            var themes = this.Get<ComboBox>("Themes");
             themes.SelectionChanged += (sender, e) =>
             {
                 if (themes.SelectedItem is CatalogTheme theme)
@@ -80,7 +80,7 @@ namespace ControlCatalog
                 }
             };
 
-            var flowDirections = this.Find<ComboBox>("FlowDirection");
+            var flowDirections = this.Get<ComboBox>("FlowDirection");
             flowDirections.SelectionChanged += (sender, e) =>
             {
                 if (flowDirections.SelectedItem is FlowDirection flowDirection)
@@ -89,7 +89,7 @@ namespace ControlCatalog
                 }
             };
 
-            var decorations = this.Find<ComboBox>("Decorations");
+            var decorations = this.Get<ComboBox>("Decorations");
             decorations.SelectionChanged += (sender, e) =>
             {
                 if (VisualRoot is Window window
@@ -99,8 +99,8 @@ namespace ControlCatalog
                 }
             };
 
-            var transparencyLevels = this.Find<ComboBox>("TransparencyLevels");
-            IDisposable backgroundSetter = null, paneBackgroundSetter = null;
+            var transparencyLevels = this.Get<ComboBox>("TransparencyLevels");
+            IDisposable? backgroundSetter = null, paneBackgroundSetter = null;
             transparencyLevels.SelectionChanged += (sender, e) =>
             {
                 backgroundSetter?.Dispose();
@@ -118,7 +118,7 @@ namespace ControlCatalog
         protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
         {
             base.OnAttachedToVisualTree(e);
-            var decorations = this.Find<ComboBox>("Decorations");
+            var decorations = this.Get<ComboBox>("Decorations");
             if (VisualRoot is Window window)
                 decorations.SelectedIndex = (int)window.SystemDecorations;
         }

+ 3 - 5
samples/ControlCatalog/MainWindow.xaml.cs

@@ -12,7 +12,7 @@ namespace ControlCatalog
     public class MainWindow : Window
     {
         private WindowNotificationManager _notificationArea;
-        private NativeMenu _recentMenu;
+        private NativeMenu? _recentMenu;
 
         public MainWindow()
         {
@@ -28,9 +28,7 @@ namespace ControlCatalog
             };
 
             DataContext = new MainWindowViewModel(_notificationArea);
-            _recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu;
-
-            ExtendClientAreaChromeHints = Avalonia.Platform.ExtendClientAreaChromeHints.OSXThickTitleBar;
+            _recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu;
         }
 
         public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit";
@@ -41,7 +39,7 @@ namespace ControlCatalog
 
         public void OnOpenClicked(object sender, EventArgs args)
         {
-            _recentMenu.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
+            _recentMenu?.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
         }
 
         public void OnCloseClicked(object sender, EventArgs args)

+ 2 - 2
samples/ControlCatalog/Models/Countries.cs

@@ -237,7 +237,7 @@ namespace ControlCatalog.Models
             yield return new Country("Zimbabwe", "SUB-SAHARAN AFRICA", 12236805, 390580, 31.3, 0, 0, 67.69, 1900, 90.7, 26.8, 28.01, 21.84);
         }
 
-        static IReadOnlyList<Country> _all;
+        static IReadOnlyList<Country>? _all;
 
         public static IReadOnlyList<Country> All
         {
@@ -253,4 +253,4 @@ namespace ControlCatalog.Models
 
         }
     }
-}
+}

+ 2 - 2
samples/ControlCatalog/Models/GDPValueConverter.cs

@@ -14,7 +14,7 @@ namespace ControlCatalog.Models
 {
     public class GDPValueConverter : IValueConverter
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             if (value is int gdp)
             {
@@ -29,7 +29,7 @@ namespace ControlCatalog.Models
             return value;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             throw new NotImplementedException();
         }

+ 8 - 8
samples/ControlCatalog/Models/Person.cs

@@ -13,8 +13,8 @@ namespace ControlCatalog.Models
 {
     public class Person : INotifyDataErrorInfo, INotifyPropertyChanged
     {
-        string _firstName;
-        string _lastName;
+        string _firstName = string.Empty;
+        string _lastName = string.Empty;
         bool _isBanned;
         private int _age;
 
@@ -76,7 +76,7 @@ namespace ControlCatalog.Models
 
         Dictionary<string, List<string>> _errorLookup = new Dictionary<string, List<string>>();
 
-        void SetError(string propertyName, string error)
+        void SetError(string propertyName, string? error)
         {
             if (string.IsNullOrEmpty(error))
             {
@@ -88,11 +88,11 @@ namespace ControlCatalog.Models
                 if (_errorLookup.TryGetValue(propertyName, out List<string> errorList))
                 {
                     errorList.Clear();
-                    errorList.Add(error);
+                    errorList.Add(error!);
                 }
                 else
                 {
-                    var errors = new List<string> { error };
+                    var errors = new List<string> { error! };
                     _errorLookup.Add(propertyName, errors);
                 }
 
@@ -102,8 +102,8 @@ namespace ControlCatalog.Models
 
         public bool HasErrors => _errorLookup.Count > 0;
 
-        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
-        public event PropertyChangedEventHandler PropertyChanged;
+        public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
+        public event PropertyChangedEventHandler? PropertyChanged;
 
         void OnErrorsChanged(string propertyName)
         {
@@ -114,7 +114,7 @@ namespace ControlCatalog.Models
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
         }
 
-        public IEnumerable GetErrors(string propertyName)
+        public IEnumerable? GetErrors(string propertyName)
         {
             if (_errorLookup.TryGetValue(propertyName, out List<string> errorList))
                 return errorList;

+ 19 - 14
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@@ -126,13 +126,13 @@ namespace ControlCatalog.Pages
             binding.Bindings.Add(new Binding("Name"));
             binding.Bindings.Add(new Binding("Abbreviation"));
 
-            var multibindingBox = this.FindControl<AutoCompleteBox>("MultiBindingBox");
+            var multibindingBox = this.Get<AutoCompleteBox>("MultiBindingBox");
             multibindingBox.ValueMemberBinding = binding;
 
-            var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox");
+            var asyncBox = this.Get<AutoCompleteBox>("AsyncBox");
             asyncBox.AsyncPopulator = PopulateAsync;
 
-            var customAutocompleteBox = this.FindControl<AutoCompleteBox>("CustomAutocompleteBox");
+            var customAutocompleteBox = this.Get<AutoCompleteBox>("CustomAutocompleteBox");
             customAutocompleteBox.Items = Sentences.SelectMany(x => x);
             customAutocompleteBox.TextFilter = LastWordContains;
             customAutocompleteBox.TextSelector = AppendWord;
@@ -144,11 +144,12 @@ namespace ControlCatalog.Pages
                     .OfType<AutoCompleteBox>();
         }
 
-        private bool StringContains(string str, string query)
+        private bool StringContains(string str, string? query)
         {
+            if (query == null) return false;
             return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
         }
-        private async Task<IEnumerable<object>> PopulateAsync(string searchText, CancellationToken cancellationToken)
+        private async Task<IEnumerable<object>> PopulateAsync(string? searchText, CancellationToken cancellationToken)
         {
             await Task.Delay(TimeSpan.FromSeconds(1.5), cancellationToken);
 
@@ -157,14 +158,14 @@ namespace ControlCatalog.Pages
                       .ToList();
         }
 
-        private bool LastWordContains(string searchText, string item)
+        private bool LastWordContains(string? searchText, string? item)
         {
-            var words = searchText.Split(' ');
+            var words = searchText?.Split(' ') ?? Array.Empty<string>();
             var options = Sentences.Select(x => x.First).ToArray();
             for (var i = 0; i < words.Length; ++i)
             {
                 var word = words[i];
-                for (var j = 0; j < options.Length; ++j)
+                for (var j = 0; word is { } && j < options.Length; ++j)
                 {
                     var option = options[j];
                     if (option == null)
@@ -183,14 +184,18 @@ namespace ControlCatalog.Pages
 
             return options.Any(x => x != null && x.Value == item);
         }
-        private string AppendWord(string text, string item)
+        private string AppendWord(string? text, string? item)
         {
-            string[] parts = text.Split(' ');
-            if (parts.Length == 0)
-                return item;
+            if (item is { })
+            {
+                string[] parts = text?.Split(' ') ?? Array.Empty<string>();
+                if (parts.Length == 0)
+                    return item;
 
-            parts[parts.Length - 1] = item;
-            return string.Join(" ", parts);
+                parts[parts.Length - 1] = item;
+                return string.Join(" ", parts);
+            }
+            return string.Empty;
         }
 
         private void InitializeComponent()

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

@@ -23,7 +23,7 @@ namespace ControlCatalog.Pages
             var spinner = (ButtonSpinner)sender;
             var txtBox = (TextBlock)spinner.Content;
 
-            int value = Array.IndexOf(_mountains, txtBox.Text);
+            int value = Array.IndexOf(_mountains, txtBox?.Text);
             if (e.Direction == SpinDirection.Increase)
                 value++;
             else

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

@@ -11,7 +11,7 @@ namespace ControlCatalog.Pages
         {
             InitializeComponent();
 
-            this.FindControl<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
+            this.Get<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
         }
 
         private void InitializeComponent()
@@ -22,7 +22,7 @@ namespace ControlCatalog.Pages
         public void OnRepeatButtonClick(object sender, object args)
         {
             repeatButtonClickCount++;
-            var textBlock = this.FindControl<TextBlock>("RepeatButtonTextBlock");
+            var textBlock = this.Get<TextBlock>("RepeatButtonTextBlock");
             textBlock.Text = $"Repeat Button: {repeatButtonClickCount}";
         }
     }

+ 1 - 2
samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml

@@ -10,8 +10,7 @@
                 Margin="0,16,0,0"
                 HorizontalAlignment="Center"
                 Spacing="16">
-      <StackPanel Orientation="Vertical"
-                  Width="200">
+      <StackPanel Orientation="Vertical">
         <TextBlock Text="SelectedDateFormat: Short"/>
         <CalendarDatePicker Name="DatePicker1"
                     SelectedDateFormat="Short"

+ 6 - 6
samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs

@@ -10,11 +10,11 @@ namespace ControlCatalog.Pages
         {
             InitializeComponent();
             
-            var dp1 = this.FindControl<CalendarDatePicker>("DatePicker1");
-            var dp2 = this.FindControl<CalendarDatePicker>("DatePicker2");
-            var dp3 = this.FindControl<CalendarDatePicker>("DatePicker3");
-            var dp4 = this.FindControl<CalendarDatePicker>("DatePicker4");
-            var dp5 = this.FindControl<CalendarDatePicker>("DatePicker5");
+            var dp1 = this.Get<CalendarDatePicker>("DatePicker1");
+            var dp2 = this.Get<CalendarDatePicker>("DatePicker2");
+            var dp3 = this.Get<CalendarDatePicker>("DatePicker3");
+            var dp4 = this.Get<CalendarDatePicker>("DatePicker4");
+            var dp5 = this.Get<CalendarDatePicker>("DatePicker5");
 
             dp1.SelectedDate = DateTime.Today;
             dp2.SelectedDate = DateTime.Today.AddDays(10);
@@ -23,7 +23,7 @@ namespace ControlCatalog.Pages
 
             dp4.TemplateApplied += (s, e) =>
             {
-                dp4.BlackoutDates.AddDatesInPast();
+                dp4.BlackoutDates?.AddDatesInPast();
             };
             
         }

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

@@ -11,11 +11,11 @@ namespace ControlCatalog.Pages
             this.InitializeComponent();
 
             var today = DateTime.Today; 
-            var cal1 = this.FindControl<Calendar>("DisplayDatesCalendar");
+            var cal1 = this.Get<Calendar>("DisplayDatesCalendar");
             cal1.DisplayDateStart = today.AddDays(-25);
             cal1.DisplayDateEnd = today.AddDays(25);
 
-            var cal2 = this.FindControl<Calendar>("BlackoutDatesCalendar");
+            var cal2 = this.Get<Calendar>("BlackoutDatesCalendar");
             cal2.BlackoutDates.AddDatesInPast();
             cal2.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6)));
         }

+ 1 - 0
samples/ControlCatalog/Pages/CarouselPage.xaml

@@ -29,6 +29,7 @@
         <ComboBoxItem>None</ComboBoxItem>
         <ComboBoxItem>Slide</ComboBoxItem>
         <ComboBoxItem>Crossfade</ComboBoxItem>
+        <ComboBoxItem>3D Rotation</ComboBoxItem>
       </ComboBox>
     </StackPanel>
 

+ 9 - 5
samples/ControlCatalog/Pages/CarouselPage.xaml.cs

@@ -16,6 +16,11 @@ namespace ControlCatalog.Pages
         public CarouselPage()
         {
             this.InitializeComponent();
+            _carousel = this.Get<Carousel>("carousel");
+            _left = this.Get<Button>("left");
+            _right = this.Get<Button>("right");
+            _transition = this.Get<ComboBox>("transition");
+            _orientation = this.Get<ComboBox>("orientation");
             _left.Click += (s, e) => _carousel.Previous();
             _right.Click += (s, e) => _carousel.Next();
             _transition.SelectionChanged += TransitionChanged;
@@ -25,11 +30,7 @@ namespace ControlCatalog.Pages
         private void InitializeComponent()
         {
             AvaloniaXamlLoader.Load(this);
-            _carousel = this.FindControl<Carousel>("carousel");
-            _left = this.FindControl<Button>("left");
-            _right = this.FindControl<Button>("right");
-            _transition = this.FindControl<ComboBox>("transition");
-            _orientation = this.FindControl<ComboBox>("orientation");
+
         }
 
         private void TransitionChanged(object sender, SelectionChangedEventArgs e)
@@ -45,6 +46,9 @@ namespace ControlCatalog.Pages
                 case 2:
                     _carousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25));
                     break;
+                case 3:
+                    _carousel.PageTransition = new Rotate3DTransition(TimeSpan.FromSeconds(0.5), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
+                    break;
             }
         }
     }

+ 11 - 11
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs

@@ -18,29 +18,29 @@ namespace ControlCatalog.Pages
 
             DataContext = new ContextPageViewModel();
 
-            _textBox = this.FindControl<TextBox>("TextBox");
+            _textBox = this.Get<TextBox>("TextBox");
 
-            var cutButton = this.FindControl<Button>("CutButton");
+            var cutButton = this.Get<Button>("CutButton");
             cutButton.Click += CloseFlyout;
 
-            var copyButton = this.FindControl<Button>("CopyButton");
+            var copyButton = this.Get<Button>("CopyButton");
             copyButton.Click += CloseFlyout;
 
-            var pasteButton = this.FindControl<Button>("PasteButton");
+            var pasteButton = this.Get<Button>("PasteButton");
             pasteButton.Click += CloseFlyout;
 
-            var clearButton = this.FindControl<Button>("ClearButton");
+            var clearButton = this.Get<Button>("ClearButton");
             clearButton.Click += CloseFlyout;
 
-            var customContextRequestedBorder = this.FindControl<Border>("CustomContextRequestedBorder");
+            var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
             customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
 
-            var cancellableContextBorder = this.FindControl<Border>("CancellableContextBorder");
+            var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
             cancellableContextBorder.ContextFlyout!.Closing += ContextFlyoutPage_Closing;
             cancellableContextBorder.ContextFlyout!.Opening += ContextFlyoutPage_Opening;
         }
 
-        private ContextPageViewModel _model;
+        private ContextPageViewModel? _model;
         protected override void OnDataContextChanged(EventArgs e)
         {
             if (_model != null)
@@ -55,7 +55,7 @@ namespace ControlCatalog.Pages
         private void ContextFlyoutPage_Closing(object sender, CancelEventArgs e)
         {
             var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox");
-            e.Cancel = cancelCloseCheckBox.IsChecked ?? false;
+            e.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
         }
 
         private void ContextFlyoutPage_Opening(object sender, EventArgs e)
@@ -63,13 +63,13 @@ namespace ControlCatalog.Pages
             if (e is CancelEventArgs cancelArgs)
             {
                 var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelOpenCheckBox");
-                cancelArgs.Cancel = cancelCloseCheckBox.IsChecked ?? false;
+                cancelArgs.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
             }
         }
 
         private void CloseFlyout(object sender, RoutedEventArgs e)
         {
-            _textBox.ContextFlyout.Hide();
+            _textBox.ContextFlyout?.Hide();
         }
 
         public void CustomContextRequested(object sender, ContextRequestedEventArgs e)

+ 3 - 3
samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs

@@ -15,15 +15,15 @@ namespace ControlCatalog.Pages
             this.InitializeComponent();
             DataContext = new ContextPageViewModel();
 
-            var customContextRequestedBorder = this.FindControl<Border>("CustomContextRequestedBorder");
+            var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
             customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
 
-            var cancellableContextBorder = this.FindControl<Border>("CancellableContextBorder");
+            var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
             cancellableContextBorder.ContextMenu!.ContextMenuClosing += ContextFlyoutPage_Closing;
             cancellableContextBorder.ContextMenu!.ContextMenuOpening += ContextFlyoutPage_Opening;
         }
 
-        private ContextPageViewModel _model;
+        private ContextPageViewModel? _model;
         protected override void OnDataContextChanged(EventArgs e)
         {
             if (_model != null)

+ 4 - 4
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@@ -21,7 +21,7 @@ namespace ControlCatalog.Pages
             var dataGridSortDescription = DataGridSortDescription.FromPath(nameof(Country.Region), ListSortDirection.Ascending, new ReversedStringComparer());
             var collectionView1 = new DataGridCollectionView(Countries.All);
             collectionView1.SortDescriptions.Add(dataGridSortDescription);
-            var dg1 = this.FindControl<DataGrid>("dataGrid1");
+            var dg1 = this.Get<DataGrid>("dataGrid1");
             dg1.IsReadOnly = true;
             dg1.LoadingRow += Dg1_LoadingRow;
             dg1.Sorting += (s, a) =>
@@ -37,7 +37,7 @@ namespace ControlCatalog.Pages
             };
             dg1.Items = collectionView1;
 
-            var dg2 = this.FindControl<DataGrid>("dataGridGrouping");
+            var dg2 = this.Get<DataGrid>("dataGridGrouping");
             dg2.IsReadOnly = true;
 
             var collectionView2 = new DataGridCollectionView(Countries.All);
@@ -45,7 +45,7 @@ namespace ControlCatalog.Pages
 
             dg2.Items = collectionView2;
 
-            var dg3 = this.FindControl<DataGrid>("dataGridEdit");
+            var dg3 = this.Get<DataGrid>("dataGridEdit");
             dg3.IsReadOnly = false;
 
             var items = new List<Person>
@@ -58,7 +58,7 @@ namespace ControlCatalog.Pages
 
             dg3.Items = collectionView3;
 
-            var addButton = this.FindControl<Button>("btnAdd");
+            var addButton = this.Get<Button>("btnAdd");
             addButton.Click += (a, b) => collectionView3.AddNew();
         }
 

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

@@ -9,12 +9,12 @@ namespace ControlCatalog.Pages
         public DateTimePickerPage()
         {
             this.InitializeComponent();
-            this.FindControl<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
+            this.Get<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
                 "for example to schedule an appointment. The DatePicker displays three controls for month, day, and year. " +
                 "These controls are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
                 "Order of month, day, and year is dynamically set based on user date settings";
 
-            this.FindControl<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
+            this.Get<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
                 "to set a reminder. The TimePicker displays three controls for hour, minute, and AM / PM(if necessary).These controls " +
                 "are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
                 "12 - hour or 24 - hour clock and visiblility of AM / PM is dynamically set based on user time settings, or can be overridden.";

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

@@ -20,7 +20,7 @@
                  Text="Window dialogs" />
       <Button Name="DecoratedWindow">Decorated _window</Button>
       <Button Name="DecoratedWindowDialog">Decorated w_indow (dialog)</Button>
-      <Button Name="Dialog">_Dialog</Button>
+      <Button Name="Dialog" ToolTip.Tip="Shows a dialog">_Dialog</Button>
       <Button Name="DialogNoTaskbar">Dialog (_No taskbar icon)</Button>
       <Button Name="OwnedWindow">Own_ed window</Button>
       <Button Name="OwnedWindowNoTaskbar">Owned window (No tas_kbar icon)</Button>

+ 43 - 22
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@@ -8,7 +8,6 @@ using Avalonia.Dialogs;
 using Avalonia.Layout;
 using Avalonia.Markup.Xaml;
 #pragma warning disable 4014
-
 namespace ControlCatalog.Pages
 {
     public class DialogsPage : UserControl
@@ -17,14 +16,14 @@ namespace ControlCatalog.Pages
         {
             this.InitializeComponent();
 
-            var results = this.FindControl<ItemsPresenter>("PickerLastResults");
-            var resultsVisible = this.FindControl<TextBlock>("PickerLastResultsVisible");
+            var results = this.Get<ItemsPresenter>("PickerLastResults");
+            var resultsVisible = this.Get<TextBlock>("PickerLastResultsVisible");
 
-            string lastSelectedDirectory = null;
+            string? lastSelectedDirectory = null;
 
-            List<FileDialogFilter> GetFilters()
+            List<FileDialogFilter>? GetFilters()
             {
-                if (this.FindControl<CheckBox>("UseFilters").IsChecked != true)
+                if (this.Get<CheckBox>("UseFilters").IsChecked != true)
                     return null;
                 return  new List<FileDialogFilter>
                 {
@@ -40,20 +39,24 @@ namespace ControlCatalog.Pages
                 };
             }
 
-            this.FindControl<Button>("OpenFile").Click += async delegate
+            this.Get<Button>("OpenFile").Click += async delegate
             {
+                // Almost guaranteed to exist
+                var fullPath = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName;
+                var initialFileName = fullPath == null ? null : System.IO.Path.GetFileName(fullPath);
+                var initialDirectory = fullPath == null ? null : System.IO.Path.GetDirectoryName(fullPath);
+
                 var result = await new OpenFileDialog()
                 {
                     Title = "Open file",
                     Filters = GetFilters(),
-                    Directory = lastSelectedDirectory,
-                    // Almost guaranteed to exist
-                    InitialFileName = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName
+                    Directory = initialDirectory,
+                    InitialFileName = initialFileName
                 }.ShowAsync(GetWindow());
                 results.Items = result;
                 resultsVisible.IsVisible = result?.Any() == true;
             };
-            this.FindControl<Button>("OpenMultipleFiles").Click += async delegate
+            this.Get<Button>("OpenMultipleFiles").Click += async delegate
             {
                 var result = await new OpenFileDialog()
                 {
@@ -65,7 +68,7 @@ namespace ControlCatalog.Pages
                 results.Items = result;
                 resultsVisible.IsVisible = result?.Any() == true;
             };
-            this.FindControl<Button>("SaveFile").Click += async delegate
+            this.Get<Button>("SaveFile").Click += async delegate
             {
                 var result = await new SaveFileDialog()
                 {
@@ -77,18 +80,23 @@ namespace ControlCatalog.Pages
                 results.Items = new[] { result };
                 resultsVisible.IsVisible = result != null;
             };
-            this.FindControl<Button>("SelectFolder").Click += async delegate
+            this.Get<Button>("SelectFolder").Click += async delegate
             {
                 var result = await new OpenFolderDialog()
                 {
                     Title = "Select folder",
                     Directory = lastSelectedDirectory,
                 }.ShowAsync(GetWindow());
-                lastSelectedDirectory = result;
+
+                if (!string.IsNullOrEmpty(result))
+                {
+                    lastSelectedDirectory = result;
+                }
+
                 results.Items = new [] { result };
                 resultsVisible.IsVisible = result != null;
             };
-            this.FindControl<Button>("OpenBoth").Click += async delegate
+            this.Get<Button>("OpenBoth").Click += async delegate
             {
                 var result = await new OpenFileDialog()
                 {
@@ -102,35 +110,35 @@ namespace ControlCatalog.Pages
                 results.Items = result;
                 resultsVisible.IsVisible = result?.Any() == true;
             };
-            this.FindControl<Button>("DecoratedWindow").Click += delegate
+            this.Get<Button>("DecoratedWindow").Click += delegate
             {
                 new DecoratedWindow().Show();
             };
-            this.FindControl<Button>("DecoratedWindowDialog").Click += delegate
+            this.Get<Button>("DecoratedWindowDialog").Click += delegate
             {
                 new DecoratedWindow().ShowDialog(GetWindow());
             };
-            this.FindControl<Button>("Dialog").Click += delegate
+            this.Get<Button>("Dialog").Click += delegate
             {
                 var window = CreateSampleWindow();
                 window.Height = 200;
                 window.ShowDialog(GetWindow());
             };
-            this.FindControl<Button>("DialogNoTaskbar").Click += delegate
+            this.Get<Button>("DialogNoTaskbar").Click += delegate
             {
                 var window = CreateSampleWindow();
                 window.Height = 200;
                 window.ShowInTaskbar = false;
                 window.ShowDialog(GetWindow());
             };
-            this.FindControl<Button>("OwnedWindow").Click += delegate
+            this.Get<Button>("OwnedWindow").Click += delegate
             {
                 var window = CreateSampleWindow();
 
                 window.Show(GetWindow());
             };
 
-            this.FindControl<Button>("OwnedWindowNoTaskbar").Click += delegate
+            this.Get<Button>("OwnedWindowNoTaskbar").Click += delegate
             {
                 var window = CreateSampleWindow();
 
@@ -143,6 +151,7 @@ namespace ControlCatalog.Pages
         private Window CreateSampleWindow()
         {
             Button button;
+            Button dialogButton;
             
             var window = new Window
             {
@@ -159,6 +168,12 @@ namespace ControlCatalog.Pages
                             HorizontalAlignment = HorizontalAlignment.Center,
                             Content = "Click to close",
                             IsDefault = true
+                        }),
+                        (dialogButton = new Button
+                        {
+                            HorizontalAlignment = HorizontalAlignment.Center,
+                            Content = "Dialog",
+                            IsDefault = false
                         })
                     }
                 },
@@ -166,11 +181,17 @@ namespace ControlCatalog.Pages
             };
 
             button.Click += (_, __) => window.Close();
+            dialogButton.Click += (_, __) =>
+            {
+                var dialog = CreateSampleWindow();
+                dialog.Height = 200;
+                dialog.ShowDialog(window);
+            };
 
             return window;
         }
 
-        Window GetWindow() => (Window)this.VisualRoot;
+        Window GetWindow() => this.VisualRoot as Window  ?? throw new NullReferenceException("Invalid Owner");
 
         private void InitializeComponent()
         {

+ 3 - 3
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@@ -14,7 +14,7 @@ namespace ControlCatalog.Pages
         public DragAndDropPage()
         {
             this.InitializeComponent();
-            _DropState = this.Find<TextBlock>("DropState");
+            _DropState = this.Get<TextBlock>("DropState");
 
             int textCount = 0;
             SetupDnd("Text", d => d.Set(DataFormats.Text,
@@ -26,8 +26,8 @@ namespace ControlCatalog.Pages
 
         void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)
         {
-            var dragMe = this.Find<Border>("DragMe" + suffix);
-            var dragState = this.Find<TextBlock>("DragState"+suffix);
+            var dragMe = this.Get<Border>("DragMe" + suffix);
+            var dragState = this.Get<TextBlock>("DragState"+suffix);
 
             async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e)
             {

+ 4 - 4
samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs

@@ -35,7 +35,7 @@ namespace ControlCatalog.Pages
 
         private void SetXamlTexts()
         {
-            var bfxt = this.FindControl<TextBlock>("ButtonFlyoutXamlText");
+            var bfxt = this.Get<TextBlock>("ButtonFlyoutXamlText");
             bfxt.Text = "<Button Content=\"Click me!\">\n" +
                         "    <Button.Flyout>\n" +
                         "        <Flyout>\n" +
@@ -45,7 +45,7 @@ namespace ControlCatalog.Pages
                         "        </Flyout>\n" +
                         "    </Button.Flyout>\n</Button>";
 
-            var mfxt = this.FindControl<TextBlock>("MenuFlyoutXamlText");
+            var mfxt = this.Get<TextBlock>("MenuFlyoutXamlText");
             mfxt.Text = "<Button Content=\"Click me!\">\n" +
                     "    <Button.Flyout>\n" +
                     "        <MenuFlyout>\n" +
@@ -54,7 +54,7 @@ namespace ControlCatalog.Pages
                     "        </MenuFlyout>\n" +
                     "    </Button.Flyout>\n</Button>";
 
-            var afxt = this.FindControl<TextBlock>("AttachedFlyoutXamlText");
+            var afxt = this.Get<TextBlock>("AttachedFlyoutXamlText");
             afxt.Text = "<Panel Name=\"AttachedFlyoutPanel\">\n" +
                 "    <FlyoutBase.AttachedFlyout>\n" +
                 "        <Flyout>\n" +
@@ -66,7 +66,7 @@ namespace ControlCatalog.Pages
                 "\n\n In DoubleTapped handler:\n" +
                 "FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);";
 
-            var sfxt = this.FindControl<TextBlock>("SharedFlyoutXamlText");
+            var sfxt = this.Get<TextBlock>("SharedFlyoutXamlText");
             sfxt.Text = "Declare a flyout in Resources:\n" +
                 "<Window.Resources>\n" +
                 "    <Flyout x:Key=\"SharedFlyout\">\n" +

+ 8 - 5
samples/ControlCatalog/Pages/ImagePage.xaml.cs

@@ -17,9 +17,9 @@ namespace ControlCatalog.Pages
         public ImagePage()
         {
             InitializeComponent();
-            _bitmapImage = this.FindControl<Image>("bitmapImage");
-            _drawingImage = this.FindControl<Image>("drawingImage");
-            _croppedImage = this.FindControl<Image>("croppedImage");
+            _bitmapImage = this.Get<Image>("bitmapImage");
+            _drawingImage = this.Get<Image>("drawingImage");
+            _croppedImage = this.Get<Image>("croppedImage");
         }
 
         private void InitializeComponent()
@@ -50,8 +50,11 @@ namespace ControlCatalog.Pages
             if (_croppedImage != null)
             {
                 var comboxBox = (ComboBox)sender;
-                var croppedBitmap = _croppedImage.Source as CroppedBitmap;
-                croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex);
+                if (_croppedImage.Source is CroppedBitmap croppedBitmap)
+                {
+                    croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex);
+                }
+                
             }
         }
 

+ 15 - 11
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@@ -24,11 +24,11 @@ namespace ControlCatalog.Pages
         public ItemsRepeaterPage()
         {
             this.InitializeComponent();
-            _repeater = this.FindControl<ItemsRepeater>("repeater");
-            _scroller = this.FindControl<ScrollViewer>("scroller");
-            _scrollToLast = this.FindControl<Button>("scrollToLast");
-            _scrollToRandom = this.FindControl<Button>("scrollToRandom");
-            _scrollToSelected = this.FindControl<Button>("scrollToSelected");
+            _repeater = this.Get<ItemsRepeater>("repeater");
+            _scroller = this.Get<ScrollViewer>("scroller");
+            _scrollToLast = this.Get<Button>("scrollToLast");
+            _scrollToRandom = this.Get<Button>("scrollToRandom");
+            _scrollToSelected = this.Get<Button>("scrollToSelected");
             _repeater.PointerPressed += RepeaterClick;
             _repeater.KeyDown += RepeaterOnKeyDown;
             _scrollToLast.Click += scrollToLast_Click;
@@ -44,8 +44,10 @@ namespace ControlCatalog.Pages
 
         public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)
         {
-            var item = (ItemsRepeaterPageViewModel.Item)e.DataContext;
-            e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
+            if (e.DataContext is ItemsRepeaterPageViewModel.Item item)
+            {
+                e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
+            }
         }
 
         private void LayoutChanged(object sender, SelectionChangedEventArgs e)
@@ -115,7 +117,7 @@ namespace ControlCatalog.Pages
         private void ScrollTo(int index)
         {
             System.Diagnostics.Debug.WriteLine("Scroll to " + index);
-            var layoutManager = ((TopLevel)VisualRoot).LayoutManager;
+            var layoutManager = ((TopLevel)VisualRoot!).LayoutManager;
             var element = _repeater.GetOrCreateElement(index);
             layoutManager.ExecuteLayoutPass();
             element.BringIntoView();
@@ -123,9 +125,11 @@ namespace ControlCatalog.Pages
 
         private void RepeaterClick(object sender, PointerPressedEventArgs e)
         {
-            var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
-            _viewModel.SelectedItem = item;
-            _selectedIndex = _viewModel.Items.IndexOf(item);
+            if ((e.Source as TextBlock)?.DataContext is ItemsRepeaterPageViewModel.Item item)
+            {
+                _viewModel.SelectedItem = item;
+                _selectedIndex = _viewModel.Items.IndexOf(item);
+            }
         }
 
         private void RepeaterOnKeyDown(object sender, KeyEventArgs e)

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

@@ -7,7 +7,7 @@ namespace ControlCatalog.Pages
 {
     public class LabelsPage : UserControl
     {
-        private Person _person;
+        private Person? _person;
 
         public LabelsPage()
         {

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

@@ -22,7 +22,7 @@ namespace ControlCatalog.Pages
             AvaloniaXamlLoader.Load(this);
         }
         
-        private MenuPageViewModel _model;
+        private MenuPageViewModel? _model;
         protected override void OnDataContextChanged(EventArgs e)
         {
             if (_model != null)

+ 68 - 0
samples/ControlCatalog/Pages/NativeEmbedPage.xaml

@@ -0,0 +1,68 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:local="using:ControlCatalog.Pages"
+             d:DesignHeight="800"
+             d:DesignWidth="400"
+             x:Class="ControlCatalog.Pages.NativeEmbedPage">
+  <DockPanel>
+    <Menu DockPanel.Dock="Top">
+      <MenuItem Header="Test">
+        <MenuItem Header="SubMenu">
+          <MenuItem Header="Item 1"/>
+          <MenuItem Header="Item 2"/>
+          <MenuItem Header="Item 3"/>
+        </MenuItem>
+        <MenuItem Header="Item 1"/>
+        <MenuItem Header="Item 2"/>
+        <MenuItem Header="Item 3"/>
+      </MenuItem>
+    </Menu>
+    <DockPanel DockPanel.Dock="Top">
+      <Button DockPanel.Dock="Right" Click="ShowPopupDelay">Show popup (delay)</Button>
+      <Button DockPanel.Dock="Right" Click="ShowPopup">Show popup</Button>
+      <Border DockPanel.Dock="Right" Background="#c0c0c0">
+        <ToolTip.Tip>
+          <ToolTip>
+            <TextBlock>Text</TextBlock>
+          </ToolTip>
+        </ToolTip.Tip>
+        <TextBlock VerticalAlignment="Center">Tooltip</TextBlock>
+      </Border>
+      <TextBox Text="Lorem ipsum dolor sit amet"/>
+
+    </DockPanel>
+    <Grid ColumnDefinitions="*,5,*"
+          RowDefinitions="*,5,*">
+      <Grid.Styles>
+        <Style Selector="DockPanel#FirstPanel:not(.mobile), DockPanel#SecondPanel:not(.mobile)">
+          <Setter Property="Grid.RowSpan" Value="3" />
+        </Style>
+        <Style Selector="DockPanel#SecondPanel:not(.mobile)">
+          <Setter Property="Grid.Column" Value="2" />
+        </Style>
+
+        <Style Selector="DockPanel#FirstPanel.mobile, DockPanel#SecondPanel.mobile">
+          <Setter Property="Grid.ColumnSpan" Value="3" />
+        </Style>
+        <Style Selector="DockPanel#SecondPanel.mobile">
+          <Setter Property="Grid.Row" Value="2" />
+        </Style>
+      </Grid.Styles>
+
+      <DockPanel x:Name="FirstPanel">
+        <CheckBox x:Name="firstVisible" DockPanel.Dock="Top"
+                  IsChecked="True" Content="Visible" />
+        <local:EmbedSample IsVisible="{Binding #firstVisible.IsChecked}"/>
+      </DockPanel>
+      <GridSplitter Grid.Row="0" Grid.RowSpan="3" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
+      <GridSplitter Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" Height="5" VerticalAlignment="Stretch" />
+      <DockPanel x:Name="SecondPanel">
+        <CheckBox x:Name="secondVisible" DockPanel.Dock="Top"
+                  IsChecked="True" Content="Visible" />
+        <local:EmbedSample IsSecond="True" IsVisible="{Binding #secondVisible.IsChecked}"/>
+      </DockPanel>
+    </Grid>
+  </DockPanel>
+</UserControl>

+ 86 - 0
samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using Avalonia.Platform;
+using Avalonia.Interactivity;
+using Avalonia.Controls;
+using Avalonia.Controls.Platform;
+using Avalonia.Markup.Xaml;
+using Avalonia;
+
+namespace ControlCatalog.Pages
+{
+    public class NativeEmbedPage : UserControl
+    {
+        public NativeEmbedPage()
+        {
+            this.InitializeComponent();
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+
+        public async void ShowPopupDelay(object sender, RoutedEventArgs args)
+        {
+            await Task.Delay(3000);
+            ShowPopup(sender, args);
+        }
+
+        public void ShowPopup(object sender, RoutedEventArgs args)
+        {
+            new ContextMenu()
+            {
+                Items = new List<MenuItem>
+            {
+                new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" }
+            }
+            }.Open((Control)sender);
+        }
+
+        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+        {
+            base.OnPropertyChanged(change);
+
+            if (change.Property == BoundsProperty)
+            {
+                var isMobile = change.GetNewValue<Rect>().Width < 1200;
+                this.Find<DockPanel>("FirstPanel")!.Classes.Set("mobile", isMobile);
+                this.Find<DockPanel>("SecondPanel")!.Classes.Set("mobile", isMobile);
+            }
+        }
+    }
+
+    public class EmbedSample : NativeControlHost
+    {
+        public static INativeDemoControl? Implementation { get; set; }
+
+        static EmbedSample()
+        {
+
+        }
+
+        public bool IsSecond { get; set; }
+
+        protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
+        {
+            return Implementation?.CreateControl(IsSecond, parent, () => base.CreateNativeControlCore(parent))
+                ?? base.CreateNativeControlCore(parent);
+        }
+
+        protected override void DestroyNativeControlCore(IPlatformHandle control)
+        {
+            base.DestroyNativeControlCore(control);
+        }
+    }
+
+    public interface INativeDemoControl
+    {
+        /// <param name="isSecond">Used to specify which control should be displayed as a demo</param>
+        /// <param name="parent"></param>
+        /// <param name="createDefault"></param>
+        IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault);
+    }
+}

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

@@ -65,7 +65,7 @@
                        Margin="2" HorizontalAlignment="Center"/>
 
         <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Value:</TextBlock>
-        <NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center"
+        <NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding DecimalValue}" VerticalAlignment="Center"
                        Margin="2" HorizontalAlignment="Center"/>
 
 

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

@@ -28,16 +28,16 @@ namespace ControlCatalog.Pages
 
     public class NumbersPageViewModel : ViewModelBase
     {
-        private IList<FormatObject> _formats;
+        private IList<FormatObject>? _formats;
         private FormatObject _selectedFormat;
-        private IList<Location> _spinnerLocations;
+        private IList<Location>? _spinnerLocations;
 
         private double _doubleValue;
         private decimal _decimalValue;
 
         public NumbersPageViewModel()
         {
-            SelectedFormat = Formats.FirstOrDefault();
+            _selectedFormat = Formats.FirstOrDefault();
         }
 
         public double DoubleValue
@@ -103,7 +103,7 @@ namespace ControlCatalog.Pages
 
     public class FormatObject
     {
-        public string Value { get; set; }
-        public string Name { get; set; }
+        public string? Value { get; set; }
+        public string? Name { get; set; }
     }
 }

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

@@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
         protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
         {
             base.OnAttachedToVisualTree(e);
-            Window w = (Window)VisualRoot;
+            Window w = (Window)VisualRoot!;
             w.PositionChanged += (sender, args) => InvalidateVisual();
         }
 

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

@@ -52,7 +52,7 @@ namespace ControlCatalog.Pages
 
         private IBitmap LoadBitmap(string uri)
         {
-            var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
+            var assets = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
             return new Bitmap(assets.Open(new Uri(uri)));
         }
 
@@ -60,7 +60,7 @@ namespace ControlCatalog.Pages
         {
             private Dock _tabPlacement;
 
-            public TabItemViewModel[] Tabs { get; set; }
+            public TabItemViewModel[]? Tabs { get; set; }
 
             public Dock TabPlacement
             {
@@ -71,10 +71,10 @@ namespace ControlCatalog.Pages
 
         private class TabItemViewModel
         {
-            public string Header { get; set; }
-            public string Text { get; set; }
-            public IBitmap Image { get; set; }
-            public bool IsEnabled { get; set; } = true;           
+            public string? Header { get; set; }
+            public string? Text { get; set; }
+            public IBitmap? Image { get; set; }
+            public bool IsEnabled { get; set; } = true;
         }
     }
 }

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

@@ -38,7 +38,7 @@ namespace ControlCatalog.Pages
 
         private class TabStripItemViewModel
         {
-            public string Header { get; set; }
+            public string? Header { get; set; }
             public bool IsEnabled { get; set; } = true;
         }
     }

+ 6 - 0
samples/ControlCatalog/Pages/TextBoxPage.xaml

@@ -66,6 +66,12 @@
                  FontFamily="Comic Sans MS"
                  InputMethod.IsInputMethodEnabled="False"
                  Foreground="Red"/>
+        <TextBox AcceptsReturn="True" 
+                 TextWrapping="Wrap" 
+                 Width="200" 
+                 Height="125"
+                 LineHeight="32"
+                 Text="Multiline TextBox with TextWrapping and increased LineHeight.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
       </StackPanel>
       <StackPanel Orientation="Vertical" Spacing="8" Margin="8">
         <Label Classes="h2" Target="{Binding #firstResMFont}">res_m fonts</Label>

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

@@ -10,7 +10,7 @@ namespace ControlCatalog.ViewModels
         {
             ExitCommand = MiniCommand.Create(() =>
             {
-                if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
+                if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
                 {
                     lifetime.Shutdown();
                 }

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

@@ -9,7 +9,7 @@ namespace ControlCatalog.ViewModels
 {
     public class ContextPageViewModel
     {
-        public Control View { get; set; }
+        public Control? View { get; set; }
         public ContextPageViewModel()
         {
             OpenCommand = MiniCommand.CreateFromTask(Open);

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

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

+ 10 - 9
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@@ -14,7 +14,7 @@ namespace ControlCatalog.ViewModels
 
         public ItemsRepeaterPageViewModel()
         {
-            Items = CreateItems();
+            _items = CreateItems();
         }
 
         public ObservableCollection<Item> Items
@@ -23,12 +23,12 @@ namespace ControlCatalog.ViewModels
             set => this.RaiseAndSetIfChanged(ref _items, value);
         }
 
-        public Item SelectedItem { get; set; }
+        public Item? SelectedItem { get; set; }
 
         public void AddItem()
         {
             var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
-            Items.Insert(index + 1, new Item(index + 1) { Text = $"New Item {_newItemIndex++}" });
+            Items.Insert(index + 1, new Item(index + 1, $"New Item {_newItemIndex++}"));
         }
 
         public void RemoveItem()
@@ -66,19 +66,20 @@ namespace ControlCatalog.ViewModels
             _newGenerationIndex++;
 
             return new ObservableCollection<Item>(
-                Enumerable.Range(1, 100000).Select(i => new Item(i)
-                {
-                    Text = $"Item {i.ToString()} {suffix}"
-                }));
+                Enumerable.Range(1, 100000).Select(i => new Item(i, $"Item {i.ToString()} {suffix}")));
         }
 
         public class Item : ViewModelBase
         {
             private double _height = double.NaN;
 
-            public Item(int index) => Index = index;
+            public Item(int index, string text)
+            {
+                Index = index;
+                Text = text;
+            }
             public int Index { get; }
-            public string Text { get; set; }
+            public string Text { get; }
             
             public double Height 
             {

+ 7 - 6
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@@ -16,11 +16,11 @@ namespace ControlCatalog.ViewModels
 
         private bool _isMenuItemChecked = true;
         private WindowState _windowState;
-        private WindowState[] _windowStates;
+        private WindowState[] _windowStates = Array.Empty<WindowState>();
         private int _transparencyLevel;
         private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome;
         private bool _extendClientAreaEnabled;
-        private bool _systemTitleBarEnabled;        
+        private bool _systemTitleBarEnabled;
         private bool _preferSystemChromeEnabled;
         private double _titleBarHeight;
 
@@ -47,14 +47,15 @@ namespace ControlCatalog.ViewModels
             {
                 var dialog = new AboutAvaloniaDialog();
 
-                var mainWindow = (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow;
-
-                await dialog.ShowDialog(mainWindow);
+                if ((App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow is { } mainWindow)
+                {
+                    await dialog.ShowDialog(mainWindow);
+                }
             });
 
             ExitCommand = MiniCommand.Create(() =>
             {
-                (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown();
+                (App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Shutdown();
             });
 
             ToggleMenuItemCheckedCommand = MiniCommand.Create(() =>

+ 4 - 4
samples/ControlCatalog/ViewModels/MenuItemViewModel.cs

@@ -5,9 +5,9 @@ namespace ControlCatalog.ViewModels
 {
     public class MenuItemViewModel
     {
-        public string Header { get; set; }
-        public ICommand Command { get; set; }
-        public object CommandParameter { get; set; }
-        public IList<MenuItemViewModel> Items { get; set; }
+        public string? Header { get; set; }
+        public ICommand? Command { get; set; }
+        public object? CommandParameter { get; set; }
+        public IList<MenuItemViewModel>? Items { get; set; }
     }
 }

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

@@ -10,7 +10,7 @@ namespace ControlCatalog.ViewModels
 {
     public class MenuPageViewModel
     {
-        public Control View { get; set; }
+        public Control? View { get; set; }
         public MenuPageViewModel()
         {
             OpenCommand = MiniCommand.CreateFromTask(Open);

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

@@ -19,8 +19,8 @@ namespace ControlCatalog.ViewModels
             });
         }
 
-        public string Title { get; set; }
-        public string Message { get; set; }
+        public string? Title { get; set; }
+        public string? Message { get; set; }
 
         public MiniCommand YesCommand { get; }
 

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

@@ -19,7 +19,7 @@ namespace ControlCatalog.ViewModels
     {
         public TransitioningContentControlPageViewModel()
         {
-            var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>();
+            var assetLoader = AvaloniaLocator.Current?.GetService<IAssetLoader>()!;
 
             var images = new string[] 
             { 
@@ -36,8 +36,8 @@ namespace ControlCatalog.ViewModels
 
             SetupTransitions();
 
-            SelectedTransition = PageTransitions[1];
-            SelectedImage = Images[0];
+            _SelectedTransition = PageTransitions[1];
+            _SelectedImage = Images[0];
         }
 
         public List<PageTransition> PageTransitions { get; } = new List<PageTransition>();
@@ -45,12 +45,12 @@ namespace ControlCatalog.ViewModels
         public List<Bitmap> Images { get; } = new List<Bitmap>();
 
 
-        private Bitmap? _SelectedImage;
+        private Bitmap _SelectedImage;
 
         /// <summary>
         /// Gets or Sets the selected image
         /// </summary>
-        public Bitmap? SelectedImage
+        public Bitmap SelectedImage
         {
             get { return _SelectedImage; }
             set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); }
@@ -160,12 +160,12 @@ namespace ControlCatalog.ViewModels
         public string DisplayTitle { get; }
 
 
-        private IPageTransition _Transition;
+        private IPageTransition? _Transition;
 
         /// <summary>
         /// Gets or sets the transition
         /// </summary>
-        public IPageTransition Transition
+        public IPageTransition? Transition
         {
             get { return _Transition; }
             set { this.RaiseAndSetIfChanged(ref _Transition, value); }
@@ -201,7 +201,7 @@ namespace ControlCatalog.ViewModels
         /// </summary>
         public TimeSpan Duration { get; set; }
 
-        public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken)
+        public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {

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

@@ -92,7 +92,7 @@ namespace ControlCatalog.ViewModels
 
         public class Node
         {
-            private ObservableCollection<Node> _children;
+            private ObservableCollection<Node>? _children;
             private int _childIndex = 10;
 
             public Node()
@@ -106,7 +106,7 @@ namespace ControlCatalog.ViewModels
                 Header = parent.Header + ' ' + index;
             }
 
-            public Node Parent { get; }
+            public Node? Parent { get; }
             public string Header { get; }
             public bool AreChildrenInitialized => _children != null;
             public ObservableCollection<Node> Children => _children ??= CreateChildren();

+ 2 - 1
samples/IntegrationTestApp/IntegrationTestApp.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <OutputType>WinExe</OutputType>
     <TargetFramework>net6.0</TargetFramework>
@@ -18,6 +18,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
   </ItemGroup>
   
   <Import Project="..\..\build\BuildTargets.targets" />

+ 1 - 0
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@@ -7,6 +7,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
   </ItemGroup>
 

Неке датотеке нису приказане због велике количине промена