55
Visual C++ Version 6 PracticeAssignments CS193W Dr. Patrick Young, Stanford University This version of Practice Assignments is for students using Visual C++ Version 6 only. I will be revising each assignment for Visual Studio .NET and handing the revised assignments out throughout the quarter. The enclosed assignments are designed to give you practical experience with the concepts covered in CS193W lecture. Each assignment should take one hour or less and ideally should be completed immediately after the corresponding lecture. Practice assignments will not be collected, however, you will be required to keep a log of which practice assignments you have completed (see last page of handout). This log will be turned in and the end of the quarter. Students taking CS193W for a grade should complete a minimum of twelve practice assignments, while students taking the class CR/NC should complete at least ten assignments. Practice assignment sections marked “For Further Study” or “Learning More” do not need to be completed to receive credit on any given assignment. Many of the assignments build on each other. If you skip an assignment, feel free to modify subsequent assignments as necessary to take in to account steps you have skipped.

VC++

Embed Size (px)

DESCRIPTION

VC++

Citation preview

Page 1: VC++

Visual C++ Version 6 Practice Assignments CS193W

Dr. Patrick Young, Stanford University

This version of Practice Assignments is for students using Visual C++ Version 6 only. I will be revising each assignment for Visual Studio .NET and handing the revised assignments out throughout the quarter.

The enclosed assignments are designed to give you practical experience with the concepts covered in CS193W lecture. Each assignment should take one hour or less and ideally should be completed immediately after the corresponding lecture.

Practice assignments will not be collected, however, you will be required to keep a log of which practice assignments you have completed (see last page of handout). This log will be turned in and the end of the quarter. Students taking CS193W for a grade should complete a minimum of twelve practice assignments, while students taking the class CR/NC should complete at least ten assignments. Practice assignment sections marked “For Further Study” or “Learning More” do not need to be completed to receive credit on any given assignment.

Many of the assignments build on each other. If you skip an assignment, feel free to modify subsequent assignments as necessary to take in to account steps you have skipped.

Page 2: VC++

2

Page 3: VC++

3

Practice 1: Overview of MFC

In our first practice assignment we will study the process of creating new applications using the Visual C++ AppWizard. We will also get an opportunity to study the default MFC applications created by AppWizard.

Creating New Applications with AppWizard

To create a new application, select the New option on the File menu. You should be presented with the dialog box shown below:

Note that there are four tabs on this dialog, as you can use it to create new files and new workspaces as well as new projects.1 We want to create a new project. Visual C++ can be used to create a variety of MFC-based projects including MFC applications, MFC-based libraries, and MFC-based ActiveX controls. In addition, it can create standard Win32 applications and Win32 libraries, COM libraries, and quite a few other types of projects. The exact project types available will depend on your version of Visual C++ (e.g., the Enterprise Edition has more available project types than the Standard Edition) and is expandable, as you can create your own project types.

1 The Workspace in Visual C++ constitutes the current set of open files. It is distinguished from a project because a workspace can contain more than one project. In addition, a workspace can include files completely unrelated to its constituent projects. Think of the workspace as being a snapshot of Visual C++, including currently active projects, open files, and even window positions.

Page 4: VC++

4

For CS193W we will be primarily concerned with the three MFC types, and for now we’re only going to work on MFC Applications. Select the “MFC AppWizard (exe)” choice, type in a project name (and optionally choose a parent directory to place it in). Then hit the “OK” button. MFC will now take you through a series of steps which will allow you to customize the application it creates.

Step One—Choosing MDI or SDI After clicking on the “OK” button, you should be presented with the dialog box below—if you’re not, it probably means you didn’t select “MFC AppWizard (exe)” as the type of application you were creating. This dialog allows us to decide whether we’re going to create an MDI (Multiple Document Interface) application or an SDI (Single Document Interface) application. You may also choose to create “Dialog-based” application. This is a simple application that consists of a single dialog box—useful for very simple applications, but not for anything complicated. For now, let’s start off by choosing SDI application.

Before you click on “Next” let’s take a moment to study one of Visual C++’s nicer features—its help system. If you’re not sure what one of the options presented actually does, right mouse click on it and select “What’s this?” from the popup menu. In most cases, this will give you a brief description of the meaning of the given option. For example, right clicking on the “What language would you like your resources in?” brings up the following description popup:

Page 5: VC++

5

Once you’ve had a chance to experiment with the right-mouse help system, go ahead and click on “Next” and let’s move on to Step 2.

Step Two—Database Support MFC can include support for interacting with a database. We probably won’t get a chance to explore this option this quarter. Let’s leave the database support set to None.

Step Three—OLE Support This step allows you to provide support for OLE Object Linking and Embedding. You can ask the AppWizard to provide code which allows your application to act as a container. This means the user can insert things like Excel spreadsheets into your document. You can also ask the AppWizard to make your application a Server. This means that your documents can be inserted into and viewed within an OLE container application such as Microsoft Word.2

2 A full-server program is a normal MFC application which also supports embedding its documents into other applications which are OLE containers. An OLE mini-server is a program which cannot run stand alone and only starts up and runs when its corresponding documents are embedded into another program’s documents.

Page 6: VC++

6

For now, go ahead and leave compound document support set to None. We’ll talk about OLE support much later in the quarter.

OLE Automation is a special kind of OLE technique which allows other programs to take control of your program. Let’s leave it unchecked. ActiveX Controls are special DLLs (dynamic load libraries) which act similar to the original built-in Windows controls. They allow programmers to extend the available number of controls by buying (or building) control types not included with Windows. You may leave the ActiveX Controls checkbox in its default checked state if you like, but we won’t be using them for much of the quarter either. Click on “Next” and we’ll move to step 4.

Step Four—Miscellaneous Support In step 4, you get a chance to determine if your new application is going to support a variety of behaviors. For now the most important ones are whether or not we want toolbars, whether or not we want status bars (that’s the bar that runs along the bottom of the application window and is used to display status messages), and whether or not we want print previewing. To find more about the other options, don’t forget your handy right-mouse menu!

In addition to these options, the options provided by the “Advanced…” button are also important. Go ahead and click on it. You should see something like this:

Page 7: VC++

7

Most Windows applications tack a three-character file extension on the end of a file name. This extension is used to identify which application created the file. Word documents, for example use the “doc” extension, while Excel documents use “xls”. This “Advanced” dialog is where you set the three-character extension you’ll be using for your documents.

The other elements of this “Advanced” dialog box include the names which will be used for your documents in a variety of different dialogs and IDs used in the Windows’ Registry and for OLE Automation support.

The “Window Styles” tab allows us to select various Window styles and initial settings for our main frame and, if we have an MDI application, child frame. We’re going to leave these alone for now. Go ahead and create a three-character extension for your documents and close the “Advanced” dialog.3

Leave the other Step 4 settings at their defaults. In general for the class, we’ll go ahead and keep the Toolbars, Status Bars, and Printing Support in (along with the 3D controls). Let’s move on to Step 5.

Step 5—Comments and Libraries The fifth step allows us to tell the AppWizard if we want it to add comments telling you where to make modifications in the AppWizard generated code (I always recommend you leave this on) and what type of linking we want to take place.

If you choose static linking, your executable will include the MFC libraries in it. This will make your application larger, but will guarantee that your application always runs (as the libraries will always be present). If you choose dynamic linking, your executable will be smaller because the MFC binaries will not be included as part of the executable. Instead your application will try to dynamically link with existing copies of the MFC DLLs (dynamic load libraries) when it runs. The downside of this is that it may load a bit slower (as it has to link dynamically) and, more importantly, there is no guarantee that the system you’re application is running on actually has the versions of the MFC DLLs you are expecting. You can take

3 You can actually give extensions longer than three characters now. The three-character limit is a vestige from the infamous old 8.3 DOS file naming scheme. 8.3 limited file names to eight uppercase characters followed by a period followed by a three character extension.

Page 8: VC++

8

care of this during your program’s installation process, but that is a bit more work (and for simple projects, you may not have an installation process at all).

The MFC Standard and Windows Explorer option at the top of the page is new to Visual C++ 6.0. Selecting Windows Explorer will create a splitter window with a tree view on the left and the main view on the right. I’m not sure why this option was added to this particular stage (probably just because there was extra space available in the dialog box …). In addition, it seems to be a bit specialized to put in the AppWizard.

Go ahead and leave all the options on this page to their default values and let’s move on to the final step.

Step 6—Class and File Names Our last step just gives us a chance to rename the automatically generated classes, if desired, and to change the normal file names for them. For now, let’s leave them at their defaults.

Experimenting with the Applications

We’re half done with the assignment for this lecture! Go ahead and finish the creation of our new SDI application. Compile it and run it. Play with the various menus and the toolbar.

Generate a new MDI application and compile it and run it.

Get familiar with the classes of both applications and get comfortable using the ClassView of the Workspace window4 to move between the various classes and member variables and functions of the pre-defined applications.

4 As you’ll recall from lecture, the Workspace window is the window on the left-side of Visual C++ which is used to display classes, files, or resources. These three tabs are referred to as ClassView, ResourceView, and FileView.

Page 9: VC++

9

Learning More

One natural question to ask is, what does changing the various AppWizard options do to the actual code? You can easily determine this on your own by creating multiple projects of the same name (in different folders) and then comparing them using WinDiff. If you aren’t already familiar with WinDiff, you should get to know this handy tool as soon as possible. It’s the Windows’ version of the UNIX diff tool which is used to compare to files. In the case of WinDiff, you can compare files or folders. WinDiff is located in the Visual Studio Tools folder in your Start Menu.

