2010-10-06 18 views
5

En mi aplicación WPF, alojo contenido Win32 utilizando HwndHost. Sin embargo, la creación de HwndHost no crea la ventana nativa. Por el contrario, esto se hace en el método BuildWindowCore() reemplazado que WPF llama en algún momento más adelante.Forzar la inicialización de HwndHost

Mi contenido alojado necesita el identificador de ventana de la ventana nativa para su propia inicialización. Desafortunadamente, no hay forma de forzar la creación de la ventana (es decir, hacer que WPF llame a BuildWindowCore), así que tengo un segundo hilo que sondea HwndHost hasta que se haya inicializado.

En .NET 4.0/WPF 4.0, se agregó un nuevo método WindowInteropHelper.EnsureHandle(). Esperaba que esto resolviera la situación, pero solo funciona para una ventana, no para un HwndHost (que no se deriva de Window). ¿Tienes alguna sugerencia de qué podría hacer?

EDIT:

me olvidó añadir algunos más restricciones para una posible solución:

  1. El HwndHost se coloca en un control que, dependiendo de la configuración del usuario, puede ser un hijo de la aplicación del principal ventana o se puede colocar en una nueva ventana (a través de un administrador de acoplamiento de terceros). Esto significa que durante la creación de la ventana no sé con certeza cuál será la ventana primaria (y por lo tanto su hWnd).
  2. Mientras que el código nativo necesita el hWnd durante su inicialización, la ventana solo se muestra cuando el usuario solicita que se muestre (es decir, es invisible al principio). Se debe evitar mostrar la ventana, solo para ocultarla inmediatamente nuevamente, si es posible.

Respuesta

3

Parece que no hay una solución perfecta. Cambié mi enfoque ligeramente en comparación con el momento de la pregunta:

En el constructor de mi clase HwndHost-derived tengo el (posible) padre hWnd como uno de los parámetros. Luego creo la ventana nativa usando el método nativo CreateWindow(), usando el padre padre dado. Guardo el hWnd creado en una propiedad separada, que utilizo en todas partes en lugar de la propiedad HwndHost's Handle. De esta forma, no necesito mostrar la ventana (esto resuelve la restricción n. ° 2).

En el método reemplazado BuildWindowCore(), comparo el padre padre dado con el que me dieron en el constructor. Si son diferentes, reparen mi ventana alojada usando el método nativo SetParent() (esto resuelve la restricción # 1). Tenga en cuenta que esto se basa en que nadie almacena el hWnd padre.

En el código, las partes pertinentes (cheques omitidas):

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

Tengo una situación similar y lo resolví de la siguiente manera:

1) Crear una clase derivada HwndHost que toma un Rect como argumento del constructor (utilizado más adelante en BuildWindowCore):

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

2) Crear una ventana de WPF con un elemento Border niño:

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

3) Crear la instancia HwndHost y añadirlo a th ventana de correo en el controlador Window_Loaded:

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

4) también en el controlador de Window_Loaded, pase el HWND a la inicialización de su clase nativa ya sea a través de P/Invoke o C++/CLI. Tengo mi clase nativa configurada para usar ese HWND como su padre y crea su propio HWND como un niño.

+0

Hay dos problemas: 1) No conozco el padre hWnd, ya que el control es luego posicionado por un administrador de acoplamiento de terceros, y la configuración de usuario almacenada determina si se muestra solo o como un " niño "de la ventana principal. 2) Es posible que el control con HwndHost no se muestre en absoluto inicialmente (dependiendo de nuevo de la configuración de usuario almacenada), pero al inicio, el código heredado necesita el hWnd. –

+0

Debería poder enganchar en el evento Loaded en su control y hacer toda la inicialización allí: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded.aspx. Si el código heredado necesita el hwnd, entonces solo necesita dejar de hacer algo con el código heredado hasta que el hwnd esté listo (que es lo que tenía que hacer). –

+0

Cita del enlace: "Se produce cuando el elemento se presenta, se procesa y está listo para la interacción". Si no muestro el control, Loaded no se disparará. –

0

Un poco tarde, pero ¿ha intentado llamar a UpdateLayout() en el control de alojamiento? Eso funcionó para mí

+0

Desafortunadamente, eso solo funciona si el control de alojamiento está visible. Ese es precisamente el problema que EnsureHandle() resuelve, pero no existe para HwndHosts. –

0

he añadido el evento OnHandleCreated a mi HwndHost clase -inherited, que contiene el IntPtr mango. Este evento se invoca dentro de BuildWindowCore().

Por lo tanto, se reduce a:

public class Win32WindowHost : HwndHost { ... }

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

trabaja un convite.

+0

Desafortunadamente, esto no funciona en mi caso. BuildWindowCore() solo se invoca cuando se muestra HwndHost. No hay otra manera de forzar la creación del hwnd nativo (es decir, forzar a WPF a llamar a BuildWindowCore()). Esto solo es posible para algo derivado de Window, a través de WindowInteropHelper.EnsureHandle(). –

Cuestiones relacionadas