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

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.

Related

How to find out which window triggered WM_PARENTNOTIFY?

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!

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

WinAPI timer callback not called while child window is in focus

I'm working on a 3D editor app using Direct3D and WinAPI. I create a main window, which has a child window that takes up a portion of the main window's client area. This is used by D3D as the render target. I then also create a separate window with CreateWindow, which is built the same way as the main window (i.e a "main window" and an internal child used as a render target), and I make this window a child of the main application window (to ensure that they are minimized/restored/closed together).
The D3D rendering is executed by the render target child windows processing their WM_PAINT messages. To reduce unnecessary overhead, I set the window procedures to only render on WM_PAINT if GetForegroundWindow and GetFocus match the respective window handles. In other words, I only want a window's rendering to be refreshed if it's on top and is focused.
This is my main message loop:
HWND mainWnd;
HWND mainRenderWnd;
HWND childWnd;
HWND childRenderWnd;
// ...
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (!TranslateAccelerator(mainWnd, accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// Run non-UI code...
}
}
When the main window gets WM_SETFOCUS, I have it set the focus to its render target child window, since I'll want to process inputs there (e.g camera controls):
// ...
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
//...
case WM_SETFOCUS:
{
SetFocus(mainRenderWnd);
return 0;
}
//...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
I do the same in the window procedure for childWnd, setting the focus to childRenderWnd. This window is opened and closed by the user, i.e at any one time it may or may not exist, but when it does and is not minimized, it needs to be the foreground window with focus. Also, to control the framerate for the child window, I use a Timer to refresh it:
static constexpr UINT_PTR RENDER_TIMER_ID = (UINT_PTR)0x200;
void TimerCallback(HWND Arg1, UINT Arg2, UINT_PTR Arg3, DWORD Arg4)
{
if (IsIconic(childWnd) || !(GetFocus() == childRenderWnd))
return;
// Invalidate the render area to make sure it gets redrawn
InvalidateRect(childRenderWnd, nullptr, false);
}
// ...
SetTimer(childWnd, RENDER_TIMER_ID, 16, (TIMERPROC)TimerCallback);
With all this set up, mainWnd and mainRenderWnd seem to work just fine. However, childRenderWnd refuses to have anything rendered to it when it is in the foreground and in focus. While debugging, I found that while this is the case, the timer callback never gets executed, nor does a WM_TIMER message get dispatched to the child window.
On the other hand, the moment I deliberately move focus out of the child window and onto the main window (while keeping both open), the timer message gets sent, and the callback is executed. Another problem is that when I minimize the app while both windows are open, and then restore them both, the render target of neither of the windows is refreshed. Instead, it seems like the focus got "flipped", as I have to click on my child window first, then my main window, and that makes it refresh properly (while the child still refuses to render anything).
What am I missing? I searched for others having problem, e.g an incorrect message pump setup blocking WM_TIMER, but nothing seems to explain what's going on here.
Thanks in advance for the help!
IInspectable and Raymond Chen's comments have helped lead me to the answer. I tried to reproduce the error in a minimal app and finally came upon the source of my trouble. Originally, the main window would just call InvalidateRect in the message loop if there were no messages to be dispatched, effectively redrawing itself every chance it got. This originally did not seem to cause any harm, the scene was rendered just fine, and the window responded to inputs. Once I introduced a second window, however, it must have flooded the message loop with paint messages, making it impossible for any timer messages to get through.
The solution, quite simply, was to give a timer to both windows to set the rate at which they would refresh their D3D render targets. Coupled with focus checks, I can now easily alternate between both windows, with only one refreshing itself at any given time.
The question remains whether the WinAPI timer system is the best choice. My bad code aside, people have mentioned that the timer's messages are low-priority, which might make it a poor choice for framerate control in the long run.
EDIT: I ended up going with IInspectable's suggestion to use a while loop within the main message loop to process to dispatch all messages, and once those are processed, I perform a frame update. This allows me to consistently update all windows and control the frame rate without risking the issue of window messages being stuck.

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.

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}