Preventing a window being moved whilst in drag mode

I am trying to prevent windows overlapping. I have a Window Manager type class that will do a rectangle test for the relevant windows, i.e. a window in question and the windows that it cannot overlap with, to ensure that the rectangles don't overlap.

The trouble is, I can't see a way of preventing the movement when they do actually overlap. I can see a means of stopping the move altogether by capturing the WM_NCLBUTTONDOWN and using the HitTest to see if the result is the HTCAPTION, if so preventing the dispatch.

However is there a means of stopping the drag action when already in drag mode. By this I mean the user has the left mouse button held down and is dragging the window. On the Mouse move I am checking to see if it overlaps. The code for this works fine so I know when they overlap but at this point how do I programmatically stop the drag action? Can I tell Windows to cancel the dragging (Don't want it to go back to the original position though - just stop on the spot)? OR ideally to keep the drag motion but keep the window in the same position (in this case it would stop the drag overlapping but if the user moves in a direction that does not cause an overlap the window would be moved).

Just to clarify my question is not how to detect the overlap it is to stop the movement when I detect an overlap. I am currently using the WM_MOVING message to detect the overlap...

Please any ideas would be much appreciated.

Thanks in advance! Joe
Have you tried sending the WM_NCLBUTTONUP message to the moving window?
Last edited on
Yessssssssssssssss!!!!!

I now stop it by simply setting the values of the rect. I can't believe I didn't think of that! I have been trying everything else, including sending a WM_NCLBUTTONUP message ( thanks gpotw!!).

One more thing (actually two but the second is ambitious :-) and possibly requires a new post )

FYI - The code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if ( ( !m_rkWindowManager.CheckOverlap( *this, Message.DragRect->Left, Message.DragRect->Top ) ) &&
                 ( m_rkWindowManager.CheckOnScreen( *this, Message.DragRect->Left, Message.DragRect->Top ) ) )
            {
                TForm::Dispatch(message);
                siLeft = Left;
                siTop = Top;
            }
            else
            {
                m_bNoMove = true;
                Message.DragRect->left = Left;
                Message.DragRect->top = Top;
                Message.DragRect->right = Left + Width;
                Message.DragRect->bottom = Top + Height;

            }


How would I set the cursor position so that when it can't overlap it will stay in same position i.e. in the caption and not just drag off the caption leaving the window behind... The cursor should then be movable again when the user releases the button.


The second problem that I had was another tricky one which I have bodged together...

It was the need to allow the user to move a window by right clicking on the caption and dragging in the same way that you can the when you left click..

My current solution to this:
Detect a WM_NCRBUTTONDOWN and use the hit test to see if caption. Set a boolean to say 'InDragMode' and set a timer (the WM_NCRBUTTONUP would reset these). When this fires (every 10 milliseconds - Rubbish I know) use the get cursor pos in the timer to get the X and Y and then manually move the form by setting the Left and Top values...

This works but is quite sloppy I'm sure you will agree. Please any advice would be greatly received.

Many Thanks,
Joe
1. SetCursorPos()
2. If WM_NCRBUTTONDOWN and hittest says it is the caption, pass on to the default window procedure a WM_NCLBUTTONDOWN message. It will then think it was a left click. Monitor the WM_NCRBUTTONUP and exchange it for a WM_NCLBUTTONUP only if you were in dragging mode (the boolean).
Thanks again, the cursor pos function works well I just need to refine where I move it to as it looks a bit twitchy at the moment.

I can't get the right click dragging to work how you suggested but maybe I am not doing it correctly...

