How to find out which window triggered WM_PARENTNOTIFY? - c++

When I set the parent of a button (A) to another button (B), I found that it won't trigger the WM_COMMAND message of button A, and that there will be a WM_PARENTNOTIFY message received by the window (parent of button A). But after looking up the reference, I found no way to get the identity of button B, like HMENU or HWND. Could someone help me?

Yes, it's sure that it seems strange that somebody put a button into another button, or say set the parent of a button to another button, but it do has some realistic meaning when the parent button has a style of BS_GROUPBOX, just as the Frame in Visual Basic.
img_button_in_button
My solution to this problem is calling SetWindowSubClass after created a new control and so that the new callback function set in calling this API can receive the real hWnd of the control triggering this event.
Something to remark:
Use return DefSubclassProc(hWnd, uMsg, wParam, lParam); if that event is not to be handled by the control.
Remove WS_CHILD style for those controls calling SetWindowSubClass, or those controls can not be correctly displayed and the only thing displayed would be just an empty window.
Thanks for all who helped me in this question!

Related

Catch OnMouseMove messages for edit control in a window created by createdialog?

I have a dialog window that is created by CreateDialog().
Within the dialog window is a text edit control which is created automatically by the CreateDialog() call, however I can't seem to catch OnMouseMove messages for it - only its parent window (of the controls, not the dialog). CreateDialog() only allows you to set a procedure function for the main dialog (not the sub objects, like the edit controls) and if I catch the OnMouseMove messages there, they only trigger for mouse movement on the main dialog itself (anywhere there is NOT a control, ex. buttons, text edit boxes, etc).
Short of creating the window manually with CreateWindowEx() (and all the sub objects), is there a way to catch the OnMouseMove messages associated with the specific text edit control by its ID or something? I have its handle retrieved by GetDlgItem().
What I am ultimately trying to accomplish is to both read the text below the mouse cursor and display a relevant tooltip if the word is recognized/matched, and I am definitely open to other alternatives if you have any ideas!
Here is the basic code :
Creation of the dialog, using the DBG_DLG template to define the controls
hDbg = CreateDialog(hCurInst, TEXT("DBG_DLG"), 0, DbgDlgProc);
The DBG_DLG template is defined in the project's .rc file. I couldn't find a simple way to paste that code here, but it has a particular text edit control that I am trying to catch with an ID of ID_OP_ED.
Relevant code from DbgDlgProc() that does NOT work, and only catches messages associated with the main dialog and not the controls themselves. Hovering over the controls causes no messages to be caught by this routine.
BOOL CALLBACK DbgDlgProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch(message)
{
case WM_INITDIALOG:
return TRUE;
case WM_MOUSEMOVE:
OnMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (DWORD)wParam);
return FALSE;
Once you have the HWND of the child text edit (from GetDlgItem()), you can subclass it directly, using SetWindowLongPtr() or better SetWindowSubclass(). Your subclass will receive all of the messages sent directly to the control.
Refer to MSDN for more details:
Subclassing Controls

Win32 C++ Subclassed label not receiving WM_PAINT after calling WM_SETTEXT

Is it normal behavior that a subclassed control does not receive WM_PAINT after you call WM_SETTEXT on it?
The parent does receive WM_CTLCOLOR, but I want to paint evertything inside my subclassed WM_PAINT message.
I assume calling InvalidateRect after calling WM_SETTEXT is the way to go?
Let me know if you want to see code. I feel like its not necessary for this question that is why I left it out initially.
Whether WM_PAINT is sent in response to WM_SETTEXT depends on what window class has been sub-classed, buttons for example are invalidated but list boxes are not (the window text for a list box is little more than a debugging aid as it is not shown in the UI).
If your class is such that setting the text should invalidate you could always add something like the following to your subclass' WindowProc:
case WM_SETTEXT: {
LRESULT res = CallWindowProc(lpfnParent, hWnd, WM_SETTEXT, wParam, lParam);
InvalidateRect(hWnd, nullptr, true);
return res;
}
That way you don't need to have an InvalidateRect each time you set the control text.

