I need to change the cursor icon when the mouse hovers a certain HWND. I achieved the mouse cursor change with
SetClassLong(hWindow, GCL_HCURSOR, (LONG)LoadCursor (NULL, IDC_CROSS));
But it applies the cursor to each element which share the same class with the specified HWND. For example, in my case, the HWND is a Button element, and it's class is "Button", so all the buttons in my window will have the same cursor. How can I just change the cursor to a specified HWND? Something like this:
SetHwndCursor(hWindow, GCL_CURSOR, Cursor); //Invented function, just to make the example
Thanks.
To show a different cursor than the class's default cursor, you need to handle the WM_SETCURSOR message for the window and call SetCursor in response to WM_SETCURSOR. For a brief example, see Displaying a Cursor.
You'll need to subclass the button to override the button's WndProc to handle WM_SETCURSOR. Use SetWindowSubclass to subclass the window (and then remove the subclassing with RemoveWindowSubclass when the button is destroyed, in response to WM_NCDESTROY—see Raymond Chen's Safer subclassing for details). SetWindowLongPtr is no longer recommended for subclassing windows.
Thanks to #IInspectable and #JonathanPotter for the information on SetWindowSubclass.
I accomplish this by handling WM_SETCURSOR for the window in question and use SetCursor.
Related
I'm working on a themed ownerdraw button using Win32 native.
Following a tutorial and a sample project on another website, I got my button almost perfect(almost without bug), but there's one which left, and I have a issue in fixing it.
Basically, I'm subclassing using SetWindowLongPtr API the window of the button, and from there, when WM_LBUTTONUP is catched, I would need to call DrawThemeBackground for reset the state of the button, OR, just handle again WM_DRAWITEM. The problem is that WM_DRAWITEM is getting called only when my mouse is on the dialog window, which is not really a problem, if there would be a way for advice the main dialog to handle it when I want it. I tried with InvalidateRect, but it is not making WM_DRAWITEM, but WM_CTLCOLORBTN.
So, my question is:
Is there any way for let WM_DRAWITEM being handled even if the mouse is out of the dialog
OR
Is there any way for advice the main dialog that it should handle again WM_DRAWITEM, evne if the mouse is out of the dialog?
Thanks alot for taking your time in replyng me.
I am programming in C++ using MFC Document/View architecture. I have been trying to achieve something similar to MFC CEdit lose focus handler. I processed the message EN_KILLFOCUS as the answer suggests.
Basically, I'm doing this: I have text drawn in the view. When the users clicks the area of the text in the view, I position and show the CEdit window on top of it so users are able to edit the text. When the edit loses focus, I get the CEdit text and save it in the document. Then invalidate the view.
However, I seem to only receive the EN_KILLFOCUS message when the view loses focus. I have to click somewhere else, e.g. a docking window, or the toolbar, to make the CEdit lose focus. I want it to happen when I click anywhere else on the view, outside the CEdit box.
I am creating the CEdit with WS_CHILD style. I tried WS_POPUP but still the same problem.
Any help would be appreciated.
Create a CEdit derived class/control. And create an instance of this class in your View
Handle the EN_KILLFOCUS in the derived class. You will be able to handle this message once user clicks outside the edit control window.
Keep your editbox as WS_CHILD.
Set focus to view on view's mouse down, call SerFocus() on view's LButtonDown.
I want to implement a simple volume up/down button using a custom bitmap, and all it's going to have is a simple hover effect, and mouse down effect.
My first idea was to process WM_MOUSEMOVE for the hover, and process WM_LBUTTONUP and WM_LBUTTONDOWN for the mouse down effect.
First, is this the easiest way to do this? I could superclass a button and just paint the bitmap and forget about the text...
Then I have the problem with the background, which should be transparent, I know I can use a mask by passing SRCAND to BitBlt, but this requires me to have 2 images for each button. Is there any way to do this with just one image? Like if I put a green background on it in my image editor, could I mask that out with a green screen like effect?
You need to create a regular button, and subclass it with SetWindowSubclass. The button must have the owner-draw style on it, which means in the parent WndProc, you are handling WM_DRAWITEM. As you correctly say, you will be BitBlt'ing or StretchBlt'ing the background of the image on.
For hover events you must do the following:
Have some sort of shared boolean between the parent and subclassed WndProc, eg. IsMousedOver
In the subclassed WndProc, process WM_MOUSEMOVE. When this message is hit, you should set IsMousedOver, then invalidate the control with InvalidateRect (this will trigger WM_DRAWITEM in the parent)
In the parent WndProc, if you receive WM_MOUSEMOVE, you should clear IsMousedOver
In WM_DRAWITEM in the parent WndProc, you should check IsMousedOver and BitBlt based on that state
If you want to process MouseUp/MouseDown, you can change the boolean to an int instead and have a tri-state. However, most implementations have MouseDown being the same as the regular button, and MouseUp is then simply the regular moused over background.
As for your query about 2 images, it may well be possible to do it with one but I haven't tried that before.
How would i make a button that will change it's position on MouseMove Event if the cursor is close enough to the center of the button in MFC ?
WM_MOUSEMOVE is not delivered to the button if the cursor is not over it (and is not captured, but you don't want that). So you have to process WM_MOUSEMOVE in the parent dialog. If you want your button to be a self-contained control, you have to subclass the parent window upon button creation.
Subclassing, in this context, means:
- you retrieve and store the parent's window proc address with GetParent()->GetWindowLong(GWL_WNDPROC)
- you set it to your procedure with SetWindowLong()
- in the procedure, you call the parent's previous window proc, after handling WM_MOUSEMOVE the way you want.
The WM_MOUSEMOVE coordinates will be relative to the screen, but you'll probably want to track the button position relative to the window that contains it. Use the ScreenToClient method on the parent window to convert, then you can compare the coordinates to see if it's close. Then use MoveWindow to move the button.
If you track the mouse cursor position you can determine when the cursor gets close to or enters the button window rect. You can then use the SetWindowPos() function to reposition the button window in the parent window client area.
I have a normal Windows GUI application (made using the API, not MFC) and as I move my mouse on and off the application and the mouse changes styles (like when you move it over the border, it changes to a resize arrow, etc.) but sometimes it "sticks" in that style, so that I can move the mouse around and it will stay in a resize arrow or whatever, even after it's off the window border. It fixes itself if I move it over another control.
It's just an inconvenience, but it looks unprofessional and I would like to fix it. How can I make it where it stays up to date all the time?
Set a valid cursor handle when you register your window class. See WNDCLASSEX::hCursor. Use LoadCursor to load a valid cursor. Like,
WNDCLASSEX wc = {0};
...
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
...
RegisterClassEx(&wc);
tenfour's answer is correct. Here's a little more background.
When the mouse moves within a window, and it's not captured, the window will get a WM_SETCURSOR message. The message name can be a little confusing. It's basically the window's opportunity to set the cursor, not an instruction to set the cursor.
A window can handle this message by calling SetCursor and returning.
A window can also punt by passing the message to DefWindowProc to get the default behavior. The default behavior is to look at the hCursor field in the WNDCLASS for the window. This is why tenfour's answer works.
(It's actually a bit more complicated than that, since the DefWindowProc first gives the parent window a chance to intervene.)
If you want to do something dynamic, like choose a cursor depending on some state variable, then you should have to handle the WM_SETCURSOR so that it calls SetCursor with whatever cursor is appropriate and then returns TRUE.
See SetCursor for details.