Please can you see if you can spot any mistakes in my coding/logic - I have been doing a lot of stuff in the Dispatch and not WndProc, my reason for this was so that I could pass through as a void* and cast to the relevant structure..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//---------------------------------------------------------------------------
void __fastcall TForm2::WndProc(TMessage &Message)
{
     switch ( Message.Msg )
     {
        // NC right mouse button down
        case WM_NCRBUTTONDOWN:
        {
            Message.Result = WM_NCLBUTTONDOWN;
            break;
        }
        // NC right mouse button up
        case WM_NCRBUTTONUP:
        {
            Message.Result = WM_NCLBUTTONUP;
            break;
        }
         default:
         {
             TForm::WndProc(Message);
             break;
         }
     }

     TForm::WndProc(Message);
}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
void __fastcall TForm2::Dispatch( void* message )
{
    switch ( static_cast<TMessage*>( message )->Msg )
    {
        // NC right mouse button down
        case WM_MOVING:
        {
            TWMMoving& Message = *static_cast<TWMMoving*>( message );

            String strLOG("");
            strLOG.sprintf( "Left: %d Top: %d", Message.DragRect->Left,
                                                Message.DragRect->Top );
            //Caption = strLOG;

            if ( ( !m_rkWindowManager.CheckOverlap( *this, Message.DragRect->Left, Message.DragRect->Top ) ) &&
                 ( m_rkWindowManager.CheckOnScreen( *this, Message.DragRect->Left, Message.DragRect->Top ) ) )
            {
                //TForm::Dispatch(message);
            }
            else
            {
                Message.DragRect->left = Left;
                Message.DragRect->top = Top;
                Message.DragRect->right = Left + Width;
                Message.DragRect->bottom = Top + Height;
                //SetCursorPos( Left, Top );
            }
            break;
        }
        // NC right mouse button down
        case WM_NCRBUTTONDOWN:
        {
            TWMNCRButtonDown& Message = *static_cast<TWMNCRButtonDown*>( message );
            if ( Message.HitTest == HTCAPTION )
            {
                Message.Result = WM_NCLBUTTONDOWN; 
            }

            break;
        }
        // NC right mouse button up
        case WM_NCRBUTTONUP:
        {
            TWMNCRButtonUp& Message = *static_cast<TWMNCRButtonUp*>( message );
            if ( Message.HitTest == HTCAPTION )
            {
                Message.Result = WM_NCLBUTTONUP; 
            }
            break;
        }
        default:
        {
            TForm::Dispatch(message);
            break;
        }

    }
}
//--------------------------------------------------------------------------- 



Please note that this is not the form that I have bodged together a right click drag with timers etc.. I wanted to try on a fresh one.

Thanks
I only saw line 9: I did not say "send WM_NCLBUTTONDOWN as the result of processing WM_NCRBUTTONDOWN". I said to exchange one message for the other:

1
2
3
4
5
6
switch (msg)
{
    case WM_NCRBUTTONDOWN:
        return DefWindowProc(hWnd, WM_NCLBUTTONDOWN, wParam, lParam);
    ....
}


So you are fooling the default window procedure to think it was a left mouse down in the non-client area. This should therefore bring the regular OS-provided dragging function.
Hi,

Where you have said return DefWindowProc, what is the function that contains this line of code? I have done the following as per your suggestion (I think) but to no avail.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void __fastcall TForm2::WndProc(TMessage &Message)
{
     switch ( Message.Msg )
     {
         case WM_NCRBUTTONDOWN:
         {
             DefWindowProc(Handle, WM_NCLBUTTONDOWN, Message.WParam, Message.LParam);
         }
         default:
         {
             TForm::WndProc(Message);
             break;
         }
     }
}


I can't do a return DefWindowProc as WndProc returns void?

Sorry to be a pain but I would love to get this working properly instead of using my bodge.

Thanks
Last edited on
Well, I wrote the example for a window procedure written without the use of any framework. You are using a framework, so call it however you like to call it. The idea is to trick Windows into thinking that the right mouse button is really a left mouse button. I also assumed blindly that wParam and lParam for one message is also good for the other message, but you should double check this @ MSDN Online.
OK I will stick with it. I did something simmilar to allow a client area drag operation. Thanks for your help it has been much appreciated!!
Topic archived. No new replies allowed.