|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis program demonstrates how to draw on the client area, it also demonstrates how to use the mouse messages. I think the best way to learn programming is programming, so, let's begin our program now. The drawing flagsThe user chooses a drawing tool to draw. For example when the user wants to draw a line, he chooses a line tool. So, we must know which tool the user has selected, the When the user clicks on a point, ( Lets begin now to build the program. Use AppWizard, in the File menu, choose New, choose MFC AppWizard (exe), and write the project name (Painter) in the Project name text box. Then in the MFC AppWizard-Step1 Dialog box, choose Single document and click Finish. In the class view, double click on the class CPainterView : public CView { . . protected: CPoint Anchor; CPoint DrawTo; CPoint OldPoint; BOOL bDrawFlag; BOOL bLineFlag; BOOL bRectangleFlag; BOOL bEllipseFlag; BOOL bFillFlag; void MakeAllFlagsFalse(); . . }; These variables when they initialized, there values are void CPainterView::MakeAllFlagsFalse()
{
bDrawFlag = FALSE;
bLineFlag = FALSE;
bRectangleFlag = FALSE;
bEllipseFlag = FALSE;
bFillFlag = FALSE;
}
Then we must call this method in the CPainterView::CPainterView()
{
// TODO: add construction code here
MakeAllFlagsFalse();
}
Building the tools bar and the menuIn the recourses view, double click the menu, and double click the
Then add 5 buttons in the tool bar. Every time you add a button in the tool bar, the editor adds a blank button in the end of the tool bar, as illustrated bellow:
Finally, connect these buttons to the menu items. Double click on the buttons on the tool bar, the Toolbar Button Properties dialog box will be shown as illustrated bellow. For example, in the ID assign
You must do the same for all the toolbar buttons. The interface between the flags and the drawing toolsOpen the class wizard, (CTRL+w), in the class name choose Add the following code to void CPainterView::OnToolsDrawfreehand()
{
MakeAllFlagsFalse();
bDrawFlag = TRUE;
}
Do the same for the following IDs:
And add the following code to there functions: void CPainterView::OnToolsEllipse() { MakeAllFlagsFalse(); bEllipseFlag = TRUE; } void CPainterView::OnToolsFillfigure() { MakeAllFlagsFalse(); bFillFlag = TRUE; } void CPainterView::OnToolsLine() { MakeAllFlagsFalse(); bLineFlag = TRUE; } void CPainterView::OnToolsRectangle() { MakeAllFlagsFalse(); bRectangleFlag = TRUE; } Draw check mark on the menu itemsOpen the class wizard, choose the void CPainterView::OnUpdateToolsDrawfreehand(CCmdUI* pCmdUI) { pCmdUI->SetCheck(bDrawFlag); } Do the same for the other IDs, and add the following code to their functions: 00 void CPainterView::OnUpdateToolsEllipse(CCmdUI* pCmdUI) { pCmdUI->SetCheck(bEllipseFlag); } void CPainterView::OnUpdateToolsFillfigure(CCmdUI* pCmdUI) { pCmdUI->SetCheck(bFillFlag); } void CPainterView::OnUpdateToolsLine(CCmdUI* pCmdUI) { pCmdUI->SetCheck(bLineFlag); } void CPainterView::OnUpdateToolsRectangle(CCmdUI* pCmdUI) { pCmdUI->SetCheck(bRectangleFlag); } The mouse eventsWhen the user clicks on the left button on the drawing area, void CPainterView::OnLButtonDown(UINT nFlags, CPoint point)
{
Anchor.x = point.x;
Anchor.y = point.y;
.
.
}
The Drawing LinesWhen the user releases the mouse button, he creates a void CPainterView::OnLButtonUp(UINT nFlags, CPoint point)
{
DrawTo.x = point.x;
DrawTo.y = point.y;
.
.
}
It also has the same parameter, Drawing linesHow can we get the device context? We can get it by the void CPainterView::OnLButtonUp(UINT nFlags, CPoint point) { DrawTo.x = point.x; DrawTo.y = point.y; CClientDC* pDC = new CClientDC(this); . . } The Then we must check the void CPainterView::OnLButtonUp(UINT nFlags, CPoint point) { . . CClientDC* pDC = new CClientDC(this); if(bLineFlag){ pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(DrawTo.x, DrawTo.y); } . . } Drawing rectanglesDrawing rectangles is easy, by checking the void CPainterView::OnLButtonUp(UINT nFlags, CPoint point) { . . if(bLineFlag){ pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(DrawTo.x, DrawTo.y); } If(bRectangleFlag){ pDC->SelectStockObject(NULL_BRUSH); pDC->Rectangle(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); } . . } That will draw a transparent rectangle, because we selected the Add the following code in the . . if(bEllipseFlag){ pDC->SelectStockObject(NULL_BRUSH); pDC->Ellipse(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); } . . Filling the shapes with colorTill now all the shapes are transparent, we can fill them by the void CPainterView::OnLButtonUp(UINT nFlags, CPoint point) { DrawTo.x = point.x; DrawTo.y = point.y; CClientDC* pDC = new CClientDC(this); if(bLineFlag){ pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(DrawTo.x, DrawTo.y); } if(bRectangleFlag){ pDC->SelectStockObject(NULL_BRUSH); pDC->Rectangle(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); } if(bEllipseFlag){ pDC->SelectStockObject(NULL_BRUSH); pDC->Ellipse(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); } if(bFillFlag){ pDC->SelectStockObject(BLACK_BRUSH); pDC->FloodFill(Anchor.x, Anchor.y, RGB(0, 0, 0)); } delete pDC; CView::OnLButtonUp(nFlags, point); } Free drawing using the mouseAdd a message handler for the void CPainterView::OnMouseMove(UINT nFlags, CPoint point) { CClientDC* pDC = new CClientDC(this); . . delete pDC; CView::OnMouseMove(nFlags, point); } Then we must check the void CPainterView::OnMouseMove(UINT nFlags, CPoint point) { CClientDC* pDC = new CClientDC(this); if((nFlags && MK_LBUTTON) && bDrawFlag){ . . } delete pDC; CView::OnMouseMove(nFlags, point); } Suppose we are drawing using the free hand. When we draw from the void CPainterView::OnMouseMove(UINT nFlags, CPoint point) { CClientDC* pDC = new CClientDC(this); if((nFlags && MK_LBUTTON) && bDrawFlag){ pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(point.x, point.y); Anchor.x = point.x; Anchor.y = point.y; } delete pDC; CView::OnMouseMove(nFlags, point); } Now our program has been finished, but there are two problems. When we draw we can't see what we have been drawing, and when we draw, and resize the window, every think will disappear. To solve the first problem, we must draw from the We can do that as follows: void CPainterView::OnMouseMove(UINT nFlags, CPoint point) { int nOldMode; . . if((nFlags && MK_LBUTTON) && bLineFlag){ nOldMode = pDC->GetROP2(); pDC->SetROP2(R2_NOT); pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(OldPoint.x, OldPoint.y); . . } . . } Then draw the new line, and copy the current point to the void CPainterView::OnMouseMove(UINT nFlags, CPoint point) { int nOldMode; . . if((nFlags && MK_LBUTTON) && bLineFlag){ nOldMode = pDC->GetROP2(); pDC->SetROP2(R2_NOT); pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(OldPoint.x, OldPoint.y); pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(point.x, point.y); OldPoint.x = point.x; OldPoint.y = point.y; pDC->SetROP2(nOldMode); } . . } Note when the user draws a line and passes over a black object, the line will appear in a white color. In the same way, add the code for drawing the rectangles and ellipses. The void CPainterView::OnMouseMove(UINT nFlags, CPoint point) { int nOldMode; CClientDC* pDC = new CClientDC(this); if((nFlags && MK_LBUTTON) && bDrawFlag){ pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(point.x, point.y); Anchor.x = point.x; Anchor.y = point.y; } if((nFlags && MK_LBUTTON) && bLineFlag){ nOldMode = pDC->GetROP2(); pDC->SetROP2(R2_NOT); pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(OldPoint.x, OldPoint.y); pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(point.x, point.y); OldPoint.x = point.x; OldPoint.y = point.y; pDC->SetROP2(nOldMode); } if((nFlags && MK_LBUTTON) && bRectangleFlag){ nOldMode = pDC->GetROP2(); pDC->SetROP2(R2_NOT); pDC->SelectStockObject(NULL_BRUSH); pDC->Rectangle(OldPoint.x, OldPoint.y, Anchor.x, Anchor.y); pDC->Rectangle(Anchor.x, Anchor.y, point.x, point.y); OldPoint.x = point.x; OldPoint.y = point.y; pDC->SetROP2(nOldMode); } if((nFlags && MK_LBUTTON) && bEllipseFlag){ nOldMode = pDC->GetROP2(); pDC->SetROP2(R2_NOT); pDC->SelectStockObject(NULL_BRUSH); pDC->Ellipse(OldPoint.x, OldPoint.y, Anchor.x, Anchor.y); pDC->Ellipse(Anchor.x, Anchor.y, point.x, point.y); OldPoint.x = point.x; OldPoint.y = point.y; pDC->SetROP2(nOldMode); } delete pDC; CView::OnMouseMove(nFlags, point); } Now, the first problem has been solved. Refreshing our drawingThe metafile is a memory object that can support the device context, so, when the window is resized (maximized, minimized, etc), we can playback the metafile. That will redraw all the shapes that we have drawn. Add this variable in the public section in the CMetaFileDC* pMetaFileDC; Then in the class constructor, add the following code: CPainterDoc::CPainterDoc()
{
pMetaFileDC = new CMetaFileDC();
pMetaFileDC->Create();
}
Now, we must reflect every thing we draw in the metafile. That means when we call the device context, we must do the same in the metafile: void CPainterView::OnLButtonUp(UINT nFlags, CPoint point) { CPainterDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); DrawTo.x = point.x; DrawTo.y = point.y; CClientDC* pDC = new CClientDC(this); if(bLineFlag){ pDC->MoveTo(Anchor.x, Anchor.y); pDC->LineTo(DrawTo.x, DrawTo.y); pDoc->pMetaFileDC->MoveTo(Anchor.x, Anchor.y); pDoc->pMetaFileDC->LineTo(DrawTo.x, DrawTo.y); } if(bRectangleFlag){ pDC->SelectStockObject(NULL_BRUSH); pDC->Rectangle(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); pDoc->pMetaFileDC->SelectStockObject(NULL_BRUSH); pDoc->pMetaFileDC->Rectangle(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); } if(bEllipseFlag){ pDC->SelectStockObject(NULL_BRUSH); pDC->Ellipse(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); pDoc->pMetaFileDC->SelectStockObject(NULL_BRUSH); pDoc->pMetaFileDC->Rectangle(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); } if(bFillFlag){ pDC->SelectStockObject(BLACK_BRUSH); pDC->FloodFill(Anchor.x, Anchor.y, RGB(0, 0, 0)); pDoc->pMetaFileDC->SelectStockObject(NULL_BRUSH); pDoc->pMetaFileDC->Rectangle(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); } delete pDC; CView::OnLButtonUp(nFlags, point); } When the window is required to redraw itself, it calls the void CPainterView::OnDraw(CDC* pDC) { CPainterDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); HMETAFILE MetaFileHandle = pDoc->pMetaFileDC->Close(); pDC->PlayMetaFile(MetaFileHandle); CMetaFileDC* ReplacementMetaFile = new CMetaFileDC(); ReplacementMetaFile->Create(); ReplacementMetaFile->PlayMetaFile(MetaFileHandle); DeleteMetaFile(MetaFileHandle); delete pDoc->pMetaFileDC; pDoc->pMetaFileDC = ReplacementMetaFile; } That will solve the second problem. Now we are ready to start our program. Enjoy.
|
||||||||||||||||||||||