Monday, November 23, 2009

Lab 8.2: Changing Text in Menu

Lab 8.2: Changing Text in MenuIn this lab, you will extend menu functionality. You will toggle the text of a menu based on the state of the application. You will also coordinate the menus and toolbars based on the state of a feature of the application.

To see a demonstration of the lab solution, click this icon.
Estimated time to complete this lab: 30 minutes
To complete the exercises in this lab, you must have the required software. For detailed information about the labs and setup for the labs, see Labs in this course.

Objectives

After completing this lab, you will be able to:
® Edit a menu resource.
® Use ClassWizard to add COMMAND handlers.
® Implement handlers.
® Use ClassWizard to add UPDATE_COMMAND_UI handlers.
® Add buttons to the toolbar.
® Add and implement COMMAND and UPDATE_COMMAND_UI handlers.

Prerequisites
Before working on this lab, you should be familiar with the following:
® How to add a menu, a submenu, and menu functionality to an MFC application.
Exercises
The following exercises provide practice with the concepts and techniques covered in this chapter:
® Exercise 1: Implementing Toggling Text
In this exercise, you will toggle the text of a menu based on the state of the pen width. When the pen width is thin, the text will read "Change to Thick." When the pen width is thick, the text will read "Change to Thin."
® Exercise 2: Coordinating Toolbar Buttons and Menus
In this exercise, you will make the toolbar and the menu work together by using a single button's pressed and unpressed states to reflect the current state of the pen width: up for thin, down for thick.

Exercise 1: Implementing Toggling Text
The code that forms the basis for this exercise is in \Labs\Ch08\Lab02\Baseline. Copy these files to your working directory.

In the baseline code, the menu items have been implemented and are active. However, the user has no indication of the pen width without drawing a line. One way to indicate pen width is to change the text of the menu item. When the pen is thin, the menu can say "Change to Thick” and when thick, "Change to Thin."

UPDATE_COMMAND_UI is sent for each menu item before the menu is displayed. This enables you to set the text of the menu based on the state of m_nPenWidth.

Add the menu strings to the string table resource

1. Choose the ResourceView pane in the project workspace.
2. Open the String Table folder and double-click the String Table item.
3. Display the properties for the blank line at the end of the string table resource. Set the ID to IDS_CHANGETHICK and set the caption to &Change to Thick.
4. Press ENTER. The properties for the new blank line appear. Set the ID to IDS_CHANGETHIN and set the caption to &Change to Thin.
5. Save Scribble.rc.
Use ClassWizard to add UPDATE _COMMAND_UI handlers for the Thick or Thin menu items
1. On the View menu, select ClassWizard or press CTRL+W.
2. Select the CScribbleDoc class and the ID_PEN_CHANGETOTHICKORTHIN object ID.
3. Select the UPDATE _COMMAND_UI message.
4. Click Add Function and type OnUpdatePenChangeToThickorThin for the member function name.

Implement the UPDATE _COMMAND_UI handlers for Thick or Thin menu items
1. Choose the OnUpdatePenChangetoThickorThin member function in ClassWizard. Click Edit Code, or open ScribDoc.cpp and scroll to OnUpdatePenChangetoThickorThin.
2. Declare a string object to hold the menu string.
CString MenuCaption;
3. As with the menu COMMAND handler, you will check the current pen width.
if (GetPenWidth() == THIN)
4. If the width is THIN, then you will load the menu string for the THICK choice.
MenuCaption.LoadString(IDS_CHANGETHICK);
5. Otherwise, you should load the menu string for the THIN choice.
MenuCaption.LoadString(IDS_CHANGETHIN);
6. Set the menu text to the menu string.
pCmdUI->SetText(MenuCaption);
7. Save ScribDoc.cpp. The complete function follows:
void CScribbleDoc::OnUpdatePenChangeToThickorThin(CCmdUI* pCmdUI)
{
CString MenuCaption;
if (GetPenWidth() == THIN)
{
MenuCaption.LoadString(IDS_CHANGETHICK);
}
else
{
MenuCaption.LoadString(IDS_CHANGETHIN);
}
pCmdUI->SetText(MenuCaption);
}
8. Build and run Scribble.
The completed code for this exercise is in \Labs\Ch08\Lab02\Ex01.

Exercise 2: Coordinating Toolbar Buttons and Menus

Continue with the files you created in Exercise 1; if you do not have a starting point for this exercise, the code that forms the basis for this exercise is in \Labs\Ch08\Lab02\Ex01.

Your menu system is now complete; however, you have not yet implemented toolbar support. In this lab, there is no simple one-to-one relationship between a toolbar button and the menu function. There are two methods for making the toolbar and the menu work together:

® Use a single button's pressed and unpressed states to reflect the current pen state: up for thin, down for thick.
– or –
® Use two buttons, one for thick and one for thin, with the pressed state reflecting the current pen.
In this exercise, you will implement the first method. The coding techniques are easily applicable to the second method.
Open the toolbar resource
1. If you do not have the resource file open, switch to the ResourceView pane and expand the Scribble folder.
2. Expand the Toolbar folder and double-click IDR_MAINFRAME.


The Toolbar editor opens and displays the default toolbar resource that AppWizard created for Scribble. The first button on the toolbar, selected by default, appears in the bottom pane (magnified view) of the editor window.



The graphics and color tools also open as part of the Toolbar editor. If these graphics tools do not appear, right-click in any blank area on the Developer Studio menu bar. Select Graphics and Colors in the pop-up menu that appears. You can drag the graphics tools to either side of the screen and dock them to get a better view of the editor window.

Delete and add toolbar buttons
1. Drag the button that you want to delete off the toolbar (in the top, or normal view pane). In this case, drag the Cut, Copy, and Paste buttons off the Scribble toolbar. (This step is optional; if you do not remove these buttons, they will appear dimmed in the running application but will not interfere with Scribble operations.)
2. To add a button, select the blank button to the right of the toolbar resource. Drag this new button to where the deleted button was. This new button receives focus in the two split panes of the editor window. (If you want the button to appear larger in the editor, choose the Magnify tool, and select the magnification factor that you want.)
3. Choose the line tool from the Graphics toolbar and choose the one-pixel-wide pen.
4. Using the magnified view of the button, draw a single-pixel line on the left of the button.
5. Choose the line tool from the Graphics toolbar and choose the two-pixel-wide pen.
6. Using the magnified view of the button, draw a two-pixel line on the right of the button.
7. Use the Text tool to type a question mark (?) between the two lines. Set the font to Times New Roman, 14-point bold.


8. Your toolbar should now look like the toolbar below.



9. Save Scribble.rc.

Assign an ID and a ToolTip

1. Double-click the toggle line button to show the Toolbar Button property sheet. Visual C++ will assign an ID to the button, but you will want to create a meaningful ID. Set the ID to ID_PEN_TOGGLE.
2. A toolbar button prompt has two parts: a status bar string and a ToolTip string. These two strings are separated by a newline (\n); you can omit the newline if you do not have a ToolTip. Set the prompt to:
Toggle line between thick and thin\nToggle pen

Implement COMMAND and UPDATE_COMMAND_UI handlers

1. Start ClassWizard by clicking ClassWizard on the View menu, or by pressing CTRL+W.
2. Select the CScribbleDoc class, Object ID ID_PEN_TOGGLE, and the COMMAND message. Click Add Function and change the function name to that of the menu COMMAND handler, OnPenChangeToThickorThin. This will cause MFC to use the same COMMAND handler for the toolbar and the menu.
3. Choose the UPDATE_COMMAND_UI message. Click Add Function and accept the default member function name.
4. With OnUpdatePenToggle selected, click Edit Code.
5. SetRadio(TRUE) causes a toolbar button to appear as if pressed. Check the width of the pen, and set the radio button state based on the width.

pCmdUI->SetRadio((GetPenWidth() == THIN)? FALSE: TRUE);

6. Save ScribDoc.cpp and build Scribble.
The completed code for this exercise is in \Labs\Ch08\Lab02\Ex02.

Implementing State Indicators

