I have a WPF application that I want to look like it is hosted inside another - non-WPF - application. In real life this non-WPF app is an ActiveX inside Internet Explorer, but for the sake of illustrating the problem I use a simple Windows Forms app.
I use the Windows API function SetParent, which there are dozens of threads on already. However, I cannot find anything written on my exact problem: A small region on the right and bottom of the WPF app is not painted inside the non-WPF app's window.
The WPF window running by itself:
The WPF window with WinForm app's window as parent:
I don't experience the problem if a swap the WPF app with a WinForms app or a plain Win32 app (like Notepad).
The WinForm code looks like this:
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);
}
Note: I'm aware that this use of SetParent is not necessarily a recommended practice, but I want to and need to find out how to do it this way, so please let me :)
I found a workaround that works fairly well: Call MoveWindow before 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);
}
In the SizeChanged event handler, I take the child out of the parent, call MoveWindow and move it back into the parent. To reduce flickering, I hide the window while doing these operations:
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);
}
it doesn't explain why MoveWindow doesn't work after SetParent, but it solves my problem, so I will mark this as the answer unless something better comes up.
You probably should resize your WPF window so that it fits into the client area of its new parent window:
[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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With