MDI Applications – Part 3

 

If an array of data is to be serialized in a windows application, then one approach is to create a CArray of pointers (of the user defined class type) in the document class. The drawback with this technique is that you need to write the global SerializeElements function for the CArray object. Another approach is to use CTypedPtrList template class instead of CArray to create objects of user defined class in the document class. For example, to create an array of CStudent objects in the document class, you would declare the following in the document class header file:

            CTypedPtrList<CObList,CStudent*> m_setofStudents;

The first parameter in the template declaration is the base class for the collection, and the second parameter is the type for parameters and return values. Because all CTypedPtrList functions are inline, use of this template does not significantly affect the size or speed of your code. Lists derived from CObList can be serialized, but those derived from CPtrList cannot. When a CTypedPtrList object is deleted, or when its elements are removed, only the pointers are removed, not the entities they reference. Thus you should delete the user defined class objects referred to by the CTypedPtrList in the DeleteContents member function of the document class. Recall that DeleteContents is called by the framework when the document is closed.

You need to include <afxtempl.h> to use CTypedPtrList class. The class provides SetAt and GetAt member functions to set or access the element at a particular position. You can iterate through the array by using GetHead (to get to the start element) and GetNext (to get to the next element) member functions.

Example: Let us create an MDI application that will allow us to draw as many circles as we want and also be able to serialize them properly. Create an MDI application, name the project mdidraw3.

Add a header file called CCircle to the project. Type the following code in it.

//--------CCircle.h------------

#ifndef CCIRCLE_H

#define CCIRCLE_H

class CCircle : public CObject {

               DECLARE_SERIAL(CCircle);

 

               int m_x1, m_y1, m_x2, m_y2; // bounding rectangle;

public:

               CCircle();

               CCircle(int x1,int y1, int x2, int y2);

               void set(int x1, int y1, int x2, int y2);

               int get_x1() const;

               int get_y1() const;

               int get_x2() const;

               int get_y2() const;

               void Draw(CDC * pDC); // drawing code

               virtual void Serialize(CArchive & ar); // override serialize

  };

#endif

Add another cpp filed CCircle to the project, type the following code in it.

//-----CCircle.cpp-----------

#include "stdafx.h"

#include "CCircle.h"

 

IMPLEMENT_SERIAL (CCircle,CObject,1);

 

CCircle::CCircle() {m_x1=0; m_y1=0; m_x2 = 100; m_y2=100; }

 

CCircle::CCircle(int x1, int y1, int x2, int y2) {

               m_x1 = x1; m_y1 = y1; m_x2 = x2; m_y2 = y2;

}

 

void CCircle::set(int x1, int y1, int x2, int y2) {

               m_x1 = x1; m_y1 = y1; m_x2 = x2; m_y2 = y2;

}

 

int CCircle::get_x1() const { return m_x1;}

int CCircle::get_y1() const { return m_y1;}

int CCircle::get_x2() const { return m_x2;}

int CCircle::get_y2() const { return m_y2;}

 

void CCircle::Draw(CDC *pDC) {

               CRect rb(m_x1,m_y1,m_x2,m_y2);

               pDC->Ellipse(&rb);

}

 

 

void CCircle::Serialize(CArchive & ar) {

               if (ar.IsLoading())

                              ar >> m_x1 >> m_y1 >> m_x2 >> m_y2 ;

               else

                              ar << m_x1 << m_y1 << m_x2 << m_y2 ;

}

 

 

Add the CTypedPtrList declaration to the document header file.

…..

#pragma once

#endif // _MSC_VER > 1000

 

#include "CCircle.h"

#include <afxtempl.h>

class CMdidraw3Doc : public CDocument

{

protected: // create from serialization only

               CMdidraw3Doc();

               DECLARE_DYNCREATE(CMdidraw3Doc)

 

// Attributes

public:

               CTypedPtrList<CObList,CCircle*> m_setofCircles;

 

Add the Type code for the Serialize function in the document class cpp file.

void CMdidraw3Doc::Serialize(CArchive& ar)

{

               if (ar.IsStoring())

               {

                              // TODO: add storing code here

               }

               else

               {

                              // TODO: add loading code here

               }

            m_setofCircles.Serialize(ar);

}

 

Add the DeleteContents function to the document class by using the class wizard.

Type the following code in it.

void CMdidraw3Doc::DeleteContents()

{

               // TODO: Add your specialized code here and/or call the base class

               while (!m_setofCircles.IsEmpty())

               {

                              delete m_setofCircles.RemoveHead();

               }

               CDocument::DeleteContents();

}

 

Add the following declarations to the view class header file.

class CMdidraw3View : public CView

{

protected: // create from serialization only

