Browse Source

Restructure native embed sample project

Max Katz 3 years ago
parent
commit
c175ef318e
28 changed files with 657 additions and 402 deletions
  1. 27 0
      Avalonia.sln
  2. 15 0
      samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj
  3. 17 0
      samples/interop/NativeEmbedSample.Desktop/Program.cs
  4. 50 0
      samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs
  5. 1 2
      samples/interop/NativeEmbedSample/App.axaml
  6. 23 0
      samples/interop/NativeEmbedSample/App.axaml.cs
  7. 0 22
      samples/interop/NativeEmbedSample/App.xaml.cs
  8. 24 91
      samples/interop/NativeEmbedSample/EmbedSample.cs
  9. 41 0
      samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs
  10. 62 0
      samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs
  11. 0 0
      samples/interop/NativeEmbedSample/Gtk/nodes-license.md
  12. 0 0
      samples/interop/NativeEmbedSample/Gtk/nodes.mp4
  13. 0 58
      samples/interop/NativeEmbedSample/GtkHelper.cs
  14. 32 0
      samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs
  15. 39 0
      samples/interop/NativeEmbedSample/Mac/MacHelper.cs
  16. 0 39
      samples/interop/NativeEmbedSample/MacHelper.cs
  17. 64 0
      samples/interop/NativeEmbedSample/MainView.axaml
  18. 45 0
      samples/interop/NativeEmbedSample/MainView.axaml.cs
  19. 10 0
      samples/interop/NativeEmbedSample/MainWindow.axaml
  20. 17 0
      samples/interop/NativeEmbedSample/MainWindow.axaml.cs
  21. 0 52
      samples/interop/NativeEmbedSample/MainWindow.xaml
  22. 0 36
      samples/interop/NativeEmbedSample/MainWindow.xaml.cs
  23. 13 11
      samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
  24. 0 17
      samples/interop/NativeEmbedSample/Program.cs
  25. 37 0
      samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs
  26. 75 0
      samples/interop/NativeEmbedSample/Win/WinApi.cs
  27. 0 74
      samples/interop/NativeEmbedSample/WinApi.cs
  28. 65 0
      samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs

+ 27 - 0
Avalonia.sln

