WebEnh

.net7 mvc jquery bootstrap json 学习中 第一次学PHP,正在研究中。自学进行时... ... 我的博客 https://enhweb.github.io/ 不错的皮肤:darkgreentrip,iMetro_HD
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

.NET桌面程序混合开发之四:键盘事件的响应

Posted on 2022-03-25 16:00  WebEnh  阅读(192)  评论(0编辑  收藏  举报

1. 问题

在生产环境中,有一些场景需要窗体来响应键盘事件(注意,是窗体响应,而不是窗体上的控件响应),如解析扫码枪的扫描结果。但在嵌入WebView2的Form程序,Host Form无法对键盘事件(如窗体的KeyPress)进行截获,同样,也无法对WebView2本身进行键盘事件的响应处理。

2. 传统的键盘事件如何响应

在解决问题前,回顾一下在C#中,传统的窗体键盘事件如何响应。

  • 在窗体属性中,设置KeyPreview属性为Ture;
    enter description here
  • 在窗体事件中,为窗体添加KeyPress事件;
    enter description here
  • 在IDE为你自动添加的响应处理函数中,编写你的处理逻辑
  1. private void Form1_KeyPress(object sender, KeyPressEventArgs e)
  2. {
  3. //编写你的响应逻辑
  4. MessageBox.Show(e.KeyChar.ToString());
  5. }

而在嵌入WebView2的Form程序,Form对键盘事件,无法按照传统处理方式进行截获,也就无法按照原来的处理方式处理了。

3. 问题产生的原因

先来梳理一下窗体响应事件的顺序。

  • 当窗体上没有任何其他控件的时候,窗体是可以直接响应这些消息的,也就是说可以正常响应键盘事件。
  • 但是当窗体存在其他控件后,我们会发现窗体再也不会响应按键消息了,因为这些消息都由其上的控件所处理掉并且不再发给父窗体。
  • 通过Form类的KeyPreview的属性,它是可以接收得到按键消息。也就是第2部分提到的处理方式

分析到这里,基本上能找到webView2加入后引发的怪异行为了。总结起来就是:WebView2拦截了键盘事件。为什么WebView2要这么做?因为WebView本看上是浏览器内核,它要通过最高级别的键盘事件处理一些浏览器事件。如F5刷新,F12调试等,你会发现,哪怕焦点不在webView上,WebView2仍然能响应F5的刷新事件。当然,浏览器的快捷键可以被禁止,但这是另一话题。不可否认的事实就是:WebView影响了消息传递机制。微软可能也意识到这个问题,但对于浏览器的快捷键与用户自定义按键事件的协调,确实是个头疼的事情。这部分是我的猜测。

4. 问题的解决

托管(managed)方式的消息响应失效,尝试非托管方式的处理。思路是:调用Win32的钩子函数,对键盘事件进行拦截,同时将扫描行为进行包装,将获得到的扫描结果(以回边键为结束符),返回托管调用方。

  • 编写单独的ScanerHook类,处理扫描键盘事件。
  • 托管代码,通过注册ScanerEvent事件响应函数,来接收扫描结果。在WinForm中的关键代码如下:
  1. private ScanerHook listener = new ScanerHook();
  2. public WebViewForm()
  3. {
  4. InitializeComponent();
  5. InitializeAsync();
  6. listener.ScanerEvent += (s) =>
  7. {
  8. webView.CoreWebView2.PostWebMessageAsString(s.Result); //将扫描结果交给页面
  9. };
  10. }
  • 当窗体打开时,在托管代码中安装钩子
  1. private void WebViewForm_Load(object sender, EventArgs e)
  2. {
  3. listener.Start();
  4. }
  • 在窗体关闭时,不要忘记卸载钩子
  1. private void WebViewForm_FormClosed(object sender, FormClosedEventArgs e)
  2. {
  3. listener.Stop();
  4. }

至此,页面已经“具备了”扫描条码(二维码)的能力。示例代码是在VS2022,用.net6实现的。

 dotnet6form.zip10.59 KB