Let’s say, for example, you want to know what checking the toolbars in Step 4 really does? Create one project with the toolbars checked. Create a second project with the same name, but in a different folder, which is exactly the same, except that the toolbars aren’t checked. Run WinDiff on both folders. What you should discover is that the CMainFrames are different. The CMainFrame on one has a CToolbar as a member variable and initializes the toolbar in its OnCreate function. The other CMainFrame doesn’t deal with toolbars at all. Voilà, you’ve isolated the difference between checking and unchecking the toolbar checkbox and you now have a better understanding of how toolbars and the main frame work.

Page 10: VC++

10

Page 11: VC++

11

Practice 2: Intro to Graphics

Over the next few weeks we will be developing a simple TicTacToe game. This game will be used to gain experience with the following concepts:

• documents and views, • basic drawing, • inputs from the mouse, • serialization, • mapping modes, • printing.

For this lecture, let’s get some practice with both MFC’s documents and views and with MFC’s drawing capabilities. This initial version of TicTacToe, we will be performing two steps: (1) we will be creating the TicTacToe document and (2) we will be drawing the TicTacToe board. Here’s a screen shot showing what the application should look like at the end of this practice assignment. Note that the board has been partially filled for debugging purposes.

Go ahead and create a new SDI project with the name TicTacToe. Use all the default behaviors, except give your documents the file extension “toe”. Thus when we learn how to store our TicTacToe games next week they’ll have file names such as “GameOne.toe”. Remember you set the file extensions in Step 4 of the AppWizard using the “Advanced” dialog. Your application should have support for printing by default (also in Step 4). Make sure that printing is set, as we’ll need that later.

Page 12: VC++

12

Working with the Document

Okay, let’s go ahead and modify our TicTacToe document. Remember, our document class stores all non-view specific information about the document.5 In this case, CTicTacToeDoc should store the state of the TicTacToe board. I recommend creating a new enum to store the state of any given square in the board (blank, filled with an X, or filled with an O) and then creating a 3 by 3 array based on your new enum to store the actual board state. This 3 by 3 array should be a member of the CTicTacToe document. Don’t forget by convention, the names of all member variables in an MFC application are preceeded by an “m_” (e.g., m_boardState).

Once you’re done adding the board in to your document, the next step is to modify CTicTacToeDoc to properly handle the new state information we’ve just added to it. There are a number of places in the document which may require modification. We must initialize the document when a new document is created, this is handled in the DeleteContents function (see below). We should also modify the Serialize function so that opening and saving documents works properly. Note that we do not have to modify OnNewDocument, OnOpenDocument, or OnSaveDocument. The default behavior of these functions provided by the framework will typically be correct for our purposes.

When a document is opened or created in an SDI application, a new CDocument object is not created, instead the existing document object is reused. Therefore, you cannot carryout document initialization in the document’s constructor. Instead initialize document contents in the document’s DeleteContents function. DeleteContents will be called by both SDI and MDI applications when a document is created or loaded.

Okay, let’s go ahead and write our own version of DeleteContents. The easiest way to do this is via the Class Wizard. To open the Class Wizard, go to the View menu or just hit Ctrl+W. The Class Wizard allows us to easily create commonly used member functions and message handlers for Win32 messages. Go to the “Class name:” pulldown and select

5 View-specific information (e.g., which page of a document a specific view is showing) is naturally stored in the view. Everything else should be stored in the document.

Page 13: VC++

13

CTicTacToeDoc. Then double click below in the “Messages:” on “DeleteContents”. This will create a new DeleteContents member function for our CTicTacToeDoc. To easily move to the new function, double click on the newly added “DeleteContents” in the “MemberFunctions” list at the bottom of the window. Go ahead and write DeleteContents. All we want to do here is initialize our 3 by 3 array.

We’re done with the document for now. In the future, we’ll take care of serializing our TicTacToe board so that we can save and open a TicTacToe game. We’ll also set things up so that the “dirty bit” gets set when the board state is modified. You may want to compile your project at this point and make sure everything is working so far.

Working with the View

Our second task for today is to actually get the board to draw properly. Remember, all drawing in an MFC application is done in the view’s OnDraw function. A stub for this function is already written for you. Go ahead and open TicTacToeView.cpp and start modify OnDraw.6

Using GDI Objects If we use the default state of our DC (or device context), the board will be drawn using a very thin pen. That’s not going to look very impressive. Instead we want to use a nice thick pen. In order to do this, we need to create a new CPen and load it in to our DC. Go ahead and create a CPen. I recommend storing it on the stack as a local variable and not on the heap using the new operator, since we’ll only be using it briefly. Don’t remember the proper parameters for creating a CPen? Not a problem, type CPen and then select your newly typed text CPen by double clicking on it. Then hit the F1 key. This will get you the on-line documentation on CPen. You can create a color to use for the CPen using the RGB macro (you can read about the parameters to RGB using the on-line documentation as well).

Once we’ve created our CPen we need to select it in to our DC. CPen, like CBrush, CFont, and a few other classes, is a GDI object. Remember the following rules when using GDI objects. Always restore the DC to its previous state when you’re done with it and always remember to delete your GDI objects when you’re done with them. Okay, we can load our CPen in to the DC using the DC’s SelectObject function. However, we need to save the object returned by SelectObject for later use. Here’s the basic structure anytime we use a GDI object:

CPen newPen(PS_SOLID,3,RGB(255,255,255)); CPen *pOldPen = pDC->SelectObject(&newPen); // load in new pen, but keep pointer to old pen ... // use new pen here pDC->SelectObject(pOldPen); // restore old pen

As you can see when we select the new pen in to the DC, we get a pointer to the old pen. When we’re done, we use the pointer to the old pen to restore the DC to its original state.

6 You can easily move to CTicTacToeView::OnDraw using the Class Wizard (Ctrl+W remember) or using the ClassView in Visual C++’s left-hand Workspace window.

Page 14: VC++

14

Drawing Okay, now we’re ready to draw. The easiest way to draw the board would be to create a fixed size board, regardless of the size of the window. However, our application will look much nicer if we size the board based on the size of the view window. In order to do this, we need to get the current size of the view window. We can do this with GetClientRect. I recommend reading about both GetClientRect and CRect the every-handy F1 key.

Once you’ve figured out where you want to draw, you can draw the actual board grid using CDC’s LineTo and MoveTo. I leave it to you to peruse the CDC class members in order to draw your TicTacToe board. Since we want our CView to properly draw a filled board as well as the empty board we’ve got now, you may want to modify your DeleteContents temporarily to fill the board with some X’s and O’s, just for testing purposes.

That’s it for today. Next lecture we’ll learn how to get mouse click information to actually change the state of the board.

Learning More

SelectObject, GetClientRect, LineTo … how do you find these functions? While we will be learning many of these functions in class, there are far too many to cover in a one quarter class. The only way to learn MFC (or any other large framework, for that matter) is a combination of practice and exploration. Explore the documentation for CDocument, CView, and CDC. Look over the capabilities of these classes. Learn what they can do. You may not remember, the exact function names you see in your explorations, but you’ll probably have at least some memory of what the capabilities are. Then, when you need them, you’ll know they exist, and you just need to look them up and find their names and signatures. The only way you will master the material is through repeated practice.

Page 15: VC++

15

Practice 3: Inputs

Our practice assignment for today is to add mouse inputs to our TicTacToe game. You will get a chance to get inputs from the keyboard in the SuperPad project which will be handed out next lecture.

Responding to a Mouse Button

As discussed in lecture, when the left mouse button is pressed, the underlying window receives a WM_LBUTTONDOWN message. When the button is released, the underlying window receives a WM_LBUTTONUP message. For our practice assignment, we’ll keep things simple and capture the WM_LBUTTONDOWN messages only.

Open your TicTacToe workspace. Open the Class Wizard (using ctrl+W) and select CTicTacToeView as the active class. Scroll down the list of messages until you see WM_LBUTTONDOWN. Double clicking on it, will create a new message handler OnLButtonDown. Double click on the newly created OnLButtonDown in the lower “Member Functions:” panel to edit the new function.

Your OnLButtonDown function should exhibit the following behavior. If the button is pressed inside a legal, empty board square, the square should be filled with either an X or an O. The first square clicked on will be filled with an X. After that, O’s and X’s will alternate. If the button is pressed, but either there isn’t a square under it, or if the square is filled, ignore it.

OnLButtonDown’s point parameter provides the position of the mouse down event relative to the current window. This is probably what you want.

Page 16: VC++

16

For any given function, always check the documentation to determine if a point is relative to the current window or the entire screen. If you need to convert between the two, check CWnd’s class members for conversion functions.

Remember, we need to separate what happens in the document from what happens in the view. Properly following the document / view model, we should have the following sequence of events when the left mouse button is pressed in a square:

• Check to see if there is a legal, empty board square under the mouse.

• If there is, change the board representation in the document to reflect the newly filled square.

• Call CDocument’s UpdateAllViews with pSender = NULL. This will force a redraw of the view, which will in turn display the newly filled square with either an ‘X’ or and ‘O’ as appropriate.

Generally speaking, your best response to user interaction is to modify the document, followed by an UpdateAllViews call. This allows us to keep all the drawing code in a single place—the CView's OnDraw function. This in turn keeps your code simple and improves maintainability.7

In order to modify the document, you’ll need to access it. The recommended way of getting the document from within the view is:

CTicTacToeDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);

Notice this is the same code used at the beginning of the OnDraw method.

Further Study

The real work of messaging is done automatically for you when you use the Class Wizard. Open up your TicTacToeView.h file and scroll down near the bottom. You should see:

// Generated message map functions protected: //{{AFX_MSG(CTicTacToeView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP()