How to kill focus of "edit" control on "Enter" key press

I have a main window created in main function. In procedure for the main window on WM_CREATE message I create a edit control which as a child of parent window using system "edit" window class. I want the focus to be transferred to main window when enter key is pressed in edit control. Since I used system class I don't have access to its procedure.
I am using C++ for this in Visual Studio 10
Since I'm new to win32 apps I want a simple solution no matter how long the code is
If your window has only one focusable control (such as an edit control), then that control will always have the focus. You cannot have a window with no focused controls, and the parent window itself is not focusable (i.e. cannot have the focus).
So you will first need to add another focusable control to your window, if you don't have one already (I couldn't tell from the question). For example, you might add an "OK" or "Cancel" button. That way, whenever you unfocus the edit control, the button can receive the focus.
Then, you will need to subclass the edit control so that you can process its key press events (e.g. WM_KEYDOWN and WM_KEYUP). To subclass a single window, call the SetWindowLongPtr function and pass the window handle along with the GWLP_WNDPROC flag and a pointer to your custom window procedure. This effectively replaces the default window procedure for that class of control with your custom window procedure. For example:
// Stores the old original window procedure for the edit control.
WNDPROC wpOldEditProc;
// The new custom window procedure for the edit control.
LRESULT CALLBACK CustomEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
{
if (wParam == VK_RETURN)
{
// The user pressed Enter, so set the focus to the other control
// on the window (where 'hwndOther' is a handle to that window).
SetFocus(hwndOther);
// Indicate that we processed the message.
return 0;
}
}
}
// Pass the messages we don't process here on to the
// original window procedure for default handling.
CallWindowProc(wpOldEditProc, hWnd, msg, wParam, lParam);
}
// ----- Add to the parent window's WM_CREATE: -----
// Create the edit control
HWND hwndEdit = CreateWindowEx(...);
// Subclass it.
wpOldEditProc = (WNDPROC)SetWindowLongPtr(hwndEdit,
GWLP_WNDPROC,
(LONG_PTR)CustomEditProc);
// Show it.
ShowWindow(hwndEdit, SW_SHOW);
// ... your other code (e.g. creating and showing the other control)
// ----- Add to the parent window's WM_DESTROY: -----
// Unsubclass the edit control.
SetWindowLongPtr(hwndEdit, GWLP_WNDPROC, (LONG_PTR)wpOldEditProc);
// ... your other code (e.g. calling PostQuitMessage(...) to close your app)
Further reading about subclassing windows is here on MSDN. The sample code there (and lots of other places on the web) assumes that you're subclassing an edit control in a dialog window. Because dialogs are special types of parent windows that handle a lot of keyboard processing automatically, you need to take extra steps to overcome this default processing done by the dialog. If you're using a regular window created with CreateWindowEx, that is not necessary.
If you want multiple edit controls that all behave the same way in response to certain key presses, it is much cleaner and better design to register a custom window subclass. Whereas the above code subclassed only a single edit control object, this approach would create a new type of custom edit control class. You could create as many instances of this new type of edit control as you wanted, and they would all behave the same way.
But I won't go into how to do that here. You can find the code online if you're interested, and your particular use case makes it a bit more complicated. In order to change the focus, the control has to know which other control it should set the focus to. This is difficult to handle globally. Using a dialog window as the parent might be advisable. It manages the Z order and setting the focus for you automatically.

C++ Win32API WM_KEYDOWN and buttons

