我之前在一篇文章中提到过,WM_KILLFOCUS消息不应该用来验证表单字段的有效性。在今天的文章中,我将介绍另一个反面示例,说明在使用WM_KILLFOCUS消息处理与焦点相关的问题时可能出现的混淆。假设,有一个编辑框控件,它使用气球提示来显示反馈信息。例如,对于一个密码输入控件,当按下键盘上的CapsLock键时,会提示用户防止用户输入错误的密码。作为开发人员,您可能希望在用户将输入焦点从密码输入字段移动到另一个控件时删除气球提示,这是合理的。因为给用户一个他根本不会用的控件提示,会让用户觉得莫名其妙。上述需求可以通过对输入控件进行子类化来实现,如下图所示:使用上面的代码进行初步测试,一切正常,但存在一个问题:当用户点击气球提示时,输入的编辑框光标突然消失了,为什么?正在发生的事情是您通过破坏焦点将要到达的窗口来破坏焦点更改过程!焦点改变过程如下:将焦点移动到新窗口。将WM_KILLFOCUS发送到失去焦点的窗口(如果有)并将WM_SETFOCUS发送到新获得焦点的窗口(如果有)但是在上面的第二步中,我们销毁了新窗口。当焦点窗口被销毁时,Windows窗口管理器会尝试寻找另一个焦点窗口,最终它会将输入焦点设置到输入框控件本身。这将启动递归焦点更改过程,告诉编辑控件它现在再次获得输入焦点。让我们看一下当用户点击工具提示窗口时焦点改变的流程。>将焦点设置到工具提示。>发送WM_KILLFOCUS消息到输入框。>EditSubclass破坏工具提示窗口。>Windows窗口管理器将焦点设置到输入框。>WM_KILLFOCUS不会发送到任何窗口。>将WM_SETFOCUS发送到输入框。>EditSubclass将WM_SETFOCUS消息传递给原始窗口过程。>EditSubclass将WM_KILLFOCUS传递给原始窗口过程。你看到这里的问题了吗?这是到原始编辑框窗口过程的消息流:WM_SETFOCUS(来自嵌套的焦点更改过程)WM_KILLFOCUS(来自原始的焦点更改过程)就编辑控件而言,它获得焦点,然后失去焦点。所以它不显示输入光标,因为编辑控件只有在有焦点时才显示光标,而递归焦点变化导致编辑控件认为它没有焦点,即使它有焦点。有很多方法可以摆脱这种混乱局面。首先,请注意您不需要子类化编辑控件,您可以对EN_KILLFOCUS通知做出反应。其次,您还可以通过向自己发布消息并在收到该消息后销毁工具提示来响应EN_KILLFOCUS。通过对已发布的消息执行此操作,您可以避免递归焦点更改,因为您的工作现在是在焦点更改周期之外完成的。总结请不要忽略这些细节问题,有经验的用户会在你的程序中感受到各种细节。如果是一个很好的细节,你的程序会加分。如果是一个不好的细节,那将不是一件有趣的事情,你的用户会慢慢离开你。