The OnLButtonDown declaration was created automatically for you by the Class Wizard. In your TicTacToeView.cpp file, there is a matching entry in the message map at the top of the file:

7 In some cases, you may want the view to respond directly, without waiting for the document to force the view to redraw. In this case, you will still need to call UpdateAllViews, in case there are any other views displaying the same document. You can also provide additional information in the UpdateAllViews identifying which parts of the document have changed. This will further improve efficiency. We will study these techniques later in the quarter.

Page 17: VC++

17

BEGIN_MESSAGE_MAP(CTicTacToeView, CView) //{{AFX_MSG_MAP(CTicTacToeView) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP()

The ON_WM_LBUTTONDOWN creates an entry in the message map, linking the WM_LBUTTONDOWN message with the new OnLButtonDown function.

Page 18: VC++

18

Page 19: VC++

19

Practice 4: Controls

This time we’ll take a break from our TicTacToe game and develop an application which allows us to keep track of a list of tasks. Here’s a screen shot showing the actual application up and running:

The user enters a new task description in the text edit box and selects a priority from the pulldown menu. Clicking on the “Add” button adds the new task to the list of tasks in the list control below.

This application will give us practice working with four different types of Windows controls: the CEdit, CComboBox, CButton, and CListCtrl. In addition it will give us another chance to work with our message handling skills.

Create a new MFC application named ToDo. The ToDo application should be an SDI (single document interface application). Follow the default settings, except remove the docking toolbar in Step 4. Give the ToDo documents the file extension “tdo” (Step 4 “Advanced” dialog box).

Create Controls

Our CToDoView includes four windows controls. We need to create member variables in our view for each of our controls. Create member variables for our CEdit, CComboBox, CButton, and CListCtrl.

Each of our controls will need a unique identification number. When our CView receives a message from one of the controls, it will use this identification number to determine where the message came from. You may #define or use const int to define four ID numbers, one for each control. I use the following convention for naming these constant ID numbers: IDC_, followed by a unique identifying name, followed by _, followed by the type of control. For example, my CComboBox has the following identifer:

Page 20: VC++

20

#define IDC_ITEMPRIORITY_COMBO 1

Start your IDC numbers with 1 (not 0). These identifiers should go somewhere near the top of your view’s *.cpp file.

As is typical for MFC classes, we not only create the various controls using the C++ constructor, we must also explicitly call a Create function on each of them. Since we want each of our controls created when the view window is created, the best place to create them is on a message handler for the view’s WM_CREATE message. Go ahead and use the Class Wizard to create a new message handler for WM_CREATE. The “Object ID:” should be CToDoView (or whatever your view class is called). If you scroll down the list of “Messages:” you should see WM_CREATE, go ahead and create a handler for it.

In the new OnCreate message handler, call Create for each of your four controls. Typically the Create function will take a number of parameters including: a set of window styles, the rectangle where the control will be placed, the parent window, and the ID number that the parent window will use to identify the control. Some Create functions may take additional parameters (the CButton control, for example, takes the text which will be displayed in the button—in our case “Add”). Always check the on-line documentation to determine what the Create function is expecting. Let’s take a look at each of the basic parameters in more detail:

Styles—Each control’s Create function will take a style parameter. The control’s style is defined by combining a number of different styles. Possible styles include the basic windows styles and control specific styles. Almost every window you create will use the WS_VISIBLE and WS_CHILD window styles. Many of them will use the WS_BORDER window style.

Any time you use a control, you should check the control specific styles. In our case, we will use the CBS_DROPDOWN style for our combo box control and the LVS_REPORT style for our list control.

Styles are combined using the C++ binary or operator. For example, our list control will be visible, a child window, and will use the list control report style, so our style parameter will be:

WS_VISIBLE | WS_CHILD | LVS_REPORT

Note that the prefix WS stands for Window Style, CBS stands for ComboBox Style, and LVS stands for List View Style.

Rectangle—The Create function will also take the rectangle where the control is to be placed.

At the time the view’s WM_CREATE message is received, we don’t quite know where the controls will be placed (as we need to know the size of the view window). We will be accurately placing the controls when the view receives a WM_SIZE message. For now, go ahead and create a dummy CRect rectangle and pass it in to each of the controls’ Create functions.

CRect tempRect(0,0,0,0); m_itemPriority.Create(…,createRect,…,…); // pass in the tempRect along with some other stuff

Parent Window—Since the view is acting as the parent window for each of the controls, pass in the view’s this pointer as the parent window.

Page 21: VC++

21

ID—The ID numbers are the constants we defined earlier. Go ahead and pass them in directly.

Again, any time you are working with a control, check the Create function to see what parameters it needs. Also check the various control styles to see which styles are appropriate for your needs.

Placing Data in Controls

The CComboBox and CListCtrl will both need to be setup to display initial data.

The CComboBox is used to select the priority of a given task. You can insert items into the CComboBox using the AddString function.

CListCtrls can be used to display information in a variety of forms. The Windows Shell Explorer, for example, uses a CListCtrl to display the contents of each folder on your computer. In the shell, you may display folder contents using large icons, small icons, lists, or details. Each of these displays corresponds to a different CListCtrl style.

In our case, we will be using the LVS_REPORT style. This style is the same one that is used by the Windows Shell Explorer when it is set to detailed. Items inserted in to a CListCtrl may include subitems. A CListCtrl in LVS_REPORT style can display both regular items and their subitems.

Our CListCtrl should have two columns, “Task” and “Priority”. You can insert columns in to a CListCtrl using the InsertColumn function. The Task Column will display the main text string associated with each item. Setup the Priority Column to display the 1st subitem of each main item using the long version of InsertColumn:

m_listOfItems.InsertColumn(1,"Priority", LVCFMT_LEFT,-1,1);

Laying Out the Controls

Now that we’ve created the controls, we need to lay them out. We should lay them out in response to any WM_SIZE message received by the view. That means we will lay them out initially when the view is originally sized and that we will be able to resize them correctly anytime the view is resized. Go ahead and create an event handler in your view for WM_SIZE.

In your OnSize event handler, pace your controls such that the CEdit, CComboBox, and the CButton are all at the top of your window. The CListCtrl should fill up the rest of the window. Have the CComboBox and CButton maintain a fixed width as the window is resized. See the screenshot on the first page for placement guidance.

Windows can be positioned and sized using CWnd’s SetWindowPos function. The controls inherit from CWnd, so they have access to this function as well. Be sure to set SetWindowPos’ nFlags parameter correctly. Check the online documentation for details.

Warning: The combo box control must be large enough to display itself not only in it’s normal state, but with the drop down menu deployed as well. If the size of the combo box is set so that it’s only large enough to show the chosen item, the menu will not be shown when you click on the arrow to the right of the combo box.

Page 22: VC++

22

In addition to setting the size of the controls themselves, you should also set the column sizes in the CListCtrl when the view is resized. You can control the column sizes using SetColumnWidth. Columns are numbered starting with 0.

Adding Items

When the user presses the “Add” button, we need to respond by adding a new task to our list. As you’ll recall from lecture, when the user interacts with a control, it sends a WM_COMMAND or WM_NOTIFY message to its parent window (the original Windows controls use WM_COMMAND while the newer Windows 95 Common Controls use WM_NOTIFY). We need to create a message handler and respond to this message.

We used the Class Wizard to add message handlers in response to mouse and keyboard inputs. Similarly, as we will see, we can also use the Class Wizard to create message handlers respond to control messages if the parent window is a dialog box. Unfortunately, there is currently no way to use the Class Wizard to create message handlers in response to control messages that or in non-dialog windows. Instead we’ll have to add our message handlers manually.

Most Windows controls generate one or more responses to various user actions. You can usually read about them on the class overview page of a given control class. For example, go to the CButton overview page in the online documentation. If you scroll to the bottom of the page, you’ll see that CButtons can generate messages in response to either single or double clicks. In this case, the buttons have specific macros defined for capturing single and double clicks. In order to write a message handler for single clicks you will need to carry out the following steps:

1. Declare a message handler in the *.h file.

2. Fill in the body of the message handler in the *.cpp file.

3. Connect the message to the message handler by modifying the class’ message map.

Let’s take a look at each of these steps in more detail:

Declaring Message Handler—Message handlers have a variety of function signatures. If we check the online documentation for CButton (on CButton’s overview page), we see that the function prototype for our message handler should be:

afx_msg void memberFxn( );

Where memberFxn is, of course, the actual name of our function. This prototype or signature is nice and simple. However, do make sure to check, as some message handlers will have parameters.

Go ahead and add a new member function to your ToDo view class using the function prototype given. I recommend giving it a name like: OnAddItemBtnClicked. Generally, MFC will prefix message handler names with “On” and I suggest you follow the same

Page 23: VC++

23

convention. Make sure you include the afx_msg.8 Message handlers are typically declared as protected member functions.

Defining the Message Handler—Of course you’ll need a corresponding function definition in your *.cpp file in order for anything to actually happen! In this case, when the “Add” button is clicked you need to add a new item to the CListCtrl at the bottom of the view window. Let’s go ahead and hold off on the details of this function until we’re done talking about message handlers. Define the function in your *.cpp file, but leave the body empty for now.

Modifying the Message Map—The view’s message map is where MFC determines which message handlers get called in response to each message. Scroll to the top of you View’s *.cpp file and you should see something like this:

BEGIN_MESSAGE_MAP(CToDoView, CView) //{{AFX_MSG_MAP(CToDoView) ON_WM_CREATE() ON_WM_SIZE() //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP()

This is the actual map. You’ll need to add an entry to this map. Before you do, though, notice that the lines between //{{AFX_MSG_MAP(CToDoView) and //}}AFX_MSG_MAP are greyed out. These lines were generated by the ClassWizard when we added message handlers for WM_CREATE and WM_SIZE. No lines should be manually entered between the two ClassWizard generated comments. Instead, we’ll add our new entry below those lines, but before the END_MESSAGE_MAP line.

Go ahead and take another look at the CButton documentation. as we can see, the message map entry takes the form ON_Notification( id, memberFxn ) where in our case, the notification is ON_BN_CLICKED. The id is whatever ID number you used when creating the actual CButton in the OnCreate handler and memberFxn is whatever name you gave your message handler in the last step. If you’ve been following the naming conventions recommended here, your message entry should look something like this:

ON_BN_CLICKED(IDC_ADDITEM_BTN,OnAddBtnClicked)

Go ahead and place your message entry just above the END_MESSAGE_MAP line. Notice that there is no semi-colon (;) used to terminate a message map entry.

Okay, we’re done with dealing with the message map. Now we need to deal with the actual body of the message handler. What are we actually going to do when the button is clicked? We need to get the information out of our CEdit and CComboBox and put them in the CListCtrl.

8 If you check the MFC source code, afx_msg doesn’t actually do anything! Nevertheless, you should prefix all message handlers declarations with afx_msg. This will let the reader know that they are message functions.

Page 24: VC++

24

Using the LVS_REPORT list control style, a CListCtrl can display information about items in columns. Each item will have a main item associated with it and one or more subitems. For our purposes, the main items will be the agenda descriptions entered from our CEdit, while the subitem will be the priority chosen in the CComboBox. Let’s take a look at how we’re going to get information in to the list control:

Setting the Main Item—You can insert items in to the list control using CListCtrl’s InsertItem member function. If you look over the documentation for this function, you’ll discover there are a number of different versions of it. I recommend using the LVITEM version. This version requires you to setup an LVITEM structure before calling InsertItem. It’s a bit annoying, but it will give you the greatest amount of control. This use of structs to provide information is very common in MFC and is an example of Win32’s non-C++ orientation coming through to MFC. Click on one of the links to the LVITEM help page and look over the LVITEM structure.

In order to add our main item in, we’ll need to get the text out of our CEdit first. If you look over the CEdit class members, you’ll discover that there doesn’t appear to be anything useful for getting text out! That’s because the function you need is actually part of CWnd’s class members (CEdit’s base class). You’ll need to use the GetWindowText function.

I leave getting the main item in to your control to you. You will need to carefully read the documentation on all functions involved.

Setting the Priority Item—Adding in the priority item works similarly. In this case, we’ll want to add priority as a subitem. You can determine which priority is selected using CComboBox’s GetCurrSel and then converting the integer returned to a string. However, there is one tricky part involved in setting the priority item. Warning: you may not add subitems using CListCtrl’s InsertItem function. You must use the SetItem member function in order to add subitems.

Okay we’re almost done with our application. The last thing you should do is clear the CEdit and return the CComboBox to normal priority so they’re ready for the next agenda item entered.

For Further Study

Notice that we didn’t modify our view’s OnDraw function at all for this assignment! In this case, all the controls did our work for us. While you probably won’t have all the details of each control memorized, you should get familiar with the controls that are available. This will save you lots of time in the future. Instead of trying to implement a particular behavior, you’ll be able to take advantage of a control which supplies the behaviors you need for free.

There’s a lot more to learn about working with controls. One thing you’ll need to know is how to find out which messages a control can generate. In many cases, this information can be found on the controls overview page, just as it was for our CButton. If you look at the CComboBox overview page, for example, you’ll discover that CComboBox can generate messages in response to all kinds of events including when the current selection has changed and when the combo box loses the keyboard focus. In some cases, unfortunately, the class overview page does not list any events. The CListCtrl overview page, for example, doesn’t tell us what messages, if any, the CListCtrl generates. In this case you’ll have to follow links to “Using CListCtrl” and then follow the link to “Processing Notification Messages in List Controls”. Unfortunately in this case, MFC doesn’t do a very good job of hiding the raw details of the OS from you.

Page 25: VC++

25

Practice 5: Menus

For this assignment we’ll be modifying TicTacToe to (1) add a “Clear Board” menu item, (2) add a keyboard accelerator, and (3) enable and disable “Clear Board” based on the current state of the board (an empty board can’t be cleared or saved).

Adding a Menu Item Let’s start by adding the “Clear Board” menu item. Menu items are resources, so you need to change the workspace window (on the left-hand side of the Visual Studio window) to display resources instead of classes. Click on the resource tab (displaying a very small bitmap of a sun and a cactus). Open the “Menu” item and double click on the IDR_MAINFRAME item. This item represents the menus associated with the main frame. As we’ll discover when working with an MDI application, MDI applications have one menu used when no child windows are open, and a separate menu for each document type.

Double clicking on IDR_MAINFRAME should open up a menu editor. We want to add the “Clear Board” menu item to the “Edit” menu, so click on the “Edit” menu. The blank menu item surrounded by a dotted outline at the bottom is used to add new menu items. Double click on the blank outlined menu item. This should bring up a properties dialog box for the item. Fill in the caption and prompt. The caption is what actually shows up in the menu. You can give it a mnemonic9 by preceding one of the letters with an “&”. To have an accelerator key displayed, follow the name of the of the menu item with a “\t” tab followed by the accelerator. For example, for our current item, we probably want the caption to look something like this:

C&lear Board\tCtrl+K

As we’ll discover, while the “\tCtrl+K” makes the accelerator show up correctly in the menu, it doesn’t actually link the Ctrl+K key to any particular action. That’s a separate step which we’ll have to take care of below.

Fill in a prompt string. If the window displaying the menu has a status bar10 the prompt string associated with each menu item will be displayed in the status bar as the item is selected. If you choose not to provide a prompt string, MFC will give you a warning message in the debug output window every time the menu is selected when you are running the program in debug mode. To see an example of prompt strings in action, select one of the Visual C++ menus and move the mouse up and down over the various menu items while looking at the lower left hand corner of the Visual C++ application. You should see a descriptive string associated with each menu item. These are the prompt strings.

9 In Windows parlance a mnemonic is a key that can be used in conjunction with the ALT key to select menus and menu items. The mnemonic is displayed as an underscore under one of the letters in the menu item’s name. For example the Edit menu can be deployed by holding down ALT and E. 10 The status bar is the bar running along the bottom of many Windows applications. In Microsoft Word, for example, the status bar displays the current page number, current column number, and other information.

Page 26: VC++

26

By convention, the ID is named “ID”, followed by an underscore (“_”) followed by the name of the menu, followed by an underscore, followed by the name of the menu item. In this case, for example, your ID should be something like this “ID_EDIT_CLEARBOARD”.11 If you leave the ID empty, Visual C++ will automatically fill in the ID using this convention once you close the window.

Since the “Clear Board” menu item is distinct from the standard “Cut”, “Copy”, and “Paste” menu items, we should probably separate it from the others using a separator. To create a seperator, click and drag the empty outlined menu item between the “Paste” item and your new “Clear Board” item. Double click on it, and instead of providing a caption, click on the seperator checkbox.

Adding Command Message Handler

Okay now we need to actually write the event handler to handle clearing the table. We can do this in the Class Wizard. On the “Message Maps” tab if you look in the “Object IDs” window you should now see an “ID_EDIT_CLEARBOARD”. This is from our new menu item!

We can respond to menu selections in any of a number of places. Remember, when a menu item is selected, the following classes are checked in order for message handlers:

• View

• Document

• Document Template

• MDI Child Frames (for MDI applications)

• Frame

• Application

In our case, the clear board message should probably be handled by the document. After all, that is what’s getting modified here. Select the CTicTacToeDoc class as your active class in the class wizard and add a new “COMMAND” (we’ll discuss the meaning of the “UPDATE_COMMAND_UI” option in a minute).

Go ahead and edit the code for your new message handler. Don’t forget that in addition to clearing the actual board data structure, you’ll also need to call UpdateAllViews to force views on the document to redraw. When you’re done, compile and run your application and make sure that selecting “Clear Board” does indeed clear the board.

11 The ID is actually an integer. However, the Visual C++ software development environment keeps track of its value for you and allows you to refer to it using ID_EDIT_CLEARBOARD. The actual relationship between IDs and integers can be found in the applications string table—which is another application resource.

Page 27: VC++

27

Adding an Accelerator

Let’s add in the keyboard accelerator for Ctrl+K now. Go to the “Resources” tab of the Workspace window. Open up the IDR_MAINFRAME resource under “Accelerators”. You should now see a list of various accelerators. Double click on the blank accelerator at the bottom and create a new accelerator for you ID_EDIT_CLEARBOARD (which should be in the list of IDs displayed in the accelerator property window).

Disabling Menu Items

“Clear Board” shouldn’t always be available. When the board is empty, for example, it can’t be cleared. Our last task will be to write an update handler for “Clear Board” so that this menu item is disabled when the board is empty.

Go back to the Class Wizard. Select ID_EDIT_CLEARBOARD and CTicTacToeDoc again. This time, however, instead of selecting COMMAND, select UPDATE_COMMAND_UI. The OnUpdate function is called whenever the menu item is shown. The menu item is enabled or disabled based on the state of the CCmdUI passed to your OnUpdate function. Go ahead and read up on the CCmdUI class in Visual C++’s on-line documentation and then write your update handler to enable the menu item only when appropriate.

Page 28: VC++

28

Page 29: VC++

29

Practice 6: Dialog Boxes

In this assignment we’ll get practice creating a modal dialog box. You’ll get a chance to work with non-modal dialogs in the SuperPad project. Our main program for this assignment will draw a color filled rectangle. A menu item will bring up our dialog box which will allow the user to select a new color. When the dialog box is closed, our rectangle should be filled with the new color. Here’s a screen shot of the main application:

The Edit menu includes a “Change Color” item which brings up the dialog box below:

If we change the RGB values listed and select “OK” the color of our filled rectangle will change. Of course, if we change the RGB values in the dialog box and choose “Cancel” no changes should occur.

Preliminaries

Let’s start out by writing the main application. Then we’ll create the dialog box and add a menu item to display the dialog. Go ahead and create an SDI application. Your document should contain one data item, a COLORREF used to store the color of the rectangle. The view should simply fill the rectangle (don’t worry about the specific size of the rectangle, anything that clearly shows the fill color will be fine for this assignment). You may have to peruse the device context (CDC class) member function documentation in order to find a suitable graphics call.

Page 30: VC++

30

Designing the Dialog Box

Okay, now that you’ve gotten the basic application up and running, now we need to design a new dialog box. Go to the Workspace window and switch it to the ResourceView. Open the resources folder (if it isn’t already) and right click on the dialog folder. Select the “Insert Dialog” item. You should be presented with a new dialog window as shown below. In addition, the “Controls” bar (shown below at right) may or may not be visible. If it’s not visible, we’re going to need it. To bring up a closed toolbar in a Visual C++, right mouse click either on top of an existing toolbar or in an empty area of Visual C++’s background. You should get a menu listing all of Visual C++’s toolbars, both open and closed. Scroll down to the one you want to open and select it. The “Controls” bar should now be displayed.

For our dialog box, we’ll want to place three static text controls and three edit controls. If you’re not sure what a particular control in the control bar is, move your mouse cursor on top of it, and leave it for a moment. A tool tip describing the control should appear.

Go ahead and drag out three static text controls and three edit controls. Somewhere on your screen (typically at the bottom of Visual C++’s main frame) should be another toolbar called the “Dialog” toolbar. Using this toolbar, you can align or distribute the various controls and make their widths and heights the same. If the toolbar isn’t visible, well, you know what to do (use the right-mouse toolbar menu as described above for the Control toolbar).

Once you’ve got the controls laid out to your satisfaction, our next step is to set the properties of each control. To get to the properties dialog box, for a particular control, right-mouse click on a control (don’t double click, that does something else …) and select the “Properties” menu item from the context menu displayed. If you don’t want to have to bring up the context menu for each control individually, you can pin the properties dialog by pressing on the push pin-shaped icon in the top-left corner of the properties dialog box. This will keep the properties dialog open until you explicitly close it.

We need to give each control an ID. I recommend using something like IDC_REDSTATIC, IDC_GREENSTATIC, IDC_BLUESTATIC for the statics and IDC_REDEDIT, etc. for the edits. You’ll note that IDC is MFC’s standard prefix for the ID of a control. In addition, you should give each of the statics a caption. If you look at the screenshot of my dialog box (see previous page), you’ll notice that each of the static text labels includes a mnemonic (remember, that’s the underscored letter that can be used in conjunction with the ALT key to

Page 31: VC++

31

select an item). Because of my mnemonics, if you were to type an ALT-G the Green text field would become active. You can make the mnemonics show up in a given static text label by using the ampersand (&) just as you did for menu items.

Depending on the order in which you laid out the various controls, the tabbing order may or may not be correct. When a Windows dialog box is active, you may use the tab key to shift the keyboard focus from one element to another. The order in which the elements are selected is called the tabbing order. The tabbing order can be set by selecting the “Tabbing Order” menu item from the “Layout” menu. Note that the “Layout” menu is only visible when the user is editing a dialog box. In order for our mnemonics to work correctly, each static text label should precede its corresponding text edit in the tabbing order. That’s how Windows knows how to handle the user of the ALT- mnemonic key sequence. You can set a new tabbing order by clicking on each control in the order you want used. Note that “OK” and “Cancel” should normally be numbers one and two.

To set the title of the dialog and the ID number for the dialog, bring up the property window for the dialog itself (right mouse click on the dialog window). Rename the dialog “Choose Color” and give it the ID number IDD_CHOOSECOLORDIALOG.

Finally, you can test your dialog box out and make sure that it works to your satisfaction by using the test switch. This is the button that looks like a light switch and it is located on the far-left of the “Dialog” toolbar.

Creating a Dialog Class

Okay, so far so good, we’ve got our dialog box all laid out. What we need to do now is to create an actual MFC class to go along with it. Go ahead and double click on the dialog box and Visual C++ should offer to create a new class for you. Agree and give it a name (I recommend something like CChooseColorDialog—typically all dialog class names end with the “Dialog”).

Once the class has been created, MFC will place you in the Class Wizard. We’ll need to get and set the values of our three text edit controls. In order to do this, we’re going to create member variables for them. Switch the current Class Wizard tab to the “Member Variables” tab. Create a new member variable for each of your three edits by:

1. Selecting the ID of the Edit.

2. Double-clicking or pressing the “Add Variable…” button.

3. Giving the new member variable a name (I recommend something like m_redValue, m_blueValue, etc.). Leave the category at Value. We’re going to take advantage of MFC’s ability create a member variable to access the value displayed in the CEdit rather than manipulating it as a CEdit ourself. Finally set the variable type to “int”. Go ahead and close the “Add Member Variable” window.

4. When you close the window, you should now have an option to set minimum and maximum values for the newly created member variable. Set them to 0 and 255 respectively.

Page 32: VC++

32

Displaying the Dialog Box

We want the dialog box displayed when the user selects the “Choose Color” menu item. Go ahead and add a “Choose Color” menu item with a corresponding Ctrl+K accelerator to the “Edit” menu. If you want it to look nice, place a separator between the “Choose Color” item and the pre-existing “Paste” item.

Create a new message handler for the ID_EDIT_CHANGECOLOR (or whatever the ID number of your new menu item is). I would recommend placing the message handler in the view, but you can do it in the document as well.

In the message handler we’ll need to carry out a number of tasks.

1. Create a CChooseColorDialog object. Generally the dialog object will be a local variable of the message handler (I recommend naming the variable “dlg”).

2. Once we’ve created the CChooseColorDialog object, we can set values for its various member variables. Set the m_redValue, m_blueValue, and m_greenValue member variables. These variables should be set to your document’s current color setting. You can get from a COLORREF back to the constituent RGB components using the macros GetRValue, GetBValue, and GetGValue.

3. Now that the member variables are set correctly it’s time to display the actual dialog, we can do this using the dialog’s DoModal member function. This function returns a value indicating whether the user clicked “OK” or “Cancel”. Therefore, the call to DoModal is usually placed in the context of an if statement, as follows:

if (dlg.DoModal() == IDOK) { // handle dialog actions ... }

where actions in response to the dialog are only carried out if the dlg.DoModal call returns IDOK.

4. The last thing we need to do is fill in the actual dialog actions. If the user selects okay, we need to get the new red, green, and blue values back out of the dialog. This is easy, as we can just retrieve them using the dialog’s m_redValue, m_blueValue, and m_greenValue member variables. Change the document to reflect these new color values and then call the document’s UpdateAllViews.

5. Finally, in order to get your view to compile, you’ll have to tell the compiler about the new dialog class. Add a #include "ChooseColorDialog.h" (or whatever the name of the appropriate file for your application is) to your view’s cpp file.

Page 33: VC++

33

Practice 7: Serialization

In this assignment we serialize our TicTacToe game. As you will recall from lecture, the MFC framework provides most of the work needed for opening and saving documents. The only work you need to do is to write the document’s Serialize function and, depending on what your document looks like, write a DeleteContents function for the document. We’ve already written the TicTacToe game’s DeleteContents function, so all that’s left is to write Serialize.

Writing the Serialize Function

This is really very easy. Just go in and modify your CDocument-based class’ Serialize function. In the Serialize function, if the archive is storing, write out each element in the TicTacToe board and whose turn is next. If the archive is reading instead of storing, read in each element of the TicTacToe board and whose turn was next. You can use CArchive’s overloaded >> and << operators in order to carry this out. If you’ve created an enumerated type to store board states (which is good software engineering), you’ll need to convert from your enumerated type to a type supported by CArchive’s >> and << operators (ints should work). Make sure that you read in elements in the exact same order that you used when writing them out.

For Further Study

Serializing TicTacToe was fairly trivial. There are a number of important serialization issues that you’ll want to keep in mind when writing your own programs. Remember that classes must be declared serializable using the DECLARE_SERIAL and IMPLEMENT_SERIAL macros if they are to support serialization. In addition, your class must includes a default constructor (i.e., a constructor which takes no parameters) if it is to be serialized.

If you’re interested in getting more practice serializing, you might want to try reimplementing TicTacToe using MFC’s array collections classes and then try serializing with these. MFC’s serialization support for collections is quite good and very handy if you choose the right class. You might also consider trying to serialize the ToDo list.

Page 34: VC++

34

Page 35: VC++

35

Practice 8: Collections

In this assignment we’ll build a new practice application—a bar graph drawing program. Here is a screenshot of the running application:

Creating the Application

The bar graph application is an MDI application. Go ahead and use the default AppWizard settings. In the advanced settings dialog box, set the file extension to “bgr”.

Defining the CDataItem Class

Define a new class CDataItem based on CObject. One easy way of creating a new class is to right-mouse click over the root “BarGraph classes” node in the ClassView window. You should see a menu item named “New Class…” Go ahead and select it, and you will be presented with the “New Class” dialog (shown at right). When this dialog starts up the class type is set to MFC Class. You’ll notice that CObject is not on the list of MFC base classes. This is because classes based on CObject-only are not considered MFC classes, they are considered “Generic Classes”. Change the tab at the top to “Generic Class” and tell MFC that your new CDataItem is derived from CObject. When your done, click on the “OK” button. MFC will complain that it can’t find appropriate

Page 36: VC++

36

headers for CObject. Ignore the warning message—the header files for CObject are already included (via StdAfx.h) and your project will compile without any problems.

Each CDataItem should have two member variables—a label and a value. The label is a CString and the value is an int. Since we’re trying to get the application up with a minimum amount of work, you may either make the members publicly accessible or, following good software engineering techniques, make them private with public accessor functions.

We’ll want to save our CDataItems to disk, so we need to make CDataItem serializable. This involves several steps:

1. Add the “DECLARE_SERIAL(CDataItem)” macro to your DataItem.h file. Don’t forget DECLARE_SERIAL macros are not followed by semicolons.

2. Add a “void Serialize(CArchive &ar);” member function declaration to your DataItem.h file.

3. Add an “IMPLEMENT_SERIAL(CDataItem, CObject, 1)” macro to your DataItem.cpp file. Again, as with DECLARE, there is no semicolon at the end of this macro.

4. Write your CDataItem::Serialize function.

Modify the Document Class

Including Template Header File We’ll be modifying the document class to contain a list of CDataItems. Since the list will be based on an MFC template class, we need to include the template header files into the project. Add the following line to your “StdAfx.h” file:

#include <afxtempl.h>

Adding Data Member Variable Add a new member variable to your document class. The variable type should be CTypedPtrList. As discussed in lecture, you may base your CTypedPtrLists on CObList or on CPtrList. In this case, since we want to serialize our list, we must base it on CObList. Since our CDataItems are based on CObject, this will work just fine. Here’s the declaration:

CTypedPtrList<CObList,CDataItem*> m_data;

You may leave this member variable public as well.

Cleaning Up Since your program is responsible for any data maintained in the CTypedPtrList, you need to handle cleanup of the data. Add a DeleteContents function to your CDocument-based class. In this function, remove and delete each member of the list.

I recommend using a while loop, which tests the list’s IsEmpty function. While the list isn’t empty, remove the head element using RemoveHead and delete it.

Page 37: VC++

37

Serialization Our last change to the document is to write the serialization function. This is very simple, as CtypedPtrLists which are based on CObList support the Serialize function. All you need to do is call the CTypedPtrList’s Serialize from inside your document’s Serialize.

Add Item Dialog Box

We’ll need to create a dialog box which allows us to add new data items to our bar graph. It should look something like this:

Go ahead and create a corresponding CDialog-based class (remember you can do this by double-clicking on the dialog while in the dialog editor). In the ClassWizard, add value member variables for both the CEdit’s.

Changes to the View Class

Add Data Item Menu Handler Create a new menu item named “Add Data Item”. Notice that because BarGraph is an MDI application there are two menu resources already defined. The IDR_BARGPHTYPE is the menu displayed when the active child frame window is of type BarGraph. The IDR_MAINFRAME represents the menus displayed when no child frame window is open. Since we want to add data items when a bar graph window is active, add your new menu item to the IDR_BARGPHTYPE menu.

If you have the time and want the practice, go ahead and add a mnemonic and an accelerator for it (don’t forget to modify the accelerator table).

Using ClassWizard, add a message handler for your new menu item. In the handler, create a CAddDataItemDialog object and display the dialog using DoModal. Don’t forget to check the return value of DoModal to see if it’s IDOK. If it’s not, the user has clicked on the Cancel button and you should ignore everything. Otherwise, create a new CDataItem based on whatever data has been entered into the dialog box. Add the CDataItem to the document’s CTypedPtrList of CDataItem*’s. Finally call the document’s UpdateAllViews.

Drawing Our last step is to actually draw the bar graphs. You can decide how fancy you want your bar graph drawing, depending on how much time you’ve already spent on this assignment so far. If you look at my screenshot on the front page, you’ll notice I’ve right-aligned the labels on my bar graphs. You don’t need to do this unless you have the time or inclination. At minimum you should draw the labels (left or right-aligned, your choice) and draw the bars. If you want to assume a maximum label size that’s fine (in fact, you could set a maximum character length using ClassWizard on the dialog-class). It’s also up to you to decide how you actually want to draw the bars. You can draw them proportionally to fit within the width of the window based on the largest value. Alternatively, you can draw them with fixed

Page 38: VC++

38

widths in relationship to values (i.e., value of 20 = 200 pixels, value of 50 = 500 pixels). If you used fixed widths, you may assume a maximum value as well.

In any case, don’t worry about the case where there are too many data items to fit within the view window. We’ll fix this later.

If you want to follow my lead, you’ll get a bit of practice iterating over MFC lists. Here’s my algorithm:

1. Go through the CDataItems calculating the maximum label size and determining the maximum value. You can determine the maximum label size given the current font using CDC’s GetTextExtent function.

2. Go back through the items again and draw them. Draw the label’s right-aligned based on the maximum label size found in Step 1. Draw the bar sizes based on the maximum value determined by Step 1.

Page 39: VC++

39

Practice 9: Mapping Modes As we’ve discussed in lecture, the Microsoft Windows platform provides a number of mapping modes. These modes, when used correctly, can make drawing much easier. In this assignment, we’ll experiment with using different mapping modes for the TicTacToe application.

For this assignment, you’ll give the user the option of dynamically choosing which one of four mapping modes the CTicTacToeView should use. Please note that this is for educational purposes only. In real applications, you would never allow the user to dynamically change the mapping mode. Instead you would choose whichever mapping mode worked best for your application domain and then you would hard code that mode in to your application.

Preliminaries

In order to study the various mapping modes, we’re going to provide a new set of menu items for TicTacToe allowing the user to pick a mapping mode. Add four new menu items to the View menu—Text Mode, LoEnglish Mode, Anisotropic Mode, and Isotropic Mode. Since each of the measurement oriented modes (LoEnglish, HiEnglish, LoMetric, HiMetric, and Twips) work the same, we’re only going to work with LoEnglish.

Add a member variable on to your CView-based class to keep track of the current mapping mode. Since Windows represents mapping modes as ints, I recommend making this variable an int. Don’t forget to initialize it somewhere. Use MM_TEXT as the initial mapping mode.

Write menu handlers for each of the new mapping mode menu items. In each case, you should set your view’s mapping mode member variable and call Invalidate (to force the view to redraw). In addition, if you’d like to get fancy, since only one of mapping mode can be active at any one time, you might consider putting a check mark next to the current mode’s menu item. You can do this by creating UPDATE_COMMAND_UI handlers for each of the mapping mode menu items. In the handler, you can call the CCmdUI’s SetCheck function.

Since the mapping mode is normally set in the OnPrepareDC function, you’ll need to create a OnPrepareDC function for your view. If you go to the ClassWizard with your view as the active class, you’ll find OnPrepareDC listed in the messages list. Double click on it to create an OnPrepareDC member function.

Mapping Modes

Okay, we’ll need to make two different sets of changes. First we’ll need to set the mapping mode in the OnPrepareDC (and for some of the modes, we’ll also need to do a bit of additional pre-drawing work) and second, we’ll need to do the actual drawing. You can setup your actual drawing code anyway you want as long as your application does the work described below. Personally, I created a separate drawing function for each of the mapping modes, and used a big switch statement in the original OnDraw to determine which function to call.

Page 40: VC++

40

As far as the actual drawing goes, if you’re feeling pressed for time, you may ignore drawing the X’s and O’s. Just draw the board for each mapping mode. That should be sufficient to give you a feel for how each mode works.

Let’s look at each of the modes in turn.

MM_TEXT In OnPrepareDC, set the mapping mode to MM_TEXT using CDC’s SetMapMode member function. Depending on how fancy your original TicTacToe was, your application might already act as if it’s in isotropic mode. Since we’re trying to understand the differences Let’s rewrite our actual drawing code so it’s simpler. Rewrite the OnDraw function so, when Text Mode is selected, your application draws a 300 pixel x 300 pixel tic-tac-toe board.

MM_LOENGLISH Working with MM_LOENGLISH is fairly straightforward as well. In the OnPrepareDC all you need to do is explicitly set the mapping mode to MM_LOENGLISH (using CDC’s SetMapMode).

For drawing, draw the tic-tac-toe board with each square exactly 1” across. Remember, in MM_LOENGLISH, each unit is 0.01”. Don’t forget that the Y coordinate increases in the opposite direction from what your used to with MM_TEXT. You’ll have to use negative y-coordinates to actually draw the board.

MM_ANISOTROPIC When working with MM_ANISOTROPIC, we’ll need to do a bit more than just set the mapping mode in the OnPrepareDC. We’ll also need to set the window and viewport extents. Let’s setup the window so that it is 300 logical units by 300 logical units. In OnPrepareDC, first set the mapping mode. Next, set the window extent to 300 x 300 using SetWindowExt. Finally, call SetViewportExt. We want to use the entire window as the viewport, so we’ll need to get the size of the view’s client rectangle using GetClientRect first. Then we’ll pass in the width and height of the client rectangle in to SetViewportRect. Just as a reminder, you must always call SetWindowExt before calling SetViewportExt.

For drawing, go ahead and draw the tic-tac-toe board to fill the window. Since our new window extent is 300 x 300, each of our tic-tac-toe squares should be 100 logical units by 100 logical units.

MM_ISOTROPIC Repeat the procedure you’ve just completed for MM_ISOTROPIC. Except for the call to SetMapMode, this should be exactly the same as for MM_ANISOTROPIC.

Page 41: VC++

41

Practice 10: Using CScrollView As we learned in lecture, if you need to support scrolling within a view, using CScrollView can make your life much easier. In this assignment, we extend the bar graph program which we originally created for practice with dialog boxes. Our original version of the bar graph program didn’t handle cases where there were too many data items to fit within the current window. In this practice assignment, we modify our bar graph program to use CScrollView. The program will dynamically change the scroll area so the user can enter as many data items as desired.

Change Base Class of CBarGraphView

Our original CBarGraphView was based on CView, not CScrollView. If we were creating our application from scratch, and knew we wanted to use CScrollView, we could change the class CBarGraphView was going to be based on in Step 6 of the AppWizard when originally creating the application. Since we’re working with an existing application, we need to change the base class manually. We need to make three changes:

1. In BarGraphView.h change the actual C++ base class of CBarGraphView from CView to CScrollView.

2. In BarGraphView.cpp, change the IMPLEMENT_DYNCREATE macro to reflect that CBarGraphView is based on CScrollView now, not CView.

3. In BarGraphView.cpp, change the BEGIN_MESSAGE_MAP macro to reflect that CBarGraphView is based on CScrollView now, not CView.

Modify OnUpdate

If we’re creating a fixed size scrolling view, we can call SetScrollSizes once in the view’s OnInitialUpdate function. In this case, however, we want to change the size of the scrolling area as the user adds additional data items. In order to do this, we need to call SetScrollSizes each time the document is changed. We can do this by calling it in the view’s OnUpdate function.

You’ll need to calculate the appropriate size of the scroll view based on how much space you think it will take to draw all the data items. The exact calculation will depend on how fancy you got in the original version of bar graph. Here are a few tips which may or may not be useful:

• If you don’t want to hassle with the line and page sizes, you can just skip those parameters.

• If you need to estimate text sizes, don’t forget, you can create a CClientDC right in your OnUpdate function.

One final note, make sure you call Invalidate from within your OnUpdate function.

Page 42: VC++

42

For Further Study

While CScrollView provides automatic support for proportional scrollbars, it provides no support for the keyboard. Users will expect the Home/End, Page Up/Page Down, and arrow keys to scroll the view window. You can add keyboard support to your application by adding keyboard message handlers. In your message handlers, you can call OnHScroll and OnVScroll and the view will react, just as if the user had manipulated the scroll bars directly.

Page 43: VC++

43

Practice 11: Multiple View Classes Some types of documents can be viewed in a variety of different ways. MFC can provide support for these documents by allowing programmers to define more than one view class for a given document type. In this practice assignment, we extend our bar graph program to support two different views of the document—our original graphical view and a new tabular view.

Here is a screenshot showing both types of views of the same document:

Creating a New View Class

Our first step is to create a new view class. The easiest way to do this is to right-mouse click on the root “BarGraph Classes” node in the ClassView. Select “New Class” and create a new class named CTableView based on CView.

The original AppWizard generated CBarGraphView class has a number of handy features that we don’t get with this new view. In particular CBarGraphView includes a GetDocument function which both retrieves the document and correctly coerces the document to a CBarGraphDoc. Copy the declaration of GetDocument from BarGraphView.h and the definition of GetDocument from BarGraphView.h and BarGraphView.cpp into the new TableView .h and .cpp files. Note that there are actually two definitions for CBarGraphView’s GetDocument—a non-debug version is found at the bottom of the BarGraphView.h file and a debug version is in the .cpp file.

Our last step with CTableView is to write the OnDraw function. Unfortunately, as you’ll discover, we won’t actually be able to test it until we make some additional changes to the program. You can either write OnDraw now and debug it later, or hold of on it until you’re able to test it. In either case, don’t worry too much about getting everything to look nice, as the main focus of this assignment is creating multiple views, not on drawing.

Telling MFC about the New View Class

Our next step is to tell MFC that we’ve defined a new view class. We do this by creating a new CDocTemplate. CDocTemplates are created in the CApplication-based class’ InitInstance function. If you scroll to the middle of the definition of CBarGraphApp::InitInstance you should see the following code:

CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_BARGPHTYPE, RUNTIME_CLASS(CBarGraphDoc), RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CBarGraphView)); AddDocTemplate(pDocTemplate);

We need to make a number of changes to CBarGraphApp. The default application doesn’t keep track of the various DocTemplate, as there’s only one. We’ll need to explicitly access each DocTemplate. The easiest way to do this is to define two variables on CBarGraphApp to keep track of our DocTemplates. Add two variables m_pGraphTemplate and

Page 44: VC++

44

m_pTableTemplate of type CMultiDocTemplate* to the application class. Now modify the code currently used to create the bar graph template as follows:

m_pGraphTemplate = new CMultiDocTemplate( IDR_BARGPHTYPE, RUNTIME_CLASS(CBarGraphDoc), RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CBarGraphView)); AddDocTemplate(m_pGraphTemplate);

Now we can access the bar graph template at a later point using the m_pGraphTemplate variable. We now need to create a second DocTemplate for our tables view. If we were adding a second document type, instead of simply a second view type we would need to create a different set of IDR strings to complement the one’s defined by IDR_BARGPHTYPE. However, since both the graph view and table view are used on the same type of document, the only change we need to make when creating our second doctemplate is to change the runtime class of the associated view. Add the following lines to InitInstance:

m_pTableTemplate = new CMultiDocTemplate( IDR_BARGPHTYPE, RUNTIME_CLASS(CBarGraphDoc), RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CTableView)); AddDocTemplate(m_pTableTemplate);

At this point, you should be able to compile and run your program. When the program runs, you’ll be confronted by a choice of creating either a BarGraph or a BarGraph. Hmmm, what’s going on here? When multiple DocTemplates are registered MFC gives the user the option of which one to use when a new document is created. If you select the top BarGraph you’ll get the original graphic view, if you select the bottom BarGraph you’ll get the new table view. Everything should work fine, except if you defined the message handler for the “Add Data Item” menu handler to CBarGraphView, you won’t be able to add data items if the active view is a table view. If you want, you can simply add a second menu handler to CTableView and copy the code to respond to the “Add Data Item” menu item from the CBarGraphView to CTableView.

Setting a Default View Type

In many cases, you won’t want the user to have to choose from the different views available when creating a new document. Let’s modify the code so our BarGraph or BarGraph dialog box doesn’t show up anymore. Add a message handler for the “New Document” menu item in your application. Define it as follows:

void CBarGraphApp::OnFileNew() { m_pGraphTemplate->OpenDocumentFile(NULL); }

Compile and run your application. Everything should work fine … except how will we ever create a Table view now?

Page 45: VC++

45

Adding New Graph and Table Windows

The “New Window” menu item in the “Window” menu allows us to create new views on the currently active document. Since we now have two different types of view, we should replace the “New Window” menu item with two new menu items “New Graph Window” and “New Table Window”. Create message handlers for the new menu items in the CBarGraphDoc. These message handlers can create new windows by accessing the DocTemplate from the CBarGraphApp. Here’s the code for the “New Table Window” handler:

void CBarGraphDoc::OnWindowNewTableWindow() { CDocTemplate* pTemplate = ((CBarGraphApp*) AfxGetApp())->m_pTableTemplate; CFrameWnd* pFrame = pTemplate->CreateNewFrame(this,NULL); pFrame->InitialUpdateFrame(this,TRUE); }

Go ahead and run your application. You should now be able to create new graph and table views of the currently active document via the “Window” menu.

Page 46: VC++

46

Page 47: VC++

47

Practice 12: Toolbars and Status Bars In order to get some practice making toobars and status bars, we’re going to make some minor modifications to your SuperPad project.

Toolbars

Add a new toolbar to SuperPad. Your new toolbar should contain buttons to bring up the find/replace dialog and bring up the goto line dialog.

Again, the steps for creating a toolbar are (1) create a new toolbar resource, (2) modify your main frame code to actually create and dock the toolbar, and (3) respond to user interactions with the toolbar.

Go ahead and create a new toolbar resource in the ResourceView. Draw buttons for each of our toolbar items. Don’t worry too much about the appearance of the toolbar buttons (we aren’t testing your graphics abilities). You’ll need to give each of your toolbar buttons an ID. Remember, you get the properties window for a toolbar button by double-clicking on the button. Give the “find dialog” toolbar button the same ID as your find dialog menu item (probably ID_EDIT_FIND). Similarly, give the “goto line dialog” toolbar button the same ID number as used by your goto line dialog menu item (probably ID_EDIT_GOTOLINE).

Once you’ve gotten the toolbar resource created, add a member variable for the toolbar to your main frame. Then create the toolbar and dock it. You can follow the code examples found already in the main frame code for the original/default toolbar created by MFC.

In this case, you won’t actually need to write any code to respond to user interactions. If your ID numbers for the toolbar buttons are exactly the same as that for the menu items, your existing message handlers should get called automatically.

Status Bar

Add two panes to the status bar to provide information on the caret’s current location. One pane should show the line number the caret is on. The second should show the character position of the caret within the current line.

First, create two new strings in the string table, one for your line pane and one for your character pane. The string values are the “default” values written in to the status panes. Don’t worry too much about the actual values, as you’ll be overwriting them, but remember, you must provide default values.

Modify the static indicators array in your MainFrame.cpp file. Add entries for your two new panes using the ID numbers you’ve just created in the string table. If you use the default pane sizes, the panes won’t be big enough to display the information you want, so in the main frame’s OnCreate, after the status bar is created, resize both panes by calling SetPaneInfo.

