惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
Google Online Security Blog
Google Online Security Blog
月光博客
月光博客
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
M
MIT News - Artificial intelligence
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
F
Full Disclosure
G
Google Developers Blog
罗磊的独立博客
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
A
About on SuperTechFans
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
GbyAI
GbyAI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
The Register - Security
The Register - Security
U
Unit 42
D
Docker
Martin Fowler
Martin Fowler
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
博客园_首页
Google DeepMind News
Google DeepMind News

博客园 - 纶巾客

Redis数据类型 Solr的Scala客户端(scalikesolr)介绍 使用Solr构建企业级的全文检索(四)---------写入文档 使用Solr构建企业级的全文检索(三)---------Schema定义 使用Solr构建企业级的全文检索(二)---------管理界面简介 使用Solr构建企业级的全文检索(一)---------开篇 再次提笔 构建插件式的应用程序框架(八)----视图服务的简单实现 构建插件式的应用程序框架(七)----基本服务 构建插件式的应用程序框架(六)----通讯机制 构建插件式的应用程序框架目录 WinForm控件开发总结目录 构建插件式的应用程序框架(五)----管理插件 构建插件式的应用程序框架(四)----服务容器 构建插件式的应用程序框架(三)----动态加载 构建插件式的应用程序框架(二)----订立契约 构建插件式的应用程序框架(一)----开篇 WinForm控件开发总结(十一)-----调试控件的设计时行为 制作VS风格的Toolbox控件
WinForm控件开发总结(十二)-----让控件处理导航键
纶巾客 · 2007-05-11 · via 博客园 - 纶巾客

    最近真的真的太忙了,以至于一个多月都没哟更新我的blog。昨天晚上,一个网上的朋友看了我的ToolBox的文章,问我一个问题,他说如何让ToolBox控件也能响应键盘操作,也就是用Updown按键来选择工具箱控件里的Item,他添加了键盘事件,但是不起作用。一开始做这个控件的时候也只是演示一下控件的制作过程,只用了很短的时间做了一个,只考虑了用鼠标选取,没有考虑键盘操作,我想要添加键盘操作无非重载KeyDown事件,针对UpDown做一些响应就可以了。可是添加了重载了OnKeyDown事件后,结果和那位朋友所说的一样,没有任何作用,我设了断点,调试了一下,发现KeyDown根本捕获不到UpDown按键的点击,是什么原因呢,是不是忘记设控件的风格以便让它能够获得焦点?于是,我使用了语句:
   

SetStyle(ControlStyles.Selectable, true);

依然没有效果,当我们在控件上按下Down键的时候,另一个控件获得了焦点。这时UpDown按钮只是起到了导航的作用就像Tab键一样。
      接下来,我在测试工程的窗体上放置了一个ListBox控件做一个对比,其实ToolBoxListBox在界面表现上有相似之处,就是都有子Item,并且在ListBox上点击Down是起作用的,ListBox并没有失去焦点,这说明这时UpDown按键没有成为导航键。我想Windows一定是对默认的导航键UpDown,Left,Right有默认的处理,除非你希望你的控件希望自己处理这些键。用反汇编工具看了一下ListBoxControl控件的源代码,发现一个有趣的函数:

protected override bool IsInputKey(Keys keyData)
{
    
if ((keyData & Keys.Alt) == Keys.Alt)
    
{
        
return false;
    }

    
switch ((keyData & Keys.KeyCode))
    
{
        
case Keys.Prior:
        
case Keys.Next:
        
case Keys.End:
        
case Keys.Home:
            
return true;
    }

    
return base.IsInputKey(keyData);
}


在这里面,ListBoxControl允许PriorNextEndHome成为有效的输入键,接着一路跟下去,看看WinForm控件的基类Control的这个函数是如何处理的:

[UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)]
protected virtual bool IsInputKey(Keys keyData)
{
    
if ((keyData & Keys.Alt) != Keys.Alt)
    
{
        
int num = 4;
        
switch ((keyData & Keys.KeyCode))
        
{
            
case Keys.Left:
            
case Keys.Up:
            
case Keys.Right:
            
case Keys.Down:
                num 
= 5;
                
break;

            
case Keys.Tab:
                num 
= 6;
                
break;
        }

        
if (this.IsHandleCreated)
        
{
            
return ((((intthis.SendMessage(0x8700)) & num) != 0);
        }

    }

    
return false;
}


      注意这一行return ((((int) this.SendMessage(0x87, 0, 0)) & num) != 0);0x87是什么windows消息呢,打开WinUser.h文件,发现是WM_GETDLGCODE,MSDN中的描述是这样的:
      The WM_GETDLGCODE message is sent to the window procedure associated with a control. By default, the system handles all keyboard input to the control; the system interprets certain types of keyboard input as dialog box navigation keys. To override this default behavior, the control can respond to the WM_GETDLGCODE message to indicate the types of input it wants to process itself.
      也就是说windows用这个消息来判断哪些类型的输入交给控件本身来处理。然后,我注意到,对于方向导航键,函数都给于一个值5this.SendMessage(0x87, 0, 0))的返回值进行与操作,那么this.SendMessage(0x87, 0, 0))的返回值都可能是什么值呢,WinUser.h中是这样声明的:
   

/*
 * Dialog Codes
 
*/

#define DLGC_WANTARROWS     0x0001      /* Control wants arrow keys         */
#define DLGC_WANTTAB        0x0002      /* Control wants tab keys           */
#define DLGC_WANTALLKEYS    0x0004      /* Control wants all keys           */
#define DLGC_WANTMESSAGE    0x0004      /* Pass message to control          */
#define DLGC_HASSETSEL      0x0008      /* Understands EM_SETSEL message    */
#define DLGC_DEFPUSHBUTTON  0x0010      /* Default pushbutton               */
#define DLGC_UNDEFPUSHBUTTON 0x0020     /* Non-default pushbutton           */
#define DLGC_RADIOBUTTON    0x0040      /* Radio button                     */
#define DLGC_WANTCHARS      0x0080      /* Want WM_CHAR messages            */
#define DLGC_STATIC         0x0100      /* Static item: don't include       */
#define DLGC_BUTTON         0x2000      /* Button item: can be checked      */

      5最贴切的表达就是DLGC_WANTMESSAGE | DLGC_WANTARROWS,也就是将方向键发送给控件处理,对于6呢,也就是DLGC_WANTMESSAGE| DLGC_WANTTAB,将Tab键发送给控件处理。
   
    从这段代码里和控件实际的行为我们可以得出一个结论,那就是,控件本身是不处理方向键和Tab键的,因为他们有默认的行为,也就是支持焦点在窗体的控件之间转换。如果你想要处理这些导航键,那么结论很简单,就是重载IsInputKey方法,它是一个保护类型的虚方法。       
      在ToolBox控件的代码里重载IsinputKey方法:

        protected override bool IsInputKey(Keys keyData)
        
{
            
if ((keyData & Keys.Alt) == Keys.Alt)
            
{
                
return false;
            }

            
switch ((keyData & Keys.KeyCode))
            
{
                
case Keys.Up:
                
case Keys.Down:                
                    
return true;
            }

            
return base.IsInputKey(keyData);

        }

       


       当用户点击的键是UpDown的时候,返回true,这时我们的OnKeyDown方法里就可以捕获到UpDown的点击事件了。