I'm reading about the GetCapture() function , which is part of the mfc.
I'm still unclear as to what it does, as well as what it means to capture the mouse , as it says here:
http://msdn.microsoft.com/en-us/library/dxa5eaaa(v=vs.80).aspx
in my book it's used in this way:
void CSketcherView::OnLButtonUp(UINY nFlags, CPoint point)
{
if(this == GetCapture())
ReleaseCapture(); // Stop capturing mouse messages
// ... add information to document
}
So what does GetCapture() return? and what does "capturing" the mouse mean?
As a concrete example for the purpose of mouse capture:
Take a window with two pushbuttons. Click on one of them and keep the mouse button held down. Now drag the mouse cursor over to the second pushbutton and release the mouse button. The first pushbutton will receive a WM_LBUTTONUP message, but the second won't, even though the mouse cursor is on top of it.
When that first pushbutton received the WM_LBUTTONDOWN message, it captured the mouse. While a window is capturing the mouse, it guarantees that it will receive all subsequent mouse events (particularly the WM_LBUTTONUP message), even if the mouse has been dragged outside of its window bounds. This is important so that it can match button-down to button-up messages and maintain proper state. It's also important for usability (if you click on one button and accidentally move away to another button, you neither want to trigger the first nor the second button).
Capturing the mouse usually means you will stil get Mouse events even when the mouse cursor position is outside the bounds of your window
The GetCapture function simply returns the current window that has the mouse capture.
Related
I am working on a c++ CLI application and am having some difficulty with events. I am wondering if I can get events to fire while the mouse button is clicked. For example, I am wanting to check whether or not the mouse has moved to the next square over only if they have the mouse clicked in. Meaning if they click on square 1 they should be able to hold that click and move the square 2 and my program recognize this.
I have run a number of different events on the mouse, including the "Click" event, but the neither the hover, mouse enter, or mouse down event get triggered while the button is pressed. The "MouseClick" event, which does the same. I tried using just the mouseDown event, but this does not let another mouseDown event, mouse enter, or hover event fire.
Short of checking mouse position I do not know what I can do. I would like to not have to do mouse position checking.
If anyone has any ideas, they would be greatly appreciated.
Clearly you'll want to pay attention to the MouseMove event so you can see the mouse moving into another square. Roughly:
void panel1_MouseMove(Object^ sender, MouseEventArgs^ e) {
if ((e->Button & System::Windows::Forms::MouseButtons::Left) ==
System::Windows::Forms::MouseButtons::Left) {
int square = MapPosToSquare(e->Location);
if (square != currentSquare) {
currentSquare = square;
OnSquareClicked(currentSquare);
}
}
}
If these "squares" are actually controls then you have a different problem. You have to set the control's Capture property to false in the MouseDown event handler so it doesn't capture the mouse.
I have a CDialog that is calling DoModal(), the dialog is opened, everything else is disabled, but for some reason I have only the keyboard capture and not the mouse.
The mouse capture is still on the last item it was on.
If I call setfocus before the DoModal, it doesn't work, but if I do setCapture before the DoModal it works.
Can anyone explain it to me?
What is the problem? I want to understand why I need to call to setCapture or releaseCapture before the DoModal (btw- releaseCapture works as well...)
There is no such thing as 'keyboard capture'. There is just keyboard focus.
A window that has the keyboard focus is the one that receives the keyboard related messages.
Mouse messages are normally sent to the window just beneath the mouse cursor, without regard to the focus. That is unless the mouse is "captured", in this case mouse messages are sent to the windows that has captured the mouse, no matter where the mouse cursor is.
That said, do you really need to capture the mouse? Actually that is rarely necessary. And it should be done only as a consequence to a user action, never on your own.
PS. Due to security concerns, in Windows there are actually two different kinds of mouse captures:
Local captures: mouse messages are sent to the capturing window, only if the mouse is over a window that belongs to the same application.
Global capture: mouse messages are sent to the capturing window, no matter where the mouse cursor is.
The SetCapture function creates a global capture only if it is called while processing a WM_?BUTTON_DOWN message.
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.
How does one check if the mouse is over a certain HWND? I have tried using the WM_MOUSELEAVE and WM_MOUSEMOVE messages to keep track, but if you click a button and drag the mouse out of the button, it doesn't receive the WM_MOUSELEAVE until the mouse is released, which is too late, because:
When you click a button, the WM_COMMAND message is only sent if:
1. The mouse was originally depressed over the button
2. The mouse is over the button
3. The mouse is released over the button
I need to replicate this functionality.
To duplicate this functionality, just call SetCapture() so that mouse messages are sent to your window even if the mouse leaves it. You can read the current mouse position as it moves and determine if it is still over your window/button (I'm still not 100% sure what you are doing). And, when the mouse button is released, you can call ReleaseCapture() to restore where mouse messages are sent.
EDIT: Oh, and you can use the Windows API function WindowFromPoint() to determine which window the mouse is over.
This is built-in to Windows, it is called 'mouse capture', SetCapture(hWnd). It ensures you get mouse messages even though the mouse has moved outside of the window. You call it on the WM_LBUTTONDOWN message notification.
In my Win32 app, I don't get WM_MOUSELEAVE messages when I hold down the left mouse button and quickly move the mouse pointer out of the window. But If I, holding down the left mouse button, start from the inside of the window and move slowly past the window edge, it'll generate a WM_MOUSELEAVE.
If I don't hold the left mouse button, I get WM_MOUSELEAVE messages every time no matter how fast the mouse pointer moves off the window.
What's the difference? What can I do to handle both cases properly?
EDIT: If I left click and hold, move out of the window and then let go of the left mouse button I get the WM_MOUSELEAVE message. But it's way too late.
On Windows 7, I was trying to make an owner-drawn button. I subclassed the button in order to get more accurate mouseenter/mouseleave events, essentially. When doing this, I used TrackMouseEvent when I got a WM_MOUSEMOVE because this is only posted when the mouse is over the button. If not already set, I would then set a boolean to specify that the mouse is over the button as well as call TrackMouseEvent so that whenever the mouse left, I could unset my boolean. However, like you, I was not getting the WM_MOUSELEAVE when I pressed and held the left mouse button on the owner drawn button, then dragging the mouse out. Upon releasing the mouse outside of the button, I suddenly get the WM_MOUSELEAVE message - way too late.
I determined that the reason for this behavior was that the default button proc's handling for WM_LBUTTONDOWN calls SetCapture, and releases it later. The usage of SetCapture is what is breaking our reception of the WM_MOUSELEAVE event. However, as a side-effect of SetCapture being called, we will get WM_MOUSEMOVE events even if the control is not under the mouse. Thus, my workaround is duplicating the logic in WM_MOUSELEAVE in the WM_MOUSEMOVE handler to unset my boolean that indicates the mouse is over the button if I get a mousemove event that is outside of my button's area. If SetCapture isn't actually used inside WM_LBUTTONDOWN for the default button proc, then we'll already be getting our WM_MOUSELEAVE message and the code will still work... so this workaround works in both cases.
Your problem sounds like it is likely identical to mine, so hopefully this helps you.
WM_MOUSELEAVE is so that you can detect the mouse leaving your window when you don't have capture. When you have capture, you are responsible for detecting that yourself (if you care).
so It doesn't make any sense to SetCapture AND TrackMouseEvent at the same time, you would use one or the other.
Now, if it would be more convenient for you to see the WM_MOUSELEAVE messages while you have capture, it's a relatively simple matter to do that by yourself in your message pump.
You would just add code that looks something like this between the GetMessage() and the DispatchMessage() calls in your message pump.
GetMessage(pmsg, ...);
.....
if ((IS_WITHIN(pmsg->message, WM_MOUSEFIRST, WM_MOUSELAST) ||
IS_WITHIN(pmsg->message, WM_NCMOUSEMOVE, WM_NCMBUTTONDBLCLK)) &&
MyMouseLeaveDetection(pmsg, g_hwndNotifyMouseLeave))
{
MSG msg = *pmsg;
msg.message = WM_MOUSELEAVE;
msg.hwnd = g_hwndNotifyMouseLeave; // window that want's
msg.lParam = 0xFFFFFFFF;
g_hwndNotifyMouseLeave = NULL;
DispatchMessage (&msg);
}
.....
TranslateMessage(pmsg);
DispatchMessage(pmsg);
As waiting for a WM_MOUSELEAVE is unreliable, the best solution I've found is to directly look at the mouse position during a WM_MOUSEMOVE. I compare the mouse position against the client area and if the position is outside then I handle that as a mouse leave.
I also make sure to call SetCapture when the mouse position is within the client area and ReleaseCapture when it leaves.