Intro Programming 324/329                                                   Courtesy of Daniar Hussain

Microsoft Foundation Classes Part 1                                     Visual/Windows Programming

                                                                                                April 25, 1998

Reference Materials:            Sections 14.1, 14.2, 14.3, and 14.9 of book.

                                    Appendix E: A Brief Introduction to Inheritance and Polymorphism

Note:   This paper assumes you have read and mastered the above information. It is highly recommended that you read the above before continuing.

This is a brief compilation of what I have learned about visual programming in C++. There are many in-born assumptions that need to be overcome in order to understand Windows programming in C++. The first is the flawed distinction between "actual" windows and controls. There is really no distinction between a control, say a text box, or a window. Both of these groups of objects are together known as windows. A window is basically any object that responds and interacts with the user.

Another common misconception is the nomenclature of text boxes. Since both labels and text boxes in Visual Basic both contain text, a more proper name is used in Visual C++. Compare the nomenclature for both objects.

Visual Basic

Visual C++

Label

CStatic

Textbox

CEdit

Note that CStatic is a static textbox, one whose contents can not be changed, i.e. a label. CEdit is also a textbox, but its contents can be changed. Note that most classes have the "C" prefix. This simply stands for "class" and is used to distinguish between classes and instances of classes.

The last common import peace of framework is what is called inheritance. Be sure you have read and understood Appendix E of our book. Inheritance, also called sub-classing, is a very important concept in the understanding of Microsoft Foundation Classes, hereforeto known as MFC.

Now we must know how to create a project that can access the MFCs. This can be done as follows:

Create a new project of type "Win32 Application" and give it a name.

Next, go to Project->Settings, and select "Using MFCs in a Shared DLL" from the first pulldown menu.

Now you can include files as usual by using the Project->Add to Project->Files... menu. Be sure to include the file "afxwin.h". This is the general header file for MFCs.

IMPORTANT:          #include <afxwin.h>

Now that our common misconceptions are out of the way, we can continue. The most important part of a Windows program is the application class. Every Windows program written in C++ must contain the application class. This is the class that is initially called to perform all of the routine Widows manipulations. Luckily, there is a default class called CWinApp that does all of the heavy duty work for us. (Had we been coding in C, we would have to use the Windows API directly, greatly increasing the problem solving complexity.) However, we want to be able to modify this class in order to suit our own needs. We do this through inheritance (sub-classing for specialization). We create our own class, call it MainApp. We then inherit the CWinApp class found in the MFC library and overload our own functions as needed. This can be accomplished as follows:

class MainApp : public CWinApp

{

public:

     virtual BOOL InitInstance();

};

The first line is used to inherit the class CWinApp. Here, we have overloaded the InitInstance() function. This is a virtual BOOL function, and I fear that even the people at Microsoft don’t know what that prefix does. This function is called when the application is "constructed". You may say, "But Dan, we have not written a constructor!" And you are right, we did not have to write a constructor because Microsoft wrote it for us. Remember, since we inherited the CWinApp class, we have inherited and have access to use their constructor. To see a list of all of the functions we have access to in any class we inherit, search for the name of the class in the InfoViewer. For example, in this case we would search for CWinApp. (CWinApp has over 200 functions for our use!)

Since we have just written a class, the program will not run unless we create an instance of the class. This must be a global variable and it must be defined outside of all functions. It can only appear once in any application. This can be done as follows:

MainApp App;

As soon as the computer encounters this statement, it calls the constructor, just like for any variable, and the constructor in turn calls the InitInstance() function we have defined.

What do we put inside InitInstance()? We create the window in this function. Before we can do that, we must write another very important class, the window class. This is the main window that will appear and hold all of the other objects. We do this by inheriting the CFrameWnd class, as follows:

class MainWindow : public CFrameWnd

{

public:

     MainWindow();

};

Note how we have overloaded the default constructor. It is this constructor that will be called in the InitInstance() function. This constructor should output a window on the screen, creating and displaying any controls that are to appear on the form.

Let us now examine the InitInstance() function:

BOOL MainApp::InitInstance()

{

     m_pMainWnd = new MainWindow();

     m_pMainWnd->ShowWindow(m_nCmdShow);

     m_pMainWnd->UpdateWindow();

return TRUE;

}

Here, we are accessing a public pointer to a window class. Note the use of the new operator to create the window. This call to new calls the MainWindow() constructor. m_pMainWnd will now point to this window. We then access the public ShowWindow(int) function, which displays the window. The m_nCmdShow variable we pass to this function simply tells it which state the window should be in (i.e. maximized, normal, or minimized). This variable is set by the user through the Program Manager. Its details should not concern us here. The next call to the UpdateWindow() function guarantees that our window will be displayed correctly, its working remains a mystery. The last thing we do is return TRUE. Again, this strange Boolean return type remains, for me at least, a complete mystery.

