5

在我的 WPF 应用程序中,我使用 HwndHost 托管 Win32 内容。但是,创建 HwndHost 不会创建本机窗口。相反,这是在BuildWindowCore()WPF 稍后调用的重写方法中完成的。

我的托管内容需要本机窗口的窗口句柄来进行自己的初始化。不幸的是,我无法强制创建窗口(即让 WPF 调用 BuildWindowCore),所以我有第二个线程轮询 HwndHost,直到它被初始化。

在 .NET 4.0 / WPF 4.0 中,WindowInteropHelper.EnsureHandle()添加了一个新方法。我曾希望这能解决这种情况,但它只适用于 Window,而不是 HwndHost(它不是从 Window 派生的)。你有什么建议我可以做的吗?

编辑:

我忘了为可能的解决方案添加更多约束:

  1. HwndHost 放置在一个控件中,根据用户设置,该控件可以是应用程序主窗口的子窗口,也可以放置在新窗口中(通过第三方停靠管理器)。这意味着在创建窗口期间,我不确定父窗口(以及它的 hWnd)将是什么。
  2. 虽然本机代码在其初始化期间需要 hWnd,但该窗口仅在用户请求显示时才显示(即一开始它是不可见的)。如果可能,应该避免需要显示窗口,然后立即再次隐藏它。
4

4 回答 4

3

似乎没有完美的解决方案。与提出问题的时间相比,我稍微改变了我的方法:

在我的 HwndHost 派生类的构造函数中,我将(可能的)父 hWnd 作为参数之一。CreateWindow()然后,我使用给定的父 hWnd使用本机方法创建本机窗口。我将创建的 hWnd 存储在一个单独的属性中,我在任何地方都使用它而不是 HwndHost 的 Handle 属性。这样,我不需要显示窗口(这解决了约束 #2)。

在被覆盖的BuildWindowCore()方法中,我将给定的父 hWnd 与我在构造函数中给出的那个进行比较。如果它们不同,我使用本机SetParent()方法重新设置我的托管窗口(这解决了约束 #1)。请注意,这依赖于没有人存储父 hWnd!

在代码中,相关部分(省略检查):

public class Win32Window : HwndHost
{
    public Win32Window(IntPtr parentHwnd)
    {
        this.ParentHwnd = parentHwnd;
        this.Win32Handle = NativeMethods.CreateWindowEx( /* parameters omitted*/ );
    }

    public IntPtr Win32Handle { get; private set; }
    private IntPtr ParentHwnd { get; set; }

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        if (hwndParent.Handle != this.ParentHwnd)
        {
            NativeMethods.SetParent(this.Win32Handle, hwndParent.Handle);
        }

        return new HandleRef(this, this.Win32Handle);
    }
}
于 2011-06-24T10:42:18.270 回答
2

我有类似的情况,我通过执行以下操作解决了它:

1) 创建一个 HwndHost 派生类,它接受一个 Rect 作为构造函数参数(后来在 BuildWindowCore 中使用):

public class MyHwndHost : HwndHost
{
    public MyHwndHost(Rect boundingBox)
    {
        BoundingBox = boundingBox;
    } 
}

2)创建一个带有子边框元素的WPF窗口:

<Window Loaded="Window_Loaded">
    <Border Name="HostElement" />
</Window>

3) 创建 HwndHost 实例并将其添加到 Window_Loaded 处理程序中的窗口:

void Window_Loaded(object sender, RoutedEventArgs e)
{
    MyHwndHost host = new MyHwndHost(LayoutInformation.GetLayoutSlot(HostElement));
    HostElement.Child = host;
}

4) 同样在 Window_Loaded 处理程序中,通过 P/Invoke 或 C++/CLI 将 HWND 传递给本机类的初始化。我将我的本地类设置为使用该 HWND 作为其父级,并创建自己的 HWND 作为子级。

于 2011-05-27T14:47:01.053 回答
1

有点晚了,但是您是否尝试过调用UpdateLayout()主机控制?这对我有用

于 2011-06-21T08:28:08.037 回答
1

我将事件添加OnHandleCreated到我的HwndHost-inherited 类中,该类包含句柄 IntPtr。此事件在内部调用BuildWindowCore()

所以归结为:

public class Win32WindowHost : HwndHost { ... }

var host = new Win32WindowHost();
host.OnHandleCreated += ( sender, e ) =>
{
    var handle = e.Handle;
    // Do stuff.
};

工作一种享受。

于 2014-02-19T11:47:36.373 回答