@@ -219,6 +219,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.Desktop", "samples\interop\NativeEmbedSample.Desktop\NativeEmbedSample.Desktop.csproj", "{F2389463-DDB4-4317-B894-D4DF9FF6B763}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -1989,6 +1991,30 @@ Global
 		{1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU
 		{1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhone.Build.0 = Release|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -2048,6 +2074,7 @@ Global
 		{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
+		{F2389463-DDB4-4317-B894-D4DF9FF6B763} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

+ 15 - 0
samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj

@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\NativeEmbedSample\NativeEmbedSample.csproj" />
+  </ItemGroup>
+
+  <Import Project="..\..\..\build\SampleApp.props" />
+  <Import Project="..\..\..\build\BuildTargets.targets" />
+  <Import Project="..\..\..\build\ReferenceCoreLibraries.props" />
+</Project>

+ 17 - 0
samples/interop/NativeEmbedSample.Desktop/Program.cs

@@ -0,0 +1,17 @@
+using Avalonia;
+using NativeEmbedSample;
+
+namespace NativeEmbedSample.Desktop;
+
+public class Program
+{
+    static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+
+    public static AppBuilder BuildAvaloniaApp()
+        => AppBuilder.Configure<App>()
+            .With(new AvaloniaNativePlatformOptions()
+            {
+            })
+            .UsePlatformDetect();
+
+}

+ 50 - 0
samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs

@@ -0,0 +1,50 @@
+#if __ANDROID__ || ANDROID
+using System;
+using System.IO;
+using System.Diagnostics;
+using Android.Views;
+using Android.Webkit;
+using Avalonia.Controls.Platform;
+using Avalonia.Platform;
+
+namespace NativeEmbedSample;
+
+public partial class EmbedSample
+{
+    private IPlatformHandle CreateAndroid(IPlatformHandle parent)
+    {
+        var button = new Android.Widget.Button(Android.App.Application.Context) { Text = "Android button" };
+
+        return new AndroidViewHandle(button);
+    }
+
+    private void DestroyAndroid(IPlatformHandle control)
+    {
+        base.DestroyNativeControlCore(control);
+    }
+}
+
+internal sealed class AndroidViewHandle : INativeControlHostDestroyableControlHandle
+{
+    private View _view;
+
+    public AndroidViewHandle(View view)
+    {
+        _view = view;
+    }
+
+    public IntPtr Handle => _view?.Handle ?? IntPtr.Zero;
+    public string HandleDescriptor => "JavaHandle";
+
+    public void Destroy()
+    {
+        _view?.Dispose();
+        _view = null;
+    }
+
+    ~AndroidViewHandle()
+    {
+        Destroy();
+    }
+}
+#endif

+ 1 - 2
samples/interop/NativeEmbedSample/App.xaml → samples/interop/NativeEmbedSample/App.axaml

@@ -2,7 +2,6 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              x:Class="NativeEmbedSample.App">
   <Application.Styles>
-    <StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
-    <StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
+    <FluentTheme />
   </Application.Styles>
 </Application>

+ 23 - 0
samples/interop/NativeEmbedSample/App.axaml.cs

@@ -0,0 +1,23 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+namespace NativeEmbedSample;
+
+public class App : Application
+{
+    public override void Initialize()
+    {
+        AvaloniaXamlLoader.Load(this);
+    }
+
+    public override void OnFrameworkInitializationCompleted()
+    {
+        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
+            desktopLifetime.MainWindow = new MainWindow();
+        else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime)
+            singleViewLifetime.MainView = new MainView();
+
+        base.OnFrameworkInitializationCompleted();
+    }
+}

+ 0 - 22
samples/interop/NativeEmbedSample/App.xaml.cs

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

+ 24 - 91
samples/interop/NativeEmbedSample/EmbedSample.cs

@@ -6,116 +6,49 @@ using System.Text;
 using Avalonia.Controls;
 using Avalonia.Platform;
 using Avalonia.Threading;
-using MonoMac.AppKit;
-using MonoMac.Foundation;
-using MonoMac.WebKit;
-using Encoding = SharpDX.Text.Encoding;
 
 namespace NativeEmbedSample
 {
-    public class EmbedSample : NativeControlHost
+    public partial class EmbedSample : NativeControlHost
     {
         public bool IsSecond { get; set; }
-        private Process _mplayer;
 
-        IPlatformHandle CreateLinux(IPlatformHandle parent)
-        {
-            if (IsSecond)
-            {
-                var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle);
-                if (chooser != null)
-                    return chooser;
-            }
-
-            var control = base.CreateNativeControlCore(parent);
-            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;
-        }
-
-        void DestroyLinux(IPlatformHandle handle)
-        {
-            _mplayer?.Kill();
-            _mplayer = null;
-            base.DestroyNativeControlCore(handle);
-        }
-
-        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
-}";
-
-        IPlatformHandle CreateWin32(IPlatformHandle parent)
-        {
-            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 PlatformHandle(handle, "HWND");
-
-        }
-
-        void DestroyWin32(IPlatformHandle handle)
-        {
-            WinApi.DestroyWindow(handle.Handle);
-        }
-
-        IPlatformHandle CreateOSX(IPlatformHandle parent)
-        {
-            // 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);
-
-        }
-
-        void DestroyOSX(IPlatformHandle handle)
-        {
-            ((MacOSViewHandle)handle).Dispose();
-        }
-        
         protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
         {
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+#if DESKTOP
+            if (OperatingSystem.IsLinux())
                 return CreateLinux(parent);
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            if (OperatingSystem.IsWindows())
                 return CreateWin32(parent);
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+            if (OperatingSystem.IsMacOS())
                 return CreateOSX(parent);
+#elif __ANDROID__ || ANDROID
+            if (OperatingSystem.IsAndroid())
+                return CreateAndroid(parent);
+#elif IOS
+            if (OperatingSystem.IsIOS())
+                return CreateIOS(parent);
+#endif
             return base.CreateNativeControlCore(parent);
         }
 
         protected override void DestroyNativeControlCore(IPlatformHandle control)
         {
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+#if DESKTOP
+            if (OperatingSystem.IsLinux())
                 DestroyLinux(control);
-            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            else if (OperatingSystem.IsWindows())
                 DestroyWin32(control);
-            else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+            else if (OperatingSystem.IsMacOS())
                 DestroyOSX(control);
-            else
-                base.DestroyNativeControlCore(control);
+#elif __ANDROID__ || ANDROID
+            if (OperatingSystem.IsAndroid())
+                DestroyAndroid(control);
+#elif IOS
+            if (OperatingSystem.IsIOS())
+                DestroyIOS(control);
+#endif
+            else base.DestroyNativeControlCore(control);
         }
     }
 }

