Frage

Ich habe eine WPF-Anwendung, die so aussehen soll, als wäre sie in einer anderen – Nicht-WPF-Anwendung – gehostet.Im wirklichen Leben ist diese Nicht-WPF-App ein ActiveX im Internet Explorer, aber um das Problem zu veranschaulichen, verwende ich eine einfache Windows Forms-App.

Ich verwende die Windows-API-Funktion SetParent, zu der es bereits Dutzende Threads gibt.Allerdings kann ich zu meinem genauen Problem nichts Geschriebenes finden: Ein kleiner Bereich rechts und unten in der WPF-App wird nicht im Fenster der Nicht-WPF-App angezeigt.

Das WPF-Fenster läuft alleine:
alt text

Das WPF-Fenster mit dem Fenster der WinForm-App als übergeordnetem Fenster:
alt text

Das Problem tritt bei mir nicht auf, wenn ich die WPF-App durch eine WinForms-App oder eine einfache Win32-App (wie Notepad) austausche.

Der WinForm-Code sieht folgendermaßen aus:

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);
    }

Notiz:Mir ist bewusst, dass diese Verwendung von SetParent nicht unbedingt eine empfohlene Vorgehensweise ist, aber ich möchte und muss herausfinden, wie das auf diese Weise geht, also lassen Sie es mich bitte :)

War es hilfreich?

Lösung

Ich habe einen Workaround gefunden, der ziemlich gut funktioniert:Rufen Sie MoveWindow vor SetParent auf:

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);
}           

Im SizeChanged-Ereignishandler nehme ich das untergeordnete Element aus dem übergeordneten Element, rufe MoveWindow auf und verschiebe es zurück in das übergeordnete Element.Um das Flackern zu reduzieren, blende ich das Fenster aus, während ich diese Vorgänge ausführe:

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);
}

Es erklärt nicht, warum MoveWindow nach SetParent nicht funktioniert, aber es löst mein Problem, daher werde ich dies als Antwort markieren, sofern nicht etwas Besseres auftaucht.

Andere Tipps

Sie sollten wahrscheinlich die Größe Ihres WPF-Fensters ändern, damit es in das passt Kundenbereich seines neuen übergeordneten Fensters:

[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)

Ich habe ein Formular/UserControl mit einem Panel verwendet.Dann habe ich das Panel als neues übergeordnetes Fenster verwendet und die neue Position und Größe des Panels mit SetWindowPos auf das untergeordnete Fenster festgelegt.

NOTIZ:MS empfiehlt, SetWindowPos nach der Verwendung von SetWindowLong (oder SetWindowLongPtr) zu verwenden, um die neuen Stile zu aktivieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top