Let us now examine the MainWindow() constructor.

MainWindow::MainWindow()

{

     int x=0,y=0,

width=400,height=600;

     Create(NULL,

          "Window Caption",

          WS_OVERLAPPED,

          CRect(x,y,width,height));

}

Note how in this constructor we access the Create(...) function. It is important to understand that the creation of every control takes two steps. The first is the memory allocation, which is accomplished in the constructor. The next step a call to the Create(...) function. This step actually plots out the control on the screen. Most beginners will forget about this step and wonder why the control does not show up. There are several parameters for this function, listed at the end. This concludes the brief introduction into Windows programming using the Microsoft C++ Foundation Classes. When you compile and run this code, you should see a fully functioning window, with all of the standard features, such as maximize, minimize, close, etc.

In the next handout, I will describe how one can add controls to the window as well as how to intercept windows message in order to associate functions with user commands.

Happy coding. :-)

CFrameWnd::Create

BOOL Create( LPCTSTR lpszClassName,

LPCTSTR lpszWindowName,

DWORD dwStyle = WS_OVERLAPPEDWINDOW,

const RECT& rect = rectDefault,

CWnd* pParentWnd = NULL,

LPCTSTR lpszMenuName = NULL,

DWORD dwExStyle = 0,

CCreateContext* pContext = NULL );

Return Value

Nonzero if initialization is successful; otherwise 0.

Parameters

lpszClassName - Points to a null-terminated character string that names the Windows class. The class name can be any name registered with the AfxRegisterWndClass global function or the RegisterClass Windows function. If NULL, uses the predefined default CFrameWnd attributes.

lpszWindowName - Points to a null-terminated character string that represents the window name. Used as text for the title bar.

dwStyle - Specifies the window style attributes. Include the FWS_ADDTOTITLE style if you want the title bar to automatically display the name of the document represented in the window.

rect - Specifies the size and position of the window. The rectDefault value allows Windows to specify the size and position of the new window.

pParentWnd - Specifies the parent window of this frame window. This parameter should be NULL for top-level frame windows.

lpszMenuName - Identifies the name of the menu resource to be used with the window. Use MAKEINTRESOURCE if the menu has an integer ID instead of a string. This parameter can be NULL.

dwExStyle - Specifies the window extended style attributes.

pContext - Specifies a pointer to a CCreateContext structure. This parameter can be NULL.

Remarks

Construct a CFrameWnd object in two steps. First invoke the constructor, which constructs the CFrameWnd object, then call Create, which creates the Windows frame window and attaches it to the CFrameWnd object. Create initializes the window's class name and window name and registers default values for its style, parent, and associated menu.

Intro Programming 324/329                                                   Courtesy of Daniar Hussain

Microsoft Foundation Classes Part 2                                     Visual/Windows Programming

                                                                                                April 26, 1998

In this handout, I will describe what I have learned about placing controls in windows and how to capture windows messages sent by these controls.

We will begin our discussion of a very simple control, CEdit. This control allows users the input of text data. To create the CEdit control, we must first declare it in the MainWindow class. There are two methods by which we can do this. The first is by creating a variable, or instance of the CEdit class as follows (place this code in the class definition of MainWindow).

CEdit Name;

This, however, is not the preferable way of doing this. This method results in a static control (not to be confused with CStatic). This control is automatically allocated at the construction of the class. The control can not be modified or deleted. The preferable method would be to create a pointer to the control. Later, one could use the new operator to dynamically allocate memory for the control. This gives the programmer power to use the delete operator to delete the control and recreate it anywhere else on the screen! Because the advantages are great and the nuisances small, the rest of this document will talk about controls created in this manner:

CEdit* Name;

Again, this declaration would go into the class definition of MainWindow. Now, inside the MainWindow() constructor, after we created the main window, we would go about creating the control as follows:

Name = new CEdit();

Name->Create(...);

Again, note the two step process (memory allocation and on-screen generation). I have left the parameters sent to CEdit.Create(...) for you to lookup in the InfoViewer or help file.

The most common question asked is How can I read/write to the text in CEdit? This is accomplished by using two accessor functions (the class does not give us direct access to the variable). The two accessor functions are called GetWindowText and SetWindowText. If you are wondering why they are called WindowText, reread part 1 of this document. GetWindowText() returns a CString of the current text inside the CEdit control. The SetWindowText(CString) is a void function, which is passed a CString to set the text of the CEdit control. We would use the following syntax (be sure you are familiar with pointers) to set or get the text of our CEdit control:

CString str;

str = Name->GetWindowText();

Name->SetWindowText(‘’Your text here.’’);

