C# Windows (触摸 -> click/focus) 窗体和控件之间的消息 (WM_xxx)

C# Windows (touch -> click/focus) messages between Form and Controls (WM_xxx)

我有一个第 3 方开源控件(哪个并不重要,重要的是它的 CefSharp 的 Chromium Web 浏览器 [v 43])。

最初,如果在控件内部单击时窗体菜单处于打开状态,则存在一个问题,菜单不会自行消失(好像控件正在吞下单击事件)。

为了避免这种情况,示例应用程序建议将 WM_MOUSEACTIVATE message between the form and the control, and reacting by posting a WM_NCLBUTTONDOWN 拦截回包含表单上的元素(在我的例子中是面板栏)。这解决了那个问题。

然而,它创造了另一个。现在,一旦您在控件(触摸屏)内部触摸至少 3 次,您就不能再触摸该控件之外的元素。您必须用鼠标单击 [外部元素] 才能恢复它再次响应触摸事件所需的焦点级别。

我发现如果我也拦截 WM_SETCURSOR 消息(到控件)并阻止泵 5 毫秒 (Thread.Sleep()),那么问题就会神奇地消失。

我很想知道发生了什么事。我认为这是一个 threading/context 问题,或者延迟 WM_SETCURSOR 消息允许表单中的相邻消息首先处理(这不可能是真的,因为它们共享同一个线程) ).

所以,我运行进行了一次测试(没有5ms的休眠)并实时记录了控件和表单接收到的所有消息。测试时,我用鼠标点击了我的面板栏(浏览器控件上方),然后(长短)触摸了控件内部的各个区域,然后再次触摸了该栏(被忽略)。

消息如下:

10/13/2015 02:37:00.295 PM  Form: WM_PARENTNOTIFY       1. [Click bar]
10/13/2015 02:37:00.295 PM  Form: WM_MOUSEACTIVATE

10/13/2015 02:37:05.458 PM  Chromium: WM_NCHITTEST      2. [Touch 1 -long]
10/13/2015 02:37:05.458 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:05.458 PM  Form: WM_PARENTNOTIFY
10/13/2015 02:37:05.458 PM  Form: 587
10/13/2015 02:37:05.458 PM  Chromium: 587
10/13/2015 02:37:05.458 PM  Chromium: WM_MOUSEACTIVATE
10/13/2015 02:37:05.458 PM  Chromium: 585
10/13/2015 02:37:05.458 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:05.458 PM  Chromium: 716
10/13/2015 02:37:05.458 PM  Chromium: WM_TOUCH  (x23)
10/13/2015 02:37:05.458 PM  Chromium: 582
10/13/2015 02:37:05.458 PM  Chromium: 581       (x22)
10/13/2015 02:37:05.674 PM  Chromium: 583
10/13/2015 02:37:05.674 PM  Chromium: 586
10/13/2015 02:37:05.674 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:05.674 PM  Chromium: WM_SETCURSOR
10/13/2015 02:37:05.674 PM  Chromium: WM_MOUSEMOVE
10/13/2015 02:37:05.674 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:05.674 PM  Form: WM_PARENTNOTIFY
10/13/2015 02:37:05.674 PM  Chromium: WM_MOUSEACTIVATE
10/13/2015 02:37:05.674 PM  Chromium: WM_SETCURSOR
10/13/2015 02:37:05.674 PM  Chromium: WM_LBUTTONDOWN
10/13/2015 02:37:05.674 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:05.674 PM  Chromium: WM_SETCURSOR
10/13/2015 02:37:05.674 PM  Chromium: WM_LBUTTONUP

10/13/2015 02:37:10.509 PM  Chromium: WM_NCHITTEST      3. [Touch 2 -long]
10/13/2015 02:37:10.509 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:10.509 PM  Form: WM_PARENTNOTIFY
10/13/2015 02:37:10.509 PM  Form: 587
10/13/2015 02:37:10.509 PM  Chromium: 587
10/13/2015 02:37:10.509 PM  Chromium: WM_MOUSEACTIVATE
10/13/2015 02:37:10.509 PM  Chromium: 585
10/13/2015 02:37:10.509 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:10.509 PM  Chromium: 716
10/13/2015 02:37:10.509 PM  Chromium: WM_TOUCH  (x27)
10/13/2015 02:37:10.509 PM  Chromium: 582
10/13/2015 02:37:10.509 PM  Chromium: 581       (x25)
10/13/2015 02:37:10.755 PM  Chromium: 583
10/13/2015 02:37:10.755 PM  Chromium: 586
10/13/2015 02:37:10.755 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:10.755 PM  Chromium: WM_SETCURSOR
10/13/2015 02:37:10.755 PM  Chromium: WM_MOUSEMOVE
10/13/2015 02:37:10.755 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:10.755 PM  Form: WM_PARENTNOTIFY
10/13/2015 02:37:10.755 PM  Chromium: WM_MOUSEACTIVATE
10/13/2015 02:37:10.755 PM  Chromium: WM_SETCURSOR
10/13/2015 02:37:10.755 PM  Chromium: WM_LBUTTONDOWN
10/13/2015 02:37:10.755 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:10.755 PM  Chromium: WM_SETCURSOR
10/13/2015 02:37:10.755 PM  Chromium: WM_LBUTTONUP

