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

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

WinForm 控件键盘消息处理剖析(转)的更多相关文章

  1. .Net WinForm 控件键盘消息处理剖析

    在WinForm控件上我们可以看到很多关于键盘消息处理的方法,比如OnKeyDown, OnKeyPress, ProcessCmdKey, ProcessDialogKey,IsInputKey等等 ...

  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. 如何在open xml excel 中存储自定义xml数据?

    如何在open xml excel 中存储自定义xml数据? 而且不能放在隐藏的cell单元格内,也不能放在隐藏的sheet内,要类似web网站的Application变量,但还不能是VBA和宏之类的 ...

  2. python appium 封装获取toast方法

    获取toast text封装,传入toast 部分文本,返回全部文本 def get_toast_text(self,text): try: toast_loc = (By.XPATH, " ...

  3. Package CJK Error: Invalid character code. 问题解决方法--xelatex和pdflatex编译的转换

    Package CJK Error: Invalid character code. 问题解决方法--xelatex和pdflatex编译的转换 解决方法:添加格式说明信息 将下面语句: \docum ...

  4. Windows Mobile入门

    转自 http://www.cnblogs.com/peterzb/archive/2009/05/12/1455256.html [准备篇]        最近安排做手机视频监控方面开发,这个对我来 ...

  5. CSS父元素高度随子元素高度变化而变化

    <html> <body> <head> <style> #menu{width:1000px;overfloat:hidden;} /* width: ...

  6. Netflix OSS、Spring Cloud还是Kubernetes? 都要吧!

    Netflix OSS是由Netflix公司主持开发的一套代码框架和库,目的是解决上了规模之后的分布式系统可能出现的一些有趣问题.对于当今时代的Java开发者们来说,Netflix OSS简直就是在云 ...

  7. 用bundler安装jeklly

    为什么要写这篇文章呢?因为官方的安装文档里,ruby的很多库没有说明怎么安装.所以需要重点说明一下.1.我的安装环境是vultr的16.04版的ubuntu.2.因为ruby的扩展库好多都是Gcc编译 ...

  8. js正则表达式实现手机号码,密码正则验证

    手机号码,密码正则验证. 分享下javascript中正则表达式进行的格式验证,常用的有手机号码,密码等. /** * 手机号码 * 移动:134[0-8],135,136,137,138,139,1 ...

  9. LeetCode 326 Power of Three(3的幂)(递归、Log函数)

    翻译 给定一个整型数,写一个函数决定它是否是3的幂(翻译可能不太合适-- 跟进: 你能否够不用不论什么循环或递归来完毕. 原文 Given an integer, write a function t ...

  10. 在/etc/my.cnf未配置 [clIent] 导到mysql找不到socket

    [root@DB mysql-5.6.16]# ./bin/mysql ERROR 2002 (HY000): Can't connect to local MySQL server through ...