From 67ff5ba53c351a9b9198d14ddd37e69b217aceed Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 6 Jun 2017 16:12:02 +0300 Subject: [PATCH 01/15] =?UTF-8?q?Initial=20implementation=20of=20Proper=20?= =?UTF-8?q?WPF=20embedding=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Avalonia.sln | 43 ++++ .../WindowsInteropTest/EmbedToWpfDemo.xaml | 3 +- .../WindowsInteropTest.csproj | 6 +- .../Embedding/EmbeddableControlRoot.cs | 2 + .../Avalonia.Win32.Interop.csproj | 101 ++++++++++ .../Properties/AssemblyInfo.cs | 36 ++++ .../Avalonia.Win32.Interop/Wpf/CursorShim.cs | 38 ++++ .../Avalonia.Win32.Interop/Wpf/Helpers.cs | 16 ++ .../Wpf/WpfAvaloniaHost.cs | 80 ++++++++ .../Wpf/WpfMouseDevice.cs | 30 +++ .../Wpf/WpfTopLevelImpl.cs | 190 ++++++++++++++++++ .../Wpf/WritableBitmapSurface.cs | 81 ++++++++ 12 files changed, 624 insertions(+), 2 deletions(-) create mode 100644 src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj create mode 100644 src/Windows/Avalonia.Win32.Interop/Properties/AssemblyInfo.cs create mode 100644 src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs create mode 100644 src/Windows/Avalonia.Win32.Interop/Wpf/Helpers.cs create mode 100644 src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs create mode 100644 src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs create mode 100644 src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs create mode 100644 src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs diff --git a/Avalonia.sln b/Avalonia.sln index f12af02236..afaee6a907 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -191,6 +191,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.LinuxFramebuffer", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13 @@ -2589,6 +2591,46 @@ Global {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Mono.Build.0 = Release|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.ActiveCfg = Release|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Mono.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Mono.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|x86.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2649,5 +2691,6 @@ Global {4D6FAF79-58B4-482F-9122-0668C346364C} = {74487168-7D91-487E-BF93-055F2251461E} {854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} EndGlobalSection EndGlobal diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml index 1115cf5768..5a346d4e8d 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WindowsInteropTest" xmlns:embedding="clr-namespace:Avalonia.Win32.Embedding;assembly=Avalonia.Win32" + xmlns:wpf="clr-namespace:Avalonia.Win32.Interop.Wpf;assembly=Avalonia.Win32.Interop" mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="400" MinWidth="500" MinHeight="400"> @@ -15,7 +16,7 @@ - + diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index 28e5e274d0..d0405a3af8 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -14,7 +14,7 @@ true - AnyCPU + x86 true full false @@ -164,6 +164,10 @@ {3e908f67-5543-4879-a1dc-08eace79b3cd} Avalonia.Direct2D1 + + {cbc4ff2f-92d4-420b-be21-9fe0b930b04e} + Avalonia.Win32.Interop + {811a76cf-1cf6-440f-963b-bbe31bd72a82} Avalonia.Win32 diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index b8d54fa67b..45b4f8eaa1 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -39,6 +39,8 @@ namespace Avalonia.Controls.Embedding } } + public Size MeasureBase(Size availableSize) => base.MeasureOverride(availableSize); + protected override Size MeasureOverride(Size availableSize) { var cs = PlatformImpl?.ClientSize ?? default(Size); diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj new file mode 100644 index 0000000000..ae61fc697f --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj @@ -0,0 +1,101 @@ + + + + + Debug + AnyCPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} + Library + Properties + Avalonia.Win32.Interop + Avalonia.Win32.Interop + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + UnmanagedMethods.cs + + + + + + + + + + + + {d211e587-d8bc-45b9-95a4-f297c8fa5200} + Avalonia.Animation + + + {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + Avalonia.Base + + + {d2221c82-4a25-4583-9b43-d791e3f6820c} + Avalonia.Controls + + + {62024b2d-53eb-4638-b26b-85eeaa54866e} + Avalonia.Input + + + {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + Avalonia.Interactivity + + + {42472427-4774-4c81-8aff-9f27b8e31721} + Avalonia.Layout + + + {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} + Avalonia.Styling + + + {eb582467-6abb-43a1-b052-e981ba910e3a} + Avalonia.Visuals + + + {fb05ac90-89ba-4f2f-a924-f37875fb547c} + Avalonia.Cairo + + + {811a76cf-1cf6-440f-963b-bbe31bd72a82} + Avalonia.Win32 + + + + \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32.Interop/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Win32.Interop/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..7c0d638381 --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Avalonia.Win32.Interop")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Avalonia.Win32.Interop")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("cbc4ff2f-92d4-420b-be21-9fe0b930b04e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs new file mode 100644 index 0000000000..6ae898ae3d --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace Avalonia.Win32.Interop.Wpf +{ + static class CursorShim + { + public static Cursor FromHCursor(IntPtr hcursor) + { + var field = typeof(Cursor).GetFields(BindingFlags.NonPublic | BindingFlags.Instance) + .FirstOrDefault(f => f.FieldType == typeof(SafeHandle)); + if (field == null) + return null; + var rv = (Cursor) FormatterServices.GetUninitializedObject(typeof(Cursor)); + field.SetValue(rv, new SafeHandleShim(hcursor)); + return rv; + } + + class SafeHandleShim : SafeHandle + { + public SafeHandleShim(IntPtr hcursor) : base(new IntPtr(-1), false) + { + this.handle = hcursor; + } + + protected override bool ReleaseHandle() => true; + + public override bool IsInvalid => false; + } + } +} diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Helpers.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/Helpers.cs new file mode 100644 index 0000000000..9c1f39e86b --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/Helpers.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Win32.Interop.Wpf +{ + static class Helpers + { + public static System.Windows.Point ToWpfPoint(this Point pt) => new System.Windows.Point(pt.X, pt.Y); + public static Point ToAvaloniaPoint(this System.Windows.Point pt) => new Point(pt.X, pt.Y); + public static System.Windows.Size ToWpfSize(this Size pt) => new System.Windows.Size(pt.Width, pt.Height); + public static Size ToAvaloniaSize(this System.Windows.Size pt) => new Size(pt.Width, pt.Height); + } +} diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs new file mode 100644 index 0000000000..2d350fbe30 --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace Avalonia.Win32.Interop.Wpf +{ + public class WpfAvaloniaHost : FrameworkElement, IDisposable + { + private WpfTopLevelImpl _impl = new WpfTopLevelImpl(); + private readonly SynchronizationContext _sync; + public WpfAvaloniaHost() + { + _sync = SynchronizationContext.Current; + _impl.ControlRoot.Prepare(); + _impl.Visibility = Visibility.Visible; + AddLogicalChild(_impl); + AddVisualChild(_impl); + } + + + public object Content + { + get => _impl.ControlRoot.Content; + set => _impl.ControlRoot.Content = value; + } + + //Separate class is needed to prevent accidential resurrection + class Disposer + { + private readonly WpfTopLevelImpl _impl; + + public Disposer(WpfTopLevelImpl impl) + { + _impl = impl; + } + + public void Callback(object state) + { + _impl.Dispose(); + } + } + + protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) + => _impl.ControlRoot.MeasureBase(constraint.ToAvaloniaSize()).ToWpfSize(); + + protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize) + { + _impl.Arrange(new System.Windows.Rect(arrangeSize)); + return arrangeSize; + } + + protected override int VisualChildrenCount => 1; + protected override System.Windows.Media.Visual GetVisualChild(int index) => _impl; + + ~WpfAvaloniaHost() + { + if (_impl != null) + _sync.Post(new Disposer(_impl).Callback, null); + } + + public void Dispose() + { + if (_impl != null) + { + RemoveVisualChild(_impl); + RemoveLogicalChild(_impl); + _impl.Dispose(); + _impl = null; + GC.SuppressFinalize(this); + } + } + } +} diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs new file mode 100644 index 0000000000..4aad80f8a5 --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs @@ -0,0 +1,30 @@ +using System; +using Avalonia.Controls.Embedding; +using Avalonia.Input; +using Avalonia.VisualTree; + +namespace Avalonia.Win32.Interop.Wpf +{ + class WpfMouseDevice : MouseDevice + { + private readonly WpfTopLevelImpl _impl; + + public WpfMouseDevice(WpfTopLevelImpl impl) + { + _impl = impl; + } + + public override void Capture(IInputElement control) + { + if (control == null) + { + System.Windows.Input.Mouse.Capture(null); + } + else if ((control.GetVisualRoot() as EmbeddableControlRoot)?.PlatformImpl != _impl) + throw new ArgumentException("Visual belongs to unknown toplevel"); + else + System.Windows.Input.Mouse.Capture(_impl); + base.Capture(control); + } + } +} \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs new file mode 100644 index 0000000000..56dc26992e --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using Avalonia.Controls.Embedding; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Layout; +using Avalonia.Platform; +using Key = Avalonia.Input.Key; +using KeyEventArgs = System.Windows.Input.KeyEventArgs; +using MouseButton = System.Windows.Input.MouseButton; + +namespace Avalonia.Win32.Interop.Wpf +{ + class WpfTopLevelImpl : FrameworkElement, IEmbeddableWindowImpl + { + private HwndSource _currentHwndSource; + private readonly HwndSourceHook _hook; + private readonly IEmbeddableWindowImpl _ttl; + private IInputRoot _inputRoot; + private readonly IEnumerable _surfaces; + private readonly IMouseDevice _mouse; + private readonly IKeyboardDevice _keyboard; + private Size _finalSize; + + public EmbeddableControlRoot ControlRoot { get; } + internal ImageSource ImageSource { get; set; } + + public WpfTopLevelImpl() + { + PresentationSource.AddSourceChangedHandler(this, OnSourceChanged); + _hook = WndProc; + _ttl = this; + _surfaces = new object[] {new WritableBitmapSurface(this)}; + _mouse = new WpfMouseDevice(this); + _keyboard = AvaloniaLocator.Current.GetService(); + + ControlRoot = new EmbeddableControlRoot(this); + SnapsToDevicePixels = true; + Focusable = true; + } + + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled) + { + if (msg == (int)UnmanagedMethods.WindowsMessage.WM_DPICHANGED) + _ttl.ScalingChanged?.Invoke(_ttl.Scaling); + return IntPtr.Zero; + } + + private void OnSourceChanged(object sender, SourceChangedEventArgs e) + { + _currentHwndSource?.RemoveHook(_hook); + _currentHwndSource = e.NewSource as HwndSource; + _currentHwndSource?.AddHook(_hook); + _ttl.ScalingChanged?.Invoke(_ttl.Scaling); + } + + public void Dispose() => _ttl.Closed?.Invoke(); + + Size ITopLevelImpl.ClientSize => _finalSize; + IMouseDevice ITopLevelImpl.MouseDevice => _mouse; + + double ITopLevelImpl.Scaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1; + + IEnumerable ITopLevelImpl.Surfaces => _surfaces; + + protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize) + { + _finalSize = finalSize.ToAvaloniaSize(); + _ttl.Resized?.Invoke(finalSize.ToAvaloniaSize()); + return base.ArrangeOverride(finalSize); + } + + protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) + => ControlRoot.MeasureBase(availableSize.ToAvaloniaSize()).ToWpfSize(); + + protected override void OnRender(DrawingContext drawingContext) + { + _ttl.Paint?.Invoke(new Rect(0, 0, ActualWidth, ActualHeight)); + if (ImageSource != null) + drawingContext.DrawImage(ImageSource, new System.Windows.Rect(0, 0, ActualWidth, ActualHeight)); + } + + void ITopLevelImpl.Invalidate(Rect rect) => InvalidateVisual(); + + void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot; + + Point ITopLevelImpl.PointToClient(Point point) => PointFromScreen(point.ToWpfPoint()).ToAvaloniaPoint(); + + Point ITopLevelImpl.PointToScreen(Point point) => PointToScreen(point.ToWpfPoint()).ToAvaloniaPoint(); + + protected override void OnLostFocus(RoutedEventArgs e) => LostFocus?.Invoke(); + + + InputModifiers GetModifiers() + { + var state = Keyboard.Modifiers; + var rv = default(InputModifiers); + if (state.HasFlag(ModifierKeys.Windows)) + rv |= InputModifiers.Windows; + if (state.HasFlag(ModifierKeys.Alt)) + rv |= InputModifiers.Alt; + if (state.HasFlag(ModifierKeys.Control)) + rv |= InputModifiers.Control; + if (state.HasFlag(ModifierKeys.Shift)) + rv |= InputModifiers.Shift; + //TODO: mouse modifiers + + + return rv; + } + + void MouseEvent(RawMouseEventType type, MouseEventArgs e) + => _ttl.Input?.Invoke(new RawMouseEventArgs(_mouse, (uint)e.Timestamp, _inputRoot, type, + e.GetPosition(this).ToAvaloniaPoint(), GetModifiers())); + + protected override void OnMouseDown(MouseButtonEventArgs e) + { + RawMouseEventType type; + if(e.ChangedButton == MouseButton.Left) + type = RawMouseEventType.LeftButtonDown; + else if (e.ChangedButton == MouseButton.Middle) + type = RawMouseEventType.MiddleButtonDown; + else if (e.ChangedButton == MouseButton.Right) + type = RawMouseEventType.RightButtonDown; + else + return; + MouseEvent(type, e); + } + + protected override void OnMouseUp(MouseButtonEventArgs e) + { + RawMouseEventType type; + if (e.ChangedButton == MouseButton.Left) + type = RawMouseEventType.LeftButtonUp; + else if (e.ChangedButton == MouseButton.Middle) + type = RawMouseEventType.MiddleButtonUp; + else if (e.ChangedButton == MouseButton.Right) + type = RawMouseEventType.RightButtonUp; + else + return; + MouseEvent(type, e); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + MouseEvent(RawMouseEventType.Move, e); + } + + protected override void OnMouseWheel(MouseWheelEventArgs e) => + _ttl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, (uint) e.Timestamp, _inputRoot, + e.GetPosition(this).ToAvaloniaPoint(), new Vector(0, e.Delta), GetModifiers())); + + protected override void OnKeyDown(KeyEventArgs e) + => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, RawKeyEventType.KeyDown, + (Key) e.Key, + GetModifiers())); + + protected override void OnKeyUp(KeyEventArgs e) + => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, RawKeyEventType.KeyUp, + (Key)e.Key, + GetModifiers())); + + protected override void OnTextInput(TextCompositionEventArgs e) + => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, e.Text)); + + void ITopLevelImpl.SetCursor(IPlatformHandle cursor) + { + if (cursor == null) + Cursor = Cursors.Arrow; + else if (cursor.HandleDescriptor == "HCURSOR") + Cursor = CursorShim.FromHCursor(cursor.Handle); + } + + Action ITopLevelImpl.Input { get; set; } //TODO + Action ITopLevelImpl.Paint { get; set; } + Action ITopLevelImpl.Resized { get; set; } + Action ITopLevelImpl.ScalingChanged { get; set; } + Action ITopLevelImpl.Closed { get; set; } + public new event Action LostFocus; + + } +} diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs new file mode 100644 index 0000000000..1dd1cb983a --- /dev/null +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Platform; +using PixelFormat = Avalonia.Platform.PixelFormat; + +namespace Avalonia.Win32.Interop.Wpf +{ + class WritableBitmapSurface : IFramebufferPlatformSurface + { + private readonly WpfTopLevelImpl _impl; + private WriteableBitmap _bitmap; + public WritableBitmapSurface(WpfTopLevelImpl impl) + { + _impl = impl; + } + + public ILockedFramebuffer Lock() + { + var scale = GetScaling(); + var size = new Size(_impl.ActualWidth * scale.X, _impl.ActualHeight * scale.Y); + var dpi = scale * 96; + if (_bitmap == null || _bitmap.PixelWidth != (int) size.Width || _bitmap.PixelHeight != (int) size.Height) + { + _bitmap = new WriteableBitmap((int) size.Width, (int) size.Height, dpi.X, dpi.Y, + PixelFormats.Bgra32, null); + } + return new LockedFramebuffer(_impl, _bitmap, dpi); + } + + internal class LockedFramebuffer : ILockedFramebuffer + { + private readonly WpfTopLevelImpl _impl; + private readonly WriteableBitmap _bitmap; + + public LockedFramebuffer(WpfTopLevelImpl impl, WriteableBitmap bitmap, Vector dpi) + { + _impl = impl; + _bitmap = bitmap; + Dpi = dpi; + _bitmap.Lock(); + } + + public void Dispose() + { + _bitmap.AddDirtyRect(new Int32Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight)); + _bitmap.Unlock(); + /* + using (var fileStream = new FileStream("c:\\tools\\wat.png", FileMode.Create)) + { + BitmapEncoder encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(_bitmap)); + encoder.Save(fileStream); + }*/ + _impl.ImageSource = _bitmap; + } + + public IntPtr Address => _bitmap.BackBuffer; + public int Width => _bitmap.PixelWidth; + public int Height => _bitmap.PixelHeight; + public int RowBytes => _bitmap.BackBufferStride; + public Vector Dpi { get; } + public PixelFormat Format => PixelFormat.Bgra8888; + } + + Vector GetScaling() + { + var src = PresentationSource.FromVisual(_impl)?.CompositionTarget; + if (src == null) + return new Vector(1, 1); + return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22); + } + } +} From b4d43be327d639cb5419046e8afc2122c2a43772 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 5 Jun 2017 23:37:42 +0300 Subject: [PATCH 02/15] DevTools now can be attached to any toplevel --- .../WindowsInteropTest/EmbedToWpfDemo.xaml.cs | 9 ++++++++- src/Avalonia.Diagnostics/DevTools.xaml.cs | 18 +++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index e60c9ced0a..eebd18dece 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -11,6 +11,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Avalonia; using Avalonia.Controls; using ControlCatalog; using Window = System.Windows.Window; @@ -25,7 +26,13 @@ namespace WindowsInteropTest public EmbedToWpfDemo() { InitializeComponent(); - Host.Content = new MainView(); + var view = new MainView(); + Host.Content = view; + view.AttachedToVisualTree += delegate + { + view.AttachDevTools(); + }; + } } } diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index b735372b59..e48cdf5681 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -16,9 +16,9 @@ namespace Avalonia { public static class WindowExtensions { - public static void AttachDevTools(this Window window) + public static void AttachDevTools(this Control control) { - Avalonia.Diagnostics.DevTools.Attach(window); + Avalonia.Diagnostics.DevTools.Attach((TopLevel)control.GetVisualRoot()); } } } @@ -27,7 +27,7 @@ namespace Avalonia.Diagnostics { public class DevTools : UserControl { - private static Dictionary s_open = new Dictionary(); + private static Dictionary s_open = new Dictionary(); private IDisposable _keySubscription; public DevTools(IControl root) @@ -43,9 +43,9 @@ namespace Avalonia.Diagnostics public IControl Root { get; } - public static IDisposable Attach(Window window) + public static IDisposable Attach(TopLevel control) { - return window.AddHandler( + return control.AddHandler( KeyDownEvent, WindowPreviewKeyDown, RoutingStrategies.Tunnel); @@ -55,16 +55,16 @@ namespace Avalonia.Diagnostics { if (e.Key == Key.F12) { - var window = (Window)sender; + var control = (TopLevel)sender; var devToolsWindow = default(Window); - if (s_open.TryGetValue(window, out devToolsWindow)) + if (s_open.TryGetValue(control, out devToolsWindow)) { devToolsWindow.Activate(); } else { - var devTools = new DevTools(window); + var devTools = new DevTools(control); devToolsWindow = new Window { @@ -78,7 +78,7 @@ namespace Avalonia.Diagnostics }; devToolsWindow.Closed += devTools.DevToolsClosed; - s_open.Add((Window)sender, devToolsWindow); + s_open.Add(control, devToolsWindow); devToolsWindow.Show(); } } From 3126901721ea3f20c4c2c784cbc9f4dcf1e8ceea Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 7 Jun 2017 17:36:01 +0300 Subject: [PATCH 03/15] WPF embedding improvements --- .../WindowsInteropTest/EmbedToWpfDemo.xaml | 11 ++++++ .../WindowsInteropTest/EmbedToWpfDemo.xaml.cs | 5 +++ src/Avalonia.Layout/Layoutable.cs | 2 +- .../Avalonia.Win32.Interop.csproj | 12 +++++++ .../Wpf/WpfAvaloniaHost.cs | 35 +++++++++++++++---- .../Wpf/WpfTopLevelImpl.cs | 11 ++++++ 6 files changed, 69 insertions(+), 7 deletions(-) diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml index 5a346d4e8d..1d8dc32a69 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml @@ -1,6 +1,7 @@  + + + + + + + + + + diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index eebd18dece..636d89dc70 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -32,6 +32,11 @@ namespace WindowsInteropTest { view.AttachDevTools(); }; + var btn = (Avalonia.Controls.Button) RightBtn.Content; + btn.Click += delegate + { + btn.Content += "!"; + }; } } diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 20050058bf..3f6d789877 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -370,7 +370,7 @@ namespace Avalonia.Layout /// /// Invalidates the measurement of the control and queues a new layout pass. /// - public void InvalidateMeasure() + public virtual void InvalidateMeasure() { if (IsMeasureValid) { diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj index ae61fc697f..e2d764c62c 100644 --- a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj +++ b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj @@ -68,6 +68,10 @@ {d2221c82-4a25-4583-9b43-d791e3f6820c} Avalonia.Controls + + {4a1abb09-9047-4bd5-a4ad-a055e52c5ee0} + Avalonia.DotNetFrameworkRuntime + {62024b2d-53eb-4638-b26b-85eeaa54866e} Avalonia.Input @@ -92,6 +96,14 @@ {fb05ac90-89ba-4f2f-a924-f37875fb547c} Avalonia.Cairo + + {3e53a01a-b331-47f3-b828-4a5717e77a24} + Avalonia.Markup.Xaml + + + {6417e941-21bc-467b-a771-0de389353ce6} + Avalonia.Markup + {811a76cf-1cf6-440f-963b-bbe31bd72a82} Avalonia.Win32 diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs index 2d350fbe30..4f85a326f0 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs @@ -1,29 +1,35 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Markup; using System.Windows.Media; +using Avalonia.Markup.Xaml.Styling; +using Avalonia.Platform; +using Avalonia.Styling; namespace Avalonia.Win32.Interop.Wpf { - public class WpfAvaloniaHost : FrameworkElement, IDisposable + [ContentProperty("Content")] + public class WpfAvaloniaHost : FrameworkElement, IDisposable, IAddChild { - private WpfTopLevelImpl _impl = new WpfTopLevelImpl(); + private WpfTopLevelImpl _impl; private readonly SynchronizationContext _sync; public WpfAvaloniaHost() { _sync = SynchronizationContext.Current; + _impl = new WpfTopLevelImpl(); _impl.ControlRoot.Prepare(); _impl.Visibility = Visibility.Visible; AddLogicalChild(_impl); AddVisualChild(_impl); } - public object Content { @@ -47,9 +53,13 @@ namespace Avalonia.Win32.Interop.Wpf } } - protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) - => _impl.ControlRoot.MeasureBase(constraint.ToAvaloniaSize()).ToWpfSize(); - + protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) + { + _impl.InvalidateMeasure(); + _impl.Measure(constraint); + return _impl.DesiredSize; + } + protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize) { _impl.Arrange(new System.Windows.Rect(arrangeSize)); @@ -76,5 +86,18 @@ namespace Avalonia.Win32.Interop.Wpf GC.SuppressFinalize(this); } } + + void IAddChild.AddChild(object value) + { + if (Content == null) + Content = value; + else + throw new InvalidOperationException(); + } + + void IAddChild.AddText(string text) + { + // + } } } diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 56dc26992e..c82b71d3a5 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -33,6 +33,15 @@ namespace Avalonia.Win32.Interop.Wpf public EmbeddableControlRoot ControlRoot { get; } internal ImageSource ImageSource { get; set; } + public class CustomControlRoot : EmbeddableControlRoot + { + public override void InvalidateMeasure() + { + base.InvalidateMeasure(); + ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); + } + } + public WpfTopLevelImpl() { PresentationSource.AddSourceChangedHandler(this, OnSourceChanged); @@ -158,6 +167,8 @@ namespace Avalonia.Win32.Interop.Wpf _ttl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, (uint) e.Timestamp, _inputRoot, e.GetPosition(this).ToAvaloniaPoint(), new Vector(0, e.Delta), GetModifiers())); + protected override void OnMouseLeave(MouseEventArgs e) => MouseEvent(RawMouseEventType.LeaveWindow, e); + protected override void OnKeyDown(KeyEventArgs e) => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, RawKeyEventType.KeyDown, (Key) e.Key, From d06c9e04eda93b0a47793ed667f7d72ad2cb125a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 7 Jun 2017 18:43:47 +0300 Subject: [PATCH 04/15] WPF integration improvements --- .../Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs | 1 + .../Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs index 4f85a326f0..e36b53199a 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs @@ -29,6 +29,7 @@ namespace Avalonia.Win32.Interop.Wpf _impl.Visibility = Visibility.Visible; AddLogicalChild(_impl); AddVisualChild(_impl); + SnapsToDevicePixels = true; } public object Content diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index c82b71d3a5..7be6b7e800 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -54,6 +54,10 @@ namespace Avalonia.Win32.Interop.Wpf ControlRoot = new EmbeddableControlRoot(this); SnapsToDevicePixels = true; Focusable = true; + DataContextChanged += delegate + { + ControlRoot.DataContext = DataContext; + }; } private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled) @@ -80,15 +84,18 @@ namespace Avalonia.Win32.Interop.Wpf IEnumerable ITopLevelImpl.Surfaces => _surfaces; + private Size _previousSize; protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize) { _finalSize = finalSize.ToAvaloniaSize(); + if (_finalSize == _previousSize) + return finalSize; + _previousSize = _finalSize; _ttl.Resized?.Invoke(finalSize.ToAvaloniaSize()); return base.ArrangeOverride(finalSize); } - protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) - => ControlRoot.MeasureBase(availableSize.ToAvaloniaSize()).ToWpfSize(); + protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) => ControlRoot.MeasureBase(availableSize.ToAvaloniaSize()).ToWpfSize(); protected override void OnRender(DrawingContext drawingContext) { From df13ab2ecb1b889a4acc759d085bd17ea810f902 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 7 Jun 2017 19:09:17 +0300 Subject: [PATCH 05/15] [TEMP] Call measure directly --- .../Embedding/EmbeddableControlRoot.cs | 12 ++++++------ .../Wpf/WpfTopLevelImpl.cs | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index 45b4f8eaa1..179dccaf76 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -22,6 +22,8 @@ namespace Avalonia.Controls.Embedding [CanBeNull] public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; + protected bool EnforceClientSize { get; set; } = true; + public void Prepare() { EnsureInitialized(); @@ -38,14 +40,12 @@ namespace Avalonia.Controls.Embedding init.EndInit(); } } - - public Size MeasureBase(Size availableSize) => base.MeasureOverride(availableSize); - + protected override Size MeasureOverride(Size availableSize) { - var cs = PlatformImpl?.ClientSize ?? default(Size); - base.MeasureOverride(cs); - return cs; + if (EnforceClientSize) + availableSize = PlatformImpl?.ClientSize ?? default(Size); + return base.MeasureOverride(availableSize); } private readonly NameScope _nameScope = new NameScope(); diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 7be6b7e800..b74358ec4d 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -35,10 +35,16 @@ namespace Avalonia.Win32.Interop.Wpf public class CustomControlRoot : EmbeddableControlRoot { + public CustomControlRoot() + { + EnforceClientSize = false; + + } + public override void InvalidateMeasure() { - base.InvalidateMeasure(); ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); + base.InvalidateMeasure(); } } @@ -95,10 +101,16 @@ namespace Avalonia.Win32.Interop.Wpf return base.ArrangeOverride(finalSize); } - protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) => ControlRoot.MeasureBase(availableSize.ToAvaloniaSize()).ToWpfSize(); + protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) + { + ControlRoot.Measure(availableSize.ToAvaloniaSize()); + return ControlRoot.DesiredSize.ToWpfSize(); + } protected override void OnRender(DrawingContext drawingContext) { + if(ActualHeight == 0 || ActualWidth == 0) + return; _ttl.Paint?.Invoke(new Rect(0, 0, ActualWidth, ActualHeight)); if (ImageSource != null) drawingContext.DrawImage(ImageSource, new System.Windows.Rect(0, 0, ActualWidth, ActualHeight)); From b84e9e1123943013cf85412b89e8c67911fcb99a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 7 Jun 2017 19:15:42 +0300 Subject: [PATCH 06/15] Use correct control root --- src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index b74358ec4d..927c975399 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -35,15 +35,14 @@ namespace Avalonia.Win32.Interop.Wpf public class CustomControlRoot : EmbeddableControlRoot { - public CustomControlRoot() + public CustomControlRoot(WpfTopLevelImpl impl) : base(impl) { EnforceClientSize = false; - } public override void InvalidateMeasure() { - ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); + ((FrameworkElement) PlatformImpl)?.InvalidateMeasure(); base.InvalidateMeasure(); } } @@ -57,7 +56,7 @@ namespace Avalonia.Win32.Interop.Wpf _mouse = new WpfMouseDevice(this); _keyboard = AvaloniaLocator.Current.GetService(); - ControlRoot = new EmbeddableControlRoot(this); + ControlRoot = new CustomControlRoot(this); SnapsToDevicePixels = true; Focusable = true; DataContextChanged += delegate From 317b0f7034ebfa00e69371a8e2d7d5378b169c3b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 7 Jun 2017 19:34:04 +0300 Subject: [PATCH 07/15] testtesttest --- src/Avalonia.Layout/Layoutable.cs | 2 +- .../Wpf/WpfTopLevelImpl.cs | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 3f6d789877..778a869003 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -399,7 +399,7 @@ namespace Avalonia.Layout } /// - void ILayoutable.ChildDesiredSizeChanged(ILayoutable control) + public virtual void ChildDesiredSizeChanged(ILayoutable control) { if (!_measuring) { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 927c975399..1aa09853ab 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -40,10 +40,23 @@ namespace Avalonia.Win32.Interop.Wpf EnforceClientSize = false; } - public override void InvalidateMeasure() + public override void ChildDesiredSizeChanged(ILayoutable control) { - ((FrameworkElement) PlatformImpl)?.InvalidateMeasure(); - base.InvalidateMeasure(); + ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); + base.ChildDesiredSizeChanged(control); + } + + protected override void HandleResized(Size clientSize) + { + ClientSize = clientSize; + LayoutManager.Instance.ExecuteLayoutPass(); + Renderer?.Resized(clientSize); + } + + protected override void ArrangeCore(Rect finalRect) + { + base.ArrangeOverride(finalRect.Size); + Bounds = finalRect; } } From 09c9d7b7d516d30192e1fdefe45125023499cac0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 7 Jun 2017 20:03:27 +0300 Subject: [PATCH 08/15] Layout integration seems to be working now --- src/Avalonia.Layout/LayoutManager.cs | 2 +- src/Avalonia.Layout/Layoutable.cs | 2 +- .../Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs | 10 ++-------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index b7b83bf852..93c2227cab 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -151,7 +151,7 @@ namespace Avalonia.Layout if (root != null) { - root.Arrange(new Rect(root.DesiredSize)); + root.Arrange(new Rect(root.ClientSize)); } else if (parent != null) { diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 778a869003..3f6d789877 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -399,7 +399,7 @@ namespace Avalonia.Layout } /// - public virtual void ChildDesiredSizeChanged(ILayoutable control) + void ILayoutable.ChildDesiredSizeChanged(ILayoutable control) { if (!_measuring) { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 1aa09853ab..c990bb708e 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -40,10 +40,10 @@ namespace Avalonia.Win32.Interop.Wpf EnforceClientSize = false; } - public override void ChildDesiredSizeChanged(ILayoutable control) + public override void InvalidateMeasure() { ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); - base.ChildDesiredSizeChanged(control); + base.InvalidateMeasure(); } protected override void HandleResized(Size clientSize) @@ -52,12 +52,6 @@ namespace Avalonia.Win32.Interop.Wpf LayoutManager.Instance.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } - - protected override void ArrangeCore(Rect finalRect) - { - base.ArrangeOverride(finalRect.Size); - Bounds = finalRect; - } } public WpfTopLevelImpl() From a2c46aceea16a9f9aced2292f0491999fa4fa5ec Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 7 Jun 2017 20:08:39 +0300 Subject: [PATCH 09/15] Removed old wpf control host and added new nuget package --- packages.cake | 15 ++++++ .../Embedding/WpfAvaloniaControlHost.cs | 52 ------------------- 2 files changed, 15 insertions(+), 52 deletions(-) delete mode 100644 src/Windows/Avalonia.Win32/Embedding/WpfAvaloniaControlHost.cs diff --git a/packages.cake b/packages.cake index 1e8e356694..bc1aeef416 100644 --- a/packages.cake +++ b/packages.cake @@ -465,6 +465,21 @@ public class Packages BasePath = context.Directory("./"), OutputDirectory = parameters.NugetRoot }, + new NuGetPackSettings() + { + Id = "Avalonia.Win32.Interoperability", + Dependencies = new [] + { + new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version }, + }, + Files = new [] + { + new NuSpecContent { Source = "Avalonia.Win32.Interop/bin/" + parameters.DirSuffix + "/Avalonia.Win32.Interop.dll", Target = "lib/net45" } + }, + BasePath = context.Directory("./src/Windows"), + OutputDirectory = parameters.NugetRoot + }, /////////////////////////////////////////////////////////////////////////////// // Avalonia.LinuxFramebuffer /////////////////////////////////////////////////////////////////////////////// diff --git a/src/Windows/Avalonia.Win32/Embedding/WpfAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32/Embedding/WpfAvaloniaControlHost.cs deleted file mode 100644 index 663f6906ed..0000000000 --- a/src/Windows/Avalonia.Win32/Embedding/WpfAvaloniaControlHost.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Forms.Integration; -using System.Windows.Interop; -using Avalonia.Controls; -using Avalonia.Win32.Interop; - -namespace Avalonia.Win32.Embedding -{ - public class WpfAvaloniaControlHost : HwndHost - { - private WinFormsAvaloniaControlHost _host; - private Avalonia.Controls.Control _content; - - public Avalonia.Controls.Control Content - { - get { return _content; } - set - { - if (_host != null) - _host.Content = value; - _content = value; - - } - } - - void DestroyHost() - { - _host?.Dispose(); - _host = null; - } - - protected override HandleRef BuildWindowCore(HandleRef hwndParent) - { - DestroyHost(); - _host = new WinFormsAvaloniaControlHost {Content = _content}; - UnmanagedMethods.SetParent(_host.Handle, hwndParent.Handle); - return new HandleRef(this, _host.Handle); - } - - protected override void DestroyWindowCore(HandleRef hwnd) - { - DestroyHost(); - } - } -} From 1d44d3f7afde95c613164b1f56ac6a95b2fa48d0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 8 Jun 2017 22:07:28 +0300 Subject: [PATCH 10/15] Removed reference to the old host --- src/Windows/Avalonia.Win32/Avalonia.Win32.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index b134f4666e..198bb7ce0d 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -57,7 +57,6 @@ Component - From abf866cf610025a6b8431e5ba2cdc527efcd4a71 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 7 Jul 2017 23:46:58 +0300 Subject: [PATCH 11/15] Fixes to get it working again after merge --- .../interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs | 3 ++- src/Avalonia.Diagnostics/DevTools.xaml.cs | 4 +--- src/Avalonia.Layout/IEmbeddedLayoutRoot.cs | 10 ++++++++++ src/Avalonia.Layout/LayoutManager.cs | 10 ++++------ .../Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs | 4 +++- 5 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 src/Avalonia.Layout/IEmbeddedLayoutRoot.cs diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index 636d89dc70..5ca2768d9e 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -27,11 +27,12 @@ namespace WindowsInteropTest { InitializeComponent(); var view = new MainView(); - Host.Content = view; + view.AttachedToVisualTree += delegate { view.AttachDevTools(); }; + Host.Content = view; var btn = (Avalonia.Controls.Button) RightBtn.Content; btn.Click += delegate { diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index e48cdf5681..85c3cfddd8 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -88,9 +88,7 @@ namespace Avalonia.Diagnostics { var devToolsWindow = (Window)sender; var devTools = (DevTools)devToolsWindow.Content; - var window = (Window)devTools.Root; - - s_open.Remove(window); + s_open.Remove((TopLevel)devTools.Root); _keySubscription.Dispose(); devToolsWindow.Closed -= DevToolsClosed; } diff --git a/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs b/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs new file mode 100644 index 0000000000..24f0ccd82e --- /dev/null +++ b/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs @@ -0,0 +1,10 @@ +namespace Avalonia.Layout +{ + /// + /// A special layout root with enforced size for Arrange pass + /// + public interface IEmbeddedLayoutRoot : ILayoutRoot + { + Size AllocatedSize { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index 0ff417e791..108922b5b9 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -186,14 +186,12 @@ namespace Avalonia.Layout if (!control.IsArrangeValid && control.IsAttachedToVisualTree) { - if (control is ILayoutRoot root) - { - root.Arrange(new Rect(root.ClientSize)); - } + if (control is IEmbeddedLayoutRoot embeddedRoot) + control.Arrange(new Rect(embeddedRoot.AllocatedSize)); + else if (control is ILayoutRoot root) + control.Arrange(new Rect(root.DesiredSize)); else - { control.Arrange(control.PreviousArrange.Value); - } } } diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index c990bb708e..229e330a33 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -33,7 +33,7 @@ namespace Avalonia.Win32.Interop.Wpf public EmbeddableControlRoot ControlRoot { get; } internal ImageSource ImageSource { get; set; } - public class CustomControlRoot : EmbeddableControlRoot + public class CustomControlRoot : EmbeddableControlRoot, IEmbeddedLayoutRoot { public CustomControlRoot(WpfTopLevelImpl impl) : base(impl) { @@ -52,6 +52,8 @@ namespace Avalonia.Win32.Interop.Wpf LayoutManager.Instance.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } + + public Size AllocatedSize => ClientSize; } public WpfTopLevelImpl() From cc354da723057dbcf911e26eb66ffa8b9086fee6 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 10 Jul 2017 17:13:52 +0300 Subject: [PATCH 12/15] Focus embedded toplevel on click --- src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 229e330a33..094929deda 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -169,6 +169,7 @@ namespace Avalonia.Win32.Interop.Wpf else return; MouseEvent(type, e); + Focus(); } protected override void OnMouseUp(MouseButtonEventArgs e) @@ -183,6 +184,7 @@ namespace Avalonia.Win32.Interop.Wpf else return; MouseEvent(type, e); + Focus(); } protected override void OnMouseMove(MouseEventArgs e) From 35f353c2db2cd3d93bb6dbe325ccdc8556f3d0c9 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 12 Jul 2017 19:31:02 +0300 Subject: [PATCH 13/15] Use OnMeasureInvalidated instead of virtual InvalidateMeasure --- src/Avalonia.Layout/Layoutable.cs | 11 ++++++++++- .../Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs | 3 +-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index a08ab77d70..523c720e2f 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -367,10 +367,18 @@ namespace Avalonia.Layout } } + + /// + /// Called by InvalidateMeasure + /// + protected virtual void OnMeasureInvalidated() + { + } + /// /// Invalidates the measurement of the control and queues a new layout pass. /// - public virtual void InvalidateMeasure() + public void InvalidateMeasure() { if (IsMeasureValid) { @@ -384,6 +392,7 @@ namespace Avalonia.Layout LayoutManager.Instance?.InvalidateMeasure(this); InvalidateVisual(); } + OnMeasureInvalidated(); } } diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 094929deda..0620c6cc57 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -40,10 +40,9 @@ namespace Avalonia.Win32.Interop.Wpf EnforceClientSize = false; } - public override void InvalidateMeasure() + protected override void OnMeasureInvalidated() { ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); - base.InvalidateMeasure(); } protected override void HandleResized(Size clientSize) From 76bc7aaafb8c76ab7612ef98a2fc3f6fd56b27a5 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 12 Jul 2017 19:57:25 +0300 Subject: [PATCH 14/15] Fixed issues from PR comments #1016 --- .../WindowsInteropTest/EmbedToWpfDemo.xaml.cs | 5 +---- src/Avalonia.Diagnostics/DevTools.xaml.cs | 15 ++++++++++++--- .../Avalonia.Win32.Interop.csproj | 2 +- .../Wpf/{Helpers.cs => WpfInteropExtensions.cs} | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) rename src/Windows/Avalonia.Win32.Interop/Wpf/{Helpers.cs => WpfInteropExtensions.cs} (94%) diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index 5ca2768d9e..1a91d67b49 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -28,10 +28,7 @@ namespace WindowsInteropTest InitializeComponent(); var view = new MainView(); - view.AttachedToVisualTree += delegate - { - view.AttachDevTools(); - }; + view.AttachDevToolsToTopLevelOnVisualTreeAttachment(); Host.Content = view; var btn = (Avalonia.Controls.Button) RightBtn.Content; btn.Click += delegate diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index 85c3cfddd8..06965ece89 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -14,12 +14,21 @@ using Avalonia.VisualTree; namespace Avalonia { - public static class WindowExtensions + public static class DevToolsExtensions { - public static void AttachDevTools(this Control control) + public static void AttachDevTools(this TopLevel control) { - Avalonia.Diagnostics.DevTools.Attach((TopLevel)control.GetVisualRoot()); + Avalonia.Diagnostics.DevTools.Attach(control); } + + public static void AttachDevToolsToTopLevelOnVisualTreeAttachment(this Control control) + { + (control.GetVisualRoot() as TopLevel)?.AttachDevTools(); + control.AttachedToVisualTree += delegate + { + (control.GetVisualRoot() as TopLevel)?.AttachDevTools(); + }; + } } } diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj index e2d764c62c..c5cd2ab64d 100644 --- a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj +++ b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj @@ -49,7 +49,7 @@ - + diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Helpers.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs similarity index 94% rename from src/Windows/Avalonia.Win32.Interop/Wpf/Helpers.cs rename to src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs index 9c1f39e86b..6433ff05e0 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/Helpers.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Avalonia.Win32.Interop.Wpf { - static class Helpers + static class WpfInteropExtensions { public static System.Windows.Point ToWpfPoint(this Point pt) => new System.Windows.Point(pt.X, pt.Y); public static Point ToAvaloniaPoint(this System.Windows.Point pt) => new Point(pt.X, pt.Y); From e5289146d99cff3f8be3a96af5f878fc787c0d73 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 14 Jul 2017 11:20:02 +0300 Subject: [PATCH 15/15] Remove AttachDevToolsToTopLevelOnVisualTreeAttachment since @grokys doesn't like it --- .../interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs | 7 +++++-- src/Avalonia.Diagnostics/DevTools.xaml.cs | 9 --------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index 1a91d67b49..c7a23c22fc 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -13,6 +13,7 @@ using System.Windows.Navigation; using System.Windows.Shapes; using Avalonia; using Avalonia.Controls; +using Avalonia.VisualTree; using ControlCatalog; using Window = System.Windows.Window; @@ -27,8 +28,10 @@ namespace WindowsInteropTest { InitializeComponent(); var view = new MainView(); - - view.AttachDevToolsToTopLevelOnVisualTreeAttachment(); + view.AttachedToVisualTree += delegate + { + ((TopLevel) view.GetVisualRoot()).AttachDevTools(); + }; Host.Content = view; var btn = (Avalonia.Controls.Button) RightBtn.Content; btn.Click += delegate diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index 06965ece89..6593a8cd42 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -20,15 +20,6 @@ namespace Avalonia { Avalonia.Diagnostics.DevTools.Attach(control); } - - public static void AttachDevToolsToTopLevelOnVisualTreeAttachment(this Control control) - { - (control.GetVisualRoot() as TopLevel)?.AttachDevTools(); - control.AttachedToVisualTree += delegate - { - (control.GetVisualRoot() as TopLevel)?.AttachDevTools(); - }; - } } }