HOWTO: Use Win32 API to Draw a Dragging Rectangle on Screen DC

ID: Q135865

The information in this article applies to:

SUMMARY

This article shows by example how to have your application draw a dragging rectangle on an arbitrary area of the screen. This action is known as "rubber banding." Utilities that perform screen capture, zooming, and so on are typical examples of such applications. Due to the change in behavior of the SetCapture API in win32, the standard technique used for rubber banding on the screen DC in 16-bit Windows no longer works under Windows NT and Windows95. This article explores alternate methods you can use to perform rubber banding in win32.

MORE INFORMATION

16-bit Method for Rubber Banding Won't Work in Win32-Based Applications

In 16-bit Windows, an application called the SetCapture API prior to starting the rubber banding. This then directed all further mouse input to the application's capture window. This allowed the user to click anywhere on the desktop including the client area of other running applications and start the drag action. Because SetCapture directs all mouse input to the capture window, it is then a simple matter for the application to draw a rubber banding rectangle as the user moves the mouse.

However, the Win32 implementation of the SetCapture API has changed so that the 16-bit method fails. SetCapture no longer directs all mouse input to an application's capture window. If an application calls SetCapture and the user then clicks another application or the desktop (an application by itself) to activate it, subsequent mouse messages are no longer directed to the capture window. The only mouse messages that a capture window receives are those generated when the application's thread is active. This is applicable to a 16-bit application running under Windows NT or Windows 95 as well as a newly written 32-bit application. There are two ways for a Win32-based application to perform rubber banding.

Method One: How to Perform Rubber Banding in Win32-Based Applications

In this method, the application imposes a restriction on the user as to how the dragging action is done. For example, the application might display a small window and require the user to depress the mouse button in the client area of the window to indicate the beginning of the rubber banding mode and drag the mouse, without releasing the mouse button, to whichever part of the desktop the user wants to capture. Upon reaching the appropriate anchor point, the user presses a key on the keyboard (for example, the CTRL key) and drags the mouse to perform the actual rubber banding. Note that during the entire process, the user keeps the mouse button depressed, so other applications are not activated, and all the mouse messages are therefore directed to the capture window.

The following sample code illustrates how this might be implemented:

   // Some global declarations.

   BOOL bDrag;
   BOOL bDraw;
   POINT ptCurrent, ptNew;

   // Inside the capture window procedure.

   case WM_LBUTTONDOWN:
      SetCapture(hwnd);
      bDrag = TRUE;   // Set flag to check for key down in mouse move
                      // message.

   case WM_MOUSEMOVE:
      if (bDrag)
      {
         if ( keyFlags & MK_CONTROL )  // CTRL key down.
         {
            bDraw = TRUE;  // Start actual drawing from next move message.
            ptCurrent.x = ptNew.x = x;  // Store the first point after.
            ptCurrent.y = ptNew.y = y;  // Converting to screen
                                        // coordinates.
            ClientToScreen (hwnd,&ptCurrent);
            ClientToScreen (hwnd,&ptNew);
            bDrag = FALSE;
         }
      }
      else if ( bDraw )
      {

         // Draw two rectangles in the screen DC to cause rubber banding.

         HDC hdc = CreateDC ( TEXT("DISPLAY"), NULL, NULL, NULL );
         SetROP2(hdc, R2_NOTXORPEN );

         // Draw over and erase the old rectangle.

         Rectangle   ( hdc, ptCurrent.x, ptCurrent.y, ptNew.x, ptNew.y );
         ptNew.x = x;
         ptNew.y = y;
         ClientToScreen (hwnd,&ptNew);

         // Draw the new rectangle.

         Rectangle   ( hdc, ptCurrent.x, ptCurrent.y, ptNew.x, ptNew.y );
         DeleteDC ( hdc );
      }

   case WM_LBUTTONUP:

      if ( bDraw )
      {

         // Don't leave orphaned rectangle on desktop; erase last
         // rectangle.

         HDC hdc;
         bDrag = FALSE;
         bDraw = FALSE;
         ReleaseCapture();
         hdc = CreateDC ( TEXT("DISPLAY"), NULL, NULL, NULL );
         SetROP2(hdc, R2_NOTXORPEN );
         Rectangle   ( hdc, ptCurrent.x, ptCurrent.y, ptNew.x, ptNew.y );
         DeleteDC ( hdc );

         // At this point the application has the coordinates of the rubber
         // banding rectangle.

      } else if ( bDrag)
      {
         // The user released the mouse button without ever pressing CTRL
         // key.

         bDraw = FALSE;
         bDrag = FALSE;
         ReleaseCapture();
      }

The drawback to this approach is that the rubber banding action may not be intuitive. The user is required to use a key in addition to the mouse now, whereas in 16-bit Windows this could have been accomplished with the mouse alone.

Method Two: How to Perform Rubber Banding in Win32-Based Applications

Another way to do rubber banding in win32 consistent with the 16-bit method is to create a popup window that covers the entire desktop and has the WS_EX_TRANSPARENT style bit set. This window can be created with no title bar or borders and is active only for the time period of the rubber banding. It is destroyed upon completion of the rubber banding - when the user releases the mouse button. This popup window becomes the capture window and the rubber banding logic would then simply be applied on the client area DC of the popup window.

Additional query words:

Keywords          : kbcode kbInput kbMouse kbNTOS kbGrpUser kbWinOS 
Issue type        : kbhowto

Last Reviewed: December 26, 1998