在WinForm控件上我们可以看到很多关于键盘消息处理的方法,比如OnKeyDown, OnKeyPress, ProcessCmdKey, ProcessDialogKey,IsInputKey等等,那么这些方法是如何被组织的,每一个方法的具体含义又是什么哪?Win32的键盘消息又是如何到达控件上的这些方法的,本文将着重阐述这些问题,对.Net WinForm控件的键盘消息处理过程进行剖析。 

1.      WinForm消息循环

大家都知道WinForm也是依赖于底层的消息机制的,通常我们的WinForm应用程序会以如下方式启动:

Application.Run(new Form());

上面的代码将会在当前线程启动一个消息循环,并且显示指定窗体。

反编译Application类的Run方法,我们可以看到这一点:

public static void Run(Form mainForm)
{
   ThreadContext.FromCurrent().RunMessageLoop(-1,new ApplicationContext(mainForm));
}

启动消息循环之后,操作系统就会将用户对于当前应用程序的UI输入转换为Windows消息发给当前线程进行处理。本文的重点不在于讲述Windows消息机制,而在于底层消息到达.Net这一层后,WinForm控件是如何处理的。

2.      消息处理

从上面可以看到通过ThreadContext类型的RunMessageLoop方法,构建了消息循环。那么对于一个特定的Windows消息,ThreadContext又是如何处理的哪?

分析ThreadContext的代码可以发现其调用关系如下:

在LocalModalMessageLoop方法中我们就可以看到对于Windows消息的处理了:

private bool LocalModalMessageLoop()
{
     // ...
     if(!PreTranslateMessage(ref msg))
     {
          // ...
          UnsafeNativeMethods.TranslateMessage(ref msg);
          UnsafeNativeMethods.DispatchMessage(ref msg);
      }
      // ...
}

可以发现对于一个特定的Windows消息,分为两个阶段进行处理:

  • PreTranslateMessage
  • DispatchMessage

WinForm控件消息的处理将从这两个地方开始。

2.1  PreTranslateMessage

PreTranslateMessage提供了一个时机,来决定是否应该Dispatch这个消息,如果返回值为False,这个消息才会派发给WinForm控件。

PreTranslateMessage分为两个层次,第一优先调用当前应用程序的IMessageFilter来进行处理,用户可以在这一层进行消息预处理或者消息过滤。如果没有被过滤掉,调用当前控件的PreProcessMessage方法进行消息预处理。

Control类型的PreProcessMessage处理流程如下:

对于WM_KeyDown消息,其预处理Control类上有三个时机:ProcessCmdKey,IsInputKey以及ProcessDialogKey;对于WM_KeyChar消息,其预处理有两个时机:IsInputChar和ProcessDialogChar。

ProcessCmdKey默认用来处理快捷键以及菜单快捷键,此方法会递归调用父控件。如果返回值为False,继续调用IsInputKey,决定是否引发KeyDown事件。如果不是InputKey,调用ProcessDialogKey来检查该键是否为导航键,或者进行一些特别的处理,此方法会递归调用父控件的处理。

IsInputChar决定输入字符是否为普通字符,如果返回值为True会引发KeyPress事件。返回值为False会调用ProcessDialogChar,ProcessDialogChar默认用来处理Mnemonic键,例如控件的文本为“&OK”, 对于Char“O”的处理。

2.2  DispatchMessage

如果PreTranslateMessage没有过滤掉该Windows消息的话,该消息将会派发到控件,交由控件的WndProc函数进行处理。

下图是控件的处理流程:

消息到达WnProc之后,会交由ProcessKeyMessage,ProcessKeyPreview以及ProcessKeyEventArgs处理。每一个方法都会返回一个Boolean值,表明控件是否已经处理了该消息。

ProcessKeyMessage会处理所有由WndProc过来的所有键消息,首先会调用父控件的ProcessKeyPreview函数,如果返回True,表明父控件已经处理。否则调用ProcessKeyEventArgs来触发控件的KeyDown,KeyPress,KeyUp事件。

3.      结语

本文着重讲述了WinForm控件对于键盘消息的处理,分析了消息预处理以及处理两个阶段的各个函数。在进行三方控件的开发中可以根据需要重载这些函数,另外也可从其设计以及实现思路中获得更多启发。

