从 WindowsFormsHost 中的 Windows 窗体控件获取 WPF 窗口
Posted
技术标签:
【中文标题】从 WindowsFormsHost 中的 Windows 窗体控件获取 WPF 窗口【英文标题】:Get WPF window from Windows Forms control in WindowsFormsHost 【发布时间】:2017-10-18 17:52:26 【问题描述】:我有一个托管在 WindowsFormsHost 中的 Windows 窗体控件。这是我用来完成此任务的 XAML:
<Window x:Class="Forms.Address.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Forms.Address"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d"
Title="New Window" Height="500" Width="720">
<Grid>
<WindowsFormsHost x:Name="host">
<local:MyFormsControl x:Name="genericName"/>
</WindowsFormsHost>
</Grid>
</Window>
我想从 WindowsFormsHost 所在的窗口监听事件。这在 Windows 窗体中很简单,因为我可以使用 FindForm
方法来获取我的控件所在的窗体。但是,出于显而易见的原因,当控件位于 WindowsFormsHost 中时,FindForm
不起作用。我的控件的父级是 System.Windows.Forms.Integration.WinFormsAdapter
,其父级为 null。
我的问题是:如何找到包含我的控件的窗口?
【问题讨论】:
Process.GetCurrentProcess().MainWindowHandle
会不会有帮助? (您已将窗口类命名为“MainWindow”,但我不完全确定它是否真的是应用程序的主窗口...)请注意,通过获取本机窗口句柄,您只能收听本机 Windows 事件消息,而不是WPF 特定事件...
不,那是用词不当;它不是主窗口。不过好想。我将更改问题以删除该详细信息。
该死,你真的把它弄复杂了 ;-) 现在我只能自发地想到其他三种可能性。 (1) 在适当的位置执行new WindowInteropHelper(wpfWindow).Handle
并将获得的句柄以某种方式传递给您的WinForms 控件。 (2) WinFormsAdapter 似乎是一个内部/未记录的类。但也许它有一些包含窗口句柄(或其他有用数据)的字段,您可以通过反射提取。 (3)使用WinAPIEnumChildWindows
枚举你的进程的所有窗口句柄,并找出一种方法来识别你所追求的窗口句柄......
我赞成方法(1)或(3)(如果可行的话)。使用反射的方法(2)有些脆弱。不属于框架库的此类类的字段/方法可能会因以后的框架更新而以无法预料的方式发生变化,这可能会破坏程序的功能。
【参考方案1】:
感谢 elgonzo,他建议我使用反射从 WinFormsAdapter 类中获取字段。以下是我找到 Window 的方法:
public static Window findParentWindow(Control control)
WindowsFormsHost host = findWPFHost(control);
return Window.GetWindow(host);
//FindParentWindow
private static WindowsFormsHost findWPFHost(Control control)
if (control.Parent != null)
return findWPFHost(control.Parent);
else
string typeName = "System.Windows.Forms.Integration.WinFormsAdapter";
if (control.GetType().FullName == typeName)
Assembly adapterAssembly = control.GetType().Assembly;
Type winFormsAdapterType = adapterAssembly.GetType(typeName);
return (WindowsFormsHost)winFormsAdapterType.InvokeMember("_host", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, control, new string[0], null);
else
throw new Exception("The top parent of a control within a host should be a System.Windows.Forms.Integration.WinFormsAdapter, but that is not what was found. Someone should propably check this out.");
//if
//FindWPFHost
我所做的是首先递归地找到 WinFormsAdapter,然后使用反射从中提取 _host
字段。这是 WPF WindowsFormsHost 对象,因此可以使用 Window.GetWindow(host)
找到它的窗口。
需要注意的是,如果使用 ElementHost 将 WindowsFormsHost 放置在 Windows 窗体中,GetWindow
将返回 null,因为没有 Window。
【讨论】:
【参考方案2】:只需向窗口和问题添加属性,然后在代码中传递另一个窗口。这听起来技术含量低,您必须能够接受妥协。不知道如何使用 100% Xaml 做到这一点。
如果您不喜欢这个,请将其与其他选项(winapi 等)的可口性进行比较。它可以是你的电话!
【讨论】:
以上是关于从 WindowsFormsHost 中的 Windows 窗体控件获取 WPF 窗口的主要内容,如果未能解决你的问题,请参考以下文章
在 WPF WindowsFormsHost 中滚动 WinForms DataGridView
WPF WindowChrome WindowsFormsHost:不透明度