Exercise 2: Implementing State Indicators
Continue with the files you created in Exercise 1, or if you do not have a starting point for this exercise, the code that forms the basis for this exercise is in \Labs\Ch08\Lab01\Ex01.

The menu items you added are now functional. However, the user has no indication of the pen width without drawing a line. The easiest way to indicate the state of an option is to use radio button marks or check marks. Because the user can only choose between the two options of thick and thin pens, you will add a radio button as the indicator of the pen width.

Use ClassWizard to add UPDATE _COMMAND_UI handlers for Thick and Thin menu items
1. On the View menu, click ClassWizard, or press CTRL+W.
2. Select the CScribbleDoc class and select the object ID ID_PEN_WIDTH_THICK.
3. Select the UPDATE _COMMAND_UI message.
4. Click Add Function and accept the default function name.
5. Repeat the previous steps for ID_PEN_WIDTH_THIN.


Implement the UPDATE _COMMAND_UI handlers for Thick and Thin menu items

1. Select the OnUpdatePenWidthThick member function and click Edit Code.
2. SetRadio and SetCheck are member functions of CCmdUI that take a BOOL value. To set the radio button indicator, check to see whether the pen width matches the menu item. Replace the //TODO comment with:
pCmdUI->SetRadio(GetPenWidth() == THICK);
3. For the Thin menu item, the same technique applies. Replace the //TODO comment with:
pCmdUI->SetRadio(GetPenWidth() == THIN);
If check marks were a more appropriate UI, you could use the same technique with CCmdUI::SetCheck.
4. Save ScribDoc.cpp.
Add a GetPenWidth member
1. Open ScribDoc.h.
2. Add to the public attributes section of CScribbleDoc.
PENWIDTH GetPenWidth() const { return m_nPenWidth; }
3. Save ScribDoc.h.
4. Build and run Scribble.

The completed code for this exercise is in \Labs\Ch08\Lab01\Ex02.


Exercise 3: Adding Toolbar Buttons
Continue with the files you created in Exercise 2, or if you do not have a starting point for this exercise, the code that forms the basis for this exercise is in \Labs\Ch08\Lab01\Ex02.


Your menu system is now complete; however, you have not yet implemented toolbar support. In this exercise, you will add two buttons to the toolbar and implement them to work with the menu.

Open the toolbar resource

1. If you do not already have the resource file open, switch to ResourceView and expand the Scribble folder.
2. Expand the toolbar folder and double-click IDR_MAINFRAME.
The Toolbar editor opens and displays the default toolbar resource that AppWizard created for Scribble. The first button on the toolbar, selected by default, appears in the bottom pane (or magnified view) of the editor window. The following illustration shows what the magnified view should look like.



The graphics and color tools also open as part of the Toolbar editor.
The following illustration shows what these two graphics tools look like.



If these graphics tools do not appear, right-click in any blank area on the Developer Studio menu bar. Select Graphics and Colors in the pop-up menu that appears. You can drag the graphics tools to either side of the screen and dock them to get a better view of the editor window.

Delete and add toolbar buttons

1. Drag the button that you want to delete off the toolbar (in the top, or normal view pane). In this case, drag the Cut, Copy and Paste buttons off the Scribble toolbar. (This step is optional; if you do not remove these buttons, they will remain disabled in the running application but otherwise will not interfere with Scribble operations.)
2. To add a button, select the blank button at the right end of the toolbar resource. Drag this new button to the former location of the Cut button. This new button receives the focus in the two split panes of the editing window. (If you want the button to appear larger in the editor, choose the Magnify tool, and select the magnification factor that you want.)
3. Click the line tool on the graphics toolbar and choose the two-pixel-wide pen. This is the smallest of the square-shaped pens.
4. Using the magnified view of the button, draw a line from the lower-left corner to the upper-right corner using a thicker pen.



5. Repeat the process, creating a thick line from the upper-left to lower-right corner.



6. Your toolbar should now look like the toolbar below.



7. Save Scribble.rc.

Associate toolbar buttons with command IDs
In the next step, you will associate the new Thick Line button with a command ID so that the button works when running the Scribble application. This step is identical to the one that you performed to associate a menu item with a command ID.

You bind the Thick Line button to ID_PEN_THICK and the Thin Line button to ID_PEN_THIN. You defined these IDs earlier for the Thick Line and Thin Line menu commands, so Visual C++ has already written a #define for the ID in Resource.h. Your only task is to associate the ID with the button.

1. Double-click the Thin Line button to show the Toolbar Button Properties property sheet. Visual C++ assigns an ID to the button, but you can select an ID from the drop-down ID list that corresponds to the menu item that the toolbar imitates. For the Thin Line button, set its ID to ID_PEN_WIDTH_THIN.

2. Repeat with the Thick Line button. Set its ID to ID_PEN_WIDTH_THICK.

By associating the command ID with the toolbar button, the string resources become active for the button as well. When the mouse passes over the button, the prompt string appears in the status line, and the ToolTip appears next to the button.

Add a ToolTip

1. Select the Thin Line toolbar button in the editor window, and choose Properties from the View menu to display the Toolbar Button Properties property sheet.

In the Prompt: box, you will see the text "Change pen style to a thin line." You entered this text as the Thin menu item in Scribble's Pen menu; it appears in the status line when the mouse passes over the menu command.

2. At the end of the prompt text, type a newline character (\n) plus the text that you want to display in the ToolTip. (There should be no space between the newline character and the text of the ToolTip.) If you want a ToolTip without a prompt string, simply start with the newline character.

Keep this text short. For Scribble, type \nThin after the existing prompt string.

Note You can have a ToolTip without a status bar string by starting the prompt string with \n.

3. Save Scribble.rc and build Scribble.

Because of the built-in toolbar support in MFC, you were able to implement new toolbar buttons without coding. In the next lab, you can programmatically connect a toolbar button with a menu item. Your application should now be able to create output like that shown in the following illustration.



The completed code for this exercise is in \Labs\Ch08\Lab01\Ex03.

Adding Static Drop-down Menus

Lab 8.1: Adding Static Drop-down Menus
In this lab, you will add functionality to the Scribble application. Using the Menu editor, you will provide a top-level menu and submenu that enable the user to set the width of the drawing pen. You will then add a radio button indicator to show the state of a menu item.


The following illustration shows you what the complete lab should look like.



To see a demonstration of the lab solution, click this icon.
Estimated time to complete this lab: 30 minutes
To complete the exercises in this lab, you must have the required software. For detailed information about the labs and setup for the labs, see Labs in this course.

Objectives

After completing this lab, you will be able to:

® Edit a menu resource.
® Use ClassWizard to add COMMAND handlers.
® Implement handlers.
® Use ClassWizard to add UPDATE_COMMAND_UI handlers.
® Set radio button or check box indicators in menus.
® Add buttons to the toolbar.

Prerequisites

Before working on this lab, you should be familiar with the following:

® In this lab, you use the Scribble application contained in the product documentation. For more information, see the online Visual C++ Tutorials in \Samples\MFC Samples\Tutorials\Scribble.
® This exercise requires that you be able to use AppWizard to create a simple single document interface (SDI) application.

Exercises

The following exercises provide practice with the concepts and techniques covered in this chapter:

® Exercise 1: Implementing a Static Drop-down Menu
In this exercise, you will create and implement a menu that controls the pen width in the Scribble application.
® Exercise 2: Implementing State Indicators
In this exercise, you will add a radio button to indicate the pen width that is currently in effect in the Scribble application.
® Exercise 3: Adding Toolbar Buttons
In this exercise, you will implement toolbar support by adding two buttons to the toolbar and connecting them to work with the menu.


Exercise 1: Implementing a Static Drop-down Menu
The code that forms the basis for this exercise is in \Labs\Ch08\Lab01\Baseline. Copy these files to your working directory.

In this exercise, you will create and implement a menu that controls the pen width in the Scribble application.

Add a Pen menu with Width and Thick and Thin drop-down menus

1. Open the project workspace to ResourceView. Open the Menu folder, and then open the IDR_SCRIBBTYPE resource.

The following illustration shows what your interface should look like.



