Many graphical applications allow the user to select one or more objects by dragging a bounding rectangle around the objects. For example, the C++Builder IDE’s Form Designer uses this technique to allow you to select components on the form.
Drawing a bounding rectangle is fairly easy but there are a couple of subtleties that it pays to be aware of. This article will explain how to draw a selection rectangle and the various ways you can go about it.
In order to draw a bounding rectangle you must respond to the form’s OnMouseDown, OnMouseMove, and OnMouseUp events. Drawing a selection rectangle requires the following steps:
I. Set a flag in the OnMouseDown event handler to indicate dragging is taking place and draw the first rectangle.
II. In the OnMouseMove event handler, erase the existing rectangle and draw a new rectangle at the current mouse point.
III. In the OnMouseUp event handler, clear the flag indicating dragging and erase the last rectangle drawn.
I’ll discuss these steps in detail in the following sections.
The action starts in the OnMouseDown event handler:
void __fastcall TMainForm::FormMouseDown(
TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Dragging = true;
DrawRect.left = X;
DrawRect.top = Y;
DrawRect.right = X;
DrawRect.bottom = Y;
Canvas->DrawFocusRect(DrawRect);
}
I first set a flag indicating that dragging is taking place. After that I create a drawing rectangle using the X and Y parameters passed to the event handler. DrawRect is a TRect variable and is declared in the private section of the form’s class. The X and Y parameters are, of course, the current position of the mouse. Finally, I draw a rectangle on the screen using the DrawFocusRect() method of the canvas (albeit a very small rectangle). I’m now going to detour for a moment and discuss DrawFocusRect() and how it works.
The selection rectangle itself can be drawn using any of several GDI drawing functions. For this article I chose the simplest of these functions, DrawFocusRect(). The TCanvas class has a DrawFocusRect() method that simply calls the Windows API function of the same name. DrawFocusRect() draws a rectangle on the canvas using a dotted line. This is the very same rectangle that Windows draws to indicate that a control has focus (hence, the name).
The key feature of DrawFocusRect() is that it is an XOR drawing operation. This means that the rectangle bits are XOR’d with the bits on the canvas. As a result, you can call DrawFocusRect() once to draw the rectangle, and call DrawFocusRect() a second time to erase the rectangle.
Now that you understand how DrawFocusRect() works we can get back to our discussion of drawing a selection rectangle.
Keeping in mind how DrawFocusRect() works, take a look at the code for the OnMouseMove event handler:
void __fastcall TMainForm::FormMouseMove(
TObject *Sender, TShiftState Shift,
int X, int Y)
{
if (Dragging) {
Canvas->DrawFocusRect(DrawRect);
DrawRect.right = X;
DrawRect.bottom = Y;
Canvas->DrawFocusRect(DrawRect);
}
}
This code first checks to see if the Dragging variable is true. The OnMouseMove event is generated every time the mouse moves and I only want to execute the rectangle drawing code if dragging is taking place.
Next I erase the current rectangle on the screen by calling DrawFocusRect(). Remember that the OnMouseDown event handler drew the initial rectangle. There will always be a rectangle on the screen when OnMouseDown is generated so this call to DrawFocusRect() is needed to erase that rectangle.
Next I update the bottom-right point of the rectangle. This will be the size of the new rectangle drawn on the screen.
Finally, I call DrawFocusRect() again to display the focus rectangle in its new location.
The drawing operation is, naturally, terminated in the OnMouseUp event handler. Here’s how that event handler looks:
void __fastcall TMainForm::FormMouseUp(
TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (Dragging) {
Dragging = false;
Canvas->DrawFocusRect(DrawRect);
}
}
This code is basically self-explanatory. It sets the Dragging variable to false and then calls DrawFocusRect() one last time to erase the focus rectangle for the last time.
The code presented in the previous sections is very basic in that it shows the steps required to draw a selection rectangle with the least amount of code. It may not, however, produce the results you want. In particular, the focus rectangle will be drawn behind any components on the form. In order to draw the focus rectangle on top of all components on the form you will need to draw not on the form’s canvas, but on the desktop itself.
Like most aspects of Windows programming with the C++Builder, you can go about drawing on the desktop in more than one way. You might use the Windows API to get a handle to the desktop’s device context (DC) and use the API to draw the focus rectangle. Another way is to use the TCanvas class to accomplish the same thing. I prefer TCanvas because it cleans up all GDI resources when the TCanvas instance is destroyed.
The first step in creating a TCanvas object that allows you to draw on the desktop is to create an instance of TCanvas and assign a handle to the desktop DC to the TCanvas object’s Handle property:
DesktopCanvas = new TCanvas;
DesktopCanvas->Handle = GetDC(0);
The Windows API function GetDC() returns a handle to a device context for a particular window. When you pass 0 to GetDC() you get a handle to the desktop DC. After setting the Handle property, you have a TCanvas object that can be used to draw directly on the desktop. You can now use the code presented earlier, replacing the form’s Canvas property with your DesktopCanvas variable.
You’ll have to take steps to insure that the mouse cursor is confined to your application’s main window and possibly some other housekeeping chores. Other than that it is relatively easy to draw on the desktop versus the form’s canvas. This technique will produce the results you are likely to require in an application that uses selection rectangles.
The example program for this article allows you to draw either on the form’s canvas or on the desktop. You can download the code from our Web site at www.bridgespublishing.com.
As you can see, drawing a selection rectangle is not difficult. The more difficult part comes in what to do after the selection rectangle is drawn (selecting objects, for example). That’s a discussion we’ll leave for another time.