10/13/2015 02:37:25.525 PM  Chromium: WM_NCHITTEST      4. [Touch 3 -short]
10/13/2015 02:37:25.525 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:25.525 PM  Form: WM_PARENTNOTIFY
10/13/2015 02:37:25.525 PM  Form: 587
10/13/2015 02:37:25.525 PM  Chromium: 587
10/13/2015 02:37:25.525 PM  Chromium: 585
10/13/2015 02:37:25.525 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:25.525 PM  Chromium: 716
10/13/2015 02:37:25.525 PM  Chromium: WM_TOUCH  (x7)
10/13/2015 02:37:25.525 PM  Chromium: 582
10/13/2015 02:37:25.525 PM  Chromium: 581       (x5)
10/13/2015 02:37:25.586 PM  Chromium: 583
10/13/2015 02:37:25.586 PM  Chromium: 586

10/13/2015 02:37:30.440 PM  Chromium: WM_NCHITTEST      5. [Touch 4 -short]
10/13/2015 02:37:30.440 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:30.440 PM  Form: WM_PARENTNOTIFY
10/13/2015 02:37:30.440 PM  Form: 587
10/13/2015 02:37:30.440 PM  Chromium: 587
10/13/2015 02:37:30.440 PM  Chromium: 585
10/13/2015 02:37:30.440 PM  Chromium: WM_NCHITTEST
10/13/2015 02:37:30.440 PM  Chromium: 716
10/13/2015 02:37:30.440 PM  Chromium: WM_TOUCH  (x10)
10/13/2015 02:37:30.440 PM  Chromium: 582
10/13/2015 02:37:30.440 PM  Chromium: 581       (x8)
10/13/2015 02:37:30.518 PM  Chromium: 583
10/13/2015 02:37:30.518 PM  Chromium: 586

10/13/2015 02:37:35.324 PM  Form: WM_NCHITTEST          6. [Bar (i button) touch -ignored]
10/13/2015 02:37:35.324 PM  Form: WM_NCHITTEST
10/13/2015 02:37:35.324 PM  Form: WM_PARENTNOTIFY
10/13/2015 02:37:35.324 PM  Form: 587
10/13/2015 02:37:35.324 PM  Form: 282

据此,第3次和第4次触碰断焦。我们可以看到 Chromium 控件在 586 之后停止接收最后一堆消息。我猜它缺少 WM_PARENTNOTIFY 备份到导致问题的表单。

我找不到关于 282、581、582、583、586 和 587 消息的任何信息。我也许可以对 586 消息做出反应,然后手动 post 一个 WM_PARENTNOTIFY 到表单?我不确定快速连续接收其中两个会有什么效果?

有谁知道为什么 WM_SETCURSOR 上的 5 毫秒睡眠让这些消息保持流动?

或者有更好的解决方法吗?

一旦我包括了面板控件、按钮和我能想到的所有鼠标事件,我就能够发现一个模式:

这是一种有效的方法:

....
10/14/2015 01:00:10.263 PM  Chromium: WM_NCHITTEST
10/14/2015 01:00:10.263 PM  Chromium: WM_SETCURSOR
10/14/2015 01:00:10.263 PM  Chromium: WM_MOUSEMOVE
10/14/2015 01:00:10.263 PM  Chromium: WM_NCHITTEST
10/14/2015 01:00:10.263 PM  Form: WM_PARENTNOTIFY
10/14/2015 01:00:10.263 PM  Chromium: WM_MOUSEACTIVATE              <--
10/14/2015 01:00:10.263 PM  panelBrowserHeader: WM_NCLBUTTONDOWN    <--
10/14/2015 01:00:10.263 PM  Chromium: WM_SETCURSOR                  <--
10/14/2015 01:00:10.263 PM  Chromium: WM_LBUTTONDOWN
10/14/2015 01:00:10.263 PM  Chromium: WM_NCHITTEST
10/14/2015 01:00:10.263 PM  Chromium: WM_SETCURSOR
10/14/2015 01:00:10.263 PM  Chromium: WM_LBUTTONUP

这是一个没有的:

....
10/14/2015 01:00:15.240 PM  Chromium: WM_NCHITTEST
10/14/2015 01:00:15.240 PM  Chromium: WM_SETCURSOR
10/14/2015 01:00:15.240 PM  Chromium: WM_MOUSEMOVE
10/14/2015 01:00:15.240 PM  Chromium: WM_NCHITTEST
10/14/2015 01:00:15.240 PM  Form: WM_PARENTNOTIFY
10/14/2015 01:00:15.240 PM  Chromium: WM_MOUSEACTIVATE              <--
10/14/2015 01:00:15.240 PM  Chromium: WM_SETCURSOR                  <--
10/14/2015 01:00:15.240 PM  panelBrowserHeader: WM_NCLBUTTONDOWN    <--
10/14/2015 01:00:15.240 PM  Chromium: WM_LBUTTONDOWN
10/14/2015 01:00:15.240 PM  Chromium: WM_MOUSELEAVE     

问题是,WM_NCLBUTTONDOWN 事件(作为对 WM_MOUSEACTIVATE 消息的反应而发布)有时会在下一个预定的 WM_SETCURSOR 消息之后发生。这似乎给人一种错觉,即触摸事件在其原始控件的边界之外完成,因此它自己触发了一条 WM_MOUSELEAVE 消息。

通过延迟 WM_SETCURSOR 消息,它确保它们以正确的顺序触发。

我仍然对此感到困惑,因为我确信消息泵是一个单线程循环时间切片通过每个控件的消息队列。因此,当我的 Chromium 控件收到 WM_CURSOR 消息时阻止执行,我认为我阻止了该 GUI 线程上存在的所有消息队列。

任何人,它回答了为什么这样一个随机 "fix" 绕过了问题的问题。