2010-10-06 86 views
5

在我的WPF应用程序中,我使用HwndHost托管Win32内容。但是,创建HwndHost不会创建本机窗口。相反,这是在覆盖的BuildWindowCore()方法中完成的,WPF将在稍后调用该方法。强制初始化HwndHost

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

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

编辑:

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

  1. 的HwndHost被放置在一个控制其中,取决于用户设置,可以是应用程序的主的子窗口或可以放置在新窗口中(通过第三方对接管理器)。这意味着在创建窗口时我不确定父窗口(以及它的hWnd)是什么。
  2. 尽管本机代码在其初始化过程中需要hWnd,但只有在用户请求显示该窗口时才会显示该窗口(即它最初不可见)。如果可能,应该避免需要显示窗口,只能立即再次隐藏窗口。

回答

3

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

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

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

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

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)创建一个HwndHost派生类需要一个矩形作为一个构造函数的参数(后来在BuildWindowCore使用):

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

2)创建一个WPF窗口与孩子Border元素:

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

3)创建HwndHost实例,并把它添加到TH在Window_Loaded处理机Ê窗口:

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

4)另外,在处理程序Window_Loaded,通过HWND到原生类的既可以通过P /调用或C++/CLI的初始化。我有我的母语课程设置为使用该HWND作为其父母,并创建自己的HWND作为孩子。

+0

有两个问题:1)我不知道父hWnd,因为控件后来由第三方对接管理器定位,并且存储的用户设置确定它是自己显示还是作为“孩子“的主窗口。 2)HwndHost的控制最初可能不会显示(取决于存储的用户设置),但在启动时,旧代码需要hWnd。 – 2011-06-24 08:26:22

+0

您应该可以挂钩到控件上的Loaded事件,并在其中进行所有初始化:http://msdn.microsoft.com/zh-cn/library/system.windows.frameworkelement.loaded.aspx。如果遗留代码需要hwnd,那么你只需要在遗留代码上做任何事情,直到hwnd准备就绪(这是我必须做的)。 – 2011-06-27 14:12:06

+0

来自链接的引用:“当元素布局,呈现并准备好进行交互时发生。”如果我没有显示控件,Loaded将不会触发。 – 2011-06-28 07:09:17

0

有点迟了,但你有没有试过在主机控制上调用UpdateLayout()?这对我有用

+0

不幸的是,这只有在宿主控件可见的情况下才有效。这正是EnsureHandle()解决的问题,但HwndHosts不存在。 – 2011-06-24 08:27:28

0

我添加一个事件OnHandleCreated到我HwndHost -inherited类,它包含手柄的IntPtr。此事件在BuildWindowCore()内调用。

所以它归结为:

public class Win32WindowHost : HwndHost { ... }

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

作品一种享受。

+0

不幸的是,这在我的情况下不起作用。只有在显示HwndHost时才调用BuildWindowCore()。没有其他办法可以强制创建本地hwnd(即强制WPF调用BuildWindowCore())。这只能通过WindowInteropHelper.EnsureHandle()从窗口派生出来。 – 2014-02-19 15:02:03