+ 41 - 0
samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs

@@ -0,0 +1,41 @@
+#if DESKTOP
+using System.IO;
+using System.Diagnostics;
+using Avalonia.Platform;
+
+namespace NativeEmbedSample;
+
+public partial class EmbedSample
+{
+    private Process _mplayer;
+
+    IPlatformHandle CreateLinux(IPlatformHandle parent)
+    {
+        if (IsSecond)
+        {
+            var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle);
+            if (chooser != null)
+                return chooser;
+        }
+
+        var control = base.CreateNativeControlCore(parent);
+        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;
+    }
+
+    void DestroyLinux(IPlatformHandle handle)
+    {
+        _mplayer?.Kill();
+        _mplayer = null;
+        base.DestroyNativeControlCore(handle);
+    }
+}
+#endif

+ 62 - 0
samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs

@@ -0,0 +1,62 @@
+#if DESKTOP
+
+using System;
+using System.Threading.Tasks;
+using Avalonia.Controls.Platform;
+using Avalonia.Platform;
+using Avalonia.Platform.Interop;
+using Avalonia.X11.NativeDialogs;
+using static Avalonia.X11.NativeDialogs.Gtk;
+using static Avalonia.X11.NativeDialogs.Glib;
+
+namespace NativeEmbedSample;
+
+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 IPlatformHandle 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;
+    }
+}
+#endif

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


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


+ 0 - 58
samples/interop/NativeEmbedSample/GtkHelper.cs

@@ -1,58 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Avalonia.Controls.Platform;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-using Avalonia.X11.NativeDialogs;
-using static Avalonia.X11.NativeDialogs.Gtk;
-using static Avalonia.X11.NativeDialogs.Glib;
-namespace NativeEmbedSample
-{
-    public 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 IPlatformHandle 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;
-        }
-    }
-}

+ 32 - 0
samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs

@@ -0,0 +1,32 @@
+#if DESKTOP
+using Avalonia.Platform;
+using Avalonia.Threading;
+using MonoMac.Foundation;
+using MonoMac.WebKit;
+
+namespace NativeEmbedSample;
+
+public partial class EmbedSample
+{
+    IPlatformHandle CreateOSX(IPlatformHandle parent)
+    {
+        // 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);
+
+    }
+
+    void DestroyOSX(IPlatformHandle handle)
+    {
+        ((MacOSViewHandle)handle).Dispose();
+    }
+}
+#endif

+ 39 - 0
samples/interop/NativeEmbedSample/Mac/MacHelper.cs