Manually declare and define on command UI handlers for both panes. Add entries for the handlers in the frame’s message map. Remember the signature for an update command UI handler is:

Page 48: VC++

48

afx_msg void OnUpdateHandler(CCmdUI *pCmdUI);

The message map entry format is:

ON_UPDATE_COMMAND_UI(ID_NUM, OnUpdateHandler)

In your handlers, get access to your view and your actual CSuperEditCtrl using CFrameWnd’s GetActiveView function. Get the current selection and convert it to the correct line number or the correct character position within the line. Place the number in a string, and call CCmdUI’s SetText. If the user currently has multiple characters selected, base your line and character indicators on the first character of the current selection.

Page 49: VC++

49

Practice 13: Storing Preferences In this practice assignment we extend our SuperPad program to allow users to change the font used to display text. The last font used when the program is exited will be stored as a preference and when SuperPad is run again, it will use the last font chosen.

To keep things simple, we’ll assume that our font can be described simply by the font name and the point size. We’ll use the built in CFontDialog class, but we’ll ignore the user’s font style choices. See the “For Further Study” section for how to correct these limitations.

Adding Preference Information to the App

We’ll store two pieces of information to handle our font preferences—the name of the font and the point size of the font. We will store this information as part of the application. In SuperPad we could store this information as part of the view, since there’s only one view per application, however as a general rule, user preferences hold across views and across documents, so the best place to put them is on the application.

Let’s start by adding member variables to your application for font name and font size. Our next step is to load values for these variables when the application starts up and to save the values when the application shuts down. To do this, need to modify the application’s InitInstance. In InitInstance, we add calls to GetProfileString to retrieve the last used font name from the registry and GetProfileInt to get the last used font size from the registry. Your code should look something like this:

m_fontname = GetProfileString("UserPreferences","FontName", "Arial"); m_fontsize = GetProfileInt("UserPreferences","FontSize",10);

Next, we add calls to WriteProfileString and WriteProfileInt to the app’s ExitInstance. These calls write the latest settings of font name and font size back out to the registry. You’ll probably need to create ExitInstance from the ClassWizard (it’s one of the standard functions ClassWizard can create for you on your app class).

Using the Preferences

Our next step is to get our editor to actually use the font and font size settings. If you’ve already got your SuperPad to support fonts, go ahead and modify the initial settings to use the font name and font size defined at the application.

If you’re version of SuperPad doesn’t yet support fonts, add a pointer to a CFont as a member variable to your view. In the view’s OnCreate function, create the new CFont object and then call CreatePointFont on it, using the font name and font size from your app class. Please note that CreatePointFont takes the font size in 1/10ths of a point. Then set the font of the edit control to your newly created CFont using SetFont.

Page 50: VC++

50

Allowing the User to Change the Font

Our next step is to provide a means for the user to change the font. We can do this by adding a “Set Font” menu item. Create the new menu item and add a handler for it on your CView. In the handler, bring up a CFontDialog. Let the user select a font. Retrieve the name and point size of the font from the dialog and (1) create a new CFont and set your control to use the new CFont and (2) change the font name and font size member variables of your app class to reflect the new font. Don’t forget to Invalidate the window so your view gets redrawn after the user changes the font.

If you’ve done everything correctly, you should be able to change the font displayed while the application is running. When you quit the application, and restart it, it should be using the last font displayed.

For Further Study

We’ve simplified this practice assignment by assuming a font is defined simply by the font name and the font size. To correctly handle all aspects of the font chosen by the user via the CFontDialog, you’ll need to add member variables for each of the parts of the LOGFONT structure found as part of the CHOOSEFONT structure in the m_cf member variable of the CFontDialog. You’ll need to store and retrieve each of these when your application initializes and exits.

Page 51: VC++

51

Practice 14: Working with Win32 In this practice assignment we’ll study straight Win32 programming. Instead of building a Win32 program from scratch, we’ll modify the Win32 squares example program from lecture. As originally written in lecture our Win32 squares program simply draws a 20x20 square wherever the left mouse button is clicked. In this assignment, we’ll extend the original to color in the square. Clicking the right mouse button will change the color of the square and move the square. Clicking the left mouse button will continue to simply move the square.

We’ll need to make a number of changes to the Win32 squares program. First, we’ll need to store color information somewhere in the program. We’ll need to use the color information to fill in the square. Finally we’ll need to respond to right mouse button clicks.

Color Information Storage

We’re are we going to store information on the color used to fill in the square? If we’re sure there’s only going to be one instance of our window, we can simply store the information as global variables. However, if we think our program might need to create multiple instances of the same window class, we’ll need to store the information as part of the window.

Let’s assume we want to keep our program as versatile as possible, so we don’t want to create a set of global variables. Instead, we’ll store red, green, and blue values as part of the window. As you’ll recall from lecture, we tell the Window’s system how much storage we want for a window as part of the WNDCLASSEX structure used to register the window class. The MyRegisterClass function created by the AppWizard is used to register the window class. The lines:

wcex.cbClsExtra = 0; wcex.cbWndExtra = 8;

tell the system that we want no bytes for storage shared by all windows in the same window class, but that each individual window needs 8 bytes of storage. Currently we are using these 8 bytes of storage to store the x and y location of the square. Change the cbWndExtra to 12 bytes—8 bytes for the x and y location, as before, and an additional 4 bytes for our COLORREF information.

User-defined storage space for each window can be initialized when the window’s WndProc responds to the WM_CREATE message. Currently our window class’ WndProc executes the following on WM_CREATE:

case WM_CREATE: SetWindowLong(hWnd,0,40); SetWindowLong(hWnd,4,40); break;

This sets both the longs at 0 bytes and 4 bytes offset to 40. Our program interprets these two longs as the x and y coordinates of our square. Let’s use the long starting at 8 bytes offset to represent our COLORREF. Add the following line of code to the WM_CREATE before the break statement:

Page 52: VC++

52

SetWindowLong(hWnd,8,RGB(255,0,0));

Filling the Square

Now that we’ve created and initialized space to store our color, it’s time to actually draw using the color. The actual drawing code in a WndProc is the code executed in response to a WM_PAINT message. Here is the current drawing code:

case WM_PAINT: hdc = BeginPaint(hWnd, &ps); xPos = GetWindowLong(hWnd,0); yPos = GetWindowLong(hWnd,4); Rectangle(hdc,xPos,yPos,xPos+20,yPos+20); EndPaint(hWnd, &ps); break;

All drawing calls must be enclosed between BeginPaint and EndPaint function calls. In MFC, these calls are taken care of by the framework when it converts WM_PAINT messages in to OnDraw function calls. As we can see, after setting up the handle to a device context (DC), we retrieve the value of the x and y position from the window storage space. Windows has no idea what we’re storing in the window storage space, however, as long as we use the same index numbers to store and retrieve the same information, everything will work correctly. Let’s add a few lines to retrieve our COLORREF value. First, declare a COLORREF variable up at the top of the function, then add a call to GetWindowLong with offset set to 8. Your lines should look something like this:

COLORREF color; … color = GetWindowLong(hWnd,8);

Now we need to take care of the actual painting. If we were programming in MFC, we would call the FillRect method on the CDC. As with most MFC calls, the CDC class’ FillRect has a Win32 equivalent. The only difference between the two is that the Win32 version is a function, not a class method and therefore requires that we pass in the handle to the DC we’re drawing in as a parameter. We’ll need to create a RECT and an HBRUSH for our call to FillRect. The CBrush member function CreateSolidBrush has a corresponding Win32 function which takes the same parameters and creates and returns an HBRUSH. Here are the lines we need to add:

COLORREF color; … color = GetWindowLong(hWnd,8); rect.left = xPos; rect.top = yPos; rect.right = xPos+20; rect.bottom = yPos+20; FillRect(hdc,&rect,CreateSolidBrush(color));

Responding to the Right Mouse Button

One last modification, we need to get our squares program to respond to right mouse button clicks by both moving the square and changing the color of the square. Add a WM_RBUTTONDOWN case to the switch statement in the WndProc. We can copy most of the code directly from the WM_LBUTTONDOWN handler. As with the left mouse button we need

Page 53: VC++

53

to decode the contents of the message by getting the x position and y position of the cursor from the hi and lo words of the lParam. We will then store the new values in to the window storage using SetWindowLong calls. Before the InvalidateRect call, however, let’s also change the value of the 8-11 bytes representing the color of the square with the following lines:

color = GetWindowLong(hWnd,8); if (color == RGB(255,0,0)) SetWindowLong(hWnd,8,RGB(0,0,255)); else SetWindowLong(hWnd,8,RGB(255,0,0));

Now our fill color should toggle between red and blue.

Page 54: VC++

54

Page 55: VC++

55

Name: ___________________

Leland ID: ________________

Student ID: _______________

CS193W Practice Log Complete the practice assignments after the corresponding lectures. When you complete an assignment, initial the practice log and enter the date you completed the assignment. Turn in the practice log on the last day of class. SITN students only may FAX a copy of this log to (650) 723-6092 instead of turning it in.

Date Completed Initials

Practice 1: Overview of MFC _____ ___

Practice 2: Intro to Graphics _____ ___

Practice 3: Inputs _____ ___

Practice 4: Controls _____ ___

Practice 5: Menus _____ ___

Practice 6: Dialog Boxes _____ ___

Practice 7: Serialization _____ ___

Practice 8: Collections _____ ___

Practice 9: Mapping Modes _____ ___

Practice 10: Using CScrollView _____ ___

Practice 11: Multiple View Classes _____ ___

Practice 12: Toolbars and Status Bars _____ ___

Practice 13: Storing Preferences _____ ___

Practice 14: Working with Win32 _____ ___