               CMdidraw3View();

               DECLARE_DYNCREATE(CMdidraw3View)

 

// Attributes

public:

               CMdidraw3Doc* GetDocument();

            int m_x1, m_y1, m_x2, m_y2;

            CCircle m_Circle;

            int m_track;

 

Using the classwizard, add the WM_LBUTTONUP,  WM_MOUSEMOVE, OnUpdate and OnInitialUpdate handlers to the view class. You also need to include the CCircle.h file in the view class cpp file.

 

void CMdidraw3View::OnLButtonUp(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

               m_track = 0;

               CMdidraw3Doc * pDoc = GetDocument();

               pDoc->SetModifiedFlag(TRUE);

               pDoc->UpdateAllViews(this);        

               CView::OnLButtonUp(nFlags, point);

}

 

void CMdidraw3View::OnLButtonUp(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

    m_track = 0;

               CMdidraw3Doc * pDoc = GetDocument();

               pDoc->SetModifiedFlag(TRUE);

               pDoc->UpdateAllViews(this);        

               CView::OnLButtonUp(nFlags, point);

}

 

void CMdidraw3View::OnMouseMove(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

               if (m_track == 1) {

                              CRect Rectold(m_x1,m_y1, m_x2, m_y2);

                              m_x2 = point.x; m_y2 = point.y;

                              CRect Rectnew(m_x1,m_y1, m_x2,m_y2);

 

                              CMdidraw3Doc * pDoc = GetDocument();

                              CCircle * pC = pDoc->m_setofCircles.GetTail();

                              pC->set(m_x1,m_y1,m_x2, m_y2);

 

                              InvalidateRect(Rectold,TRUE);

                              InvalidateRect(Rectnew,TRUE);

               }

               CView::OnMouseMove(nFlags, point);

}

 

void CMdidraw3View::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)

{

               // TODO: Add your specialized code here and/or call the base class

              

               InvalidateRect(NULL);     

}

 

void CMdidraw3View::OnInitialUpdate()

{

               CView::OnInitialUpdate();

              

               // TODO: Add your specialized code here and/or call the base class

               InvalidateRect(NULL);

}

 

Write the following code in the OnDraw function in the view class cpp file.

void CMdidraw3View::OnDraw(CDC* pDC)

{

               CMdidraw3Doc* pDoc = GetDocument();

               ASSERT_VALID(pDoc);

               // TODO: add draw code for native data here

               POSITION pos = pDoc->m_setofCircles.GetHeadPosition();

               CCircle * pC;

               while (pos) {

                              pC=pDoc->m_setofCircles.GetNext(pos);

                              pC->Draw(pDC);

               }

}

Build and test the application. You should be able to draw multiple circles, and be able to serialize the document successfully.

 

Selecting Shapes:

            We will modify the above application so that the user can select a shape by clicking the left mouse button on a drawn shape. Since the left mouse click is being used to draw the circle right now, we will add two toolbar buttons (and corresponding menu items) so that the user could either be in the circle drawing mode or the shape selection mode.

 

            Create two new toolbar buttons by modifying the IDR_MAINFRAME toolbar resource. The first toolbar button will have a shape of circle on it (give it an ID of ID_DRAW_CIRCLE), the second button will have a shape of arrow on it (give it an ID of ID_MODE_ARROW). Add two data members to the view class header file as shown below:

                        CString m_SelectedShapeOnToolbar;

