committed by
GitHub
30 changed files with 161 additions and 1217 deletions
@ -1,35 +1,14 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Data; |
|||
using System.Drawing; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using System.Windows.Forms; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Rendering; |
|||
using Avalonia.VisualTree; |
|||
using System.Windows.Forms; |
|||
using ControlCatalog; |
|||
|
|||
namespace WindowsInteropTest |
|||
{ |
|||
public partial class EmbedToWinFormsDemo : Form |
|||
{ |
|||
private readonly IRenderer _renderer; |
|||
|
|||
public EmbedToWinFormsDemo() |
|||
{ |
|||
InitializeComponent(); |
|||
avaloniaHost.Content = new MainView(); |
|||
_renderer = ((TopLevel)avaloniaHost.Content.GetVisualRoot()).Renderer; |
|||
_renderer.Start(); |
|||
} |
|||
|
|||
protected override void OnClosed(EventArgs e) |
|||
{ |
|||
_renderer.Stop(); |
|||
base.OnClosed(e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,33 +0,0 @@ |
|||
<Window x:Class="WindowsInteropTest.EmbedToWpfDemo" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:av="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
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"> |
|||
<DockPanel> |
|||
<GroupBox DockPanel.Dock="Left" Header="WPF"> |
|||
<StackPanel> |
|||
<Slider/> |
|||
<Calendar/> |
|||
</StackPanel> |
|||
</GroupBox> |
|||
<GroupBox Header="Avalonia button" DockPanel.Dock="Bottom"> |
|||
<wpf:WpfAvaloniaHost > |
|||
<av:Button Content="Avalonia button"/> |
|||
</wpf:WpfAvaloniaHost> |
|||
</GroupBox> |
|||
<GroupBox Header="AvBtn" DockPanel.Dock="Right"> |
|||
<wpf:WpfAvaloniaHost x:Name="RightBtn"> |
|||
<av:Button Content="Avalonia button 2"/> |
|||
</wpf:WpfAvaloniaHost> |
|||
</GroupBox> |
|||
<GroupBox Header="Avalonia"> |
|||
<wpf:WpfAvaloniaHost x:Name="Host"/> |
|||
</GroupBox> |
|||
</DockPanel> |
|||
</Window> |
|||
@ -1,52 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Input; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Rendering; |
|||
using Avalonia.VisualTree; |
|||
using ControlCatalog; |
|||
using Window = System.Windows.Window; |
|||
|
|||
namespace WindowsInteropTest |
|||
{ |
|||
/// <summary>
|
|||
/// Interaction logic for EmbedToWpfDemo.xaml
|
|||
/// </summary>
|
|||
public partial class EmbedToWpfDemo : Window |
|||
{ |
|||
private IRenderer _renderer; |
|||
public EmbedToWpfDemo() |
|||
{ |
|||
InitializeComponent(); |
|||
var view = new MainView(); |
|||
Host.Content = view; |
|||
var tl = (TopLevel)view.GetVisualRoot(); |
|||
tl.AttachDevTools(); |
|||
_renderer = tl.Renderer; |
|||
_renderer.Start(); |
|||
var btn = (Avalonia.Controls.Button) RightBtn.Content; |
|||
btn.Click += delegate |
|||
{ |
|||
btn.Content += "!"; |
|||
}; |
|||
|
|||
} |
|||
|
|||
protected override void OnClosed(EventArgs e) |
|||
{ |
|||
_renderer.Stop(); |
|||
base.OnClosed(e); |
|||
} |
|||
} |
|||
} |
|||
@ -1,76 +0,0 @@ |
|||
namespace WindowsInteropTest |
|||
{ |
|||
partial class SelectorForm |
|||
{ |
|||
/// <summary>
|
|||
/// Required designer variable.
|
|||
/// </summary>
|
|||
private System.ComponentModel.IContainer components = null; |
|||
|
|||
/// <summary>
|
|||
/// Clean up any resources being used.
|
|||
/// </summary>
|
|||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing && (components != null)) |
|||
{ |
|||
components.Dispose(); |
|||
} |
|||
base.Dispose(disposing); |
|||
} |
|||
|
|||
#region Windows Form Designer generated code
|
|||
|
|||
/// <summary>
|
|||
/// Required method for Designer support - do not modify
|
|||
/// the contents of this method with the code editor.
|
|||
/// </summary>
|
|||
private void InitializeComponent() |
|||
{ |
|||
this.btnEmbedToWinForms = new System.Windows.Forms.Button(); |
|||
this.btnEmbedToWpf = new System.Windows.Forms.Button(); |
|||
this.SuspendLayout(); |
|||
//
|
|||
// btnEmbedToWinForms
|
|||
//
|
|||
this.btnEmbedToWinForms.Location = new System.Drawing.Point(12, 12); |
|||
this.btnEmbedToWinForms.Name = "btnEmbedToWinForms"; |
|||
this.btnEmbedToWinForms.Size = new System.Drawing.Size(201, 86); |
|||
this.btnEmbedToWinForms.TabIndex = 0; |
|||
this.btnEmbedToWinForms.Text = "Embed to WinForms"; |
|||
this.btnEmbedToWinForms.UseVisualStyleBackColor = true; |
|||
this.btnEmbedToWinForms.Click += new System.EventHandler(this.btnEmbedToWinForms_Click); |
|||
//
|
|||
// btnEmbedToWpf
|
|||
//
|
|||
this.btnEmbedToWpf.Location = new System.Drawing.Point(219, 12); |
|||
this.btnEmbedToWpf.Name = "btnEmbedToWpf"; |
|||
this.btnEmbedToWpf.Size = new System.Drawing.Size(201, 86); |
|||
this.btnEmbedToWpf.TabIndex = 1; |
|||
this.btnEmbedToWpf.Text = "Embed to WPF"; |
|||
this.btnEmbedToWpf.UseVisualStyleBackColor = true; |
|||
this.btnEmbedToWpf.Click += new System.EventHandler(this.btnEmbedToWpf_Click); |
|||
//
|
|||
// SelectorForm
|
|||
//
|
|||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |
|||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |
|||
this.ClientSize = new System.Drawing.Size(432, 284); |
|||
this.Controls.Add(this.btnEmbedToWpf); |
|||
this.Controls.Add(this.btnEmbedToWinForms); |
|||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; |
|||
this.MaximizeBox = false; |
|||
this.Name = "SelectorForm"; |
|||
this.Text = "Interop"; |
|||
this.ResumeLayout(false); |
|||
|
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
private System.Windows.Forms.Button btnEmbedToWinForms; |
|||
private System.Windows.Forms.Button btnEmbedToWpf; |
|||
} |
|||
} |
|||
|
|||
@ -1,30 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Data; |
|||
using System.Drawing; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using System.Windows.Forms; |
|||
|
|||
namespace WindowsInteropTest |
|||
{ |
|||
public partial class SelectorForm : Form |
|||
{ |
|||
public SelectorForm() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
private void btnEmbedToWinForms_Click(object sender, EventArgs e) |
|||
{ |
|||
new EmbedToWinFormsDemo().ShowDialog(this); |
|||
} |
|||
|
|||
private void btnEmbedToWpf_Click(object sender, EventArgs e) |
|||
{ |
|||
new EmbedToWpfDemo().ShowDialog(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,120 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<root> |
|||
<!-- |
|||
Microsoft ResX Schema |
|||
|
|||
Version 2.0 |
|||
|
|||
The primary goals of this format is to allow a simple XML format |
|||
that is mostly human readable. The generation and parsing of the |
|||
various data types are done through the TypeConverter classes |
|||
associated with the data types. |
|||
|
|||
Example: |
|||
|
|||
... ado.net/XML headers & schema ... |
|||
<resheader name="resmimetype">text/microsoft-resx</resheader> |
|||
<resheader name="version">2.0</resheader> |
|||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> |
|||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> |
|||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> |
|||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> |
|||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> |
|||
<value>[base64 mime encoded serialized .NET Framework object]</value> |
|||
</data> |
|||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> |
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> |
|||
<comment>This is a comment</comment> |
|||
</data> |
|||
|
|||
There are any number of "resheader" rows that contain simple |
|||
name/value pairs. |
|||
|
|||
Each data row contains a name, and value. The row also contains a |
|||
type or mimetype. Type corresponds to a .NET class that support |
|||
text/value conversion through the TypeConverter architecture. |
|||
Classes that don't support this are serialized and stored with the |
|||
mimetype set. |
|||
|
|||
The mimetype is used for serialized objects, and tells the |
|||
ResXResourceReader how to depersist the object. This is currently not |
|||
extensible. For a given mimetype the value must be set accordingly: |
|||
|
|||
Note - application/x-microsoft.net.object.binary.base64 is the format |
|||
that the ResXResourceWriter will generate, however the reader can |
|||
read any of the formats listed below. |
|||
|
|||
mimetype: application/x-microsoft.net.object.binary.base64 |
|||
value : The object must be serialized with |
|||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter |
|||
: and then encoded with base64 encoding. |
|||
|
|||
mimetype: application/x-microsoft.net.object.soap.base64 |
|||
value : The object must be serialized with |
|||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter |
|||
: and then encoded with base64 encoding. |
|||
|
|||
mimetype: application/x-microsoft.net.object.bytearray.base64 |
|||
value : The object must be serialized into a byte array |
|||
: using a System.ComponentModel.TypeConverter |
|||
: and then encoded with base64 encoding. |
|||
--> |
|||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> |
|||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> |
|||
<xsd:element name="root" msdata:IsDataSet="true"> |
|||
<xsd:complexType> |
|||
<xsd:choice maxOccurs="unbounded"> |
|||
<xsd:element name="metadata"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" use="required" type="xsd:string" /> |
|||
<xsd:attribute name="type" type="xsd:string" /> |
|||
<xsd:attribute name="mimetype" type="xsd:string" /> |
|||
<xsd:attribute ref="xml:space" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="assembly"> |
|||
<xsd:complexType> |
|||
<xsd:attribute name="alias" type="xsd:string" /> |
|||
<xsd:attribute name="name" type="xsd:string" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="data"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
|||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> |
|||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> |
|||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> |
|||
<xsd:attribute ref="xml:space" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="resheader"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" type="xsd:string" use="required" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
</xsd:choice> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
</xsd:schema> |
|||
<resheader name="resmimetype"> |
|||
<value>text/microsoft-resx</value> |
|||
</resheader> |
|||
<resheader name="version"> |
|||
<value>2.0</value> |
|||
</resheader> |
|||
<resheader name="reader"> |
|||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
|||
</resheader> |
|||
<resheader name="writer"> |
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
|||
</resheader> |
|||
</root> |
|||
@ -1,15 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<OutputType>WinExe</OutputType> |
|||
<TargetFramework>net461</TargetFramework> |
|||
<PlatformTarget>x64</PlatformTarget> |
|||
<TargetFrameworks>net461;net6.0-windows</TargetFrameworks> |
|||
<UseWPF>true</UseWPF> |
|||
<UseWindowsForms>true</UseWindowsForms> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
|||
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj" /> |
|||
<ProjectReference Include="..\..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> |
|||
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32.Interoperability\Avalonia.Win32.Interoperability.csproj" /> |
|||
<ProjectReference Include="..\..\ControlCatalog\ControlCatalog.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,75 +0,0 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Windows.Forms; |
|||
using Avalonia.Controls.Embedding; |
|||
using Avalonia.Input; |
|||
using Avalonia.VisualTree; |
|||
using Avalonia.Win32.Interop; |
|||
using WinFormsControl = System.Windows.Forms.Control; |
|||
|
|||
namespace Avalonia.Win32.Embedding |
|||
{ |
|||
[ToolboxItem(true)] |
|||
public class WinFormsAvaloniaControlHost : WinFormsControl |
|||
{ |
|||
private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot(); |
|||
|
|||
private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero; |
|||
|
|||
public WinFormsAvaloniaControlHost() |
|||
{ |
|||
SetStyle(ControlStyles.AllPaintingInWmPaint, true); |
|||
UnmanagedMethods.SetParent(WindowHandle, Handle); |
|||
_root.Prepare(); |
|||
if (_root.IsFocused) |
|||
_root.FocusManager.ClearFocus(); |
|||
_root.GotFocus += RootGotFocus; |
|||
|
|||
FixPosition(); |
|||
} |
|||
|
|||
public Avalonia.Controls.Control Content |
|||
{ |
|||
get { return (Avalonia.Controls.Control)_root.Content; } |
|||
set { _root.Content = value; } |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
_root.Dispose(); |
|||
base.Dispose(disposing); |
|||
} |
|||
|
|||
private void RootGotFocus(object sender, Interactivity.RoutedEventArgs e) |
|||
{ |
|||
UnmanagedMethods.SetFocus(WindowHandle); |
|||
} |
|||
|
|||
protected override void OnGotFocus(EventArgs e) |
|||
{ |
|||
if (_root != null) |
|||
UnmanagedMethods.SetFocus(WindowHandle); |
|||
} |
|||
|
|||
|
|||
void FixPosition() |
|||
{ |
|||
if (_root != null && Width > 0 && Height > 0) |
|||
UnmanagedMethods.MoveWindow(WindowHandle, 0, 0, Width, Height, true); |
|||
} |
|||
|
|||
|
|||
|
|||
protected override void OnResize(EventArgs e) |
|||
{ |
|||
FixPosition(); |
|||
base.OnResize(e); |
|||
} |
|||
|
|||
protected override void OnPaint(PaintEventArgs e) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Serialization; |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,202 +0,0 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Windows; |
|||
using System.Windows.Interop; |
|||
using Avalonia.Direct2D1; |
|||
using SharpDX.Direct2D1; |
|||
using SharpDX.Direct3D11; |
|||
using SharpDX.Direct3D9; |
|||
using SharpDX.DXGI; |
|||
using AlphaMode = SharpDX.Direct2D1.AlphaMode; |
|||
using Device = SharpDX.Direct3D11.Device; |
|||
using Format = SharpDX.DXGI.Format; |
|||
using Query = SharpDX.Direct3D11.Query; |
|||
using QueryType = SharpDX.Direct3D11.QueryType; |
|||
using RenderTarget = SharpDX.Direct2D1.RenderTarget; |
|||
using Surface = SharpDX.DXGI.Surface; |
|||
using Usage = SharpDX.Direct3D9.Usage; |
|||
|
|||
namespace Avalonia.Win32.Interop.Wpf |
|||
{ |
|||
class Direct2DImageSurface : IExternalDirect2DRenderTargetSurface, IDisposable |
|||
{ |
|||
class SwapBuffer: IDisposable |
|||
{ |
|||
private readonly Query _event; |
|||
private readonly SharpDX.Direct3D11.Resource _resource; |
|||
private readonly SharpDX.Direct3D11.Resource _sharedResource; |
|||
public SharpDX.Direct3D9.Surface Texture { get; } |
|||
public RenderTarget Target { get;} |
|||
public IntSize Size { get; } |
|||
|
|||
public SwapBuffer(IntSize size, Vector dpi) |
|||
{ |
|||
int width = (int) size.Width; |
|||
int height = (int) size.Height; |
|||
_event = new Query(s_dxDevice, new QueryDescription {Type = QueryType.Event}); |
|||
using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription |
|||
{ |
|||
Width = width, |
|||
Height = height, |
|||
ArraySize = 1, |
|||
MipLevels = 1, |
|||
Format = Format.B8G8R8A8_UNorm, |
|||
Usage = ResourceUsage.Default, |
|||
SampleDescription = new SampleDescription(2, 0), |
|||
BindFlags = BindFlags.RenderTarget, |
|||
})) |
|||
using (var surface = texture.QueryInterface<Surface>()) |
|||
|
|||
{ |
|||
_resource = texture.QueryInterface<SharpDX.Direct3D11.Resource>(); |
|||
|
|||
Target = new RenderTarget(Direct2D1Platform.Direct2D1Factory, surface, |
|||
new RenderTargetProperties |
|||
{ |
|||
DpiX = (float) dpi.X, |
|||
DpiY = (float) dpi.Y, |
|||
MinLevel = FeatureLevel.Level_10, |
|||
PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied), |
|||
|
|||
}); |
|||
} |
|||
using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription |
|||
{ |
|||
Width = width, |
|||
Height = height, |
|||
ArraySize = 1, |
|||
MipLevels = 1, |
|||
Format = Format.B8G8R8A8_UNorm, |
|||
Usage = ResourceUsage.Default, |
|||
SampleDescription = new SampleDescription(1, 0), |
|||
BindFlags = BindFlags.RenderTarget|BindFlags.ShaderResource, |
|||
OptionFlags = ResourceOptionFlags.Shared, |
|||
})) |
|||
using (var resource = texture.QueryInterface<SharpDX.DXGI.Resource>()) |
|||
{ |
|||
_sharedResource = texture.QueryInterface<SharpDX.Direct3D11.Resource>(); |
|||
var handle = resource.SharedHandle; |
|||
using (var texture9 = new Texture(s_d3DDevice, texture.Description.Width, |
|||
texture.Description.Height, 1, |
|||
Usage.RenderTarget, SharpDX.Direct3D9.Format.A8R8G8B8, Pool.Default, ref handle)) |
|||
Texture = texture9.GetSurfaceLevel(0); |
|||
} |
|||
Size = size; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
Texture?.Dispose(); |
|||
Target?.Dispose(); |
|||
_resource?.Dispose(); |
|||
_sharedResource?.Dispose(); |
|||
_event?.Dispose(); |
|||
} |
|||
|
|||
public void Flush() |
|||
{ |
|||
s_dxDevice.ImmediateContext.ResolveSubresource(_resource, 0, _sharedResource, 0, Format.B8G8R8A8_UNorm); |
|||
s_dxDevice.ImmediateContext.Flush(); |
|||
s_dxDevice.ImmediateContext.End(_event); |
|||
s_dxDevice.ImmediateContext.GetData(_event).Dispose(); |
|||
} |
|||
} |
|||
|
|||
private D3DImage _image; |
|||
private SwapBuffer _backBuffer; |
|||
private readonly WpfTopLevelImpl _impl; |
|||
private static Device s_dxDevice; |
|||
private static Direct3DEx s_d3DContext; |
|||
private static DeviceEx s_d3DDevice; |
|||
private Vector _oldDpi; |
|||
|
|||
|
|||
[DllImport("user32.dll", SetLastError = false)] |
|||
private static extern IntPtr GetDesktopWindow(); |
|||
|
|||
static void EnsureDirectX() |
|||
{ |
|||
if(s_d3DDevice != null) |
|||
return; |
|||
s_d3DContext = new Direct3DEx(); |
|||
|
|||
SharpDX.Direct3D9.PresentParameters presentparams = new SharpDX.Direct3D9.PresentParameters |
|||
{ |
|||
Windowed = true, |
|||
SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard, |
|||
DeviceWindowHandle = GetDesktopWindow(), |
|||
PresentationInterval = PresentInterval.Default |
|||
}; |
|||
s_dxDevice = s_dxDevice ?? AvaloniaLocator.Current.GetRequiredService<SharpDX.DXGI.Device>() |
|||
.QueryInterface<SharpDX.Direct3D11.Device>(); |
|||
s_d3DDevice = new DeviceEx(s_d3DContext, 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded | CreateFlags.FpuPreserve, presentparams); |
|||
|
|||
} |
|||
|
|||
public Direct2DImageSurface(WpfTopLevelImpl impl) |
|||
{ |
|||
_impl = impl; |
|||
} |
|||
|
|||
public RenderTarget GetOrCreateRenderTarget() |
|||
{ |
|||
EnsureDirectX(); |
|||
var scale = _impl.GetScaling(); |
|||
var size = new IntSize(_impl.ActualWidth * scale.X, _impl.ActualHeight * scale.Y); |
|||
var dpi = scale * 96; |
|||
|
|||
if (_backBuffer!=null && _backBuffer.Size == size) |
|||
return _backBuffer.Target; |
|||
|
|||
if (_image == null || _oldDpi.X != dpi.X || _oldDpi.Y != dpi.Y) |
|||
{ |
|||
_image = new D3DImage(dpi.X, dpi.Y); |
|||
_oldDpi = dpi; |
|||
} |
|||
_impl.ImageSource = _image; |
|||
|
|||
RemoveAndDispose(ref _backBuffer); |
|||
if (size == default) |
|||
{ |
|||
_image.Lock(); |
|||
_image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero); |
|||
_image.Unlock(); |
|||
return null; |
|||
} |
|||
_backBuffer = new SwapBuffer(size, dpi); |
|||
|
|||
return _backBuffer.Target; |
|||
} |
|||
|
|||
static void RemoveAndDispose<T>(ref T d) where T : IDisposable |
|||
{ |
|||
d?.Dispose(); |
|||
d = default; |
|||
} |
|||
|
|||
void Swap() |
|||
{ |
|||
_backBuffer.Flush(); |
|||
_image.Lock(); |
|||
_image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _backBuffer?.Texture?.NativePointer ?? IntPtr.Zero, true); |
|||
_image.AddDirtyRect(new Int32Rect(0, 0, _image.PixelWidth, _image.PixelHeight)); |
|||
_image.Unlock(); |
|||
} |
|||
|
|||
public void DestroyRenderTarget() |
|||
{ |
|||
RemoveAndDispose(ref _backBuffer); |
|||
} |
|||
|
|||
public void BeforeDrawing() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public void AfterDrawing() => Swap(); |
|||
public void Dispose() |
|||
{ |
|||
RemoveAndDispose(ref _backBuffer); |
|||
} |
|||
} |
|||
} |
|||
@ -1,55 +0,0 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Win32.Interop.Wpf |
|||
{ |
|||
struct IntSize : IEquatable<IntSize> |
|||
{ |
|||
public bool Equals(IntSize other) |
|||
{ |
|||
return Width == other.Width && Height == other.Height; |
|||
} |
|||
|
|||
public IntSize(int width, int height) |
|||
{ |
|||
Width = width; |
|||
Height = height; |
|||
} |
|||
|
|||
public IntSize(double width, double height) : this((int) width, (int) height) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public static implicit operator IntSize(System.Windows.Size size) |
|||
{ |
|||
return new IntSize {Width = (int) size.Width, Height = (int) size.Height}; |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
return obj is IntSize size && Equals(size); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
return (Width * 397) ^ Height; |
|||
} |
|||
} |
|||
|
|||
public static bool operator ==(IntSize left, IntSize right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
public static bool operator !=(IntSize left, IntSize right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
public int Width { get; set; } |
|||
public int Height { get; set; } |
|||
} |
|||
} |
|||
@ -1,110 +0,0 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Windows; |
|||
using System.Windows.Markup; |
|||
|
|||
namespace Avalonia.Win32.Interop.Wpf |
|||
{ |
|||
[ContentProperty("Content")] |
|||
public class WpfAvaloniaHost : FrameworkElement, IDisposable, IAddChild |
|||
{ |
|||
private WpfTopLevelImpl _impl; |
|||
private readonly SynchronizationContext _sync; |
|||
private bool _hasChildren; |
|||
public WpfAvaloniaHost() |
|||
{ |
|||
_sync = SynchronizationContext.Current; |
|||
_impl = new WpfTopLevelImpl(); |
|||
_impl.ControlRoot.Prepare(); |
|||
_impl.Visibility = Visibility.Visible; |
|||
SnapsToDevicePixels = true; |
|||
UseLayoutRounding = true; |
|||
PresentationSource.AddSourceChangedHandler(this, OnSourceChanged); |
|||
} |
|||
|
|||
private void OnSourceChanged(object sender, SourceChangedEventArgs e) |
|||
{ |
|||
if (e.NewSource != null && !_hasChildren) |
|||
{ |
|||
AddLogicalChild(_impl); |
|||
AddVisualChild(_impl); |
|||
_hasChildren = true; |
|||
} |
|||
else |
|||
{ |
|||
RemoveVisualChild(_impl); |
|||
RemoveLogicalChild(_impl); |
|||
_hasChildren = false; |
|||
} |
|||
} |
|||
|
|||
public object Content |
|||
{ |
|||
get => _impl.ControlRoot.Content; |
|||
set => _impl.ControlRoot.Content = value; |
|||
} |
|||
|
|||
//Separate class is needed to prevent accidental 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.InvalidateMeasure(); |
|||
_impl.Measure(constraint); |
|||
return _impl.DesiredSize; |
|||
} |
|||
|
|||
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); |
|||
} |
|||
} |
|||
|
|||
void IAddChild.AddChild(object value) |
|||
{ |
|||
if (Content == null) |
|||
Content = value; |
|||
else |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
void IAddChild.AddText(string text) |
|||
{ |
|||
//
|
|||
} |
|||
} |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
namespace Avalonia.Win32.Interop.Wpf |
|||
{ |
|||
static class WpfInteropExtensions |
|||
{ |
|||
public static System.Windows.Point ToWpfPoint(this Point pt) => new System.Windows.Point(pt.X, pt.Y); |
|||
public static System.Windows.Point ToWpfPoint(this PixelPoint 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 PixelPoint ToAvaloniaPixelPoint(this System.Windows.Point pt) => new PixelPoint((int)pt.X, (int)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); |
|||
} |
|||
} |
|||
@ -1,40 +0,0 @@ |
|||
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) : base(new WpfMousePointer(impl)) |
|||
{ |
|||
_impl = impl; |
|||
} |
|||
|
|||
class WpfMousePointer : Pointer |
|||
{ |
|||
private readonly WpfTopLevelImpl _impl; |
|||
|
|||
public WpfMousePointer(WpfTopLevelImpl impl) : base(Pointer.GetNextFreeId(), PointerType.Mouse, true) |
|||
{ |
|||
_impl = impl; |
|||
} |
|||
|
|||
protected override void PlatformCapture(IInputElement control) |
|||
{ |
|||
if (control == null) |
|||
{ |
|||
System.Windows.Input.Mouse.Capture(null); |
|||
} |
|||
else if (((control as Visual)?.GetVisualRoot() as EmbeddableControlRoot)?.PlatformImpl != _impl) |
|||
throw new ArgumentException("Visual belongs to unknown toplevel"); |
|||
else |
|||
System.Windows.Input.Mouse.Capture(_impl); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -1,254 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Windows; |
|||
using System.Windows.Input; |
|||
using System.Windows.Interop; |
|||
using System.Windows.Media; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Embedding; |
|||
using Avalonia.Input; |
|||
using Avalonia.Input.Raw; |
|||
using Avalonia.Layout; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering; |
|||
using Avalonia.Rendering.Composition; |
|||
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, ITopLevelImpl |
|||
{ |
|||
private HwndSource _currentHwndSource; |
|||
private readonly HwndSourceHook _hook; |
|||
private readonly ITopLevelImpl _ttl; |
|||
private IInputRoot _inputRoot; |
|||
private readonly IEnumerable<object> _surfaces; |
|||
private readonly IMouseDevice _mouse; |
|||
private readonly IKeyboardDevice _keyboard; |
|||
private Size _finalSize; |
|||
|
|||
public EmbeddableControlRoot ControlRoot { get; } |
|||
internal ImageSource ImageSource { get; set; } |
|||
|
|||
public class CustomControlRoot : EmbeddableControlRoot, IEmbeddedLayoutRoot |
|||
{ |
|||
public CustomControlRoot(WpfTopLevelImpl impl) : base(impl) |
|||
{ |
|||
EnforceClientSize = false; |
|||
} |
|||
|
|||
protected override void OnMeasureInvalidated() |
|||
{ |
|||
((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); |
|||
} |
|||
|
|||
public Size AllocatedSize => ClientSize; |
|||
} |
|||
|
|||
public WpfTopLevelImpl() |
|||
{ |
|||
PresentationSource.AddSourceChangedHandler(this, OnSourceChanged); |
|||
_hook = WndProc; |
|||
_ttl = this; |
|||
_surfaces = new object[] {new WritableBitmapSurface(this), new Direct2DImageSurface(this)}; |
|||
_mouse = new WpfMouseDevice(this); |
|||
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>(); |
|||
|
|||
ControlRoot = new CustomControlRoot(this); |
|||
SnapsToDevicePixels = true; |
|||
Focusable = true; |
|||
DataContextChanged += delegate |
|||
{ |
|||
ControlRoot.DataContext = DataContext; |
|||
}; |
|||
} |
|||
|
|||
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.RenderScaling); |
|||
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.RenderScaling); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_ttl.Closed?.Invoke(); |
|||
foreach(var d in _surfaces.OfType<IDisposable>()) |
|||
d.Dispose(); |
|||
} |
|||
|
|||
Size ITopLevelImpl.ClientSize => _finalSize; |
|||
Size? ITopLevelImpl.FrameSize => null; |
|||
|
|||
double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1; |
|||
|
|||
IEnumerable<object> 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(), WindowResizeReason.Unspecified); |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
|
|||
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)); |
|||
} |
|||
|
|||
|
|||
public Compositor Compositor => Win32Platform.Compositor; |
|||
void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot; |
|||
|
|||
Point ITopLevelImpl.PointToClient(PixelPoint point) => PointFromScreen(point.ToWpfPoint()).ToAvaloniaPoint(); |
|||
|
|||
PixelPoint ITopLevelImpl.PointToScreen(Point point) => PointToScreen(point.ToWpfPoint()).ToAvaloniaPixelPoint(); |
|||
|
|||
protected override void OnLostFocus(RoutedEventArgs e) => LostFocus?.Invoke(); |
|||
|
|||
static RawInputModifiers GetModifiers(MouseEventArgs e) |
|||
{ |
|||
var state = Keyboard.Modifiers; |
|||
var rv = default(RawInputModifiers); |
|||
if (state.HasFlag(ModifierKeys.Windows)) |
|||
rv |= RawInputModifiers.Meta; |
|||
if (state.HasFlag(ModifierKeys.Alt)) |
|||
rv |= RawInputModifiers.Alt; |
|||
if (state.HasFlag(ModifierKeys.Control)) |
|||
rv |= RawInputModifiers.Control; |
|||
if (state.HasFlag(ModifierKeys.Shift)) |
|||
rv |= RawInputModifiers.Shift; |
|||
if (e != null) |
|||
{ |
|||
if (e.LeftButton == MouseButtonState.Pressed) |
|||
rv |= RawInputModifiers.LeftMouseButton; |
|||
if (e.RightButton == MouseButtonState.Pressed) |
|||
rv |= RawInputModifiers.RightMouseButton; |
|||
if (e.MiddleButton == MouseButtonState.Pressed) |
|||
rv |= RawInputModifiers.MiddleMouseButton; |
|||
} |
|||
return rv; |
|||
} |
|||
|
|||
void MouseEvent(RawPointerEventType type, MouseEventArgs e) |
|||
=> _ttl.Input?.Invoke(new RawPointerEventArgs(_mouse, (uint)e.Timestamp, _inputRoot, type, |
|||
e.GetPosition(this).ToAvaloniaPoint(), GetModifiers(e))); |
|||
|
|||
protected override void OnMouseDown(MouseButtonEventArgs e) |
|||
{ |
|||
RawPointerEventType type; |
|||
if(e.ChangedButton == MouseButton.Left) |
|||
type = RawPointerEventType.LeftButtonDown; |
|||
else if (e.ChangedButton == MouseButton.Middle) |
|||
type = RawPointerEventType.MiddleButtonDown; |
|||
else if (e.ChangedButton == MouseButton.Right) |
|||
type = RawPointerEventType.RightButtonDown; |
|||
else |
|||
return; |
|||
MouseEvent(type, e); |
|||
Focus(); |
|||
} |
|||
|
|||
protected override void OnMouseUp(MouseButtonEventArgs e) |
|||
{ |
|||
RawPointerEventType type; |
|||
if (e.ChangedButton == MouseButton.Left) |
|||
type = RawPointerEventType.LeftButtonUp; |
|||
else if (e.ChangedButton == MouseButton.Middle) |
|||
type = RawPointerEventType.MiddleButtonUp; |
|||
else if (e.ChangedButton == MouseButton.Right) |
|||
type = RawPointerEventType.RightButtonUp; |
|||
else |
|||
return; |
|||
MouseEvent(type, e); |
|||
Focus(); |
|||
} |
|||
|
|||
protected override void OnMouseMove(MouseEventArgs e) |
|||
{ |
|||
MouseEvent(RawPointerEventType.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(e))); |
|||
|
|||
protected override void OnMouseLeave(MouseEventArgs e) => MouseEvent(RawPointerEventType.LeaveWindow, e); |
|||
|
|||
protected override void OnKeyDown(KeyEventArgs e) |
|||
=> _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, RawKeyEventType.KeyDown, |
|||
(Key) e.Key, |
|||
GetModifiers(null))); |
|||
|
|||
protected override void OnKeyUp(KeyEventArgs e) |
|||
=> _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, _inputRoot, RawKeyEventType.KeyUp, |
|||
(Key)e.Key, |
|||
GetModifiers(null))); |
|||
|
|||
protected override void OnTextInput(TextCompositionEventArgs e) |
|||
=> _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, e.Text)); |
|||
|
|||
void ITopLevelImpl.SetCursor(ICursorImpl cursor) |
|||
{ |
|||
if (cursor == null) |
|||
Cursor = Cursors.Arrow; |
|||
else if (cursor is IPlatformHandle handle) |
|||
Cursor = CursorShim.FromHCursor(handle.Handle); |
|||
} |
|||
|
|||
Action<RawInputEventArgs> ITopLevelImpl.Input { get; set; } //TODO
|
|||
Action<Rect> ITopLevelImpl.Paint { get; set; } |
|||
Action<Size, WindowResizeReason> ITopLevelImpl.Resized { get; set; } |
|||
Action<double> ITopLevelImpl.ScalingChanged { get; set; } |
|||
|
|||
Action<WindowTransparencyLevel> ITopLevelImpl.TransparencyLevelChanged { get; set; } |
|||
|
|||
Action ITopLevelImpl.Closed { get; set; } |
|||
public new Action LostFocus { get; set; } |
|||
|
|||
internal Vector GetScaling() |
|||
{ |
|||
var src = PresentationSource.FromVisual(this)?.CompositionTarget; |
|||
if (src == null) |
|||
return new Vector(1, 1); |
|||
return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22); |
|||
} |
|||
|
|||
public IPopupImpl CreatePopup() => null; |
|||
|
|||
public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevel) { } |
|||
|
|||
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None; |
|||
|
|||
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { } |
|||
|
|||
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1); |
|||
|
|||
public object TryGetFeature(Type featureType) => null; |
|||
} |
|||
} |
|||
@ -1,67 +0,0 @@ |
|||
using System; |
|||
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 = _impl.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, |
|||
System.Windows.Media.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 PixelSize Size => new PixelSize(_bitmap.PixelWidth, _bitmap.PixelHeight); |
|||
public int RowBytes => _bitmap.BackBufferStride; |
|||
public Vector Dpi { get; } |
|||
public PixelFormat Format => PixelFormat.Bgra8888; |
|||
} |
|||
} |
|||
} |
|||
@ -1,20 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>net461</TargetFramework> |
|||
<TargetFrameworks>net461;net6.0-windows</TargetFrameworks> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
<UseWpf>true</UseWpf> |
|||
<UseWindowsForms>true</UseWindowsForms> |
|||
<ExtrasEnableImplicitWinFormsReferences>true</ExtrasEnableImplicitWinFormsReferences> |
|||
<EnableWindowsTargeting>true</EnableWindowsTargeting> |
|||
<UseDirect3D9>true</UseDirect3D9> |
|||
<PackageId>Avalonia.Win32.Interoperability</PackageId> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> |
|||
<ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj" /> |
|||
<ProjectReference Include="..\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\..\build\SharpDX.props" /> |
|||
<Import Project="..\..\..\build\NullableEnable.props" /> |
|||
<Import Project="..\..\..\build\DevAnalyzers.props" /> |
|||
<Import Project="..\..\..\build\TrimmingEnable.props" /> |
|||
</Project> |
|||
@ -0,0 +1,116 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Windows.Forms; |
|||
using Avalonia.Controls.Embedding; |
|||
using Avalonia.Win32.Interop; |
|||
using WinFormsControl = System.Windows.Forms.Control; |
|||
using AvControl = Avalonia.Controls.Control; |
|||
|
|||
namespace Avalonia.Win32.Interoperability; |
|||
|
|||
/// <summary>
|
|||
/// An element that allows you to host a Avalonia control on a Windows Forms page.
|
|||
/// </summary>
|
|||
[ToolboxItem(true)] |
|||
public class WinFormsAvaloniaControlHost : WinFormsControl |
|||
{ |
|||
private AvControl? _content; |
|||
private EmbeddableControlRoot? _root; |
|||
|
|||
private IntPtr WindowHandle => _root?.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="WinFormsAvaloniaControlHost"/> class.
|
|||
/// </summary>
|
|||
public WinFormsAvaloniaControlHost() |
|||
{ |
|||
SetStyle(ControlStyles.AllPaintingInWmPaint, true); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the Avalonia control hosted by the <see cref="WinFormsAvaloniaControlHost"/> element.
|
|||
/// </summary>
|
|||
public AvControl? Content |
|||
{ |
|||
get => _content; |
|||
set |
|||
{ |
|||
if (_content != value) |
|||
{ |
|||
_content = value; |
|||
if (_root is not null) |
|||
{ |
|||
_root.Content = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void OnHandleCreated(EventArgs e) |
|||
{ |
|||
_root = new(); |
|||
_root.Content = _content; |
|||
_root.Prepare(); |
|||
_root.StartRendering(); |
|||
_root.GotFocus += RootGotFocus; |
|||
|
|||
FixPosition(); |
|||
|
|||
UnmanagedMethods.SetParent(WindowHandle, Handle); |
|||
base.OnHandleCreated(e); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void OnHandleDestroyed(EventArgs e) |
|||
{ |
|||
_root?.StopRendering(); |
|||
_root?.Dispose(); |
|||
_root = null; |
|||
base.OnHandleDestroyed(e); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
{ |
|||
_root?.Dispose(); |
|||
_root = null; |
|||
} |
|||
base.Dispose(disposing); |
|||
} |
|||
|
|||
private void RootGotFocus(object? sender, Interactivity.RoutedEventArgs e) |
|||
{ |
|||
UnmanagedMethods.SetFocus(WindowHandle); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void OnGotFocus(EventArgs e) |
|||
{ |
|||
var handle = WindowHandle; |
|||
if (handle != default) |
|||
UnmanagedMethods.SetFocus(handle); |
|||
} |
|||
|
|||
private void FixPosition() |
|||
{ |
|||
var handle = WindowHandle; |
|||
if (handle != default && Width > 0 && Height > 0) |
|||
UnmanagedMethods.MoveWindow(handle, 0, 0, Width, Height, true); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void OnResize(EventArgs e) |
|||
{ |
|||
FixPosition(); |
|||
base.OnResize(e); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void OnPaint(PaintEventArgs e) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue