Icons are used to represent minimized child windows in an MDI application. Icons also are widely used by Windows itself. When a program is minimized, its icon is displayed in the Windows 95 taskbar. When you're using the Explorer, the icon representing an application associated with each file is displayed next to the file's name. Windows displays the program's icon in the upper-left corner of the main window title bar.
The Windows 95 Explorer uses the icon resources associated with your program when determining which icons to display. If large and small icons are available, the Explorer uses the icon resources from your application's EXE file. However, if you provide only a large icon, the Explorer synthesizes a small icon, which usually results in a small, distorted icon.
Just a Minute: Icons also are used in dialog boxes. For example, the message dialog boxes discussed in Section 4, "Dialog Boxes and C++ Classes," use icons to indicate the type of message conveyed. It's also common practice to include an application's icon in the About dialog box.
There are several different types of icon resources. In Section 18, "List View Controls," you will use large and small icons in a list view control. Four different types of icons are available:
Time Saver: An image list is ideal for collecting icon images. Any image stored in an image list can be converted into an icon using the ExtractIcon member function. Image lists are covered in Section 17, "Using Image Lists and Bitmaps."
Just a Minute: Because icons are resources, you add them to a program's resource file just as you do bitmaps, menus, and dialog boxes. You create new icons using the resource editor.
When creating a new project, AppWizard creates a set of default icons for your project automatically. You can use the Developer Studio image editor to edit or create new icons for your project.
To open the image editor, open the ResourceView in the project workspace and then open the Icon folder. Double-click any icon resource contained in the folder to open the editor. In an MDI application created by AppWizard, two icon resources will be defined for a new project:
The color palette is displayed whenever you are editing an image resource. The color palette consists of several colored boxes. To change the color of the current drawing tool, click the color you want. There are two special color icons in the color palette:
HICON hIcon = AfxGetApp()->LoadIcon( IDI_LOGO );Because LoadIcon is a CWinApp member function, a pointer to the application's CWinApp object must be fetched using the AfxGetApp function.
After the icon has been loaded, display it by calling DrawIcon:
pDC->DrawIcon( 0,0, hIcon );The DrawIcon function is a member of the CDC class. The coordinates and icon handle must be passed as parameters.
After using LoadIcon, release the icon resource by calling the DestroyIcon function:
DestroyIcon( hIcon );
CAUTION: If you forget to call DestroyIcon, the memory allocated for the icon isn't released.
After opening the icon, use the image editor tools to modify the icon as you want. Every application written for Windows can have its icon displayed in large and small formats. If you edit one of the icon formats, make sure you make corresponding changes in all formats supported by the icon. To display and edit all the available formats, click the drop-down combo box above the image editor, which displays all the supported formats for the icon. Selecting a new format loads that version of the icon into the image editor.
Every child window type also has a unique icon. You can edit that icon just as you do the program's main icon. As discussed earlier this section, the child window icon is named with a shared resource identifier in the form IDR_MYAPPTYPE, where the application is named MyApp.
HICON hIcon = m_imageList.ExtractIcon( 2 );Using this member function is useful when several icons must be stored together.
Figure 14.1
The new icons used in the DCTest example.
Use the two icons created earlier to label buttons in the DCTest About dialog box. Modify the IDD_ABOUT dialog box by adding an extra button to it, as shown in Figure 14.2.
The About dialog box used in the Icon example.
Use the values from Table 14.1 to set the attributes for the two buttons in the About dialog box. Use ClassWizard to add the two buttons to the CAboutDlg class as CButton variables.
| ID | Variable Name | Control Type | Attributes |
| IDOK | m_btnOkay | CButton | Visible, Tabstop, Icon, Default |
| IDCANCEL | m_btnCancel | CButton | Visible, Tabstop, Icon |
Buttons that have icon labels instead of text must have the Icon attribute set. Review each button's Properties dialog box under the Styles tab and make sure the Icon option is checked.
// Implementation public: "CAboutDlg(); protected: HICON m_hIconOkay; HICON m_hIconCancel;The icons are added to the dialog box's buttons when the dialog box receives the WM_INITDIALOG message. Using ClassWizard, add a message-handling function for WM_INITDIALOG to the CAboutDlg class. Use the default name provided by ClassWizard, OnInitDialog. Edit the OnInitDialog member function so it looks like the code provided in Listing 14.2.
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CWinApp* pApp = AfxGetApp();
if( pApp != 0 )
{
m_hIconOkay = pApp->LoadIcon( IDI_GREEN );
m_hIconCancel = pApp->LoadIcon( IDI_RED );
ASSERT(m_hIconOkay);
ASSERT(m_hIconCancel);
m_btnOkay.SetIcon( m_hIconOkay );
m_btnCancel.SetIcon( m_hIconCancel );
}
return TRUE;
}
The source code in Listing 14.2 loads the two stop-light icons created
earlier. After the icons are loaded, the icon handles are passed to the
SetIcon function for each of the buttons contained in the dialog
box.
Just a Minute: When an icon is drawn on a button, the icon is clipped if necessary. The icon isn't scaled to fit inside the button; it is displayed "actual size." This might mean that you must experiment with the relative sizes of the icon and the button.
As the dialog box is destroyed, the icons previously loaded using LoadIcon must be destroyed. Use the source code from Listing 14.3 to create the CAboutDlg class destructor.
CAboutDlg::"CAboutDlg()
{
DestroyIcon( m_hIconOkay );
DestroyIcon( m_hIconCancel );
}
Compile and run the DCTest example. Figure 14.3 shows the DCTest About
box with icons placed in the pushbutton controls.
The DCTest dialog box after adding icons to the pushbutton controls.
Just a Minute: The cursor is an important part of the feedback supplied to a user of a Windows program. Changing the style of cursor is an easy way to alert the user that a change of some type has occurred. Many times, changing the cursor is the only type of feedback required.
Create the cursor shown in Figure 14.4 and name it IDC_BANG. To create a cursor resource, right-click in the resource view window and choose Insert... from the pop-up menu; then select Cursor from the Resource Type dialog box. The editing tools you use to create a cursor are the same ones you used to create icons earlier in this section. The standard Windows naming convention is for cursors to have names beginning with IDC_.
Every cursor has a hotspot. The hotspot for the arrow cursor is located at the very tip of the arrow. The default hotspot for a cursor is the upper-left corner of the cursor. The cursor-image editor enables you to move the hotspot to a position that is reasonable for the cursor image.
The IDC_BANG cursor inside the Developer Studio image editor.
For example, the IDC_BANG cursor you created in the previous section will not work properly if a new hotspot isn't defined. Because the current hotspot is part of the background, this cursor won't work as well for operations in which the mouse clicks must be accurate. One solution, as shown in Figure 14.5, is to modify the cursor to add a well-defined hotspot to the cursor--in this case a bull's-eye, or target, in the upper-left corner of the cursor bitmap.
The new version of IDC_BANG, with a hotspot and a bull's-eye.
The hotspot control is a button located above the edited image. Click the hotspot button and then click the new hotspot pixel. For IDC_BANG, create a new hotspot in the center of the bull's-eye.
To change the current cursor for a window, you handle the WM_SETCURSOR message. Using ClassWizard, add a message-handling function for WM_SETCURSOR to the CAboutDlg class. Listing 14.4 contains source code for OnSetCursor that changes the cursor to IDC_BANG.
BOOL CAboutDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest,
UINT message)
{
// Load and set the new cursor. Return TRUE to stop
// further processing of this message.
CWinApp* pApp = AfxGetApp();
HICON hIconBang = pApp->LoadCursor( IDC_BANG );
SetCursor( hIconBang );
return TRUE;
}
BOOL CAboutDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest,
UINT message)
{
BOOL bReturn;
CRect rcBtn;
CPoint ptCursor;
//
// Calculate the current cursor position, and change the
// cursor if we're not over the OK button.
//
CWnd* pBtn = GetDlgItem( IDOK );
pBtn->GetWindowRect( rcBtn );
GetCursorPos( &ptCursor );
if( rcBtn.PtInRect( ptCursor ) == FALSE )
{
// Load and set the new cursor. Return TRUE to stop
// further processing of this message.
CWinApp* pApp = AfxGetApp();
HICON hIconBang = pApp->LoadCursor( IDC_BANG );
SetCursor( hIconBang );
bReturn = TRUE;
}
else
{
// We're over the OK button, use the default cursor.
bReturn = CDialog::OnSetCursor(pWnd, nHitTest, message);
}
return bReturn;
}
The two key lines in Listing 14.5 retrieve the current mouse cursor position
as a CPoint object. The
CPoint object is tested to see
whether it is inside the boundary of the OK pushbutton:
GetCursorPos( &ptCursor );
if( rcBtn.PtInRect( ptCursor ) == FALSE )
{
// cursor not over rectangle
}
| Cursor Name | Description |
| IDC_ARROW | Arrow cursor |
| IDC_IBEAM | I-beam cursor |
| IDC_WAIT | Sectionglass cursor |
| IDC_CROSS | Crosshair cursor |
| IDC_UPARROW | Up-arrow cursor |
| IDC_SIZENWSE | Sizing cursor, points northwest and southeast |
| IDC_SIZENESW | Sizing cursor, points northeast and southwest |
| IDC_SIZEWE | Sizing cursor, points west and east |
| IDC_SIZENS | Sizing cursor, points north and south |
| IDC_SIZEALL | Sizing cursor, points north, south, east, and west |
| IDC_NO | "No" cursor (circle with a slash through it) |
| IDC_APPSTARTING | Application-starting cursor |
| IDC_HELP | Help cursor |
| IDI_APPLICATION | Application icon |
| IDI_HAND | Stop sign icon |
| IDI_QUESTION | Question mark icon |
| IDI_EXCLAMATION | Exclamation point icon |
| IDI_ASTERISK | Asterisk or information icon |
| IDI_WINLOGO | Windows logo icon |
Using these cursors is similar to using stock objects. Listing 14.6 uses the IDC_UPARROW cursor in response to WM_SETCURSOR.
BOOL CAboutDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest,
UINT message)
{
// Load and set the new cursor. Return TRUE to stop
// further processing of this message.
CWinApp* pApp = AfxGetApp();
HICON hIcon = pApp->LoadStandardCursor( IDC_UPARROW );
SetCursor( hIcon );
return TRUE;
}
A cursor set in response to the WM_SETCURSOR message will interfere
with the remaining examples in the section. After you are finished with
this example, remove the OnSetCursor function using ClassWizard.
Just a Minute: When a large amount of processing is performed, ignoring input from the user is common. It's considered good manners for a Windows program to change the cursor to an sectionglass when user input won't be acknowledged.
A common place to ignore user input is during long initialization routines. It's common to display a user interface but disregard user input until the initialization is complete. It can take several seconds before an application is ready for input, particularly in applications that work with large amounts of data that must be initialized. In these cases, you should use the BeginWaitCursor and EndWaitCursor functions.
To demonstrate how these functions are used, add a message-handling function to the CAboutDlg class using ClassWizard. Add a message-handling function for WM_TIMER, and accept the default function name provided by ClassWizard. Listing 14.7 contains the source code for the OnInitDialog and OnTimer functions.
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CWinApp* pApp = AfxGetApp();
if( pApp != 0 )
{
m_hIconOkay = pApp->LoadIcon( IDI_GREEN );
m_hIconCancel = pApp->LoadIcon( IDI_RED );
ASSERT(m_hIconOkay);
ASSERT(m_hIconCancel);
m_btnOkay.SetIcon( m_hIconOkay );
m_btnCancel.SetIcon( m_hIconCancel );
}
SetCapture();
BeginWaitCursor();
SetTimer( 1, 15000, NULL );
return TRUE;
}
void CAboutDlg::OnTimer(UINT nIDEvent)
{
ReleaseCapture();
EndWaitCursor();
KillTimer( 1 );
}
In Listing 14.7, the OnInitDialog function simulates the beginning
of a long processing period. The
SetCapture and BeginWaitCursor
functions are called to change the cursor to an sectionglass. While changed,
the cursor cannot be used to interact with any controls. A five-second
timer is started, which calls the OnTimer function when the timer
expires. The OnTimer function restores the cursor and kills the
timer.
Time Saver: The order of the statements in OnInitDialog is important. Before calling BeginWaitCursor, the mouse must be captured using SetCapture; otherwise, the sectionglass cursor immediately reverts to the arrow cursor.
As an example, force the cursor to stay over the Cursor project's About dialog box. Using ClassWizard, add message-handling functions for WM_DESTROY and WM_MOVE to the CAboutDlg class. Add the source code in Listing 14.8 to the CAbout::OnMove and CAbout::OnDestroy member functions.
void CAboutDlg::OnMove(int x, int y)
{
CDialog::OnMove(x, y);
CRect rcCursor;
GetWindowRect( rcCursor );
ClipCursor( &rcCursor );
}
void CAboutDlg::OnDestroy()
{
ClipCursor( NULL );
CDialog::OnDestroy();
}
When the MFC framework creates the About dialog box and moves it to the
center of the view window, the WM_MOVE message is sent to the
CAboutDlg class. Inside the OnMove function, the dialog
box's screen coordinates are used to set the clipping rectangle for the
cursor. When the dialog box is destroyed, the WM_DESTROY message
is handled by the CAboutDlg::OnDestroy function, and the clipping
rectangle is reset.
CAUTION: It is important to reset the cursor clipping region by calling ClipCursor(NULL) when the window is destroyed or when the clipping region is no longer needed. If this function isn't called, the cursor will be restricted to the requested rectangle even after the window has disappeared.
A Assigning an icon to a button is much simpler than using the CBitmapButton class. It is also more efficient because Windows will handle drawing the image instead of the MFC class library.
Q The CDC::DrawIcon function will only draw an icon the size of its original image. How can I draw an icon larger than its original size?
A You can use the DrawIconEx function to draw an icon to an arbitrary size. Unlike DrawIcon, the DrawIconEx function isn't a member of the CDC class. You must pass the internal handle used by the CDC object as the first parameter in the call to DrawIconEx, as shown in the OnDraw function:
void CMyTestView::OnDraw(CDC* pDC)
{
CRect rc;
GetClientRect(&rc);
HICON hIcon = AfxGetApp()->LoadIcon(IDI_TEST);
DrawIconEx(pDC->m_hDC,
0,
0,
hIcon,
rc.Width(),
rc.Height(),
0,
NULL,
DI_NORMAL);
DestroyIcon(hIcon);
}
2. What is the name of the area on the cursor that is used as the current mouse location?
3. What function is used to restrict a cursor to a specific rectangle?
4. What function is used to trap all mouse messages?
5. What function is used to change the current cursor?
6. What message must be handled in order to change the current cursor?
7. What is the size of an application's small icon?
8. What function is used to change to the sectionglass cursor?
Download Sample Programs - DCTest.zip(Icon and Cursor Demo)
|
|
|
|