Windows lets you control the mouse cursor's appearance. When used properly, cursors give your application a polished look while providing visual feedback for users. (Of course, as with all GUI features, you shouldn't overdo it.) Although VCL makes cursor changes a breeze, you need to be aware of certain aspects, which we'll discuss in this article.
By default, the Cursor property is set to crDefault. The crDefault style tells Windows to display the default cursor for that type of window (or component, if you prefer). For instance, if you have an Edit component on a form, then Windows will, by default, change the cursor to the I-beam shape when you pass it over that component. (This change happens only at runtime, not at design time.) You don't have to do anything to get the default cursor for your components, since the Cursor property always defaults to crDefault.
Besides changing cursors for an individual component, you can change the Cursor property for the Screen object. In VCL, the Screen object represents your application's client area. If you want a cursor change to take place regardless of the component that the cursor is over, then you'll need to modify the Cursor property of the Screen object, rather than the Cursor property of the components or of the form. Let's see why this is so.
Cursors operate on at least three levels as far as VCL is concerned. If you change the Cursor property for an individual component, then the cursor changes only when it's over that component. If you change the Cursor property for the form, then the cursor will change when it's over the form but not when it's over any of the components on the form. However, if you change the Cursor property for the Screen object, then the cursor will change when it's over the form and when it's over all components on the form. Whether you change the cursor for an individual component, for the form, or for the entire application depends on your needs for the present circumstance.
An out-of-the-box C++Builder application includes many cursors. Table A lists them and briefly describes each one.
Table A: VCL cursors
| Cursor | Description |
| crDefault | Default cursor for the component |
| crNone | No cursor; cursor disappears when over the component |
| crArrow | Standard arrow |
| crCross | Crosshair |
| crIBeam | Text-editing I-beam |
| crSize | Same as crArrow |
| crSizeNESW | Sizing cursor oriented diagonally from northeast to southwest |
| crSizeNS | Sizing cursor oriented vertically |
| crSizeNWSE | Sizing cursor oriented diagonally from northwest to southeast |
| crSizeWE | Sizing cursor oriented horizontally |
| crUpArrow | Arrow pointing up |
| crHourGlass | Hourglass |
| crDrag | Arrow with a blank page in the lower-right corner |
| crNoDrop | Diagonal slash through a white circle |
| crHSplit | Black double-vertical bar with arrows pointing right and left |
| crVSplit | Black double-horizontal bar with arrows pointing up and down |
| crMultiDrag | Arrow with three blank pages in the lower-right corner |
| crSQLWait | SQL beneath a wait cursor |
| crNo | Diagonal slash through a black circle |
| crAppStart | Arrow combined with an hourglass |
| crHelp | Arrow next to a black question mark |
VCL provides some of the cursors listed in Table A; the rest are built into Windows. It isn't necessary to know which are which.
If you don't want to use one of the built-in cursors, you can add your own custom cursors to the Cursors array. Before we discuss changing cursors at runtime, let's take a look at installing custom cursors.
Let's examine each of these actions in more detail.
I'm not going to describe the exact steps required to create a cursor--check resource editor documentation for details. The important thing is that when you're done, you'll end up with either a compiled resource file (RES) or a resource script file (RC).
To add the cursor resource to your EXE, just choose Add To Project from C++Builder's Project menu or click the Add To Project button on the ToolBar. When the Add To Project dialog box opens, locate the RES or RC file in which the cursor resides and click OK. Now, the C++Builder IDE will add either a
USERES()or
USERC()directive to your project source file. For example, if you had a cursor in a file called CROSS.RES, the IDE would add the following line to your project source file:
USERES("cross.res");
The next time you click the Run button, do a Make, or do a Build All, the linker will bind the cursor resource to the executable file. Now that the cursor is in the EXE, you need to put it to use.
Add the cursor to the array using the Windows API function LoadCursor(), which looks like this:
Screen->Cursors[1]= LoadCursor(HInstance,"IC_PENCIL");This line tells VCL to load the cursor called
IC_PENCILfrom the program's executable file and assign it to index 1 of the Cursors array. If you have several cursors, you may want to define and use constants rather than raw index numbers, as follows:
const TCursor crPencil = 1; Screen->Cursors[crPencil] = LoadCursor(HInstance, "IC_PENCIL");Once you've loaded your new cursor into the Cursors array, you can use it. Let's look at how to do so.
While you can change cursors at design time, you'll most often want to change the cursor at runtime. For example, if your program starts a lengthy process, you should let the user know by changing the cursor to the hourglass shape (sometimes called the wait cursor) for the duration of the process, then back to the arrow shape when the process finishes.
To change a cursor at runtime, you set the Cursor property of the form, Screen object, or other component to one of the cursors contained in the Cursors array:
Screen->Cursor = crHourGlass;It's that simple for the built-in types. You can use any of the constants from Table A to quickly and easily change the cursors in your applications at runtime.
If you want to change to a custom cursor that you've previously added to the Cursors array, then you specify the actual array index:
Screen->Cursor = (TCursor)1;While the cast to TCursor isn't strictly necessary, the step avoids the compiler warning Assigning int to TCursor. The program would probably work fine anyway, but I always apply the cast to avoid the warning.
A better approach is to use a constant for your cursor, as we discussed earlier. Then you can use this line:
Screen->Cursor = crPencil;Now you have more readable code that will compile without issuing a warning.
In many circumstances, you don't want the cursor to revert to the arrow shape when it's over the nonclient areas. For example, a critical process in an application should alert the user by displaying the hourglass cursor and should then prevent the user from doing anything else in the application until the process has finished. If this type of behavior is what you're after, then you must take extra steps to get VCL to perform for you.
Essentially, you need to grab the WM_SETCURSOR message and handle it yourself. (See the article "Incorporate Custom Message-Handling in Your Applications" for details on handling messages outside of VCL.) After you set up the message map for your application, your custom message handler for the WM_SETCURSOR message should look something like this:
void __fastcall TMainForm::OnSetCursor(TWMSetCursor &Message) {
if (working) {
// load the stock Windows hourglass cursor
::SetCursor(LoadCursor(NULL, IDC_WAIT));
Message.Result= true;
}
else
TForm::Dispatch(&Message);
}
When you use this technique to change the cursor, it won't revert to the arrow shape when it moves over the nonclient area of the application. While this technique involves a little more work, it gives you more flexibility than you get with the pure VCL method.
Kent Reisdorph is a editor of the C++Builder Developer's Journal as well as director of systems and services at TurboPower Software Company, and a member of TeamB, Borland's volunteer online support group. He's the author of Teach Yourself C++Builder in 21 Days and Teach Yourself C++Builder in 14 Days. You can contact Kent at editor@bridgespublishing.com.