Размещение приложения WPF внутри приложения не-WPF с использованием WIn32 SetParent () - PullRequest
8 голосов
/ 09 ноября 2010

У меня есть приложение WPF, которое, как мне кажется, размещено внутри другого приложения, не являющегося WPF.В реальной жизни это не WPF-приложение представляет собой ActiveX в Internet Explorer, но для иллюстрации проблемы я использую простое приложение Windows Forms.

Я использую функцию Windows API SetParent, в которой уже десятки потоков.Тем не менее, я не могу найти ничего написанного о моей конкретной проблеме: Небольшая область справа и снизу приложения WPF не закрашена в окне не-WPF приложения .

Окно WPFвыполняется сам по себе:
alt text

Окно WPF с окном приложения WinForm в качестве родителя:
alt text

У меня нет проблем при замене WPFприложение с приложением WinForms или обычное приложение Win32 (например, Блокнот).

Код WinForm выглядит следующим образом:

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

Примечание. Мне известно, что использование SetParent не являетсяобязательно рекомендуемая практика, но я хочу и должен выяснить, как это сделать, поэтому, пожалуйста, дайте мне:)

Ответы [ 3 ]

6 голосов
/ 10 ноября 2010

Я нашел обходной путь, который работает довольно хорошо: вызовите MoveWindow перед 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);
}           

В обработчике событий SizeChanged я вынимаю дочерний элемент из родительского, вызываю MoveWindow и перемещаю его обратно в родительский. Чтобы уменьшить мерцание, я скрываю окно при выполнении этих операций:

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

это не объясняет, почему MoveWindow не работает после SetParent, но решает мою проблему, поэтому я помечу это как ответ, если не появится что-то лучшее.

3 голосов
/ 09 ноября 2010

Возможно, вам следует изменить размер окна WPF так, чтобы оно вписывалось в клиентскую область его нового родительского окна:

[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)
0 голосов
/ 26 января 2011

Я использовал Form / UserControl с панелью. Затем я использовал Panel в качестве нового родителя и установил новое положение и размер панели с SetWindowPos для дочернего окна.

ПРИМЕЧАНИЕ. MS рекомендует использовать SetWindowPos после использования SetWindowLong (или SetWindowLongPtr) для активации новых стилей.

...