                        int m_Color;  // selected shape will have a gray background rather than white

 

In the constructor for the view class (in the view class cpp file), set the m_SelectedShapeOnToolbar to “CIRCLE”.

CMdidraw3View::CMdidraw3View()

{

               // TODO: add construction code here

               m_SelectedShapeOnToolbar = "CIRCLE";

               m_Color = WHITE_BRUSH;

}

 

Add two menu items under a new top level menu called “Drawing Mode”. The menu items are to be called “Draw Circle” (ID_DRAW_CIRCLE) and “Arrow Mode” (ID_MODE_ARROW). Write the following code in the event handlers for these menu items.

void CMdidraw3View::OnDrawCircle()

{

               m_SelectedShapeOnToolbar = "CIRCLE";

               m_Color = WHITE_BRUSH;

}            

 

void CMdidraw3View::OnModeArrow()

{

               // TODO: Add your command handler code here

               m_SelectedShapeOnToolbar = "ARROW";

}

 

Modify the WM_LBUTTONDOWN and WM_LBUTTONUP handlers to as shown below.

void CMdidraw3View::OnLButtonDown(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

               if (m_SelectedShapeOnToolbar == "CIRCLE") {

                              m_x1 = point.x;

                              m_y1 = point.y;

                              m_x2 = point.x+1;

                              m_y2 = point.y+1;

                              CMdidraw3Doc * pDoc = GetDocument();

                              CCircle * pCircle = new CCircle(m_x1,m_y1,m_x2,m_y2);

                              pDoc->m_setofCircles.AddTail(pCircle);

                              m_track = 1;

               }

               if (m_SelectedShapeOnToolbar == "ARROW") // selection mode

               {

                              m_Color = WHITE_BRUSH;

                              CClientDC dc(this);

                              OnDraw(&dc);  // direct call, clear old selections

                                                                             // InvalidateRect(NULL) will not work

                                                                             // because of delay in message posting

 

                              // cycle through all objects to see if mouse pressed

                              // point is in one of the shapes that have been drawn

                              CMdidraw3Doc* pDoc = GetDocument();

                              ASSERT_VALID(pDoc);

                              POSITION pos = pDoc->m_setofCircles.GetHeadPosition();

                              CCircle * pC;

                              int x1, y1, x2, y2;

                              CRect rect_shape;

                              while (pos) {

                                             pC=pDoc->m_setofCircles.GetNext(pos);

                                             x1 = pC->get_x1();

                                             y1 = pC->get_y1();

                                             x2 = pC->get_x2();

                                             y2 = pC->get_y2();

                                             rect_shape.top = y1;

                                             rect_shape.left = x1;

                                             rect_shape.bottom = y2;

                                             rect_shape.right = x2;

                                             if (rect_shape.PtInRect(point)) // mouse click is inside rectangle

                                             {

                                                            m_Color = GRAY_BRUSH;

                                                            InvalidateRect(rect_shape,TRUE);

                                                            break;

                                             }

                              }

               }

               CView::OnLButtonDown(nFlags, point);

}

 

void CMdidraw3View::OnLButtonUp(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

            if (m_SelectedShapeOnToolbar != "ARROW") {

                              m_track = 0;

                              CMdidraw3Doc * pDoc = GetDocument();

                              pDoc->SetModifiedFlag(TRUE);

                              pDoc->UpdateAllViews(this);

            }

               CView::OnLButtonUp(nFlags, point);

}

 

 

Add the UP_COMMAND_UI event handlers for the two menu items ID_DRAW_CIRCLE and ID_MODE_ARROW, the code is shown below.

void CMdidraw3View::OnUpdateDrawCircle(CCmdUI* pCmdUI)

{

               // TODO: Add your command update UI handler code here

               if (m_SelectedShapeOnToolbar == "CIRCLE")

                              pCmdUI->SetCheck(1);

               else

                              pCmdUI->SetCheck(0);   

}

 

void CMdidraw3View::OnUpdateModeArrow(CCmdUI* pCmdUI)

{

               // TODO: Add your command update UI handler code here

               if (m_SelectedShapeOnToolbar == "ARROW")

                              pCmdUI->SetCheck(1);

               else

                              pCmdUI->SetCheck(0);   

}

 

The modified OnDraw code is shown below.

void CMdidraw3View::OnDraw(CDC* pDC)

{

               CMdidraw3Doc* pDoc = GetDocument();

               ASSERT_VALID(pDoc);

               // TODO: add draw code for native data here

            pDC->SelectStockObject(m_Color);

               POSITION pos = pDoc->m_setofCircles.GetHeadPosition();

               CCircle * pC;

               while (pos) {

                              pC=pDoc->m_setofCircles.GetNext(pos);

                              pC->Draw(pDC);

               }

            m_Color = WHITE_BRUSH;  // reset default color

}

Build and test the application. You should be able to select any object by clicking on it. The selected shape will be painted in gray background.

 

 

Moving Shapes:

            In order to be able to select and then move a shape to a different position, we need to keep track of the exact coordinates at which the mouse was clicked on the shape so that as we move it, the relative position of the shape is kept proportionate to the click on the shape. For this purpose we will add the following data members to the view class header file.

class CMdidraw3View : public CView

{

protected: // create from serialization only

               CMdidraw3View();

               DECLARE_DYNCREATE(CMdidraw3View)

 

// Attributes

public:

               CMdidraw3Doc* GetDocument();

               int m_x1, m_y1, m_x2, m_y2;

               CCircle m_Circle;

               int m_track;

               CString m_SelectedShapeOnToolbar;

               int m_Color;   // for setting color of the selected object

               // -----following members added for moving shapes

               CPoint m_ptTopLeft ;  // top left of the selected shape, logical

               CSize m_szSizeOffset; // offset from top left to capture point, device

               CSize m_szbRect;      // logical size of bounding rect

               BOOL m_bCaptured; // mouse capture

               CCircle * m_pC;

 

 

The modified WM_LBUTTONDOWN, WM_MOUSEMOVE and WM_LBUTTONUP handlers are shown below.

void CMdidraw3View::OnLButtonDown(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

               CClientDC dc(this);

               OnPrepareDC(&dc);  //  important for CScrollView

               CPoint pos_mouse_logical(point);

               dc.DPtoLP(&pos_mouse_logical); // mouse pos in logical coord.

               if (m_SelectedShapeOnToolbar == "CIRCLE") {

                              m_x1 = pos_mouse_logical.x;  // all shape data is stored in

                              m_y1 = pos_mouse_logical.y;  // in logical coordinates

                              m_x2 = pos_mouse_logical.x+1;

                              m_y2 = pos_mouse_logical.y+1;

                              CMdidraw3Doc * pDoc = GetDocument();

                              CCircle * pCircle = new CCircle(m_x1,m_y1,m_x2,m_y2);

                              pDoc->m_setofCircles.AddTail(pCircle);

                              m_track = 1;

               }

               if (m_SelectedShapeOnToolbar == "ARROW") // selection mode

               {

                              m_Color = WHITE_BRUSH;

                              OnDraw(&dc);  // direct call, clear old selections

                                                                             // InvalidateRect(NULL) will not work

                                                                             // because of delay in message posting

 

                              // cycle through all objects to see if mouse pressed

                              // point is in one of the shapes that have been drawn

                              CMdidraw3Doc* pDoc = GetDocument();

                              ASSERT_VALID(pDoc);

                              POSITION pos = pDoc->m_setofCircles.GetHeadPosition();

                              CCircle * pC;

                              CRect rect_shape;

                              while (pos) {

                                             pC=pDoc->m_setofCircles.GetNext(pos);

                                             rect_shape.top = pC->get_y1();

                                             rect_shape.left = pC->get_x1();

                                             rect_shape.bottom = pC->get_y2();

                                             rect_shape.right = pC->get_x2();

                                             m_szbRect.cx = rect_shape.Width();

                                             m_szbRect.cy = rect_shape.Height();

                                             m_ptTopLeft.x = pC->get_x1();

                                             m_ptTopLeft.y = pC->get_y1();

                                             m_pC = pC;

 

                                             dc.LPtoDP(rect_shape); // now in device coord.

                                             CRgn creg;

                                             creg.CreateEllipticRgnIndirect(rect_shape);

 

                                             if (creg.PtInRegion(point)) // mouse click is inside rectangle

                                             {

                                                            SetCapture(); // mouse messages to sent to this window

                                                            m_bCaptured = TRUE;

                                                            CPoint ptTopLeft(m_ptTopLeft); // logical units

                                                            dc.LPtoDP(&ptTopLeft);

                                                            m_szSizeOffset = point - ptTopLeft; // device units

                                                            ::SetCursor(::LoadCursor(NULL,IDC_CROSS));

                                                            m_Color = GRAY_BRUSH;

                                                            InvalidateRect(rect_shape,TRUE);

                                                            break;

                                             }

                              }

               }

               CView::OnLButtonDown(nFlags, point);

}

 

void CMdidraw3View::OnLButtonUp(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

               if (m_bCaptured) {

                              ::ReleaseCapture();

                              m_bCaptured = FALSE;

               }

               if (m_SelectedShapeOnToolbar != "ARROW") {

                              m_track = 0;

                              CMdidraw3Doc * pDoc = GetDocument();

                              pDoc->SetModifiedFlag(TRUE);

                              pDoc->UpdateAllViews(this);

                              InvalidateRect(NULL); // draw this view

               }

               CView::OnLButtonUp(nFlags, point);

}

 

void CMdidraw3View::OnMouseMove(UINT nFlags, CPoint point)

{

               // TODO: Add your message handler code here and/or call default

               CClientDC dc(this);

               OnPrepareDC(&dc);

               CMdidraw3Doc * pDoc = GetDocument();

 

               if (m_bCaptured) {                            // true when moving a shape

                              CRect rectold(m_ptTopLeft,m_szbRect);

                              dc.LPtoDP(rectold); // before the move

                              InvalidateRect(rectold,TRUE);  // erase old shape

                                               // Invalidate rects are always in device units

                              m_ptTopLeft = point - m_szSizeOffset;

                              dc.DPtoLP(&m_ptTopLeft);

                              CRect rectnew(m_ptTopLeft,m_szbRect);

                              m_pC->set(rectnew.left,rectnew.top,rectnew.right,rectnew.bottom);

                              dc.LPtoDP(rectnew);

                              InvalidateRect(rectnew,TRUE); // draw new shape

 

                              pDoc->SetModifiedFlag(TRUE);

                              pDoc->UpdateAllViews(this);

               }

               if (m_track == 1) // when drawing a new shape

               {

                              CRect Rectold(m_x1,m_y1, m_x2, m_y2);

                              CPoint p2(point);

                              dc.DPtoLP(&p2);    // now mouse pos. in logical coordinates

                              m_x2 = p2.x; m_y2 = p2.y;

                              CRect Rectnew(m_x1,m_y1, m_x2,m_y2);

 

                              CCircle * pC = pDoc->m_setofCircles.GetTail();

                              pC->set(m_x1,m_y1,m_x2, m_y2);

                              dc.LPtoDP(Rectold);

                              dc.LPtoDP(Rectnew);

                              InvalidateRect(Rectold,TRUE);

                              InvalidateRect(Rectnew,TRUE);

               }            

               CView::OnMouseMove(nFlags, point);

}

 

The constructor for the view class will look as:

CMdidraw3View::CMdidraw3View()

{

               // TODO: add construction code here

               m_SelectedShapeOnToolbar = "CIRCLE";

               m_Color = WHITE_BRUSH;

               m_bCaptured = FALSE;

}

 

The OnDraw will be as shown below:

void CMdidraw3View::OnDraw(CDC* pDC)

{

               CMdidraw3Doc* pDoc = GetDocument();

               ASSERT_VALID(pDoc);

               // TODO: add draw code for native data here

               pDC->SelectStockObject(m_Color);

               POSITION pos = pDoc->m_setofCircles.GetHeadPosition();

               CCircle * pC;

               while (pos) {

                              pC=pDoc->m_setofCircles.GetNext(pos);

                              pC->Draw(pDC);

               }

               m_Color = WHITE_BRUSH;  // reset default color

}

Build and test the application. You should be able to move shapes by pressing the mouse and dragging it.

 

Providing Zooming:

The important concept in zooming (as well as scrolling) is that there is a virtual drawing surface referred to as the “window”. We can view a portion of this surface through a “viewport”. The ratio of “window extent” to “viewport extent” provides the zooming factor if the mapping mode is set to MM_ISOTROPIC or MM_ANISOTROPIC. There are member functions in the device context class that allow us to set the viewport extent and the window extent e.g.,

               PDC->SetMapMode(MM_ISOTROPIC);

               pDC->SetWindowExtent(1000,1000);

               pDC->SetViewportExtent(200,200);   // now 1000 logical units correspond to 200 pixels

               ….

               PDC->MoveTo(0,-100);

               PDC->LineTo(100,-100);  // this line will be 20 pixels long

               PDC->SetViewportExtent(400,400);

            PDC->MoveTo(0,-100);

               PDC->LineTo(100,-100);  // Now this line will be 40 pixels long

 

Note that window in general refers to logical units and viewport refers to device units. MM_ISOTROPIC performs a uniform scaling based on the ratio of the window extent to viewport extent whereas MM_ANISOTRPIC allows us to pick a different scaling for the X-axis and the Y-axis.

 

We will modify the mdidraw3 application to incorporate zooming in it. Add a data member to the view class header file:

float m_zoomFactor;

 

Add another line to the view class constructor to initialize the zoom factor as shown below:

            m_zoomFactor = 1.0;

 

Modify the IDR_MDIDRATYPE menu resource to add a top level menu item called “Zoom”. Add three menu items under it call “Zoom in” (ID_ZOOM_IN), “Zoom out” (ID_ZOOM_OUT), and “No Zoom” (ID_ZOOM_ONE). The menu handlers for the COMMAND and UPDATE_COMMAND_UI events for these menus are shown below.

void CMdidraw3View::OnZoomIn()

{

               // TODO: Add your command handler code here

               if (m_zoomFactor < 3.0) {

                              m_zoomFactor = m_zoomFactor * 1.25; // 25 % zooming

                              Invalidate();

               }

}

 

void CMdidraw3View::OnZoomOut()

{

               // TODO: Add your command handler code here

               if (m_zoomFactor > 0.5) {

                              m_zoomFactor = m_zoomFactor / 1.25; // 25 % reduction in zooming

                              Invalidate();

               }

}

 

void CMdidraw3View::OnUpdateZoomIn(CCmdUI* pCmdUI)

{

               // TODO: Add your command update UI handler code here

               if (m_zoomFactor >= 3.0)

                              pCmdUI->Enable(FALSE);

               else

                              pCmdUI->Enable(TRUE);

}

 

void CMdidraw3View::OnUpdateZoomOut(CCmdUI* pCmdUI)

{

               // TODO: Add your command update UI handler code here

               if (m_zoomFactor < 0.5)

                              pCmdUI->Enable(FALSE);

               else

                              pCmdUI->Enable(TRUE);

}

void CMdidraw3View::OnZoomOne()

{

               // TODO: Add your command handler code here

               m_zoomFactor = 1.0;       

               Invalidate();

}

Through the class wizard, add the OnPrepareDC function to the view class. Type the following code in it.

void CMdidraw3View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)

{

               // TODO: Add your specialized code here and/or call the base class

               pDC->SetMapMode(MM_ISOTROPIC);

               float zoomFactor = m_zoomFactor;

               if (pDC->IsPrinting())

                              zoomFactor = 1; // print without zooming

               CSize logsize; // width,height of display in millimeters

               logsize.cx=10*pDC->GetDeviceCaps(HORZSIZE);

               logsize.cy = 10*pDC->GetDeviceCaps(VERTSIZE);;

               pDC->SetWindowExt(logsize);

               CSize devsize; // width, height of display in pixels

               devsize.cx=zoomFactor*pDC->GetDeviceCaps(HORZRES);

               devsize.cy = zoomFactor*pDC->GetDeviceCaps(VERTRES);;

               pDC->SetViewportExt(devsize);

              

               CView::OnPrepareDC(pDC, pInfo);

}

 

Build and test the application. You should be able to zoom in and out.

 

Providing Scrolling View:

MFC provides a CScrollView class that takes care of the primary scrolling tasks such as creating the virtual window and mapping it to a viewport, changing the viewport origin when trying to scroll etc..

Create a new MDI project called mdidraw4. When going through the application wizard steps, choose the base class for view to be CScrollView instead of the default CVew.

CScrollView does not support zooming, so we will not add any zooming code. Majority of the code here will be same as the mdidraw3 application (except for no zooming related handlers). The OnInitialUpdate for the view class will now look as:

void CMdidraw4View::OnInitialUpdate()

{

               CScrollView::OnInitialUpdate();

 

               CSize sizeTotal;

               // TODO: calculate the total size of this view

               m_SelectedShapeOnToolbar = "ARROW";

               CSize sztotal(20000,30000); // total virtual size

               CSize szpage(sztotal.cx/4, sztotal.cy/4);

               CSize szline(sztotal.cx/200, sztotal.cy/200);

               SetScrollSizes(MM_HIMETRIC,sztotal,szpage,szline);

//             InvalidateRect(NULL);

}

 

Rest of the code in this application is same as mdidraw3.

 

Build and test the application.