我注意到 WinForms 有许多处理命令或键 (Process*()
) 和(预)过滤系统消息的方法,但我仍然不清楚它们各自的目的。
官方文档有些晦涩难懂,我没有找到任何明确完整的回应。
我讲的是以下几种方法:
PreFilterMessage(ref Message m)
ProcessCmdKey(ref Message msg, Keys keyData)
WndProc(ref Message m)
ProcessDialogKey(Keys keyData)
PreProcessMessage(ref Message msg)
ProcessKeyMessage(ref Message m)
ProcessKeyPreview(ref Message m)
一些用于拦截键(如 ProcessCmdKey
或 ProcessDialogKey
),一些用于拦截消息(彼此)。但是为什么方法那么多呢?它们的用途和用例是什么?
我想每个方法的执行顺序是不同的。
这是我所知道的(或者我认为知道的):
PreFilterMessage
:首先拦截消息。您可以在这里停止以下所有方法的消息分发!ProcessCmdKey
:拦截所有键,甚至是组合键、特殊键和命令键。很适合检测整个表单上的快捷键(如 Ctrl + D)。您可以在此处停止分发 key 。WndProc
:过滤后秒拦截消息?我只用它来检测用户是否点击了右上角的“X”,但我想这可以用另一种方法实现!ProcessDialogKey
:只拦截一个键,可能在ProcessCmdKey
之后,所有键的控件事件之前。PreProcessMessage
:在WndProc
之前和PreFilterMessage
之后?我不知道为什么要使用它。ProcessKeyMessage
:拦截关键消息。好像很少用到。ProcessKeyPreview
:在预览事件之前拦截 key ?也很少使用。
在深度上,我认为这是正确的执行顺序:
- 前置过滤器
- 过滤器
- 预处理
- 过程
- 事件
为什么这么多步骤?
任何信息或具体用例将不胜感激!
最佳答案
native Windows GUI 应用程序通常有 一个 消息循环,底层 winapi 调用是 GetMessage()。但是有 许多 窗口获取消息,底层 winapi 调用是 DispatchMessage()。在您的 .NET 应用程序中,您有一个 Application.Run() 调用,但有许多 WndProc() 方法,每个控件一个。它们中的大多数都隐藏在 .NET Framework 代码中,仅当您覆盖它时才会公开。
通常需要 Hook 到消息循环中,在消息被分派(dispatch)到控件并到达 WndProc() 之前拦截消息。最明显的原因是键盘快捷键,无论哪个控件具有焦点,您都希望对它们进行操作。如果您必须在每个 控件上使用 KeyDown 来检测快捷方式,那当然会非常痛苦。以及不太明显的原因,例如,ActiveX 控件因必须与主机协商而引人注目。
Winforms 提供了很多 的扩展点来拦截消息。太多了,真的,但有点不可避免的副作用,因为不想预测它们在哪些情况下可能有用。按顺序:
- IMessageFilter.PreFilterMessage() 让您可以在 GetMessage() 检索到消息后立即查看消息。请记住,您只能看到发布到消息队列的消息,底层调用是 PostMessage(),您看不到已发送的消息。这限制了它对用户输入、键盘和鼠标消息的可用性。很少对此进行修补,但您可以使用它来使鼠标的行为有所不同并检测用户是否正在与您的程序交互。例如,想要在一段时间不活动后自动注销用户的程序员使用它。
- OnPreviewKeyDown() 和 PreviewKeyDown 事件。特定于具有焦点的控件。控件在测试快捷方式之前拦截击键的一种方法。例如,您可以在使用光标键移动焦点之前检测光标键。覆盖 IsInputKey() 的替代方法。
- PreProcessMessage()。与 PreFilterMessage 非常相似,但特定于具有焦点的控件。对于 ActiveX 控件很重要,我个人从未用过它。
- ProcessCmdKey()。这是实现您自己的快捷键的主要方法。
- IsInputKey(),允许控件投票决定是否应将通常用于导航的键发送给控件。例如,当您需要使用光标键时,您可以覆盖此方法。
- ProcessDialogKey()。就像 ProcessCmdKey() 一样,但过滤了应该被视为输入键的击键。默认实现使键盘消息冒泡到父控件,使您可以选择重写 ProcessCmdKey() 的位置。除了停止冒泡之外,我想不出您想要覆盖它的充分理由,而且从来没有这样做过。
- IsInputChar(),与 IsInputKey 非常相似,但用于 KeyPress 事件。我自己从未使用过它,而是一种早期过滤输入的方法。
- ProcessDialogChar(),可用于提供键入键快捷键击行为。这很不寻常。
- WndProc(),处理消息的主力方法。您重写它以允许控件响应现有事件未涵盖的消息。或者自定义现有 native 控件的行为。
- ProcessKeyEventArgs(),生成键盘事件(OnKeyDown、OnKeyUp、OnKeyPress)的通用方法,由 Control.WndProc() 调用。我想不出覆盖它的理由,以实现古怪的 Form.KeyPreview 属性(VB6 兼容属性)而著称,这可能是它暴露的原因。
确实是一个曲折的迷宫。
尽量保持理智,始终覆盖 ProcessCmdKey() 以实现快捷键。覆盖 IsInputKey() 让您的控件看到导航键。并且仅覆盖 WndProc() 以自定义现有控件。
关于c# - 所有 Process* 方法和所有消息过滤器的目的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26158578/