强制初始化HwndHost

时间:2010-10-06 09:15:25

标签: .net wpf wpf-4.0 hwndhost

在我的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 个答案:

答案 0 :(得分:3)

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

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

在重写的BuildWindowCore()方法中,我将给定的父hWnd与构造函数中给出的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);
    }
}

答案 1 :(得分:1)

我有类似的情况,我通过以下方式解决了这个问题:

1)创建一个HwndHost派生类,该类将Rect作为构造函数参数(稍后在BuildWindowCore中使用):

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

2)使用子Border元素创建一个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。

答案 2 :(得分:0)

有点晚了,但你试过在主机控件上调用UpdateLayout()吗?这对我有用

答案 3 :(得分:0)

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

所以归结为:

public class Win32WindowHost : HwndHost { ... }

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

努力享受。