Hosting aplicación WPF dentro de la no-aplicación WPF utilizando WIn32 SetParent()

StackOverflow https://stackoverflow.com/questions/4135891

  •  30-09-2019
  •  | 
  •  

Pregunta

Tengo una aplicación de WPF que quiero ver como es alojado dentro de otra - no-WPF - aplicación.En la vida real esto no aplicación WPF es un ActiveX dentro de Internet Explorer, pero en aras de ilustrar el problema yo uso una simple aplicación de Formularios de Windows.

Puedo utilizar la función API de Windows SetParent, que hay decenas de hilos en el ya.Sin embargo, no puedo encontrar nada escrito en mi problema exacto: Una región pequeña en la parte derecha e inferior de la aplicación WPF no está pintado en el interior de la no-WPF ventana de la aplicación.

La ventana WPF funcionando por sí mismo:
alt text

La ventana de WPF con WinForm de ventana de la aplicación como su padre:
alt text

Yo no experimentan el problema de si un swap de la aplicación WPF con una aplicación WinForms o un simple Win32 aplicación (como el Bloc de notas).

El WinForm código se parece a esto:

private void Form1_Load(object sender, EventArgs e)
    {
        // Start process
        var psi = new ProcessStartInfo("c:\\wpfapp\\wpfapp\\bin\\Debug\\wpfapp.exe");
        psi.WindowStyle = ProcessWindowStyle.Normal;
        _process = Process.Start(psi);

        // Sleep until new process is ready
        Thread.Sleep(1000);

        // Set new process's parent to this window
        SetParent(_process.MainWindowHandle, this.Handle);

        // Remove WS_POPUP and add WS_CHILD window style to child window
        const int GWL_STYLE = -16;
        const long WS_POPUP = 0x80000000;
        const long WS_CHILD = 0x40000000;
        long style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
        style = (style & ~(WS_POPUP)) | WS_CHILD;
        SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style);

        // Move and resize child window to fit into parent's
        MoveWindow(_process.MainWindowHandle, 0, 0, this.Width, this.Height, true);
    }

Nota:Soy consciente de que este uso de SetParent no es necesariamente una práctica recomendada, pero quiero y necesito encontrar la manera de hacerlo de esta manera, así que por favor, hágamelo :)

¿Fue útil?

Solución

He encontrado una solución que funciona bastante bien: Call MoveWindow antes SetParent:

private void Form1_Load(object sender, EventArgs e)
{
     // Start process            
     var psi = new ProcessStartInfo("C:\\WpfApp\\WpfApp.exe");
     psi.WindowStyle = ProcessWindowStyle.Normal;
     _process = Process.Start(psi);

     // Sleep until new process is ready
     Thread.Sleep(3000);

     // Move and resize child window to fit into parent's
     Rectangle rect;
     GetClientRect(this.Handle, out rect);
     MoveWindow(_process.MainWindowHandle, 0, 0, rect.Right - rect.Left, rect.Bottom - rect.Top, true);

     // Set new process's parent to this window
     SetParent(_process.MainWindowHandle, this.Handle);

     // Remove WS_POPUP and add WS_CHILD window style to child window
     long style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
     style = (style & ~(WS_POPUP) & ~(WS_CAPTION)) & ~(WS_THICKFRAME) | WS_CHILD;
     SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style);
}           

En el controlador de eventos SizeChanged, que toma al niño fuera de la matriz, llame MoveWindow y moverlo de nuevo en la matriz. Para reducir el parpadeo, escondo la ventana mientras se hace estas operaciones:

private void Form1_SizeChanged(object sender, EventArgs e)
{
     ShowWindow(_process.MainWindowHandle, SW_HIDE);

     var ptr = new IntPtr();
     SetParent(_process.MainWindowHandle, ptr);

     Rectangle rect;
     GetClientRect(this.Handle, out rect);

     MoveWindow(_process.MainWindowHandle, 0, 0, rect.Right - rect.Left, rect.Bottom - rect.Top, true);            
     SetParent(_process.MainWindowHandle, this.Handle);
     ShowWindow(_process.MainWindowHandle, SW_SHOW);
}

que no explica por qué MoveWindow no funciona después de SetParent, pero que resuelve mi problema, así que voy a marcar esto como la respuesta a menos que venga algo mejor arriba.

Otros consejos

Probablemente debería cambiar el tamaño de la ventana de WPF para que quepa en el área de cliente de su nueva ventana padre:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

// Move and resize child window to fit into parent's client area.
RECT rc = new RECT();
GetClientRect(this.Handle, out rc);
MoveWindow(_process.MainWindowHandle, 0, 0, rc.Right - rc.Left,
    rc.Bottom - rc.Top, true)

I usó una forma / UserControl con un panel. Entonces utilicé el Panel como el nuevo padre y establecer la nueva posición y el tamaño del panel con SetWindowPos a la ventana secundaria.

NOTA:. MS recomienda utilizar SetWindowPos después de usar SetWindowLong (o SetWindowLongPtr) para activar los nuevos estilos

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top