I'm having a problem receiving message in WM_KEYDOWN. WM_KEYDOWN works just fine until I click any button in my app. From that point it no longer receives my input from the keyboard. How to fix it?
If you are using Win32 controls such as CreateWindowEx(NULL, L"BUTTON", ... this is expected Each control is actually a child window and is capturing all of the window messages after it has focus.
Once the button is clicked you can capture the WM_COMMAND - BM_CLICK message to then call SetFocus(hwnd) to refocus on your window (as Giswin mentioned).
Probably your window has no focus before you click any button on your app. you can add code somewhere in your app to set focus programmatically:
yourwindow->SetFocus();
or use winapi:
::SetFocus(hWnd);
Just in case anyone is wondering, I (unsurprisingly) noticed the same behavior for handling WM_CHAR responses in my WindowProcedure callback as well. As soon as you click a button, the focus changes from the main window to the button control (which is a child window) and keyboard presses no longer have any effect.
As suggested by #NTSCCobalt, adding a simple SetFocus(main window handler) in your WM_COMMAND cases will solve the problem, e.g.
case DEL__BUTTON:{
<Button specific code>
SetFocus(hwnd);
return 0;
}

The curious problem of the missing WM_NCLBUTTONUP message when a window isn't maximised

I've got a window that I handle WM_NCLBUTTONUP messages, in order to handle clicks on custom buttons in the caption bar. This works great when the window is maximised, but when it's not, the WM_NCLBUTTONUP message never arrives! I do get a WM_NCLBUTTONDOWN message though. Strangely WM_NCLBUTTONUP does arrive if I click on the right of the menu bar, but anywhere along the caption bar / window frame, the message never arrives.
After a while of debugging I discovered that if I set a breakpoint on CMainFrame::OnNcLButtonDown(), clicked the caption bar, but keep the mouse button held down, let the debugger break in the function, hit F5 to continue debugging, then release the mouse button - magically WM_NCLBUTTONUP is sent!!
My question is two-fold, (1) what the hell is going on? (2) how do I get around this "problem".
I also note that there are several other people on the internet who have the same issue (a quick Google reveals lots of other people with the same issue, but no solution).
Edit
Thanks for the first two replies, I've tried calling ReleaseCapture in NCLButtonDown, but it has no effect (in fact, it returns NULL, indicating a capture is not in place). I can only assume that the base class (def window proc) functionality may set a capture. I shall investigate on Monday...
I've had this same problem. The issue is indeed that a left button click on the window caption starts a drag, and thus mouse capture, which prevents WM_NCLBUTTONUP from arriving.
The solution is to override WM_NCHITTEST:
LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
switch (nMsg)
{
...
case WM_NCHITTEST:
Point p(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam);
ScreenToClient(p);
if (myButtonRect.Contains(p))
{
return HTBORDER;
}
break;
}
return DefWindowProc(hWnd, nMsg, wParam, lParam);
}
So essentially you inform Windows that the area occupied by your button is not part of the window caption, but a non-specific part of the non-client area (HTBORDER).
Footnote: If you have called SetCapture() and not yet called ReleaseCapture() when you expect the WM_NCLBUTTONDOWN message to come in, it won't arrive even with the above change. This can be irritating since it's normal to capture the mouse during interaction with such custom buttons so that you can cancel the click/highlight if the mouse leaves the window. However, as an alternative to using capture, you might consider SetTimer()/KillTimer() with a short (eg. 100 ms) interval, which won't cause WM_NCLBUTTONUP messages to vanish.
A wild guess - some code is capturing the mouse, probably to facilitate the window move when you grab the title. That would explain also why breaking in the debugger would cause the message to show up - the debugger interaction is clearing the mouse capture.
I would suggest you run Spy++ on that window and it's children and try to figure out who gets the button up message.
As to how to fix it - can't help you there without looking at the actual code. You'll have to figure out who the culprit is and look at their code.
To add to Franci Penov's answer, a click on the title bar is interpreted as the start of a drag to reposition the window. The window is capturing the mouse so it can perform the drag. Since a maximized window can't be dragged, the capture is skipped and the message routes normally.
include ReleaseCapture() in WM_NCLBUTTONDOWN {code block}