从 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 拖放

wpf怎么使用WindowsFormsHost

在 WPF WindowsFormsHost 中滚动 WinForms DataGridView

WPF WindowChrome WindowsFormsHost:不透明度

WindowsFormsHost下MouseWheel失效的解决办法

WPF中不规则窗体与WindowsFormsHost控件的兼容问题完美解决方案