Recipe 11.4 Restrict Mouse Movement to a Specific Region

11.4.1 Problem

You'd like to be able to restrict mouse-cursor movement to certain areas of the current form. You think it would help users of your application if the mouse stays where it needs to be until they're done with it. How can you limit mouse movement in Access?

11.4.2 Solution

The Windows API's ClipCursor subroutine will limit the movement of the mouse to a single form or region on a form, as you'll see in this solution.

To try out this technique, load and run the frmClip form from 11-04.MDB. This form, shown in Figure 11-5, limits the mouse movement to the area of the form once you click the large button. If you click the button again or close the form, code attached to either event frees the mouse cursor to move anywhere on the screen. If you move the form, Windows frees the mouse cursor for you.

Figure 11-5. frmClip limits mouse movement to the area of the form

To use this technique in your own applications, follow these steps:

  1. Import the module basClipCursor from 11-04.MDB. This module contains the function declarations and user-defined types that you'll need.

  2. To limit the mouse to a single form, you'll need to get the form coordinates and tell Windows to use those coordinates as limits for the mouse. To do this, you'll need code something like the following (because this code fragment uses Me, it must be in a form's module, not a global module):

    Dim typRect as acb_tagRect
    Call acb_apiGetWindowRect (Me.Hwnd, typRect)
    Call acb_apiClipCursor(typRect)
  3. To free the mouse cursor, use code like this:

    Call acb_apiClipCursor(ByVal vbNullString)

See Recipe 11.4.3 for an example.

11.4.3 Discussion

The ClipCursor API routine (aliased as acb_apiClipCursor in the code) expects as its only parameter a user-defined data type consisting of four long integers representing coordinates of a rectangle. This data type has been declared for you in basClipCursor as acb_tagRect. This is a common data structure, used often with API routines that interact with the screen or printer. It is defined as follows:

Type acb_tagRect
   lngLeft As Long
   lngTop As Long
   lngRight As Long
   lngBottom As Long
End Type

When you want to restrict mouse movement, you'll need to retrieve the coordinates of the current form. You can accomplish this by calling the GetWindowRect API function (aliased as acb_apiGetWindowRect in the code), which will fill in an acb_tagRect structure with the left, top, right, and bottom coordinates of the window whose handle you pass it. Therefore, by calling acb_apiGetWindowRect with the handle of the current form, you'll retrieve the coordinates of that form in pixels:

Dim typRect as acb_tagRect
Call acb_apiGetWindowRect (Me.hWnd, typRect)

Once you've got a structure containing the coordinates of the current form, you can call ClipCursor and pass that filled-in structure to it. The sample form combines these API calls, as shown here:

Private Sub cmdClip_Click( )
   Dim typRect As acb_tagRect
   Static sstrCaption As String

   ' Static variable to keep track of clipping
   Static blnClip As Boolean

   If blnClip Then
      Me.cmdClip.Caption = sstrCaption
      Call acb_apiClipCursor(ByVal vbNullString)
      sstrCaption = Me.cmdClip.Caption
      Me.cmdClip.Caption = "Free the Mouse!"
      Call acb_apiGetWindowRect(Me.hWnd, typRect)
      Call acb_apiClipCursor(typRect)
   End If
   blnClip = Not blnClip
End Sub

In the sample routine, which is executed each time you click the large button on frmClip, blnClip alternates between True and False, keeping track of whether mouse clipping is currently in effect. If it is, the routine calls acb_apiClipCursor to disable clipping and resets the button's caption. If clipping is not in effect, the routine stores away the original caption, sets a new one ("Free the Mouse!"), retrieves the form's coordinates, and finally calls acb_apiClipCursor to restrict the cursor's movement.

To end the mouse-cursor restrictions, send a null value to acb_apiClipCursor. To do that, pass the vbNullString intrinsic constant by value. Because the acb_apiClipCursor procedure has been declared to accept any type of parameter, you can send it a structure in one call and a null value in another.

The method presented in this solution is not foolproof in Access. You're taking control of a feature that Access normally controls itself, and sometimes the interaction may be unpredictable. In this case, if you restrict the mouse movement to a single form, but then use the mouse to move or resize the form, Access will free the mouse for you. Therefore, if you want to force users to stay on a single form, you're better off using a modal form instead. If, on the other hand, you're just trying to ensure that the mouse remains in the area of the form where the users need it to be, the method described here is appropriate. Restricting the mouse movement is not meant for every application, but if you want to help your users out a little, try it.