Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
This tutorial shows how to create a "Paint" style program that allows the user to draw freehand, create
shapes and output text to the screen. Some of the things you will learn:
Notice that "Random Colors" has the "Checked" property selected. This is an option in the menu item
properties.
Select the "Pop-up" property for popup menus like the ones above. For example
Here are the other popup menus we need to create:
The last three items are going to be radio buttons. Only one of them can be selected at one time. The
selected item will have a black circle in front of it. This will be accomplished later in code.
The last three items will be radio buttons.
We first add some member variables to the ChildView class. The ones relating to color are shown in
bold face.
These go in the .h file:
// Attributes
public:
int xPos, yPos;
bool mouseDown;
COLORREF color;
bool randomColors;
int shapeType, shapeWidth, shapeHeight;
int penWidth;
COLORREF background;
CString text;
LOGFONT* lfPtr;
int textBackground;
CChildView::CChildView()
{
mouseDown = false;
shapeType = 0; shapeWidth = shapeHeight = 50;
randomColors = true;
color = 0;
penWidth = 3;
text = "";
textBackground = 2;
lfPtr = NULL; // no font chosen yet (use default font)
background = RGB(40,0,70); // dark blue/violet
}
We set the background color in the PreCreateWindow() function:
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_UPARROW), CreateSolidBrush(background), NULL);
return TRUE;
}
We want to let the user select a color or choose random colors. This is done using the COMMAND
and UPDATE_COMMAND_UI messages for the Random Colors menu item:
void CChildView::OnRandomcolors()
{
randomColors = !randomColors;
}
But how does the user choose a particualt (non-random) color? Windows is kind enough to provide a
built in dialog class for this: CColorDialog.
void CChildView::OnChooseColor()
{
randomColors = false;
CColorDialog dlg;
dlg.m_cc.Flags |= CC_FULLOPEN; // opens custom color selector
dlg.DoModal(); // open color dialog
color = dlg.GetColor(); // set current color to the one chosen
}
The value of GetColor() will be whatever color is last selected when then dialog is closed. Notice
that we can select either a basic color or create our own color.
The last message we need to handle if for "Erase". This one is simple:
void CChildView::OnErase()
{
color = background;
randomColors = false;
}
When we draw, the Pen color will be the same as the background color.
Selecting the Shape
The shape menu will be used to set the variable shapeType. This variable can have the values 0, 1
or 2 corresponding to rectangle, oval or random. Here are the message handlers for the COMMAND
and UPDATE_COMMAND_UI messages:
void CChildView::OnRectangle()
{
shapeType = 0;
void CChildView::OnOval()
{
shapeType = 1;
}
void CChildView::OnRandomshape()
{
shapeType = 2;
}
// use menu items as radio buttons
void CChildView::OnUpdateRectangle(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(shapeType == 0);
}
Choosing Sizes
There are two dialogs we have created using the resource editor. These are associated with the
CPenWidth and CShapeSize dialog classes. Remember how to do this? Right click on the dialog
and select Class Wizard. You will be prompted to create a new class for the dialog.
We associate member variables m_penWidth with the Pen Width edit box and m_shapeWidth and
m_shapeHeight with the other two edit boxes.
void CChildView::OnShapesize()
{
SizeDlg dlg;
dlg.m_shapeHeight = shapeHeight;
dlg.m_shapeWidth = shapeWidth;
dlg.DoModal();
shapeWidth = dlg.m_shapeWidth;
shapeHeight = dlg.m_shapeHeight;
Notice that we transfer data into the dialog before opening it (via DoModal()) so that the user will
know what the current values are. Then we transfer data back to the member variables of CChildView
(e.g. penWidth) after closing the dialog. There is no need to call UpdateData() in this situation.
Creating Text
We want to be able to output text as well as shapes. Therefore we have a variable text in
CChildView of type CString. The user enters text via the Text dialog:
We create a CText class for this dialog and associate CString variable m_Text with the edit box.
Now we can add the following message handler for the Create Text menu item:
void CChildView::OnSelectText()
{
CText dlg;
dlg.m_Text = text;
dlg.DoModal();
text = dlg.m_Text;
The member variable text of CChildView will be used when we want to display text.
Selecting a Font
Again Windows provides us with a handy Common Dialog for this: CFontDialog. Here is the message
handler for the Select Font menu item:
void CChildView::OnFont()
{
CFontDialog dlg;
dlg.DoModal();
if (lfPtr == NULL) lfPtr = new LOGFONT;
dlg.GetCurrentFont(lfPtr);
}
Notice that we use the member variable lfPtr (its a pointer to a struct) to store the new font
attributes. This isn't itself a font but will be used later to create the font when we are ready to output
some text.
Text can be displayed in a rectangle with some background color. Or we can make the background
transparent. Here are the menu items again:
And the event handlers for these three radio button menu items:
void CChildView::OnFontTransparentbackground()
{
textBackground = 0;
}
void CChildView::OnFontWhitebackground()
{
textBackground = 1;
}
void CChildView::OnUpdateFontWhitebackground(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(textBackground == 1);
}
void CChildView::OnFontColorbackground()
{
textBackground = 2;
}
When the user clicks the left mouse button we want to record the position and set the mouseDown
flag in case this is a drag event. If this is a Shift Click we want to output the value of our text member
variable. We could also test for the MK_CONTROL flag if we want to handle Control-click events.
COLORREF CChildView::getColor()
{
if (randomColors) return RGB(rand()%256,rand()%256,rand()%256);
return color;
We use the mouse move messages only when the left mouse button is down. This is a dragging even.
We are going to draw whereever the cursor is:
void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
if (!mouseDown) return;
CClientDC dc(this);
// we need to create a Pen to draw lines
CPen p;
p.CreatePen(0,penWidth,getColor());
dc.SelectObject(&p);
dc.MoveTo(xPos,yPos);
dc.LineTo(point.x,point.y);
xPos = point.x;
yPos = point.y;
// the next line avoids that "freezing" problem
dc.SelectStockObject(NULL_PEN);
CWnd ::OnMouseMove(nFlags, point);
}
switch (shapeType) {
case 0:
dc.Rectangle(xPos -h/2,yPos - w/2,xPos +h/2,yPos + w/2); break;
case 1:
dc.Ellipse (xPos -h/2,yPos - w/2,xPos +h/2,yPos + w/2); break;
case 2:
POINT points[20];
int size = rand()%8 + 5;
for (int i = 0; i< size; i++) {
points[i].x = point.x+rand()%h- h/2;
points[i].y = point.y+rand()%w -w/2;
}
dc.Polygon(points,size);
}
dc.SelectStockObject(NULL_BRUSH);
CWnd ::OnRButtonDown(nFlags, point);
}
The shapes will be centered around the cursor when we click. Polygons will have a random number of
points configured in random locations. Zigzags basically.
Draw.exe