2. In the Menu editor, click the dotted box to the right of Help and drag it to the position between the View and Window items.
3. Double-click the dotted box to display the Menu Item Properties property sheet. Set the caption to &Pen, which will display as Pen. To keep the Properties property sheet on top, click the pushpin in the upper-left corner of the sheet.
4. Click the dotted box below the Pen menu. Set the caption of this menu item to &Width. Because this item has a submenu, select the Pop-up property.
5. Click the dotted box to the right of the Width menu item. Set this item's caption to Thi&n. The Menu editor assigns ID_PEN_WIDTH_THIN as this item's ID when you go to another menu item, so you can ignore the ID field. Set the prompt of this item to "Change pen style to a thin line."
6. Click the dotted box below the Thin menu item. Set the caption of this item to Thic&k. The Menu editor will assign ID_PEN_WIDTH_THICK as this item's ID when you select another menu item, so you can ignore the ID field. Set the message string of this item to "Change pen style to a thick line." The following illustration shows what the Menu Item Properties property sheet should look like.



7. Close the Menu Item Properties property sheet.

Use ClassWizard to add handlers for the Thick and Thin menu items

1. On the View menu, click ClassWizard, or press CTRL+W. Click the Message Maps tab.
2. Select CScribbleDoc as the class name, ID_PEN_WIDTH_THIN as the object ID, and COMMAND as the message. Click Add Function to add the function and accept the default OnPenWidthThin as the function name.
3. Repeat this procedure to add the default handler for ID_PEN_WIDTH_THICK.

Note Consider the following guidelines when implementing handlers:

— In general, put handlers in the command-target class where they have the widest scope needed.
— When attributes are shared by multiple views or frame windows, put them in the common document.
— If attributes are not shared, put them in the view(s) or window(s) that use them.

4. Click Edit Code to go to ScribDoc.cpp in the OnPenWidthThick handler. Replace the commented line:

// TODO: Add your command handler code here with:

ChangePen(THICK);

You have not yet defined ChangePen or THICK; you can do this in the next section.

5. Replace the comment in OnPenWidthThin with:

ChangePen(THIN);

6. Save Scribdoc.cpp. The complete functions follow.

void CScribbleDoc::OnPenWidthThin()
{
ChangePen(THIN);
}
void CScribbleDoc::OnPenWidthThick()
{
ChangePen(THICK);
}

Provide a function to update the pen width

Scribble has a member that holds the document's current pen, m_penCur, and a member that holds the current pen's width.

1. Right-click CScribbleDoc in ClassView, and add ChangePen as a protected function.
void CScribbleDoc::ChangePen(PENWIDTH penWidth)
2. Set the current width member to the passed width. Write code for ChangePen as follows.
m_nPenWidth = penWidth;
3. Because Windows GDI objects are a limited resource, destroy them when they are no longer needed. Scribble does not need the old pen once a new one has been created, so delete the current GDI pen associated with the m_penCur object.

m_penCur.DeleteObject();

4. Create a new GDI pen to associate with the m_penCur object.
m_penCur.CreatePen( PS_SOLID,
m_nPenWidth,
RGB(0,0,0));
5. Save Scribdoc.cpp. The complete function follows.
void CScribbleDoc::ChangePen(PENWIDTH penWidth)
{
m_nPenWidth = penWidth;
m_penCur.DeleteObject();
m_penCur.CreatePen( PS_SOLID,
m_nPenWidth,
RGB(0,0,0));
}

Integrate the changes into Scribble

While you can define a thick pen of five pixels and a thin pen of two pixels by hard-coding these values, this makes the code difficult to maintain. Instead, enumerate these values.

1. Open ScribDoc.h.
2. In the protected attributes section of the CScribbleDoc class declaration, define PENWIDTH.
enum PENWIDTH {THIN = 2, THICK = 5};
3. Change the type of m_nPenWidth from UINT to PENWIDTH.
PENWIDTH m_nPenWidth;
4. Save ScribDoc.h.
5. Open ScribDoc.cpp and find the InitDocument function.
6. Replace the following:
m_nPenWidth = 2;
with its enumerated equivalent:
m_nPenWidth = THIN;
7. Save ScribDoc.cpp.
8. Build and run Scribble.

The completed code for this exercise is in \Labs\Ch08\Lab01\Ex01

StatusBar in visual basics

StatusBar

Status bars appear at the bottom of the application screen as a series of panes. These panes display information, usually in the form of text strings, although graphics can also be displayed.

To see a sample SDI application window with a status bar at the bottom, click this icon.





This section includes the following topics:

® Introduction to Status Bars
® Specifying Status Bars in AppWizard
® Adding a Pane to the Status Bar
® Displaying Status Bar States

Introduction to Status Bars

The default status bar displays the status of the CAPS LOCK key, the NUM LOCK key, and the SCROLL LOCK key. When the user clicks a menu item or toolbar button in an application, the status bar displays menu prompts that describe the basic functionality of the selected item.


Status bars are created from the CStatusBar class and take an array of IDs, one ID for each of the panes it contains. When you create an application in AppWizard and select Initial status bar in Step 4, AppWizard creates this array for your status bar. The array is placed in the source file for your MainFrame window class.

This sample code shows the array that AppWizard provides:

static UINT BASED_CODE indicators[] =
{ ID_SEPARATOR, // message line indicator
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};

The status bar panes are arranged and numbered horizontally along the status bar from left to right, starting at pane 0. You can add panes by adding IDs to the array. You can size the panes as needed and add separators by using ID_SEPARATOR elements.

After all the other panes are in place, the leftmost pane, at position 0, takes up all the remaining space on the status bar. This pane is most often used as a message area where the prompt strings of the various UI elements are displayed.

Like a toolbar, the status bar object is embedded in its parent frame window and is constructed automatically when the frame window is constructed. Since status bar panes are indicators of the state of an application, a call to the SetIndicators member function of class CStatusBar associates an ID from the array with each pane.

Specifying Status Bars in AppWizard

When you use AppWizard to build your application, you can specify an initial status bar. If you indicate that you want a status bar in your application, AppWizard does the following:

® Adds a protected CStatusBar data member to the MainFrame class header file, as shown in the following example:

// mainfrm.h : header file for the main frame class
class CMainFrame : public CMDIFrameWnd {
...
protected: // control bar embedded members
...
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
...
};

® Calls the CStatusBar::Create member function that creates the status bar and its windows. AppWizard inserts the following code:

// mainfrm.cpp : implementation file for the main frame class
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
...
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
...
}

Adding a Pane to the Status Bar

If you want to provide additional information in the status bar, you can add panes and fill them with text or graphics.

Developer Studio does not contain a specialized editor for status bars; however, you can add a status bar pane by using the Menu editor to create a menu item on a dummy menu, and then writing code to manage the pane.

The following steps describe how to add a pane to the status bar. The rest of the topic explains the steps in more detail.

To add a pane to the status bar

1. Use the Menu editor to create a menu item on a dummy menu. You need the command ID of the menu item to access the status bar pane.
2. In the Prompt box on the Menu Item Properties property sheet, type in a string to initialize and size the pane. The pane size is based on the length of the string that you entered.
3. Modify the array to add the new command ID at the appropriate location. If you want the new pane to be the second pane from the left, it should appear as the second item in the array.
4. Write the code to manage the pane display.

By default, new status bar panes are enabled. If a pane is associated with a command ID, the pane displays the prompt string for that command ID.

Creating the Associated Command ID

Along with the command ID, it is important to supply an initial prompt string for this dummy menu item. The string serves two purposes: first, it is used by the application's framework to size the new pane when it is created; second, the string is used to initialize the pane and is displayed in the pane by the application on startup.

Modifying the Array

The panes of the status bar are represented by elements in an array, found in the file Mainfrm.cpp. Add the command ID associated with the new pane to this array. The position of the command ID in the array determines the order in which it will appear in the status bar. Note that the first element, ID_SEPARATOR, represents the default area, pane 0, where menu and toolbar button prompts are displayed.

Setting Pane Text

You can set text in a status bar pane in three ways:

® Use the member function CStatusBar::SetPaneText to write to the pane immediately.
® Write the information in an update-command handler associated with the pane.
® Call CWnd::SetWindowText to update the text in pane 0 only.

Using CStatusBar::SetPaneText

The leftmost pane in the default status bar displays the menu and toolbar button prompts. Panes are consecutively numbered from the left, beginning with pane 0. You can write text to pane 0 with the functions CFrameWnd::SetMessageText or CWnd::SetWindowText. You can write to any pane in the status bar with the function CStatusBar::SetPaneText, as shown:

BOOL CStatusBar::SetPaneText(int nIndex, LPCTSTR
lpszNewText, BOOL bUpdate = TRUE)
SetPaneText returns a nonzero value if successful; otherwise, it returns zero. SetPaneText sets the pane text to the string pointed to by lpszNewText. An index can be dynamically determined with the function CStatusBar::CommandToIndex.

Creating an Update Command Handler

If the text that you display is better calculated during idle time, you can create an update command handler for the pane's associated command ID, and then write to the pane by using CCmdUI::SetText, as shown:

// in a command UI handler
// to activate the status bar pane
pCmdUI->Enable(TRUE);
// to modify text in status bar
pCmdUI->SetText(HelloPhrase);


Calling CWnd::SetWindowText
The third method for setting pane text can be used only when writing to pane 0. The pointer variable in the function call points to the location of the string to be displayed.

void CWnd::SetWindowText ( LPCTSTR lpszString );

Displaying Status Bar States

Status bars can maintain various states. The state of a status bar can change while the status bar is visible. For example, when a button is clicked, it appears as if it is pressed; if a toolbar button is unavailable, it appears dimmed.

Status Bar States

The defining borders of a status bar pane can be set or removed. In addition, text in a status bar pane can be hidden or visible, or changed dynamically.

Using Command Handlers to Change Status Bar States

Command handlers for status bar panes are called whenever the system reaches an idle state. (For more information, search for CWinThread::OnIdle in Visual C++ Help.) The handlers allow the visible states of the panes to change dynamically.

The following table lists familiar CCmdUI member functions available in a command handler. These handlers affect the visual state of status bars rather than menu items.

Function Description

Enable Enables or disables the user interface. For status bars, Enable hides or shows text in a status bar pane.


SetCheck Sets the border of a status bar pane.
SetRadio Same as SetCheck.
SetText In a status bar pane, SetText changes the text that is displayed.


Sample Aplications

Here are short descriptions of the sample applications related to this chapter. These sample applications are located in the folder \Samples\Ch08.


Sample application folder
Description of application

\Menus1 A simple SDI application with menu handlers. Draws text in the middle of the client area.

\Menus2 Same as Menus1, with command update handlers added.
\Menus3 Same as Menus2, except that four command handlers point to one function. Shows that multiple message map entries can point to the same function.
\StatBar Similar to Menus1. Shows how to add a pane to the default status bar and write text into the new pane by using a dummy menu item.
\CustSBar Similar to Menus1. Shows how to replace the default status bar with a custom status bar. This custom status bar duplicates the functionality of the default status bar (CAPS state, status line) and has graphics drawn in one of its panes.
\Tool Similar to Menus1. Shows how to add icons to the default toolbar that duplicate the functionality of menu items.

Self-Check Questions

1. Typically, how does an MFC application fill its menu bar with top-level menus?

a A. You must create all top level menus by using the Menu editor.
a B. There is no resource for the menu bar; it is created dynamically by the application’s framework.
a C. ClassWizard is used to add a CMenu object for each top-level menu.
aa D. AppWizard supplies the project with default menu resources.

2. How do you create an accelerator key resource and associate it to an existing menu item (for example, \tCtrl+C for the Copy command)?
a A. In the Menu editor, in the Properties dialog box, append the menu item caption with the appropriate string (\tCtrl+C).
a B. In the string table, locate the associated command, and edit the caption property to append the accelerator string (\tCtrl+C).
a C. In the Accelerator editor, create a new accelerator for the corresponding key sequence, and then associate it to the appropriate existing command ID.
aa D. You must hand-edit the resource file because Developer Studio does not contain an editor for shortcut keys.

3. Which one of following statements is true about toolbars and toolbar buttons?

a A. A toolbar is a window (a Windows 95 common control) that contains a single bitmap.
a B. If you do not choose AppWizard support for toolbars, it is not possible to later add them to your application.
a C. A toolbar button must be associated with an existing, displayable menu item.
a D. Each toolbar button must always have an associated ToolTip string.

4. Which one of following statements is true about status bars and their panes?

a A. They can be easily managed with the status bar editor in Developer Studio.
a B. Indicator pane number 0, which is used to display prompt strings, is always sized to fit the string "ID_SEPARATOR" within it.
a C. The status bar is owned by the application’s view object.
a D. Text can be written to any pane by using either CCmdUI::SetText in an update command handler, or

CStatusBar::SetPaneText in any function.

5. When are ON_UPDATE_COMMAND_UI handlers called?
a A. In the OnUpdateCmd handler.
a B. During idle processing.
a C. During command routing only.
aa D. During general message routing.

Toolbars in visual basics

Toolbars

A toolbar is a window that contains one or more rows of command buttons. These buttons provide a visual method by which the user can perform specific tasks that can also be performed through the menus in the application. Because toolbar buttons are visual representations of menu commands, toolbars mimic much of the behavior and implementation of menus.

Toolbar button graphics are based on a single bitmap that contains a row of button images. The following illustration shows a sample toolbar.



When you create an application using AppWizard, Step 4 of the creation process enables you to specify whether you want the default toolbar (IDR_MAINFRAME) added to your MFC-based application. If you choose the default toolbar, the MFC framework adds the toolbar and the default functionality.

This section includes the following topics:

® Modifying the Toolbar
® Implementing Toolbar Buttons
® Displaying Toolbar States
® Toolbars and ToolTips

Modifying the Toolbar

You use the Toolbar editor to add a new button to a toolbar or to change the appearance or location of a button on a toolbar.

The Toolbar editor has three panes. The top pane contains a bitmap of the toolbar, and reflects the changes that occur in the other two panes. To view the Toolbar editor, click Toolbar Editor on the Image menu.

To add a button to the toolbar

1. In your project workspace view, select the ResourceView tab.
2. Click the Toolbar folder, and double-click the blank button at the far right. This button is provided by the Toolbar editor and is a placeholder rather than part of the toolbar.
3. Supply the information for the queries posed in the Toolbar Button Properties property sheet. If you want to associate the button with an existing command, select the Command ID from the drop-down list of IDs provided.

To reposition a button
— In the Toolbar editor, drag the button to a new location on the toolbar.

To copy a button
— In the Toolbar editor, hold down the CTRL key while dragging and dropping the button you want to copy.

To add or delete a separator
— In the Toolbar editor, drag the button so that it is next to the place where you want to add or remove the separator. The separator will automatically be added or deleted according to how you place the button.

To delete a button

— In the Toolbar editor, drag the button off the toolbar. You cannot delete the blank placeholder button on the right end.

To see a demonstration of how to extend the default toolbar, click this icon.

Implementing Toolbar Buttons

Clicking a toolbar button generates a command, just as choosing a menu item does. As a result, you must add a command handler to implement toolbar button functionality. A toolbar button with no associated command handler appears dimmed and is unavailable to the application.

Associating a Toolbar Button with an Existing Menu Item Command

If you have already implemented the command handler for a menu item, you can implement a toolbar button by giving it the same command ID as the associated menu item. You assign the command ID in the Toolbar Button Properties property sheet. If the menu item already has a prompt string that appears in the status bar, the same prompt string also appears in the button's status bar display.

Associating a Toolbar Button with a New Command

If you need to create a command for a toolbar button that has no associated menu item, you must first create a menu item to associate with the button, and then add the menu item to a dummy menu. You create a dummy menu as you would any other menu; however, the dummy menu does not appear in the user interface.

Displaying Toolbar States

Toolbars can maintain various states, and the state of a toolbar can change while the toolbar is visible. For example, when the user clicks a toolbar button, the button appears as if it is pressed; if a toolbar button is unavailable, it appears dimmed.

Toolbar States

Like menu items, toolbar buttons are displayed differently based on whether they are available or unavailable (normal or dimmed). When you click a toolbar button, its appearance changes. An available button appears to be raised, while a clicked button looks as if it has been pressed.

Buttons on a toolbar, like menu items, are functionally and visibly disabled until a command handler is added to support button selection. To change the state of a toolbar button at run time, the application must provide a command handler to change its enabled or selection state.

Using Command Handlers to Change Toolbar States

Command handlers for toolbar buttons are called whenever the system reaches an idle state. (For more information, search for CWinThread::OnIdle in Visual C++ Help.) The handlers allow the visible states of toolbars to change dynamically.

The following table lists common CCmdUI member functions available in a command handler. In this context, the handlers affect the visual state of toolbar buttons rather than menu items.

Function Description

Enable Enables or disables the user interface. For toolbars, this shows the button in its enabled or disabled state.

SetCheck Selects a toolbar button (shows as depressed).
SetRadio Same as SetCheck.

Toolbars and ToolTips

A ToolTip is a small pop-up window that appears when the mouse pointer is paused over a toolbar button or other user interface element, such as an icon. Generally, a ToolTip contains a single, short line of descriptive text about the associated user-interface element. The following illustration shows a toolbar with a ToolTip.



Adding ToolTips

Developer Studio supports ToolTips for menus and toolbar buttons.
To add a ToolTip, append a text string to the status bar prompt string on the Menu Item Properties property sheet of the menu item. ToolTip strings should be brief, consisting of one or a few words. The ToolTip string is preceded by \n and follows the prompt string; for example:

Open an existing document\nOpen
Open an existing document is the prompt string, and \nOpen is the appended ToolTip string. Note that there are no spaces around the \n separator character.

Ideally, you should supply a ToolTip string for every menu item in an application. Because toolbar buttons are usually associated with existing menu commands, ToolTips are generated automatically.

Disabling ToolTips

There are instances when you do not want or need ToolTips. In these cases, disable ToolTips for your application by commenting out these lines of code from CMainFrame::OnCreate:

//TODO: Remove this if you don't want ToolTips
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY)

Locating the Appropriate Class for the Command Handler

Locating the Appropriate Class for the Command Handler

When deciding where to add the command handler, consider which class should logically perform the action that the command will carry out. For example, in the preceding code example, the Colors menu handler should be placed in the CMenus1View class because the View class has full access to all the functions of the document class.

Typically, in single document interface (SDI) single-pane applications, command handlers are located in the View class. In SDI multiple-pane applications, such as splitter windows, and multiple document interface (MDI) applications, command handlers are located in the CMainFrame class.

Updating the Appearance of Menus

When designing the interface for a Windows-based application, remember that users must be able to see the results of menu actions and choices that they have made. You can write code that updates the look of the interface to reflect these actions. For example, you can add a check mark next to a menu item that the user has selected.

This section describes situations where the appearance of a menu requires updating, gives an overview of how you do this, and provides the necessary programming details.

This section includes the following topics:

Updating Menus

Menu items are updated in response to the user's clicking the menu name. Menu items must be updated before they can be displayed.

When a user clicks a menu name, the Windows operating system sends a WM_INITMENU or WM_INITMENUPOPUP message to the framework, and the framework calls the update command handler.

When a menu item must be updated, the framework searches for and calls the update command handler associated with the menu item. The handler provides Windows with instructions about how each menu item should appear after a user action. Menu items must be updated before they are displayed.

Note In contrast, toolbars and status bars are updated during idle-time processing by CWinApp::OnIdle. Later sections in this chapter address toolbars and status bars.

The CCmdUI class provides the context for updating user-interface objects. For example, CCmdUI::Enable enables menu items so they can be selected, or disables them and displays them dimmed to indicate that they are unavailable.

By default, MFC displays all menu items as enabled if they have an associated command handler. Menu items with no handler are unavailable and appear dimmed.

Adding an Update Command Handler
For menu items that require visual updating, such as adding a check mark to indicate that the selected text is now bold, an update command handler changes the appearance of the menu item.

The following illustration shows the Message Map tab in ClassWizard. An Object ID is selected (ID_COLORS_BLACK) and the message selected is UPDATE_COMMAND_UI. The lower portion of the illustration shows the update handlers available. From here, you can add any update command handlers needed. To see an illustration of the Message Map tab in ClassWizard, click this icon.





Code in the Update Command Handler

The update handler receives a pointer to the command's user-interface object — in this case, a menu — as an argument. Use this argument to invoke the appropriate member function to update the user interface.

The following table summarizes the CCmdUI member functions as they pertain to menu items, dialog-box buttons, and controls.

Function Description


Enable Enables or disables the user interface item.

SetCheck Adds or clears a check mark.

SetRadio For a menu item, adds or clears a dot.

SetText Changes text displayed for a command user-interface item.

Note These member functions perform different types of updates, depending on the user-interface object specified. For example, SetCheck can place or clear a check mark next to a menu item, or it can place or clear a check mark in a check box, or it can affect the appearance of the border on a status-bar pane.

To add update commands for menu items

1. Start ClassWizard. In the Class name list box, find the appropriate class.
2. In the Object IDs list box, locate the ID of the item to which you want to add an update handler.
3. In the Messages list box, select Update Command UI.
4. Click Add Function, and accept the default name.

Adding an Accelerator Key to a Menu

After you have added basic functionality to a menu, you can provide a keyboard-based approach to the same functionality. Accelerator keys are keystrokes that carry out a command without displaying menus and menu items.

Note The term "accelerator key" is used only in documentation for developers. In documentation for end users, an accelerator key is referred to as a "shortcut key."


For the developer, "shortcut key" refers to the underscored character on a menu or menu item. The end user reads about these underscored characters as "access keys."

In the interface, accelerator keys are found to the right of menu items on the menu. For example, if you click the Edit menu in Microsoft Word, you will see that CTRL+C is the standard accelerator for the Copy menu command.

An accelerator key produces the same command message and is processed by the same command handler as its corresponding menu item.

To implement accelerator keys for a menu item, you must complete two steps:

1. To add the accelerator combination to a menu, add the keystroke combination to the Caption string on the Menu Item Properties property sheet.

2. To connect the keystroke combination to the appropriate command ID, use the Accelerator editor to add the accelerator resource.

These steps are described in detail in the following procedures.

The following illustration shows the Menu Item Properties property sheet, which you use to add accelerators to menu items.





The following illustration shows the Accelerator Properties property sheet in the Accelerator editor.




u To add an accelerator key combination to the right of a menu item

1. In the Menu editor, double-click the menu item you want to edit. This displays the Menu Item Properties property sheet.

2. In the Caption box, edit the menu item's string. At the end of the existing caption, type \t followed by the string that represents the accelerator key combination.

The \t places a tab after the existing caption. For example, in a typical Copy command, the caption string appears as follows:

&Copy\tCtrl+C



To connect a keystroke combination to a command ID

1. In your project, click the ResourceView tab.
2. Click the Accelerator folder to open it, and double-click the accelerator table resource to open it.
3. On the Insert menu, click New Accelerator to display the Accelerator Properties property sheet.
4. In the Accelerator Properties property sheet, make the following changes:
a. In the ID box, select the corresponding command ID.
b. Select Next Key Typed.
c. Type the keystroke combination as if you were using the actual accelerator key.
5. Verify that the entry in the Key text box reflects the combination that you just typed, and then close the property sheet.

When implementing accelerator keys, follow these guidelines for interface design:

® Be sure that each accelerator combination is unique.
When creating accelerator keys, do not use reserved key combinations and keys used for backward compatibility with existing applications.

Windows interface guidelines reserve common key combinations for typical Windows-based commands, such as Copy (CTRL+C) and Print (CTRL+P), or for functionality within the Windows operating system.

For backward compatibility, some applications continue to support SHIFT+DELETE, CTRL+INSERT, and SHIFT+INSERT for the Cut, Copy, and Paste commands, respectively.

