Date: | August 8, 2005 / year-entry #216 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20050808-16/?p=34673 |
Comments: | 8 |
Summary: | I had noted last year that WM_KILLFOCUS is the wrong time to do field validation. Here's another example of how messing with the focus during a WM_KILLFOCUS message can create confusion. Consider an edit control that displays feedback via a balloon tip. For example, password edit controls often warn you if you're typing your password... |
I had noted last year that
Consider an edit control that displays feedback via a balloon tip. For example, password edit controls often warn you if you're typing your password while CapsLock is in effect. One of the things you probably want to do is to remove the balloon tip if the user moves focus to another control, since there's no point telling the user about a problem with something they aren't using. You might be tempted to subclass the edit control and do something like this: LRESULT CALLBACK EditSubclass( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { ... case WM_KILLFOCUS: if (hwndBalloonTip) { DestroyWindow(hwndBalloonTip); hwndBalloonTip = NULL; } break; ... } return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam); } When you give this code a shot, it works great... unless the user clicks on the balloon tip itself the edit control's caret (the blinking insertion point thingie) disappears. What happened? What happened is that you gummed up the focus change process by destroying the window that focus was going to! The focus change process goes like this:
But in the second step, we destroyed the new focus window. When the focus window is destroyed, the window manager tries to find a new focus window, and it settles upon the edit control itself. This starts a recursive focus change cycle, telling the edit control that it now has focus again. Let's look at the flow in this nested focus change scenario when the user clicks on the tooltip window.
Do you see the problem yet? Look at the message traffic as it reaches the original edit control window procedure:
As far as the edit control is concerned, it gained focus then lost it. Therefore, no caret, since the edit control displays a caret only when it has focus, and your recursive focus changing has resulted in the edit control thinking it doesn't have focus even though it does. There are many ways out of this mess.
First, notice that you don't need to subclass the edit control;
you can just react to the |
Comments (8)
Comments are closed. |
But its OK to using WM_KILLFOCUS if you aren’t creating any windows, right?
Please read the linked article "WM_KILLFOCUS is the wrong time to do validation".
Basicaly, the problem is that EditSubclass first destroys tooltip, then pass WM_KILLFOCUS to original edit proc. Should it first send WM_KILLFOCUS to the edit window, things will work fine
It amazes me that these things still cause problems at this stage of win32 development.
Surely there’s a big list somewhere, that says: "If you want to achieve result ‘A’, write code ‘X’; if you want to achieve result ‘B’, write code ‘Y’", and so on?
I don’t do win32 UI directly using the APIs, so I can’t answer that question. But if I started to do it, I’d sure be looking for that list! Why try re-inventing the wheel?
TC
There’s no need to create a user-defined message in order to do a PostMessage so that you can destroy the window outside of the focus processing cycle. Just use WM_CLOSE.
For example:
case WM_KILLFOCUS:
if (hwndBalloonTip) {
PostMessage(hwndBalloonTip, WM_CLOSE, 0, 0);
hwndBalloonTip = NULL;
}
break;
If the tooltip balloon window is owned by a different thread, wouldn’t you have a race between the posted message being handled and your window procedure finishing? If the posted message wins, you might have the same problem.
That may not even be an issue, though.
It gums up the activation system.
What is the window you destroy in WM_KILLFOCUS handles WM_MOUSEACTIVATE by returning MA_NOACTIVATE?