创建 WPF 窗口作为 Win32 MDI 主机窗口的 MDI 子窗口

问题描述 投票:0回答:1

我有一个本机 Win32 MDI 应用程序。该应用程序调用创建 WPF 窗口的非托管代码,并调用 User32.SetParent() 将其包装到 Win32 MDI 窗口中。这工作正常,但 WPF 窗口的行为与 MDI 不同 - 例如激活 WPF 窗口会使 Win32 窗口成为背景窗口。

我的问题 - 是否可以将 WPF 窗口转换/标记为 MDI 窗口?我知道应该可以在本机代码下创建 MDI 子项,然后将 WPF 控件包装在里面,但我不想这样做。

c# wpf windows winapi mdi
1个回答
0
投票

TL;DR: 接近起来并不难,但在这里使用 WPF 并不实用。最佳实践是使用 WinForms 作为 MDI 子窗口,并使用

ElementHost
托管 WPF 内容

更长的版本: 你可以通过

HwndSource
获得 75%:

var winWPFContent = new HwndSource(classStyle: 8, style: 0x56cc0000, exStyle: 0x00050140,
    x: 100, y: 100, width: 600, height: 400, "Example title", hwnd);
winWPFContent.RootVisual = new UserControl1();

这会起作用,但会失败:

  • 轨道激活(标题栏变亮和变暗)
  • 正确最大化/恢复(可能会“卡住”最大化)
  • 使用下一个/上一个在 MDI 窗口中循环(将循环到桌面窗口,而不是停留在 MDI 中)
  • 像普通 MDI 窗口一样调整大小/移动(不受 MDI 框架限制)

我认为加速键和选项卡导航也存在问题,可以通过

IKeyboardInputSink
解决,但我还没有审查这些问题。

大多数问题都可以通过调用

DefMDIChildProc
根据文档来解决,但 WPF 是硬编码来调用
DefWindowProcW
。我找不到公开的 API 来影响它或拦截调用。
HwndSource
钩子 run before 处理诸如调整大小和移动之类的事情。

如果我们不顾一切,我们可以使用反射来调用

HwndWrapper.AddHookLast

来拦截对
DefWindowProcW
的调用并替换
DefMDIChildProc

var winWPFContent = new HwndSource(classStyle: 8, style: 0x56cc0000, exStyle: 0x00050140, x: 100, y: 100, width: 600, height: 400, "Example title", hwnd); winWPFContent.AddHookLast(MdiChildHook); winWPFContent.RootVisual = new UserControl1(); internal static class HwndSourceDirtyHackyExtensions { public static void AddHookLast(this HwndSource hwndSource, HwndSourceHook hook) { // Get HwndWrapper var a = typeof(HwndSource).GetField("_hwndWrapper", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("_hwndWrapper not found"); var wrapper = a.GetValue(hwndSource) ?? throw new InvalidOperationException("_hwndWrapper null"); // Call AddHookLast var miAddHookLast = wrapper.GetType().GetMethod("AddHookLast", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("AddHookLast not found"); var tHwndWrapperHook = miAddHookLast.GetParameters().Single().ParameterType; var hookDelegate = Delegate.CreateDelegate(tHwndWrapperHook, hook.Method); miAddHookLast.Invoke(wrapper, new object[] { hookDelegate }); } }
这在最大化/恢复和选项卡导航方面仍然存在问题,但在其他方面似乎表现正常。不过,你必须比我更乐观才能将其投入生产。

使用winform。

© www.soinside.com 2019 - 2024. All rights reserved.