.Net WinForm 控件键盘消息处理剖析的更多相关文章

  1. WinForm 控件键盘消息处理剖析(转)

    一直想整理键盘事件的调用顺序,刚好看见园子里的这篇文章,写的不错,就转载了:http://www.cnblogs.com/tedzhao/archive/2010/09/07/1820557.html ...

  2. 通过WinForm控件创建的WPF控件无法输入的问题

    今天把写的一个WPF程序发布到别的机器上执行,发现一个比较奇怪的问题:在那个机器上用英文输入法无法输入数字,非要切换到中文输入法才行:但在我的机器上却是好好的. 最开始以为是输入法的问题,弄了好一阵子 ...

  3. 通过C# WinForm控件创建的WPF WIndow窗口控件无法输入的问题

    原文:通过WinForm控件创建的WPF 控件无法输入的问题 今天把写的一个WPF程序发布到别的机器上执行,发现一个比较奇怪的问题:在那个机器上用英文输入法无法输入数字,非要切换到中文输入法才行:但在 ...

  4. 在WPF中使用WinForm控件方法

    1.      首先添加对如下两个dll文件的引用:WindowsFormsIntegration.dll,System.Windows.Forms.dll. 2.      在要使用WinForm控 ...

  5. WPF 调用WinForm控件

    WPF可以使用WindowsFormsHost控件做为容器去显示WinForm控件,类似的用法网上到处都是,就是拖一个WindowsFormsHost控件winHost1到WPF页面上,让后设置win ...

  6. WinForm控件TreeView 只部分节点显示 CheckBox

    WinForm控件TreeView 只部分节点显示  CheckBox 用过asp.net的应该知道,要在treeview中实现上述功能可以使用ShowCheckBox 属性指定那些节点显示check ...

  7. Winform控件重写

    Winform控件重写 因为最近的项目中越来越多的遇到了比较特殊的一些控件,有时候我们自己封装一下可能更加方便我们的使用,下面是我们项目中用到的,简单做一个记录. TextBox控件重写 主要的控制代 ...

  8. MFC编程入门之九(对话框:为控件添加消息处理函数)

    这一节讲的主要内容是如何为控件添加消息处理函数. MFC为对话框和控件定义了诸多消息,我们对他们操作时会触发消息,这些消息最终由消息处理函数处理,比如我们点击按钮时就会产生BN_CLICKED消息,修 ...

  9. c#Winform控件总结

    1. C# WinForm控件.自定义控件整理(大全) (http://www.cnblogs.com/top5/archive/2010/04/29/1724039.html) 2. c#窗体控件用 ...

随机推荐

  1. CMMI-4中19个PA的大致描述

    组织过程资产库下面有组织级标准过程库, 这个库里一共有19各PA(就是标准过程啦) PA的英文是Process Area       CM(配置管理过程,英文是Configuration Manage ...

  2. 微信原始demo

    有可能下载最新的demo下载不到这个原版的精简demo了. 这里为大家提供一下.以下代码为原始未改动代码   01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 ...

  3. 三种方法实现PCA算法(Python)

    主成分分析,即Principal Component Analysis(PCA),是多元统计中的重要内容,也广泛应用于机器学习和其它领域.它的主要作用是对高维数据进行降维.PCA把原先的n个特征用数目 ...

  4. IE CSS Bugs 列表和解决方法

    我们在开发中我们常会在IE中遇到很多莫名的bug,尤其是老态龙钟的IE6浏览器.为了提高我们的开发效率,需要经常总结,整理工作中遇到的问题.我们在网络上找到的IE Bugs 资料是零散的.不过,在老外 ...

  5. PHP开发中需要注意几点事项,新手少走弯路必备知识

    这篇文章主要介绍了PHP开发需要注意的几点事项总结,非常详细,需要的朋友可以参考下.新手多看看避免走弯路. 1.使用内嵌的HTML代码,而不是PHP的echo语句. 因为PHP是一门嵌入式Web编程语 ...

  6. hdu 4109 dfs+剪枝优化

    求最久时间即在无环有向图里求最远路径 dfs+剪枝优化 从0节点(自己添加的)出发,0到1~n个节点之间的距离为1.mt[i]表示从0点到第i个节点眼下所得的最长路径 #include<iost ...

  7. HDOJ 题目3555 Bomb(数位DP)

    Bomb Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Total Subm ...

  8. 在linux上手动搭建svn服务器

    svn服务器的搭建 环境: linux CentOS 7 安装: 1.安装svn服务器 yum install subversion 2.查看版本 svnserve --version 3.创建版本库 ...

  9. ABP框架 - 规约

    文档目录 本节内容: 简介 示例 创建规约类 在仓储里使用规约 组合规约 讨论 何时使用 何时不用 简介 规约模式是一个特别的软件设计模式,业务逻辑可以使用boolean逻辑重新链接业务逻辑(维基百科 ...

  10. Git远程库版本回滚

    在git的一般使用中,如果发现错误的将不想staging的文件add进入index之后,想回退取消,这就叫做git代码库回滚: 指的是将代码库某分支退回到以前的某个commit id.可以使用命令:g ...