I am required to somehow allow a window to be moved using a right click drag option. I have done this via some sneeky code which I don't really like but it works.
Currently I am trapping the WM_NCRBUTTONDOWN message and setting a bool to say that I am in move mode, resetting this variable when the WM_NCRBUTTONUP is received. While in moving mode I am checking for WMNCMouseMove and moving the form myself. As this is slow to keep up with quick mouse movements I also set a timer to do the same using the GetCursorPos.
I have recently discovered the SwapMouseButton function and this does exactly what I want. However there is a but! I need to swap it conditionally and only when the user has right clicked the CAPTION area of a window.
So I swap the mouse buttons round when I receive the WM_NCRBUTTONDOWN and reset on WM_NCRBUTTONUP. However this will work on subsequent clicks provided the WM_NCRBUTTONUP never reset it. This I think is because the clicking event has already happened so is to late to swap which is why it works for subsequent right click hits but not the right click and drag which caused the swapping of the mouse!
Can anybody see a way around this. Baring in mind that I need the left click functionality for the close, minimize, maximise buttons etc. Right click onyl for the moving!
Please Help!!!!!
Thanks, Joe
Instead of manually tracking the mouse or swapping the buttons, there is an easier way - in response to WM_NCRBUTTONDOWN
, simply put the window into a move mode natively by sending it a special WM_SYSCOMMAND
message, then it will handle all the hard work of dragging for you, eg:
const WPARAM MOUSE_MOVE = SC_MOVE + 2;
case WM_NCRBUTTONDOWN:
{
POINT pt;
GetCursorPos(&pt);
SendMessage(Handle, WM_NCRBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
SendMessage(Handle, WM_SYSCOMMAND, MOUSE_MOVE, MAKELPARAM(pt.x, pt.y));
break;
}
See Q114593 for more details.
Update: I wasn't able to get WM_SYSCOMMAND
to work with right-clicking (it works great when left-clicking on child controls, though). I think the OS is doing some internally processing that simply makes it impossible to work correctly. So I went with the approach of intercepting mouse movements to move the window manually, making it operate correctly with fast mouse movements without the use of a timer by using SetCapture()
instead:
bool RightButton_CanMove = false;
bool RightButton_WindowMoved = false;
POINTS LastPoint;
void __fastcall TForm1::WndProc(TMessage &Message)
{
switch( Message.Msg )
{
case WM_NCRBUTTONDOWN:
LastPoint = MAKEPOINTS(Message.LParam);
RightButton_CanMove = true;
RightButton_WindowMoved = false;
SetCapture(Handle);
Message.Result = 0;
return;
case WM_MOUSEMOVE:
if( (Message.WParam & MK_RBUTTON) && (RightButton_CanMove) )
{
POINT CurPoint;
GetCursorPos(&CurPoint);
SetWindowPos(Handle, NULL, Left + (CurPoint.x - LastPoint.x), Top + (CurPoint.y - LastPoint.y), 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
LastPoint.x = CurPoint.x;
LastPoint.y = CurPoint.y;
RightButton_WindowMoved = true;
}
break;
case WM_RBUTTONUP:
if( RightButton_CanMove )
{
RightButton_CanMove = false;
ReleaseCapture();
if( RightButton_WindowMoved )
{
RightButton_WindowMoved = false;
Message.Result = 0;
return;
}
}
break;
}
TForm::WndProc(Message);
}
The code differentiates between right-click and right-move operations. If the user right-clicks the mouse without moving it, the default popup menu is allowed to appear normally. If the user holds down the right mouse button down and moves the window around, the default popup is not displayed when the user releases the right button.