前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >将 WPF 嵌入到 MFC 中,无法响应键盘输入

将 WPF 嵌入到 MFC 中,无法响应键盘输入

作者头像
jgrass
发布2024-12-25 18:35:46
发布2024-12-25 18:35:46
6000
代码可运行
举报
文章被收录于专栏:蔻丁杂记蔻丁杂记
运行总次数:0
代码可运行

将 WPF 窗口嵌入到 MFC 窗口中 中提到,可以将 WPF 嵌入到 MFC 窗口中, 但遗留了一个没有发现的问题,WPF 界面,无法响应键盘的输入。

示例源码已经在 https://gitee.com/Jasongrass/DemoPark/tree/master/Code/Embed_WPF_to_MFC/MFCMerge

🍕 问题调查

遇到键盘无法响应,怀疑就是消息循环的处理问题,但不确定具体的细节。

首先尝试将 WPF 的窗口运行,放在一个独立的线程中,类似这样:

代码语言:javascript
代码运行次数:0
复制
private static void StartNewWindow(){    Thread staThread = new Thread(() =>    {        // 创建一个新的窗口        _mainWindow = new MainWindow() { Top = -100000 };        _mainWindow.Show();        var interopHelper = new WindowInteropHelper(_mainWindow);        _mainWindowPtr = (int)(interopHelper.Handle);        MainWindow.myHwnd = _mainWindowPtr;        // 开始消息循环        System.Windows.Threading.Dispatcher.Run();    });    // 设置线程为 STA    staThread.SetApartmentState(ApartmentState.STA);    staThread.Start();}

WPF 界面在 MFC 中首次加载之后,确实可以在 TextBox 输入,但只要 MFC 获取焦点,再重新回到 WPF 界面,就无法输入了。

其实,在另一个线程或者进程启动 WPF,对这个问题是没有帮助的,因为只要设置成了父子窗口,消息循环就会合并。

使用 SetParent 跨进程设置父子窗口时的一些问题(小心卡死) - walterlv

继续调查,在搜索中,看到类似 ElementHost HwndSource 这样的关键词,以为是需要用这些将 WPF 窗口包装一下,再嵌入到 MFC 中,但实际上也是无效的。

ElementHost

Is it possible to host WPF Core (.NET 5.0) content in an MFC application? - Microsoft Q&A(https://learn.microsoft.com/en-us/answers/questions/695877/is-it-possible-to-host-wpf-core-(-net-5-0%29-content)

HwndSource

Host WPF content in Win32 | Microsoft Learn

HwndSource Class (System.Windows.Interop) | Microsoft Learn

How do I host WPF content in MFC Applications? - Stack Overflow

问题的关键不在这里

🍕 问题解决

问题的关键是 WM_GETDLGCODE 这个消息

winapi - Non-Modal WPF control hosted in MFC Dialog does not receive keyboard input - Stack Overflow

WM_GETDLGCODE message (Winuser.h) - Win32 apps | Microsoft Learn

By default, the system handles all keyboard input to the control; the system interprets certain types of keyboard input as dialog box navigation keys. To override this default behavior, the control can respond to the WM_GETDLGCODE message to indicate the types of input it wants to process itself. 默认情况下,系统处理控件的所有键盘输入;系统将某些类型的键盘输入解释为对话框导航键。要覆盖此默认行为,控件可以响应 WM_GETDLGCODE 消息以指示它想要自行处理的输入类型。

默认情况下,在 dialog 中,键盘输入是被拦截的,所以只需要处理 WM_GETDLGCODE 就可以了。

代码语言:javascript
代码运行次数:0
复制
using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Interop;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;
namespace MyWPFApp{    public partial class MainWindow : Window    {        // 定义窗口过程的委托        private HwndSourceHook? _hwndSourceHook;
        public MainWindow()        {            InitializeComponent();            Loaded += MainWindow_Loaded;        }
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)        {            HwndSource? source = PresentationSource.FromVisual(this) as HwndSource;            if (source != null)            {                // 在窗口初始化时设置窗口过程钩子                _hwndSourceHook = new HwndSourceHook(WndProc);                source.AddHook(_hwndSourceHook);            }        }
        // 窗口过程函数        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)        {            const int WM_GETDLGCODE = 0x0087;            const int DLGC_WANTALLKEYS = 4;
            // 处理 WM_GETDLGCODE 消息            if (msg == WM_GETDLGCODE)            {                handled = true;                return new IntPtr(DLGC_WANTALLKEYS);            }
            return IntPtr.Zero; // 继续传递其他消息        }
        protected override void OnClosed(EventArgs e)        {            // 清理钩子            if (_hwndSourceHook != null)            {                HwndSource? source = PresentationSource.FromVisual(this) as HwndSource;                if (source != null)                {                    source.RemoveHook(_hwndSourceHook);                }            }
            base.OnClosed(e);        }    }}

🍕 More about WM_GETDLGCODE

搜索的时候发现 Raymond Chen 大佬的两篇文章,可以更好的理解 WM_GETDLGCODE

那些不理解对话管理器的人注定要重新实现它,而且很糟糕 - Those who do not understand the dialog manager are doomed to reimplement it, badly - The Old New Thing

WM_GETDLGCODE 消息是一条查询消息,不应修改状态 - The WM_GETDLGCODE message is a query message and should not modify state - The Old New Thing

How to eat keys in WM_KEYDOWN - Stack Overflow

🍕 OTHER

WPF 让窗口激活作为前台最上层窗口的方法 | 林德熙

原文链接: https://cloud.tencent.com/developer/article/2481582

本作品采用 「署名 4.0 国际」 许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024年11月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🍕 问题调查
  • 🍕 问题解决
  • 🍕 More about WM_GETDLGCODE
  • 🍕 OTHER
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档