@@ -0,0 +1,39 @@
+#if DESKTOP
+using System;
+using Avalonia.Platform;
+using MonoMac.AppKit;
+
+namespace NativeEmbedSample;
+
+internal class MacHelper
+{
+    private static bool _isInitialized;
+
+    public static void EnsureInitialized()
+    {
+        if (_isInitialized)
+            return;
+        _isInitialized = true;
+        NSApplication.Init();
+    }
+}
+
+internal class MacOSViewHandle : IPlatformHandle, IDisposable
+{
+    private NSView _view;
+
+    public MacOSViewHandle(NSView view)
+    {
+        _view = view;
+    }
+
+    public IntPtr Handle => _view?.Handle ?? IntPtr.Zero;
+    public string HandleDescriptor => "NSView";
+
+    public void Dispose()
+    {
+        _view.Dispose();
+        _view = null;
+    }
+}
+#endif

+ 0 - 39
samples/interop/NativeEmbedSample/MacHelper.cs

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

+ 64 - 0
samples/interop/NativeEmbedSample/MainView.axaml

@@ -0,0 +1,64 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:local="clr-namespace:NativeEmbedSample"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="NativeEmbedSample.MainView">
+  <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>

+ 45 - 0
samples/interop/NativeEmbedSample/MainView.axaml.cs

@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace NativeEmbedSample;
+
+public class MainView : UserControl
+{
+    public MainView()
+    {
+        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);
+        }
+    }
+}

+ 10 - 0
samples/interop/NativeEmbedSample/MainWindow.axaml

@@ -0,0 +1,10 @@
+<Window xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:nativeEmbedSample="clr-namespace:NativeEmbedSample"
+        x:Class="NativeEmbedSample.MainWindow"
+        MinWidth="500" MinHeight="300"
+        Width="1024" Height="800"
+        Title="Native embedding sample">
+  <nativeEmbedSample:MainView />
+</Window>
+

+ 17 - 0
samples/interop/NativeEmbedSample/MainWindow.axaml.cs

@@ -0,0 +1,17 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace NativeEmbedSample;
+
+public class MainWindow : Window
+{
+    public MainWindow()
+    {
+        AvaloniaXamlLoader.Load(this);
+#if DEBUG && DESKTOP
+        this.AttachDevTools();
+#endif
+    }
+}
+

+ 0 - 52
samples/interop/NativeEmbedSample/MainWindow.xaml

@@ -1,52 +0,0 @@
-<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
-        Width="1024" Height="800"
-        Title="Native embedding sample"
-        xmlns:local="clr-namespace:NativeEmbedSample"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        x:Class="NativeEmbedSample.MainWindow">
-  <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>Tooltip</TextBlock>
-      </Border>
-      <TextBox Text="Lorem ipsum dolor sit amet"/>
-      
-    </DockPanel>
-    <Grid ColumnDefinitions="*,5,*">
-      <DockPanel>
-        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
-          <CheckBox x:Name="firstVisible" IsChecked="True"/>
-          <TextBlock>Visible</TextBlock>
-        </StackPanel>
-        <local:EmbedSample IsVisible="{Binding #firstVisible.IsChecked}"/>
-      </DockPanel>
-      <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
-      <DockPanel Grid.Column="2">
-        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
-          <CheckBox x:Name="secondVisible" IsChecked="True"/>
-          <TextBlock>Visible</TextBlock>
-        </StackPanel>
-        <local:EmbedSample IsSecond="True" IsVisible="{Binding #secondVisible.IsChecked}"/>
-      </DockPanel>
-    </Grid>
-  </DockPanel>
-</Window>

+ 0 - 36
samples/interop/NativeEmbedSample/MainWindow.xaml.cs

@@ -1,36 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Interactivity;
-using Avalonia.Markup.Xaml;
-
-namespace NativeEmbedSample
-{
-    public class MainWindow : Window
-    {
-        public MainWindow()
-        {
-            AvaloniaXamlLoader.Load(this);
-            this.AttachDevTools();
-        }
-
-        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);
-        }
-    }
-}

+ 13 - 11
samples/interop/NativeEmbedSample/NativeEmbedSample.csproj

