As I have mentioned before, I am creating a stop watch application using Visual C++ express and win32. One of the features I wanted to implement, was to be able to drag/move the display window using mouse.  I had implemented this window using the WS_EX_TOOLWINDOW and WS_POPUPWINDOW style. This meant that the window would not have a caption bar, and hence no way to drag/move the window. Even the ‘Move’ command of system menu would fail, as there was no caption bar. There could be many ways to overcome this problem. I am going to list here the method that I used.

Here are some basic steps to solve this :

  1. Indicate to our window, that we want to drag or move. This can be done thru a menu option,  mouse event, a button, a key etc I chose to do it whenever the left button is held down. This would mean processing the WM_LBUTTONDOWN event on the main window.
  2. Capture the mouse, so that Windows will pass all the mouse events to our window.
  3. Process the WM_MOUSEMOVE event to actually move the window.
  4. Indicate to the Window,  that we are done moving. Again, this can be done thru a menu option,  mouse event, a button, a key etc I chose to do it whenever the left button is released and process WM_LBUTTONUP. This would mimic the typical drag and drop behavior, ensuring consistency with windows.

So let us see an example of the same. As I mentioned earlier, I was going to mimic the typical drag and drop behaviour. Whenever the user would press and hold the left mouse button, I would enter the drag state and capture the mouse to the window. This can be achived by processing the WM_LBUTTONDOWN command in our Window Procedure.

case WM_LBUTTONDOWN:
     dragWindow = true;
     SetCapture(hWnd);
     break;

Windows will always send our window procedure the WM_MOUSEMOVE command, irrespective of any button press etc.  If we were to just process the WM_MOUSEMOVE command and move our window, it would move everytime we moved the mouse, irrespective of any buttons being pressed. To overcome this we use a variable, which I called ‘dragWindow’. We want to move our window in the WM_MOUSEMOVE message only if this variable is true. Thus we set it to true whenever the left mouse button is pressed.

Also the SetCapture(HWND hWnd) function captures the mouse for us. This will ensure that our window recieves each and every mouse event, irrespective of whether it is within our client window or anywhere else. We should release the mouse whenever we are done with our movements. To do this we use the function called ReleaseCapture() as follows

case WM_LBUTTONUP:
     ReleaseCapture();
     dragWindow = false;
     break;

The WM_LBUTTONUP message is sent whenever the left button is released, thus indicating that our drag operation is complete. Thus we also set the dragWindow variable to false. Now to implement the actual movement code.

To acutally move the window we have to process the WM_MOUSEMOVE messages. Everytime we recieve the message, we set a new position for our window. This could be done in many ways, like using SetWindowPos() API or destroying and recreating our window or using MoveWindow() api. I chose to use the MoveWindow API, because it seems to exist  just to facilitate  the dragging and moving of windows.

case WM_MOUSEMOVE:
     if (dragWindow == true)
     {
         RECT mainWindowRect;
         POINT pos;
         int windowWidth, windowHeight;

         pos.x = (int)(short) LOWORD(lParam);
         pos.y = (int)(short) HIWORD(lParam);

         GetWindowRect(hWnd,&mainWindowRect);
         windowHeight = mainWindowRect.bottom - mainWindowRect.top;
         windowWidth = mainWindowRect.right - mainWindowRect.left;

         ClientToScreen(hWnd, &pos);
         MoveWindow(hWnd, pos.x, pos.y, windowWidth, windowHeight, TRUE);
     }
     break;

The WM_MOUSEMOVE message is sent whenever the mouse moves inside our window, but as we have captured the mouse, it would be recieved irrespective of mouse position. The lParam variable contains the new ‘X’ and ‘Y’ value of the cursor as the LOWORD and the HIWORD. The position returned is in the client space and not in screen space. To convert this position to actual screen space co-ordinates, we use the ClientToScreen(HWND, *RECT) function.  Once we have this information, we calculate the window widht and height.

Uptill now we haven’t moved the window. To move the window, we invoke the MoveWindow() API.  The function takes six parameters, the window handle which we are moving, new x and y co-ordinates, new width and height. The last variable indicates whether we want to refresh the window while moving. If it is set to TRUE, then the function will send out a WM_PAINT message, otherwise we will have to explicitly redraw the window after MoveWindow.

Well that is about it. Please do leave a comment if you find this useful or if there is anything missing.

Advertisements