Хостинг приложение WPF внутри приложения без WPF с использованием win32 setParent ()
Вопрос
У меня есть приложение WPF, которое я хочу выглядеть так, как будто оно размещено внутри другого - приложение без WPF. В реальной жизни это приложение без WPF является ActiveX Intole Internet Explorer, но ради иллюстрации проблемы я использую простое приложение Windows Forms.
Я использую функцию Windows API SetParent, на которой уже есть десятки потоков. Тем не менее, я не могу найти ничего, что написано по моей точной проблеме: Небольшая область справа и нижней части приложения WPF не окрашен в окно приложения без WPF.
Окно WPF, работающее само по себе:
Окно WPF с окном приложения Winform в качестве родителя:
Я не сталкиваюсь с проблемой, если замените приложение 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 не обязательно является рекомендуемой практикой, но я хочу, и мне нужно выяснить, как это сделать так, поэтому, пожалуйста, дайте мне :)
Решение
Я нашел обходной путь, который работает довольно хорошо: звоните 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 Event я вынимаю ребенка из родителей, вызовут 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, но решает мою проблему, поэтому я отмечу это как ответ, если не появится что -то лучшее.
Другие советы
Вы, вероятно, должны изменить размер своего окна 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)
Я использовал форму / usercontrol с панелью. Затем я использовал панель в качестве нового родителя и установил новую позицию и размер панели с SetWindowpos в окно ребенка.
Примечание. MS рекомендует использовать SetWindowPos после использования SetWindowlong (или setWindowlongptr) для активации новых стилей.