استضافة تطبيق WPF داخل تطبيق غير WPF باستخدام Win32 SetParent ()
سؤال
لديّ تطبيق WPF أريد أن أبدو وكأنه يتم استضافته داخل تطبيق آخر - غير WPF -. في الحياة الحقيقية ، يعد هذا التطبيق غير WPF عبارة عن ActiveX داخل Internet Explorer ، ولكن من أجل توضيح المشكلة التي أستخدمها في تطبيق Windows Forms البسيط.
يمكنني استخدام وظيفة Windows API SetParent ، والتي توجد العشرات من الخيوط الموجودة بالفعل. ومع ذلك ، لا يمكنني العثور على أي شيء مكتوب في مشكلتي الدقيقة: لا يتم رسم منطقة صغيرة على يمين وأسفل تطبيق WPF داخل نافذة تطبيق غير WPF.
نافذة WPF تعمل بمفردها:
نافذة WPF مع نافذة WinForm App كأم:
لا أواجه المشكلة إذا تم تبديل تطبيق 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 ، أخرج الطفل من الوالد ، واتصل 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) لتنشيط الأنماط الجديدة.