We can place a button, or any other control on the screen for that matter, on the screen, by replacing the constructor CEdit() with that for a button (CButton()) or other control. The table below lists the Visual Basic name and the equivalent Visual C++ names for the most common controls:

Visual Basic Name

Visual C++ Name

Constructor

Label

CStatic

CStatic()

Text-box

CEdit

CEdit()

Command Button

CButton

CButton()

Scroll-bar

CScrollBar

CScrollBar()

List-box

CListBox

CListBox()

Combo-box

CComboBox

CComboBox()

Dialog-box

CDialog

CDialog()

Frame

CStatic with SS_WHITEFRAME drawstyle

CStatic()

Child Form

CFrameWnd

CFrameWnd()

Now that we know how to place controls on the screen, we need to know how they can interact with each other. This is accomplished through Windows Messaging. Whenever the user does something to a control, it immediately sends its parent (declared on creation) its ID number and the action that was performed on it. Each control residing in a common parent must own its own ID number. IDs can by any number greater than 99, and they are assigned to a control at creation. (This is usually the last parameter we send to a control’s create function.) Since ID numbers can be so difficult to work with otherwise, it is good programming practice to define each ID at the top of the header file, appending the second letter of the control name to the word ID, placing an underscore, and then the particular name of the control. For a button, the ID definition would look as follows:

#define IDB_Name 100

Notice that there is no semicolon. This is important because the compiler will simply replace any occurrence of this word with whatever follows it, including any semicolons. During creation, this ID number would be the last parameter that we need to send. Be sure to keep a unique ID for each control, or things could get chaotic.

The next thing we need to do is tell Windows to intercept any controls with a certain ID. For example, when the button we have just declared is clicked, it sends its ID to the parent window, in our case MainWindow. In MainWindow, we need to write what’s called a MessageMap to intercept this ID number, and call a certain function. The function(s) tied to a control must meet certain restrictions, such as they need to return void and take no parameters, for obvious reasons. We create the message map as follows:

We must place the following declaration inside the public section of the parent of the button control, in our case the MainWindow class.

DECLARE_MESSAGE_MAP()

Note the all caps and no semicolon. This is important. This is not a native C++ command, but rather a Microsoft MFC-specific macro. The exact reason for this involves virtual functions and is beyond this document.

With the declaration of the message map in the class definition, we now define the message map (i.e. write the code for it in the CPP file) as follows:

BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd)

     ON_BN_CLICKED(IDB_Name, HandleButton)

END_MESSAGE_MAP()

Again, since we are using Microsoft’s macros, we do not need semicolons. We pass the BEGIN_MESSAGE_MAP(...) function the parent of the control (i.e. the class name where we placed the DECLARE_MESSAGE_MAP() function). We also must pass it the class that the parent was derived from, in our case the CFrameWnd. This is done because if we do not provide a function for a particular button ID, the messaging subsystem will look for a generic function to call inside CFrameWnd (in our case).

The next macro defines the event that we want to capture; see a generic list at the end of this document. To this macro we pass the ID number of the control as well as the function name that should be called when that action of that control is intercepted. Finally, we must END_MESSAGE_MAP().

The only thing that remains is to write the HandleButton() function. Remember this must be a void function and it can not take any parameters. It must also be prefixed with the special afx_msg keyword. The complete function declaration, to be placed in the MainWindow class follows:

afx_msg void HandleButton();

The actual implementation varies depending on the purpose of the button. In this case, we will change the text in the CEdit control we created earlier when the button is clicked.

void HandleButton()

{

     CEditName->SetWindowText("Your first windows message!");

}

Note that there is no afx_msg prefix in the implementation.

Congratulations, you now know how to create windows, place controls, and intercept windows messages. With this basic framework, you can now do all that you could in Visual Basic in C++. Although it is a little bit more cumbersome, the trouble is more than repaid in the ability to write efficient and class-oriented C++ code.

 

CButton Handlers

Map Entry

Function Prototype

ON_BN_CLICKED( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_BN_DISABLE( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_BN_DOUBLECLICKED( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_BN_HILITE( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_BN_PAINT( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_BN_UNHILITE( <id>, <memberFxn> )

afx_msg void memberFxn( );

 

CEdit Handlers

Map Entry

Function Prototype

ON_EN_CHANGE( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_EN_ERRSPACE( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_EN_HSCROLL( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_EN_KILLFOCUS( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_EN_MAXTEXT( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_EN_SETFOCUS( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_EN_UPDATE( <id>, <memberFxn> )

afx_msg void memberFxn( );

ON_EN_VSCROLL( <id>, <memberFxn> )

afx_msg void memberFxn( );


Copyright © 1998, by Daniar Hussain
All rights reserved, unless otherwise stated.