® Provide menu-driven methods to gain access to commands. Accelerator keys should never be the only method of access.

Using Shortcut Menus

You can use shortcut menus to provide users with an optional way to access commonly used commands. Shortcut menus provide an efficient, object-centered method for interacting with an application.

Shortcut menus are generally displayed at the location of the mouse pointer. The content of a shortcut menu depends on what the user selects with the pointer. For example, if the user selects a string of text, a shortcut menu often displays common formatting or editing options. Users access shortcut menus by clicking the right mouse button.

Note Although shortcut menus are optional in Windows 95-based applications, the right mouse button is reserved strictly for access to shortcut menus.

Shortcut menu items should never be the only method available to access a command.

For more information about shortcut menus, see Windows Interface Guidelines for Software Design.

This section includes the following topics:

Adding a Shortcut Menu

The methods for adding shortcut menus differ from the methods used to add top-level menus. You can create a shortcut menu in three ways:

® If you know all of the menu items that you want to appear in a shortcut menu, you can use the Resource editor to easily create shortcut menus.

® If you know only part of the menu content at design time, you can create the known part of the shortcut menu with the Resource editor, and then use AppendMenu to add other menu items later.

® If none of the contents of the pop-up is known until run time, you can use the Developer Studio Gallery and follow the steps below.

To add a shortcut menu resource using the Developer Studio Gallery

1. On the Developer Studio Project menu, click Add To Project, and then click Components and Controls.
2. Double-click the Developer Studio Components folder.
3. Select Pop-up Menu, click Insert, and then click OK.
4. Add the pop-up menu to an appropriate class in your application, such as the View class, and then click OK.
5. Modify the pop-up menu by removing the items that you do not want, and adding menu items as needed.
6. In the class where you put the shortcut menu, locate the OnContextMenu command handler.
7. Change the this variable to a call to ::AfxGetMainWnd.
8. If necessary, add code to determine whether the user has selected an item with the mouse pointer. For example, if the user clicks the right mouse button in an area for which a shortcut menu is not relevant, do not display a shortcut menu.

Some other possibilities for building shortcut menus are:
® If you need to build a menu dynamically, use a CMenu pointer Popup, and the functions CreatePopupMenu and AppendMenu.
® You also can create a menu that is a combination of a menu created with the Menu editor and a dynamic menu.

For more information about methods of creating a shortcut menu, search for AppendMenu in Visual C++ Help.

Adding a Shortcut Menu Handler
Once you have the interface for your shortcut menus, you need to associate functionality with each item on the menu; this requires a message handler. When you use the Developer Studio Gallery to add a shortcut menu, it adds the following information to your application:

A declaration for an OnContextMenu member function in the declaration of your view class.
An ON_WM_CONTEXTMENU macro in the message map for your view class.
A skeletal definition for the OnContextMenu member function.
An overloaded PreTranslateMessage function, so that SHIFT+F10 invokes your shortcut menu, as it does in Microsoft Word and Microsoft Excel.

Note As long as the pop-up menu commands are the same commands that you have defined elsewhere, you can use the same command IDs.

The following sample code shows how to write the handler OnContextMenu. OnContextMenu receives the location of the mouse event in screen coordinates, not in client coordinates like as do handlers such as OnLButtonDown. Before testing, the function converts the screen coordinates to client coordinates by using CWnd::ScreenToClient. To see the sample code, click this icon.

void CMenusDynamicView::OnContextMenu(CWnd*, CPoint point)
{
// First, we have to determine if the mouse is
// somewhere on the phrase. Begin by obtaining a
// rectangle that bounds the phrase.
CRect BoundingRectangle = GetPhraseBounds();
// Because the parameter is in screen coordinates,
// we'll have to convert it to client coordinates
// before doing any hit-testing.
CPoint pt(point);
ScreenToClient(&pt);
// Then, if the mouse is not the rectangle, exit this
// function.
if (FALSE == BoundingRectangle.PtInRect(pt))
return;
CMenu menu;
VERIFY(menu.LoadMenu(CG_IDR_POPUP_MENUS_DYNAMIC_VIEW));
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
// The four existing menu items (black, red, green
// and blue) were added to the popup menu using the
// menu editor. For demonstration purposes, 3 more
// menu items will be dynamically added to the popup.
// They'll be separated from the first 4.
pPopup->AppendMenu(MF_SEPARATOR);
CString prompt;
for (int i = 0; i < 3; i++)
{
prompt.LoadString(ID_COLORS_CYAN + i);
pPopup->AppendMenu(MF_STRING,
ID_COLORS_CYAN + i, prompt);
}
// Follow the chain of owners to find a window that's
// not a child. This window will serve as the owner
// of the popup menu.
CWnd* pWndPopupOwner = this;
while (pWndPopupOwner->GetStyle() & WS_CHILD)
pWndPopupOwner = pWndPopupOwner->GetParent();
// Finally, display the popup menu.
pPopup->TrackPopupMenu(
TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x, point.y, pWndPopupOwner);
}
//Here's the function GetPhraseBounds:
CRect CMenusDynamicView::GetPhraseBounds()
{
CClientDC dc(this);
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
CMenusDynamicDoc* pDoc = GetDocument();
CString temp = pDoc->GetPhrase();
CSize cs = dc.GetTextExtent(temp, temp.GetLength());
// Recall that the phrase is centered on a point in
// the exact middle of the view. See View class's OnDraw.
// This makes creating a bounding rectangle a bit tricky.
// We'll have to divide the text extents by 2, and take into
// account the amount of descent of the current font.
CRect r;
GetClientRect(&r);
int x = (r.right / 2) - (cs.cx / 2);
int y = (r.bottom / 2) - (cs.cy / 2) - tm.tmDescent;
return CRect(CPoint(x, y), cs);
}

Adding User Interface Features

Adding User Interface Features

This chapter describes how to create and implement the basic elements of the user interface: menus, toolbars, and status bars.

Menus and toolbars provide the user with access to commands. In general, status bars provide the user with information about the status of the application, such as the position of the cursor or the current time.

After outlining the major types of menus available in Windows-based applications, the chapter describes the steps you can follow to develop a user interface, starting with adding menus and command handlers. From there, the chapter covers designing and implementing toolbars and status bars.

Objectives

After completing this chapter, you will be able to:

® Add menus, accelerator keys, status bar menu prompts, and toolbar buttons to an application.
® Explain the routing of a command message.
® Dynamically change the state of a menu item.
® Incorporate a shortcut menu into an application.
® Add additional panes and graphics to a status bar.

Menus

This section provides an introduction to creating and using menus in Windows-based applications, and reviews the architecture of menus under Windows 95. This section is designed for developers who are new to the Windows operating system.

This section includes the following topics:

® Types of Menus
® Building Menus
® Updating the Appearance of Menus
® Adding an Accelerator Key to a Menu

Using Shortcut Menus

Types of Menus

From a programming perspective, all menus in Windows 95-based applications fall into two categories: top-level menus and pop-up menus. Top-level menus are attached to the application's primary, or top-level, window, and pop-up menus are usually attached to other menus or menu items.

Top-Level Menus

A top-level menu consists of a menu name and menu items. The menu name indicates the general functionality and purpose of commands on the menu. Common top-level menu names are File, Edit, Window, and Help. A menu item appears as an individual choice on a menu, and invokes a particular functionality when chosen.

Pop-Up Menus

This chapter explains how to implement three types of pop-up menus: drop-down menus, submenus, and shortcut menus.

Menu type Purpose

Drop-down menus Drop-down menus appear when a menu title is selected from the top-level menu. For example, when you click the top-level Edit menu title, a drop-down menu displays editing commands.

Submenus

Submenus appear when a command is chosen and an additional menu choice is required. In Windows-based applications, a triangular symbol that points to the right of a menu item lets the user know that another menu is displayed when that command is chosen.

Shortcut menus
Shortcut menus appear at the location of the mouse pointer. When you click the right mouse button, a shortcut menu provides context-sensitive commands. For example, if you want the user to have access to common editing and formatting options when the cursor is located over a text string, you can provide those commands on a shortcut menu.

Building Menus