@@ -1,30 +1,32 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.0</TargetFramework>
-    <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
+    <TargetFrameworks>net6.0;net6.0-android;net6.0-ios</TargetFrameworks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    
+    <DefineConstants Condition="'$(TargetFramework)' == 'net6.0'">$(DefineConstants);DESKTOP</DefineConstants>
   </PropertyGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
+  </ItemGroup>
+  
+  <ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
     <PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
     <ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
     <ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
     <PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
-
-    <AvaloniaResource Include="**\*.xaml">
-      <SubType>Designer</SubType>
-    </AvaloniaResource>
     <None Remove="nodes.mp4" />
-    <Content Include="nodes.mp4">
+    
+    <Compile Include="..\..\..\src\Avalonia.X11\NativeDialogs\Gtk.cs">
+      <Link>Gtk\Gtk.cs</Link>
+    </Compile>
+    
+    <Content Include="Gtk\nodes.mp4">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Compile Include="..\..\..\src\Avalonia.X11\NativeDialogs\Gtk.cs" />
   </ItemGroup>
 
-  <Import Project="..\..\..\build\SampleApp.props" />
   <Import Project="..\..\..\build\BuildTargets.targets" />
-  <Import Project="..\..\..\build\ReferenceCoreLibraries.props" />
 </Project>

+ 0 - 17
samples/interop/NativeEmbedSample/Program.cs

@@ -1,17 +0,0 @@
-using Avalonia;
-
-namespace NativeEmbedSample
-{
-    class Program
-    {
-        static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
-
-        public static AppBuilder BuildAvaloniaApp()
-            => AppBuilder.Configure<App>()
-                .With(new AvaloniaNativePlatformOptions()
-                {
-                })
-                .UsePlatformDetect();
-
-    }
-}

+ 37 - 0
samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs

@@ -0,0 +1,37 @@
+#if DESKTOP
+using System;
+using System.Text;
+using Avalonia.Platform;
+
+namespace NativeEmbedSample;
+
+public partial class EmbedSample
+{
+    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
+}";
+
+    IPlatformHandle CreateWin32(IPlatformHandle parent)
+    {
+        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 PlatformHandle(handle, "HWND");
+
+    }
+
+    void DestroyWin32(IPlatformHandle handle)
+    {
+        WinApi.DestroyWindow(handle.Handle);
+    }
+}
+#endif

+ 75 - 0
samples/interop/NativeEmbedSample/Win/WinApi.cs

@@ -0,0 +1,75 @@
+#if DESKTOP
+using System;
+using System.Runtime.InteropServices;
+
+namespace NativeEmbedSample;
+
+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);
+}
+#endif

+ 0 - 74
samples/interop/NativeEmbedSample/WinApi.cs

@@ -1,74 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace NativeEmbedSample
-{
-    public 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);
-    }
-}

+ 65 - 0
samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs

@@ -0,0 +1,65 @@
+#if IOS
+using System;
+using System.IO;
+using System.Diagnostics;
+using Avalonia.Controls.Platform;
+using Avalonia.Platform;
+using CoreGraphics;
+using Foundation;
+using UIKit;
+using WebKit;
+
+namespace NativeEmbedSample;
+
+public partial class EmbedSample
+{
+    private IPlatformHandle CreateIOS(IPlatformHandle parent)
+    {
+        if (IsSecond)
+        {
+            var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration());
+            webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/")));
+
+            return new UIViewHandle(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 UIViewHandle(button);   
+        }
+    }
+
+    private void DestroyIOS(IPlatformHandle control)
+    {
+        base.DestroyNativeControlCore(control);
+    }
+}
+
+internal class UIViewHandle : INativeControlHostDestroyableControlHandle
+{
+    private UIView _view;
+
+    public UIViewHandle(UIView view)
+    {
+        _view = view;
+    }
+
+    public IntPtr Handle => _view?.Handle ?? IntPtr.Zero;
+    public string HandleDescriptor => "UIView";
+
+    public void Destroy()
+    {
+        _view?.Dispose();
+        _view = null;
+    }
+}
+#endif