
Contact
C
C++
Visual Basic
Java
JavaScript
DHTML
Style Sheets
About
Normalization
Active X
TDC Binding
PHP
Perl and CGI
Flash
XML
SQL
Chat
MCSE
Linux
Cabling
|

Objective:
This project will compile to produce a simple calculator using the MFC. As you debug, look at how pointers are used along with MFC member functions to communicate with the Windows API. This project will consist of 4
files. Don't forget that you must go to your Visual C++ menu and select:
Project -> Settings -> Microsoft Foundation Classes and then
select "Use MFC in a shared DLL" and click "OK".
1.
Launch your Visual C++ compiler.
2. Select File->New and then select "Win 32
Application" from the list of available options.
3. In the Project Name box, type "Calculator".
4. Click "OK". On the next screen, make sure "Create an
Empty Project" is selected and click "Finish".
5. The compiler will tell you it is creating a new
skeleton project. Select "OK" to continue.
6. Add files to the project. Click File->New and select
"C/C++ Header File" for the files below with the ".h"
extension. Select "C++
Source File" for the files with the ".cpp"
extension, and "Text File" for files with the ".rc"
extension. Do not forget to reformat
text that wraps around multiple lines in the HTML.
This will create errors in your C++ programs unless this
text is placed on the
same line.
7. Then paste the code in the HTML tables below into the
appropriate C++ project files.
8. Click Project->Settings and select "Use MFC in a
Shared DLL" from the combobox. Click "OK".
9. Rebuild, compile and link.
File 1:
resource.h
// ©2004 C. Germany File 1 of 3. Type this into a file and save it as "resource.h".
// Defines Message Numbers. Don't forget to add a carriage return after the last #define!
#define IDD_Calculator 101
#define IDR_MENU1 102
#define IDC_INPUTOUTPUT 1001
#define IDC_1 1002
#define IDC_2 1003
#define IDC_3 1004
#define IDC_4 1005
#define IDC_5 1006
#define IDC_6 1007
#define IDC_7 1008
#define IDC_8 1009
#define IDC_9 1010
#define IDC_0 1011
#define IDC_PERIOD 1012
#define IDC_PLUSMINUS 1013
#define IDC_SQUAREROOT 1013
#define IDC_PLUS 1014
#define IDC_ADD 1014
#define IDC_MINUS 1015
#define IDC_SUBTRACT 1015
#define IDC_MULTIPLY 1016
#define IDC_DIVIDE 1017
#define IDC_EQUALS 1018
#define IDC_SQUARE 1019
#define IDC_CUBE 1020
#define IDC_ERASE 1021
#define IDC_MEMORY1 1022
#define IDC_MEMORY2 1023
#define IDC_CEL 1024
#define IDC_CEL2 1025
#define IDC_FAR 1025
#define IDC_BUTTON1 1026
#define IDC_QUIT 1026
#define IDC_POWER 1027
#define ID_FILE_EXIT 40001
#define ID_OTHER_HELP 40002
#define ID_OTHER_ABOUT 40003
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40004
#define _APS_NEXT_CONTROL_VALUE 1027
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
|
|
Notice that the file above is once again
defining our message identifiers, so that
later we can listen for them and they can be
used to launch message handlers. |
File 2: calculator.cpp
// File 2 of 3. The "WinMain()" portion of the calculator program with MFC dialog box
// Save as "calculator.cpp"
#include <afxwin.h> #include "resource.h" #include <cmath>
#include <afxwin.h> #include <afxcmn.h>
#include "resource.h" #include <cmath>
CEdit * InputOutput;
double Number1;
double Number2;
double Result;
double Memory;
bool NextExpression;
bool NeedToClear;
bool ClickedEquals;
bool PasteMemory;
char oper;
class Calculator : public CDialog
{
public:
Calculator(CWnd* pParent = NULL): CDialog(Calculator::IDD, pParent) { }
enum{IDD = IDD_Calculator};
CBrush CalculatorBrush;
virtual BOOL OnInitDialog()
{
CDialog::OnInitDialog();
InputOutput = (CEdit *) GetDlgItem(IDC_InputOutput);
CalculatorBrush.CreateSolidBrush(RGB(255, 255, 255));
NextExpression = false;
NeedToClear = false;
ClickedEquals = false;
PasteMemory = false;
Number1 = 0.0;
Number2 = 0.0;
Result = 0.0;
Memory = 0.0;
oper = ' ';
return true;
}
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
switch (nCtlColor)
{
case CTLCOLOR_EDIT: pDC->SetTextColor(RGB(255,0,255));
case CTLCOLOR_STATIC: pDC->SetTextColor(RGB(255,0,255));
case CTLCOLOR_DLG: return CalculatorBrush;
default: return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}
}
BOOL PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN)
{
int nVirtKey = (int) pMsg->wParam;
if(nVirtKey == VK_UP) { MessageBox("Up Key Pressed!"); }
if(nVirtKey == VK_DOWN) { MessageBox("Down Key Pressed!"); }
if(nVirtKey == '\r') { Equals(); }
if(nVirtKey == 43) { MessageBox("+!"); Calculate('a'); } if(nVirtKey == 45) { Calculate('s'); } if(nVirtKey == 42) { Calculate('m'); } if(nVirtKey == 47) { Calculate('d'); } if(nVirtKey == 61) { Equals(); } }
return CDialog::PreTranslateMessage(pMsg);
}
afx_msg void key0() { KeyNumber("0"); }
afx_msg void key1() { KeyNumber("1"); }
afx_msg void key2() { KeyNumber("2"); }
afx_msg void key3() { KeyNumber("3"); }
afx_msg void key4() { KeyNumber("4"); }
afx_msg void key5() { KeyNumber("5"); }
afx_msg void key6() { KeyNumber("6"); }
afx_msg void key7() { KeyNumber("7"); }
afx_msg void key8() { KeyNumber("8"); }
afx_msg void key9() { KeyNumber("9"); }
afx_msg void keyPeriod() { KeyNumber("."); }
afx_msg void keyEquals() { Equals(); }
afx_msg void keyAdd() { Calculate('a'); }
afx_msg void keySubtract() { Calculate('s'); }
afx_msg void keyMultiply() { Calculate('m'); }
afx_msg void keyDivide() { Calculate('d'); }
afx_msg void keyPower() { Calculate('p'); }
afx_msg void keyCube() { Cube(); }
afx_msg void keySquare() { Square(); }
afx_msg void keySquareRoot() { SquareRoot(); }
afx_msg void keyMemory() { MemoryKey(); }
afx_msg void EraseMemory() { MemoryClear(); }
afx_msg void keyCE() { Erase(); }
afx_msg void keyFarenheit() { CelciusToFarenheit(); }
afx_msg void keyCelcius() { FarenheitToCelcius(); }
afx_msg void keyMiles() { MilesToKilometers(); }
afx_msg void keyKilometers() { KilometersToMiles(); }
afx_msg void Quit() { EndDialog(IDOK); }
afx_msg void About()
{
MessageBox("Calculator 3.0 - 2006 - C. Germany", "Calculator 3.0 - HELP");
}
afx_msg void Help()
{
char HELPTEXT[5][55] = {
"This program is a fully functional calculator.\r\n",
"It was created as an MFC tutorial for students.\n",
"Its purpose is to display the logic processes\n",
"and progam flow between the user interface and \n",
"processing of input through the MFC CEdit control."
};
char Message[280] = "";
for(int z = 0; z < 5; z++)
{ strncat(Message, HELPTEXT[z], 55); }
MessageBox(Message, "Calculator 3.0 - HELP");
}
void KeyNumber(char x[1])
{
if(NeedToClear)
{
InputOutput->SetWindowText("");
NeedToClear = false;
}
char num[21] = "";
InputOutput->GetWindowText(num, 20);
strncat(num,x,20);
InputOutput->SetWindowText(num);
}
void Calculate(char op)
{
oper = op;
char num[21];
InputOutput->GetWindowText(num,20);
if(NextExpression)
{
if(!ClickedEquals)
{
Number2 = atof(num);
switch(oper)
{
case 'a' : Number1 = Number1 + Number2; break;
case 's' : Number1 = Number1 - Number2; break;
case 'm' : Number1 = Number1 * Number2; break;
case 'd' : if(Number2 == 0) { Number1 = 0; }
else { Number1 = Number1 / Number2; }
break;
case 'p' : Number1 = pow(Number1,Number2); break;
default : break;
}
char Answer[20];
sprintf(Answer,"%g",Number1);
InputOutput->SetWindowText(Answer);
NeedToClear = true;
}
else { ClickedEquals = false; }
}
else
{
Number1 = atof(num);
NeedToClear = true;
NextExpression = true;
}
} void Equals()
{
Calculate(oper);
NeedToClear = true;
ClickedEquals = true;
}
void Erase()
{
InputOutput->SetWindowText("");
NextExpression = false;
NeedToClear = false;
ClickedEquals = false;
Number1 = 0.0;
Number2 = 0.0;
Result = 0.0;
oper = ' ';
}
void Cube()
{
char num[20] = "";
double numtemp = 0.0;
InputOutput->GetWindowText(num, 10);
numtemp = atof(num);
numtemp = numtemp * numtemp * numtemp;
sprintf(num,"%g",numtemp);
InputOutput->SetWindowText(num);
}
void Square()
{
char num[20] = "";
double numtemp = 0.0;
InputOutput->GetWindowText(num, 10);
numtemp = atof(num);
numtemp = numtemp * numtemp;
sprintf(num,"%g",numtemp);
InputOutput->SetWindowText(num);
}
void SquareRoot()
{
char num[20] = "";
InputOutput->GetWindowText(num, 10);
Number1 = atof(num);
Number1 = sqrt(Number1);
sprintf(num,"%g",Number1);
InputOutput->SetWindowText(num);
}
void MemoryKey()
{
char num[20] = "";
if(PasteMemory)
{
sprintf(num,"%g",Memory);
InputOutput->SetWindowText(num);
}
else
{
InputOutput->GetWindowText(num, 10);
Memory = atof(num);
PasteMemory = true;
}
}
void MemoryClear()
{
PasteMemory = false;
Memory = 0.0;
}
void CelciusToFarenheit()
{
char num[20];
InputOutput->GetWindowText(num, 15);
double Farenheit = atof(num);
Farenheit = (Farenheit * 9 / 5) + 32;
sprintf(num,"%g",Farenheit);
InputOutput->SetWindowText(num);
}
void FarenheitToCelcius()
{
char num[20];
InputOutput->GetWindowText(num, 15);
double Celcius = atof(num);
Celcius = ((Celcius - 32) * 5) / 9;
sprintf(num,"%g",Celcius);
InputOutput->SetWindowText(num);
}
void KilometersToMiles()
{
char num[20] = "";
InputOutput->GetWindowText(num,10);
double x = atof(num);
x = x / 1.609347;
sprintf(num,"%g",x);
InputOutput->SetWindowText(num);
}
void MilesToKilometers()
{
char num[20] = "";
InputOutput->GetWindowText(num,10);
double x = x * 1.609347;
sprintf(num,"%g",x);
InputOutput->SetWindowText(num);
}
DECLARE_MESSAGE_MAP()
}; BEGIN_MESSAGE_MAP(Calculator, CDialog)
ON_WM_CTLCOLOR() ON_COMMAND(IDC_Num0, key0)
ON_COMMAND(IDC_Num1, key1)
ON_COMMAND(IDC_Num2, key2)
ON_COMMAND(IDC_Num3, key3)
ON_COMMAND(IDC_Num4, key4)
ON_COMMAND(IDC_Num5, key5)
ON_COMMAND(IDC_Num6, key6)
ON_COMMAND(IDC_Num7, key7)
ON_COMMAND(IDC_Num8, key8)
ON_COMMAND(IDC_Num9, key9)
ON_COMMAND(IDC_Num7, key7)
ON_COMMAND(IDC_Num8, key8)
ON_COMMAND(IDC_Num9, key9)
ON_COMMAND(IDC_NumPeriod, keyPeriod)
ON_COMMAND(IDC_NumEquals, keyEquals)
ON_COMMAND(IDC_NumAdd, keyAdd)
ON_COMMAND(IDC_NumSubtract, keySubtract)
ON_COMMAND(IDC_NumMultiply, keyMultiply)
ON_COMMAND(IDC_NumDivide, keyDivide)
ON_COMMAND(IDC_NumCube, keyCube)
ON_COMMAND(IDC_NumSquare, keySquare)
ON_COMMAND(IDC_NumSquareRoot, keySquareRoot)
ON_COMMAND(IDC_POWER,keyPower)
ON_COMMAND(IDC_NumMemory, keyMemory)
ON_COMMAND(IDC_MemoryErase, EraseMemory)
ON_COMMAND(IDC_NumCE, keyCE)
ON_COMMAND(IDC_Farenheit, keyFarenheit)
ON_COMMAND(IDC_Celcius, keyCelcius)
ON_COMMAND(IDC_Miles, keyMiles)
ON_COMMAND(IDC_Kilometers, keyKilometers)
ON_COMMAND(IDC_QUIT, Quit)
ON_COMMAND(ID_FILE_EXIT,Quit)
ON_COMMAND(ID_OTHER_HELP,Help)
ON_COMMAND(ID_OTHER_ABOUT,About)
END_MESSAGE_MAP()
class Calc : public CWinApp
{
public:
Calc() { }
virtual BOOL InitInstance()
{
CWinApp::InitInstance();
Calculator x;
m_pMainWnd = &x;
INT_PTR nResponse = x.DoModal();
return FALSE;
} DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(Calc, CWinApp)
END_MESSAGE_MAP()
Calc SuperFun;
|
| In the file above, this time we derive from the
CDialog
class instead of the CFrameWnd class. We must
choose either CFrameWnd or CDialog to derive from,
but in this instance we choose CDialog, since we
will need to receive user input in the window we
create. We define the "Calculator" class
specification, which derives from the CDialog class.
Calculator's
constructor specifies the name of the resource file
to use for this project, passing it in as the string
argument "CALCULATOR".
Next, we
prototype our message handler functions inside the
class specification. They will be defined
outside, in the source file. Notice that each
message handler function is prefixed with "afx_msg",
indicating it is a special message handler function,
as opposed to a normal one.
Finally, we
declare our mandatory message map, since we are
using a resource file, by writing "DECLARE_MESSAGE_MAP". |
| In the source
file above, we begin by defining message handler
functions for each of the buttons in our resource
file, and each message handler corresponds to a
calculator function. Here we introduce the
CEdit
object, a textbox which the user can type input
into. This input is received as string data,
and so ultimately it is an ASCII
char array. Since
this is the case, if we want to perform calculations
on what the user types in, we must take what we
receive as string data and convert it to an integer,
float, double or long data type.
We convert this
string data to an integer using the
atoi()
function. With the atoi() function, any string
data that you pass to it as an argument will be
converted to an integer. Since CEdit boxes
only display string data, we will have to use the
opposite of atoi(), which is the
itoa()
function. The itoa() function reverses
the process and converts the integer data back into
a char array.
This string data can then be displayed.
In order to
retrieve the values typed into the CEdit controls,
we must first find their addresses by invoking the
GetDlgItem()
function, which is then cast to a pointer to a CEdit
object and then assigned to a pointer to a CEdit
object. The reason that we have to explicitly
cast the pointer to a CEdit object on the right side
of the assignment operator (using parentheses)
before assigning it to our CEdit pointer on the left
side of the assignment operator is interesting.
We are actually casting a base class pointer (a
pointer to a CWnd class object) to a derived class
pointer (a pointer to a CEdit class object).
We must call GetDlgItem() each time the message
handler is called, since objects will go out of
scope each time windows are deleted and created.
We don't want our pointers to end up pointing to
objects that have fallen out of scope and therefore
no longer exist.
GetDlgItem()
merely sets up pointers to the CEdit objects.
In order to access the text from these items, we
must use the
GetWindowText() function. GetWindowText()
is inherited from CWnd. It retrieves the
string data in the text box so that it can then be
assigned to a char array. The length of our
char array must correspond to the length of the
textbox specified in the resource file. We add
one for a terminating NULL.
Later, we must
use
SetWindowText(), also derived from CWnd, to
place our text string back into the CEdit objects.
We then call the function,
SetFocus(),
to move the cursor back into the CEdit text box so
that the user can enter another value.
Finally,
we create a class, "CalculatorApplication" that
derives from our mandatory CWinApp class.
Inside this class specification, we have changed the
"boilerplate code" used in previous examples that we
would normally place inside InitInstance().
Here, we:
- Create an
instance of Calculator which derives from the CDialog class
- Call
DoModal(), which makes the application modal (no
other windows allowed until this window is
closed)
- The value
"FALSE" will be returned to indicate that the
application has ended
We then
instantiate an object of our class in order to
launch the window and run the application. |
File 3: calculator.rc
//File 3 of 3. Resource file for the project (all the visual MFC //stuff).
//This was done using the visual editor. After clicking your way into glory,
//you can save it as "calculator.rc" file and open it in a text editor.
#include "resource.h"
#include "afxres.h"
IDD_Calculator DIALOGEX 0, 0, 217, 143
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
WS_SYSMENU
CAPTION "Calculator 3.0 - 2006 C. Germany "
MENU IDR_MENU1
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_INPUTOUTPUT,10,11,197,15,ES_RIGHT | ES_AUTOHSCROLL |
ES_NUMBER
PUSHBUTTON "1",IDC_1,16,76,18,15
PUSHBUTTON "2",IDC_2,38,76,18,15
PUSHBUTTON "3",IDC_3,60,76,18,15
PUSHBUTTON "4",IDC_4,16,57,18,15
PUSHBUTTON "5",IDC_5,38,57,18,15
PUSHBUTTON "6",IDC_6,60,57,18,15
PUSHBUTTON "7",IDC_7,16,39,18,15
PUSHBUTTON "8",IDC_8,38,39,18,15
PUSHBUTTON "9",IDC_9,60,39,18,15
PUSHBUTTON "0",IDC_0,16,94,18,15
PUSHBUTTON ".",IDC_PERIOD,60,94,18,15
PUSHBUTTON "Sq&Rt",IDC_SQUAREROOT,180,77,18,15
GROUPBOX "Numeric",IDC_STATIC,10,28,76,86,BS_CENTER
PUSHBUTTON "&+",IDC_ADD,99,41,18,15
PUSHBUTTON "&-",IDC_SUBTRACT,99,59,18,15
PUSHBUTTON "&*",IDC_MULTIPLY,99,77,18,15
PUSHBUTTON "&/",IDC_DIVIDE,99,95,18,15
GROUPBOX "Operator",IDC_STATIC,89,28,38,87,BS_CENTER
PUSHBUTTON "&=",IDC_EQUALS,38,94,18,15
PUSHBUTTON "&Sq",IDC_SQUARE,180,41,18,15
PUSHBUTTON "C&u",IDC_CUBE,180,59,18,15
PUSHBUTTON "&CE",IDC_ERASE,139,42,18,15
GROUPBOX "Function",IDC_STATIC,130,28,38,87,BS_CENTER
PUSHBUTTON "M1",IDC_MEMORY1,139,59,18,15
PUSHBUTTON "M2",IDC_MEMORY2,139,76,18,15
PUSHBUTTON "CELCIUS",IDC_CEL,12,118,43,15
PUSHBUTTON "FARENHEIT",IDC_FAR,62,118,43,15
GROUPBOX "Special",IDC_STATIC,170,29,38,87,BS_CENTER
PUSHBUTTON "&QUIT",IDC_QUIT,138,95,21,12
PUSHBUTTON "&Pow",IDC_POWER,180,95,18,15
END
IDR_MENU1 MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END
POPUP "&Other"
BEGIN
MENUITEM "&Help", ID_OTHER_HELP
MENUITEM "&About", ID_OTHER_ABOUT
END
END
|
The resource
file above introduces several new resource objects
and their features. The fist of these is the
style definition. The style definition may
specify attributes as follows:
STYLE:
-
DS_MODALFRAME - makes the window modal
(nothing can be accessed until the window is
closed.
-
WS_POPUP - makes a stand-alone window, it is
not the child of another window.
-
WS_CAPTION - makes a window with a title
bar.
-
WS_SYSMENU - makes a window with maximize,
minimize and restore.
-
WS_VISIBLE - makes a window visible.
-
WS_BORDER - creates a window with a border.
-
WS_TABSTOP - prevents a button from
receiving focus() when the TAB key is pressed.
Static Text Controls:
- CTEXT
- centered text, no message identifier needed
since static text boxes do not generate messages
or take input.
- RTEXT
- right aligned text, no message identifier
needed since static text boxes do not generate
messages or take input.
- LTEXT
- left aligned text, no message identifier
needed since static text boxes do not generate
messages or take input.
EDITTEXT
- creates an editable text box that takes input and
transmits messages.
DEFPUSHBUTTON - defines default
pushbutton.
PUSHBUTTON
- creates a pushbutton.
SS_CENTER - centers text in a CStatic Window.
|
| For your next
project, take this calculator program and use it as
a template. Plug into it algorithms for doing
conversions between English and metric measurement
systems, computing sales tax or converting decimal
to binary or hexadecimal. Add buttons, message
identifiers, and message handlers. You can
create a super fantastic calculator of your own! |
©2004 C. Germany

|
|