When you use AppWizard to create a new application, it generates standard menus with their associated command identifiers (IDs), along with default command handlers. The command handlers reside in various classes. In the File menu, for example, the application class handles the New and Open commands; the document class handles the Save, Save As, and Close commands; and the view class handles the Print and Print Preview commands.

Adding more menus to your application is a two-step process.

1. Use the Menu editor to create menus.
The Menu editor is a graphical editor that you can use to add, delete, and change menus and menu items. In the Menu editor, you associate a menu item with a command ID.
2. Use ClassWizard to write the code to support menu functionality.
You use ClassWizard to add, delete, and maintain command ID and command-handler entries in the message map.

This section describes how to add a menu, set menu properties, and work with command handlers in your applications.

This section includes the following topics:

Adding a Menu and Setting Its Properties

When you design a menu, keep in mind conventional user interface design guidelines for grouping commands and offering visual clues to the user. This illustration shows many of these visual clues of menu items that are set in the Menu Item Properties property sheet.




To add a top-level menu or menu bar
1. In your project workspace view, click the ResourceView tab.
2. Right-click the Menu folder and click Insert Menu. This inserts a blank menu resource.
3. Double-click the empty placeholder on the menu bar to invoke the Menu Item Properties property sheet.
4. Type a caption for the menu. The caption will appear in the menu bar.

To add a pop-up menu

1. Using the Menu editor, open the menu resource that is to include the new pop-up menu.
2. Create the menu item that is to invoke the pop-up menu by selecting its Pop-up check box. A new menu item appears to the right.
3. Fill in this menu.


To set a menu item's properties

1. Right-click the menu item and click Properties to invoke the Menu Item Properties property sheet.
2. Assign a caption, an ID number, and a string for the menu item. You can either select an ID from the drop-down combo box or create a new one for your menu item. Pre-existing IDs typically have a prompt string associated with them.


Note As an alternative, you can add a caption and prompt string, press ENTER, and let the Menu editor assign the ID.

Implementing a Command Handler
Once you have added a menu item and set its properties, you need to create a command handler for the menu item and add the code that implements the menu item. A command handler is the function called by the MFC command routing mechanism in response to the user's choosing a particular menu item.


Creating a Command Handler

You create a command handler by invoking ClassWizard.
To see an illustration of the MFC ClassWizard dialog box, click this icon.




Note To start ClassWizard, press CTRL+W in the Developer Studio environment.

To add a command handler to a menu resource

1. Start ClassWizard. In the Class name drop-down list box, select the class to which you want to add the handler.
2. In the Object IDs list box, select the menu ID.
3. In the Messages list box, select COMMAND.
4. Click Add Function.
The Add Member Function dialog box appears.
5. Click OK to accept the default name that appears in the Member Function Name box.
ClassWizard makes two changes to your source code:

ClassWizard updates the message map declaration and implementation by adding the entries for the new command. For example, here is the implementation after the command handler for CMenus1View is added. Note that ClassWizard adds its message macros between the //{{ and //}} comments.

BEGIN_MESSAGE_MAP(CMenus1View, CView)
//{{AFX_MSG_MAP(CMenus1View)
ON_COMMAND(ID_COLORS_BLACK, OnColorsBlack)
ON_COMMAND(ID_COLORS_BLUE, OnColorsBlue)
ON_COMMAND(ID_COLORS_GREEN, OnColorsGreen)
ON_COMMAND(ID_COLORS_RED, OnColorsRed)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()


ClassWizard adds a member function with an empty body, called a stub, to the end of the corresponding class .cpp file, and adds an associated prototype to the .h file.

Note In most cases, ClassWizard is used to add message-map entries and member-function stubs to the command target class.

Message macros added between the //{{AFX_MSG_MAP and //}}AFX_MSG_MAP comments are interpreted by ClassWizard. Any code that you add manually to the message maps must be placed outside these comments, usually after the last comment but before the END_MESSAGE_MAP macro.

6. Click Edit Code, and then add the functionality you want to the handler.


Adding Code to the Command Handler

To finish the command handler, add code to the command handler to provide the functionality you want for the menu item. For example, if the menu item changes the background color, you could use this code:

void CMenus1View::OnColorsBlack()
{
CMenus1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDoc->SetColor(BLACK);
Invalidate();
}

Drawing Graphics and Text to the Screen

Lab 7.1: Drawing Graphics and Text to the Screen
In this lab, you will create a simple editor of shapes and text. This editor allows the user to add, delete, and duplicate polygon and text items.

Users can add a polygon or text item through one of three mechanisms: menu selection, toolbar, or context-sensitive menu. They can delete or duplicate a polygon or text item through the context-sensitive menu. They can also place items at the top or the bottom of the Z-order.

The default polygon is a triangle, but users can modify the shape of the polygon. They can add and delete vertices or additional points to the polygon. They can add a vertex by dragging outward from the side of the polygon. They can delete a vertex by right-clicking on the polygon near the target vertex. Users can also modify the polygon's color, transparency, and outline properties by right-clicking the item and clicking the Properties command.

Users can customize the color and font of text items and they can alter the text string itself.

Finally, users can save and load their data to disk. Zooming and panning capabilities are also implemented.

To see a demonstration of the lab solution, click this icon.

Estimated time to complete this lab: 120 minutes

To complete the exercises in this lab, you must have the required software. For detailed information about the labs and setup for the labs, see Labs in this course.

Objectives

After completing this lab, you will be able to:

® Implement an application's OnDraw member function.
® Use the CPen, CFont, and CBrush stock GDI objects.
® Manipulate mapping modes.
® Set viewport and window origins.

Prerequisites

There are no prerequisites for this lab.

Exercises

The following exercises provide practice with the concepts and techniques covered in this chapter:
® Exercise 1: Coding the OnDraw Function
In this exercise you will add code to the view’s OnDraw function. Once this code is added, you will be able to add generic items to the document.
® Exercise 2: Implementing the Polygon Draw Function
In this exercise you will replace the code within the document's member function, NewPolygon, with code to create an instance of the CPolygon class. Also, you will implement the CPolygon draw function.
® Exercise 3: Implementing the Text Draw Function
In this exercise you will replace the code within the document's member function, NewText, with code to create an instance of the CText class. Also, you will implement the CText draw function.
® Exercise 4: Adding Zoom Capabilities
In this exercise you will implement a zoom capability on the document by creating code to use a scaling variable. The scale, a member variable of the view, is the ratio of item size to pixel size. This scale is used to generate the proper viewport and window extents for the DC.

Exercise 1: Coding the OnDraw Function

The code that forms the basis for this exercise is in \Labs\Ch07\Lab01\Baseline.
In this exercise you will add code to the view's OnDraw function. Once this code is added you will be able to add generic items to the document; exercises later in this chapter will add specific drawing support for the polygon and text types.

Add generic drawing capability to the OnDraw function
1. Copy the baseline project.
2. Edit the CPolyEditView::OnDraw function.
3. Within the empty brackets, add the following code to call the document's Draw function. When this is supplied, the user-defined function will draw all document-related data.

pDoc->draw(pDC);
4. Add code to draw the document's logical origin and the view window's inner rectangle. Whether or not these are drawn is based on the view's two member variables, m_bShowOrigin and m_bShowInnerBox.
if (m_bShowOrigin)
pDoc->drawOrigin(pDC);
if (m_bShowInnerBox)
drawInnerBox(pDC);
5. Build and run the application.
6. To test this version of the application, on the Edit menu, click Add Polygon.

This adds a movable rectangle at the center of the client window. Once one or more items are created, you can use the context menu to duplicate, delete, and move items within the document's Z-order. Use the View menu to turn off drawing the document's origin and the view’s inner window.

You can find the code for this completed exercise in \Labs\Ch07\Lab01\Ex01.

Exercise 2: Implementing the Polygon Draw Function

Continue with the files you created in Exercise 1, or if you do not have a starting point for this exercise, the code that forms the basis for this exercise is in \Labs\Ch07\Lab01\Ex01.

In this exercise you will replace the code within the document's member function, NewPolygon, with code to create an instance of the CPolygon class. Also, you will implement the CPolygon draw function.

