1.按钮获得焦点的粗框
在Dev C++中创建一个Windows Application项目,在自带的初始代码的基础上开发,在消息循环WM_CREATE
中使用CreateWindow/Ex()
来创建控件,类名写上Button
(在头文件commctrl.h
中,还可以使用WC_BUTTON
作为类名),样式加上BS_PUSHBUTTON
,编译运行,效果如图:
当它获得焦点(用鼠标按住,然后离开按钮,再松开鼠标)时,看样子并没有变化。但是如果我们把样式改为BS_DEFPUSHBUTTON
时,效果如图:
但是,设置了这个样式后,只要这个样式还在,那么就一直有蓝色边框(没有启用6.0组件视觉样式的是黑色边框)。但是,如果开发过MFC、.NET、VBasic的话,就可以发现它们的按钮是,如果有很多按钮,谁有键盘焦点,谁就有边框。如果细心观察,MessageBox()
的按钮也会这样。
当按钮获得焦点后,按空格键就相当于是鼠标点击了,为了给用户不错的体验,知道当前焦点在哪个按钮上,给它加上蓝色边框,更合适。起先我觉得,MFC和MessageBox()
的按钮与我们自己创建的不是一种,但是后来我实验了一下,先挂一个CBT Hook,在回调中调用GetDlgItem()
和GetClassName()
取得按钮类名,然后输出,发现类名就是一般的按钮Button
。
于是,我开始尝试手动实现。要捕获按钮控件的焦点事件,可以加上BS_NOTIFY
样式,这样当获得/失去焦点时,会产生WM_COMMAND
消息,wParam
的高位字就是捕获的动作(BN_SETFOCUS
获得焦点,BN_KILLFOCUS
失去焦点),在这里面对给获得焦点的按钮加上BS_DEFPUSHBUTTON
样式,失去焦点删除这个样式。似乎可行了。
2.使用Tab键切换焦点
给所有控件加上WS_TABSTOP
样式,预期效果是按下Tab键,焦点会转移到下一个控件,从而允许按空格或者其他按键操作控件。可是在Dev C++默认的代码之上开发,却压根没有作用。怎么办?
我搜索了一通,发现这个网页,原来有人也有一样的困惑。
https://social.msdn.microsoft.com/Forums/en-US/b4c80260-7acf-4989-bce7-d377e33e61d7/tabstop-not-working-on-a-pure-win32-application
其中给出的办法是分发消息时做一下判断IsDialogMessage()
,如果为假再发送给消息处理函数,为真就不动,会自己处理。具体就是:
在入口函数WinMain()
的最后找到对消息队列的处理,如下:
/*
This is the heart of our program where all input is processed and
sent to WndProc. Note that GetMessage blocks code flow until it receives something, so
this loop will not produce unreasonably high CPU usage
*/
while (GetMessage(&msg, NULL, 0, 0) > 0) { /* If no error is received... */
TranslateMessage(&msg); /* Translate key codes to chars if present */
DispatchMessage(&msg); /* Send it to WndProc */
}
加上对消息的判断,如下:
while (GetMessage(&msg, NULL, 0, 0) > 0) { /* If no error is received... */
if (!IsDialogMessage(hwnd, &msg)) {
TranslateMessage(&msg); /* Translate key codes to chars if present */
DispatchMessage(&msg); /* Send it to WndProc */
}
}
运行后效果就是,按下Tab,焦点果然顺利地后移了。它还附赠一个惊喜:不需要手动对按钮焦点作处理,它会自己帮助我们为焦点按钮加上边框。