YOU CAN CODE!

 

With The Case Of UCanCode.net  Release The Power OF  Visual C++ !   HomeProducts | PurchaseSupport | Downloads  
Download Evaluation
Pricing & Purchase?
E-XD++Visual C++/ MFC Products
Overview
Features Tour 
Electronic Form Solution
Visualization & HMI Solution
Power system HMI Solution
CAD Drawing and Printing Solution

Bar code labeling Solution
Workflow Solution

Coal industry HMI Solution
Instrumentation Gauge Solution

Report Printing Solution
Graphical modeling Solution
GIS mapping solution

Visio graphics solution
Industrial control SCADA &HMI Solution
BPM business process Solution

Industrial monitoring Solution
Flowchart and diagramming Solution
Organization Diagram Solution

Graphic editor Source Code
UML drawing editor Source Code
Map Diagramming Solution

Architectural Graphic Drawing Solution
Request Evaluation
Purchase
ActiveX COM Products
Overview
Download
Purchase
Technical Support
  General Q & A
Discussion Board
Contact Us

Links

Get Ready to Unleash the Power of UCanCode .NET

 


UCanCode Software focuses on general application software development. We provide complete solution for developers. No matter you want to develop a simple database workflow application, or an large flow/diagram based system, our product will provide a complete solution for you. Our product had been used by hundreds of top companies around the world!

"100% source code provided! Free you from not daring to use components because of unable to master the key technology of components!"


VC++ MFC Example: Paint and draw source code, CClientDC
A simple program that demonstrates how to use mouse messages and how to draw using MFC.

By Aymen++. 

Introduction

This 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 flags

The 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 bDrawFlag for free drawing, bLineFlag for the line, bRectangleFlag for the rectangle, bEllipseFlag for the ellipse, and bFillFlag if the user wants to fill the shape with color.

When the user clicks on a point, (Anchor), and draws the mouse pointer to a point (DrawTo), and we have the bLineFlag = TRUE, we can easily draw the line when the user releases the mouse button.

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 CPainterView class, the PainterView.h file will be opened. You can see the CPainterView class declaration. Add these variables and MakeAllFlagsFalse() method in the protected section in the class declaration:

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 FALSE. But we need to make them all FALSE if one of them is changed by the MakeALlFlagsFalse() method, we can simply write this method like this:

void CPainterView::MakeAllFlagsFalse()
{
    bDrawFlag = FALSE; 
    bLineFlag = FALSE; 
    bRectangleFlag = FALSE; 
    bEllipseFlag = FALSE; 
    bFillFlag = FALSE; 
}

Then we must call this method in the CPainterView class constructor:

CPainterView::CPainterView()
{
    // TODO: add construction code here
    MakeAllFlagsFalse(); 
}

Building the tools bar and the menu

In the recourses view, double click the menu, and double click the IDR_MAINFRAME, add new menu, call it Tools, give this menu five items, as illustrated bellow:

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 ID_TOOLS_RECTANGLE for the rectangle button.

You must do the same for all the toolbar buttons.

The interface between the flags and the drawing tools

Open the class wizard, (CTRL+w), in the class name choose CPaintView, in the object ID's choose ID_TOOLS_DRAWFREEHAND, in the messages, choose command, and click add function button, then click edit code. The Class Wizard will add the OnToolsDrawfreehand() function.

Add the following code to OnToolsDrawfreehand():

void CPainterView::OnToolsDrawfreehand() 
{
    MakeAllFlagsFalse(); 
    bDrawFlag = TRUE;     
}

Do the same for the following IDs:

  • ID_TOOLS_ELLIPSE

  • ID_TOOLS_FILLFIGURE

  • ID_TOOLS_LINE

  • ID_TOOLS_RECTANGLE

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 items

Open the class wizard, choose the CPaintView class in the class name, in the ID's choose ID_TOOLS_DRAWFREEHAND, in the message choose UPDATE_COMMAND_UI, then press add function, and edit code, add this code to the OnUpdateToolsDrawFreehand function:

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 events

When the user clicks on the left button on the drawing area, WM_LBUTTON message will be sent, we can add a message handler for this message, in the class wizard. Be sure that the CPaintView class has been chosen in the class name, in the object ID's, choose CPaintView. In the message, scroll till you find the WM_LBUTTONDOWN, and press add function, and edit code. Add the following code to the function handler for the WM_LBUTTONDOWN message:

void CPainterView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    Anchor.x = point.x; 
    Anchor.y = point.y; 
.
.
}

The OnLButtonDown takes the point parameter, this parameter is a CPoint class, it saves the point that the user clicked.

Drawing Lines

When the user releases the mouse button, he creates a DrawTo point, and we must register this point. When the user releases the left mouse button, WM_LBUTTONUP message will be sent. Open the class wizard to add a function handler for the WM_LBUTTONUP message as illustrated before, add the following code to the message handler:

void CPainterView::OnLButtonUp(UINT nFlags, CPoint point) 
{
DrawTo.x = point.x; 
    DrawTo.y = point.y;
.
.
}

It also has the same parameter, CPoint point.

Drawing lines

How can we get the device context? We can get it by the CClientDC class which is inherited from the CDC class. Add the following code to the OnLButtonUp message handler:

void CPainterView::OnLButtonUp(UINT nFlags, CPoint point) 
{
DrawTo.x = point.x; 
    DrawTo.y = point.y; 

    CClientDC* pDC = new CClientDC(this);
.
.
}

The this points to the current object.

Then we must check the bLineFlag. If bLineFlag is TRUE, we are ready to draw the line by moving to the Anchor point (the point that the user clicked on), then we draw the line to the DrawTo point:

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 rectangles

Drawing rectangles is easy, by checking the bRectangleFlag and adding this code:

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 NULL_BRUSH in the SelectStockObject.

Add the following code in the OnLButtonUp handler to draw ellipses:

.
.
if(bEllipseFlag){
        pDC->SelectStockObject(NULL_BRUSH); 
        pDC->Ellipse(Anchor.x, Anchor.y, DrawTo.x, DrawTo.y); 
        }
.
.

Filling the shapes with color

Till now all the shapes are transparent, we can fill them by the FloodFill method. I'll use BLACK_BRUSH for the filling brush. When we finish, the OnLButtonUp handler must be like this:

Collapse

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 mouse

Add a message handler for the WM_MOUSEMOVE message:

void CPainterView::OnMouseMove(UINT nFlags, CPoint point) 
{
    CClientDC* pDC = new CClientDC(this); 
    .
    .    
    delete pDC; 
    CView::OnMouseMove(nFlags, point);
}

Then we must check the bDrawFlag, if the mouse is still moving and the left button still unreleased. We can do that by the nFlags and the MK_LBUTTON:

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 Anchor point to the current point, the current point will become the Anchor point, like this:

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 Anchor to the DrawTo, then when the mouse moves (the user is still clicking on the left button), we must save the DrawTo in OldPoint and clear the line that we have been drawing and draw a new line to the DrawTo point.

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 OldPoint:

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 OnMouseMove handler must be like this:

Collapse

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 drawing

The 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 CPaintDoc class declaration:

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:

Collapse

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 OnDraw() function. What we have to do now is show the metafile. Add the following code to OnDraw function:

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.

 

 

Copyright ?1998-2024 UCanCode.Net Software , all rights reserved.
Other product and company names herein may be the trademarks of their respective owners.

Please direct your questions or comments to webmaster@ucancode.net