Add code to create new polygons

1. Remove the existing code within the inner brackets in the CPolyEditDoc::NewPolygon function.
2. Create a new polygon and add three points to the object:
// Create a new default polygon.
CPolygon* pPolygon= new CPolygon;

// Add some points

pPolygon->Add(CPoint(-100, -100));
pPolygon->Add(CPoint(0, 100));
pPolygon->Add(CPoint(100, -100));
3. Specify the polygon's position by adjusting all points of the polygon by adding an offset to them. Then add the polygon to the document’s existing collection of items. The variable, pos, was passed in as an argument to the NewPolygon function and specifies the logical position of the polygon in the drawing.
// Offset it by the specified amount.
pPolygon->Offset(pos);

// Add the polygon to the document.

POSITION position= m_items.AddTail(pPolygon);
Extend the CPolygon Draw functionality

1. Edit the CPolygon draw function, and then construct a CPen object using the object's line style, line thickness, and line color.
CPen pen(m_styleLine,m_thickLine,m_colorLine);
2. Create the polygon's brush by initializing a LOGBRUSH structure and then calling the CBrush CreateBrushIndirect function. Use the polygon's member variables to initialize the structure.

CBrush brush;
LOGBRUSH BrushStruct;
BrushStruct.lbColor= m_colorBrush;
BrushStruct.lbStyle= m_styleBrush;
BrushStruct.lbHatch= m_hatchBrush;
brush.CreateBrushIndirect(&BrushStruct);
3. For a hatched brush style, have the brush's hatch origin follow the polygon as it is being moved. Do this by fixing the brush origin to the polygon's first point. Also, set the DC's background mode to transparent so that those objects behind the polygon are visible when a hatch brush is selected.
// Set the brush origin to the polygon’s first point.
pDC->SetBrushOrg(m_pts[0].x % 8, m_pts[0].y % 8);

// Should only be evident for hatched brushes

pDC->SetBkMode(TRANSPARENT);

4. Select the pen and brush into the DC's context while saving the old values.

CPen* pOldPen= pDC->SelectObject(&pen);
CBrush* pOldBrush= pDC->SelectObject(&brush);
5. Call the DC's Polygon method on the polygon's point data to draw the polygon.
pDC->Polygon(GetData(), GetSize());
6. Reselect the old pen and brush into the DC’s context.
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
7. The complete code for the Draw function is shown in the following example code:
void CPolygon::draw(CDC * pDC)
{
CPen pen(m_styleLine,m_thickLine,m_colorLine);
CBrush brush;
LOGBRUSH BrushStruct;
BrushStruct.lbColor= m_colorBrush;
BrushStruct.lbStyle= m_styleBrush;
BrushStruct.lbHatch= m_hatchBrush;
brush.CreateBrushIndirect(&BrushStruct);

// so that hatch moves with the polygon

pDC->SetBrushOrg(m_pts[0].x % 8, m_pts[0].y % 8);

// Should only be evident for hatched brushes

pDC->SetBkMode(TRANSPARENT);

CPen* pOldPen= pDC->SelectObject(&pen);

CBrush* pOldBrush= pDC->SelectObject(&brush);

pDC->Polygon(GetData(), GetSize());

pDC->SelectObject(pOldPen);

pDC->SelectObject(pOldBrush);

}

8. Build and run the application.

Test it by creating several polygons. Change the shape of a polygon by dragging a vertex. Add a vertex by dragging the mouse near one of its borders. Try the different pen and brush styles by right-clicking a polygon and clicking the Properties command.

You can find the code for this completed exercise in \Labs\Ch07\Lab01\Ex02.

Exercise 3: Implementing the Text Draw Function

Continue with the files you created in Exercise 2 or, if you do not have a starting point for this exercise, the code that forms the basis for this exercise is in \Labs\Ch07\Lab01\Ex02.

In this exercise you will replace the code within the document's member function, NewText, with code to create an instance of the CText class. Also, you will implement the CText draw function.

Create a new text object

1. Remove the existing code within the inner brackets of the CPolyEditDoc::NewText function.
2. Create a new CText object and set its position by calling the CText member function, Offset.
// Create a new text item.
CText* pText= new CText;

// Adjust position

pText->Offset(pos);
3. Add the object to the existing collection of items.
// add to collection
m_items.AddTail(pText);

Draw the new text object
1. Edit the CText draw function. Set the DC's background mode to transparent so those items behind the text are visible. Also, set the text color to the CText object's color.
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(m_color);
2. Construct a default CFont object. Use the CText member variable to initialize the CFont object. Select the font into the DC's context. Write the text out at the object's position.
CFont font;
font.CreateFontIndirect(&m_lf);
CFont* pOldFont= pDC->SelectObject(&font);
pDC->TextOut(m_pos.x, m_pos.y, m_string);

3. Save the text's extent for later processing and reselect the old font.

// Save this value for HitTest operation.
m_sz= pDC->GetTextExtent(m_string);

pDC->SelectObject(pOldFont);

4. Build and run the application. Test it by creating several text objects. Modify the text message by right-clicking it and clicking the Properties command. Also try modifying the font, color, and point size.

You can find the code for this completed exercise in \Labs\Ch07\Lab01\Ex03.

Exercise 4: Adding Zoom Capabilities

Continue with the files you created in Exercise 3 or, if you do not have a starting point for this exercise, the code that forms the basis for this exercise is in \Labs\Ch07\Lab01\Ex03.

In this exercise you will implement a zoom capability on the document by creating code to use a scaling variable. The scale, a member variable of the view, is the ratio of item size to pixel size. This scale is used to generate the proper viewport and window extents for the DC.

Enable zooming

1. Edit the CPolyEditView constructor. Replace the following code:
m_minScale = m_maxScale = 1.0;
with the code below:

m_minScale = MIN_SCALE;
m_maxScale = MAX_SCALE;

This change will allow for a range of scales, which in turn will enable the Zoom In and Zoom Out menus and toolbar buttons. Examine the OnUpdate handlers for these menus to see how this is done.
2. Edit the CPolyEditView SetView function, a user-defined function for setting the DC's origins and extents. Notice that the passed parameters specify the center of the view window (in logical units) as well as the scale. Delete the code within the innermost brackets of the function.
3. Add the following code to obtain the size of the viewing window. This size will be used to determine the center of the view (in device units).
// Get view coordinates
CRect rect;
GetClientRect(&rect);
4. Set the mapping mode.
// Set the mapping mode
pDC->SetMapMode(MM_ISOTROPIC);
5. Calculate the window and viewport extents based upon the scale. Since the scale can be a less than 1 as well as beyond the maximum allowed extent value, you need to check the scale and then adjust the extent values as required.
// Determine the extents based upon scale.
int WinExt,ViewExt;
if (scale > THRES_SCALE)
{
WinExt= (int) THRES_SCALE;
ViewExt= (int) (scale/WinExt); // parentheses required!
}
else if (scale >= 1.0)
{
WinExt= (int) scale;
ViewExt= (int) 1.0;
}
else if (scale >= 1.0/THRES_SCALE)
{
WinExt= (int) 1.0;
ViewExt= (int) (1/scale);
}
else
{
ViewExt= (int) THRES_SCALE;
WinExt= (int) (scale/ViewExt);
}

6. Set the origin and extent for both the viewport and the window.
// Set the origins
pDC->SetViewportOrg(rect.Width()/2, rect.Height()/2);
pDC->SetWindowOrg(x,y);

// SetWindowExt must come before SetViewportExt.

pDC->SetWindowExt(WinExt,WinExt);
pDC->SetViewportExt(ViewExt, -ViewExt);
// 2nd parameter is negative to get positive y axis going up.
7. Build and run the application.

Test the zoom capability by clicking first the Zoom Out toolbar button several times, and then the Zoom In toolbar button several times. Notice how the origin lines get thicker as you zoom in. This is because the line thickness is specified as a 1. A thickness of zero would always keep its width to one pixel wide.

You can find the code for this completed exercise in \Labs\Ch07\Lab01\Ex04.