Home

The Document/View Architecture: Introduction to Views

 

The Fundamentals of a View

When you create an application, you usually intend for the users to see, view, and possibly manipulate its documents. The basic process of making up a program is to create the application using a class derived from CWinApp and surrounding the program with a frame either based on CFrameWnd or from one of its derived classes. Such a basic program is mainly meant to show the “presence” of an application. To truly assist the user with files, the program should be equipped with a document. A document is not truly something a user can see. It is a representation of a file. To actually present a document to the user, you should provide an object called a view.

 

Introduction to the View Classes

Different applications have various ways to present their documents. To support these variances, the MFC provides different view-based classes. To lay a foundation for those classes, the MFC provides a class named CView. Although the MFC provides various view-based classes, it is likely that none of them would fit the particular functionality you want to implement. For this reason, the CView class accomplishes two purposes. Not only is it the ancestor to the other view-based classes, it is still a view in its own right and you can use it to create an application that none of the other classes can handle. For these two and other reasons, you should be quite familiar with the CView class.

Creation of a CView-Based Application

CView is an abstract class. In other words, you cannot just declare a CView variable. For example, the following code produces an error:

CView *view = new CView;

Therefore, to use CView, you must derive a class from it. What makes CView a pure abstract class is that one of its methods is declared pure. When deriving your class from CView, you must override a method named OnDraw. The syntax of the CView::OnDraw() method is:

virtual void OnDraw(CDC* pDC) = 0;

Notice that it is declared pure (= 0).

Practical Learning Practical Learning: Introducing View-Based Applications

  1. If necessary, start Microsoft Visual C++. To create a new program, on the main menu, click File -> New Project…
  2. In the Templates list, click MFC Application
  3. Set the Name to GCS1 and click OK
  4. In the first page of the MFC Application Wizard, click Next
  5. In the right list, click Single Document and click Next twice
  6. In the File Extension edit box, type gcs
  7. In the Main Frame Caption text box, type Georgetown Cleaning Services
     
  8. Click Next three times
  9. Set the Number of Files On Recent File List to 8 and click Next
  10. Click Finish
  11. Click Class View and click CMainFrame
  12. Below the list of classes, double-click PreCreateWindow and make the following changes:
     
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
    		return -1;
    
    	. . .
    
    	// TODO: Delete these three lines if 
    	// you don't want the toolbar to be dockable
    	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    	EnableDocking(CBRS_ALIGN_ANY);
    	DockControlBar(&m_wndToolBar);
    
    	CenterWindow();
    
    	return 0;
    }
    
    BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
    	if( !CFrameWnd::PreCreateWindow(cs) )
    		return FALSE;
    	// TODO: Modify the Window class or styles here by modifying
    	// the CREATESTRUCT cs
    	cs.cx = 550;
    	cs.cy = 630;
    	cs.style &= ~FWS_ADDTOTITLE;
    
    	return TRUE;
    }
  13. On the main menu, click Project -> Add Resource…
  14. In the Add Resource dialog box, click Dialog and click New
  15. Using the Properties window, change the ID of the dialog box to IDB_CLEANINGORDER_DLG
  16. Right-click the dialog box and click Add Class…
  17. Set the Class Name to CCleaningOrderDlg
  18. Click Finish
     
  19. Design the dialog box as follows:
     
    Control ID Caption Additional Properties
    Group Box Group Box   Order Identification  
    Static Text Static Text   Customer Name:  
    Edit Control Edit Control IDC_CUSTOMERNAME    
    Static Text Static Text   Customer Phone:  
    Edit Control Edit Control IDC_CUSTOMERPHONE    
    Static Text Static Text   Date Left:  
    Date Time Picker Date Time Picker IDC_DATELEFT    
    Static Text Static Text   Time Left:  
    Date Time Picker Date Time Picker IDC_TIMELEFT    
    Static Text Static Text   Date Expected:  
    Date Time Picker Date Time Picker IDC_DATEEXPECTED    
    Static Text Static Text   Time Expected:  
    Date Time Picker Date Time Picker IDC_TIMEEXPECTED    
    Group Box   Order Processing  
    Static Text Static Text   Item Type  
    Static Text Static Text   Unit Price  
    Static Text Static Text   Qty  
    Static Text Static Text   Sub Total  
    Static Text Static Text   Shirts  
    Edit Control Edit Control IDC_SHIRTS_UNITPRICE   Align Text: Right
    Edit Control Edit Control IDC_SHIRTS_QTY   Align Text: Right
    Number: True
    Edit Control Edit Control IDC_SHIRTS_SUBTOTAL   Align Text: Right
    Static Text Static Text   Pants  
    Edit Control Edit Control IDC_PANTS_UNITPRICE   Align Text: Right
    Edit Control Edit Control IDC_PANTS_QTY   Align Text: Right
    Number: True
    Edit Control Edit Control IDC_PANTS_SUBTOTAL   Align Text: Right
    ComboBox ComboBox IDC_ITEM1NAME   Data: None;Women Suit;Dress;Regular Skirt;Skirt With Hook;Men 's Suit 2Pc;Men 's Suit 3Pc;Sweaters;Silk Shirt;Tie;Coat;Jacket;Swede;
    Edit Control Edit Control IDC_ITEM1UNITPRICE   Align Text: Right
    Edit Control Edit Control IDC_ITEM1QUANTITY   Align Text: Right
    Number: True
    Edit Control Edit Control IDC_ITEM1SUBTOTAL   Align Text: Right
    ComboBox ComboBox IDC_ITEM2NAME   Data: None;Women Suit;Dress;Regular Skirt;Skirt With Hook;Men 's Suit 2Pc;Men 's Suit 3Pc;Sweaters;Silk Shirt;Tie;Coat;Jacket;Swede;
    Edit Control Edit Control IDC_ITEM2UNITPRICE   Align Text: Right
    Edit Control Edit Control IDC_ITEM2QUANTITY   Align Text: Right
    Number: True
    Edit Control Edit Control IDC_ITEM2SUBTOTAL   Align Text: Right
    ComboBox ComboBox IDC_ITEM3NAME   Data: None;Women Suit;Dress;Regular Skirt;Skirt With Hook;Men 's Suit 2Pc;Men 's Suit 3Pc;Sweaters;Silk Shirt;Tie;Coat;Jacket;Swede;
    Edit Control Edit Control IDC_ITEM3UNITPRICE   Align Text: Right
    Edit Control Edit Control IDC_ITEM3QUANTITY   Align Text: Right
    Number: True
    Edit Control Edit Control IDC_SUBTOTAL3   Align Text: Right
    ComboBox ComboBox IDC_ITEM4NAME   Data: None;Women Suit;Dress;Regular Skirt;Skirt With Hook;Men 's Suit 2Pc;Men 's Suit 3Pc;Sweaters;Silk Shirt;Tie;Coat;Jacket;Swede;
    Edit Control Edit Control IDC_ITEM4UNITPRICE   Align Text: Right
    Edit Control Edit Control IDC_ITEM4QUANTITY   Align Text: Right
    Number: True
    Edit Control Edit Control IDC_ITEM4SUBTOTAL   Align Text: Right
    Group Box GroupBox   Order Summary  
    Static Text Static Text   Cleaning Total:  
    Edit Control Edit Control IDC_CLEANINGTOTAL   Align Text: Right
    Static Text Static Text   Tax Rate:  
    Edit Control Edit Control IDC_TAXRATE   Align Text: Right
    Static Text Static Text   %  
    Static Text Static Text   Tax Amount:  
    Edit Control Edit Control IDC_TAXAMOUNT   Align Text: Right
    Static Text Static Text   Order Price:  
    Edit Control Edit Control IDC_ORDERTOTAL   Align Text: Right
    Button IDOK OK/Submit  
    Button IDC_RESET Reset  
    Button IDCCANCEL Cancel/Close  
  20. Right-click each control and click Add Variable
  21. Add the control variables as follows:
     
    Identifier Value Variable
    Type Name
    IDC_CUSTOMERNAME CString m_CustomerName
    IDC_CUSTOMERPHONE CString m_CustomerPhone
    IDC_DATELEFT CTime m_DateLeft
    IDC_TIMELEFT CTime m_TimeLeft
    IDC_DATEEXPECTED CTime m_DateExpected
    IDC_TIMEEXPECTED CTime m_TimeExpected
    IDC_SHIRTS_UNITPRICE double m_ShirtsUnitPrice
    IDC_SHIRTS_QTY int m_ShirtsQuantity
    IDC_SHIRTS_SUBTOTAL CString m_ShirtsSubTotal
    IDC_PANTS_UNITPRICE double m_PantsUnitPrice
    IDC_PANTS_QTY int m_PantsQuantity
    IDC_PANTS_SUBTOTAL CString m_PantsSubTotal
    IDC_ITEM1NAME CString m_Item1Name
    IDC_ITEM1UNITPRICE double m_Item1UnitPrice1
    IDC_ITEM1QUANTITY int m_Item1Quantity1
    IDC_ITEM1SUBTOTAL CString m_Item1SubTotal1
    IDC_ITEM2NAME CString m_Item2Name
    IDC_ITEM2UNITPRICE double m_Item2UnitPrice
    IDC_ITEM2QUANTITY int m_Item2Quantity
    IDC_ITEM2SUBTOTAL CString m_Item2SubTotal
    IDC_ITEM3NAME CString m_Item3Name
    IDC_ITEM3UNITPRICE double m_Item3UnitPrice
    IDC_ITEM3QUANTITY int m_Item3Quantity
    IDC_ITEM3SUBTOTAL CString m_Item3SubTotal
    IDC_ITEM4NAME CString m_Item4Name
    IDC_ITEM4UNITPRICE double m_Item4UnitPrice
    IDC_ITEM4QUANTITY int m_Item4Quantity
    IDC_ITEM4SUBTOTAL CString m_Item4SubTotal
    IDC_CLEANINGTOTAL CString m_CleaningTotal
    IDC_TAXRATE double m_TaxRate
    IDC_TAXAMOUNT CString m_TaxAmount
    IDC_ORDERTOTAL CString m_OrderTotal
  22. In the Class View, double-click CCleaningOrderDlg to access the dialog box’ header file and declare a CString variable for each Date Time Picker control as follows:
     
    #pragma once
    
    
    // CCleaningOrderDlg dialog
    
    class CCleaningOrderDlg : public CDialog
    {
    	DECLARE_DYNAMIC(CCleaningOrderDlg)
    
    public:
    	CCleaningOrderDlg(CWnd* pParent = NULL); // standard constructor
    	virtual ~CCleaningOrderDlg();
    
    	// Dialog Data
    	enum { IDD = IDB_CLEANINGORDER_DLG };
    
    protected:
    	virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
    
    	DECLARE_MESSAGE_MAP()
    
    	CString m_CustomerName;
    	CString m_CustomerPhone;
    
    	CTime m_DateLeft;
    	CTime m_TimeLeft;
    	CTime m_DateExpected;
    	CTime m_TimeExpected;
    
    	CString strDateLeft;
    	CString strTimeLeft;
    	CString strDateExpected;
    	CString strTimeExpected;
    
    	double m_ShirtsUnitPrice;
    	int m_ShirtsQuantity;
    	CString m_ShirtsSubTotal;
    	double m_PantsUnitPrice;
    
    	int m_PantsQuantity;
    	CString m_PantsSubTotal;
    	CString m_Item1Name;
    
    	double m_Item1UnitPrice;
    	int m_Item1Quantity;
    	CString m_Item1SubTotal;
    
    	CString m_Item2Name;
    	double m_Item2UnitPrice;
    	int m_Item2Quantity;
    	CString m_Item2SubTotal;
    
    	CString m_Item3Name;
    	double m_Item3UnitPrice;
    	int m_Item3Quantity;
    	CString m_Item3SubTotal;
    
    	CString m_Item4Name;
    	double m_Item4UnitPrice;
    	int m_Item4Quantity;
    	CString m_Item4SubTotal;
    
    	CString m_CleaningTotal;
    	double m_TaxRate;
    	CString m_TaxAmount;
    	CString m_OrderTotal;
    };
  23. In the Class View, click CCleaningOrderDlg and, below the list of classes, double-click CCleacningOderDlg to access its constructor and change it as follows:
     
    CCleaningOrderDlg::CCleaningOrderDlg(CWnd* pParent /*=NULL*/)
    	: CDialog(CCleaningOrderDlg::IDD, pParent)
    	, m_CustomerName(_T(""))
    	, m_CustomerPhone(_T(""))
    	, m_DateLeft(CTime::GetCurrentTime())
    	, m_TimeLeft(CTime::GetCurrentTime())
    	, m_DateExpected(CTime::GetCurrentTime())
    	, m_TimeExpected(CTime::GetCurrentTime())
    	, m_ShirtsUnitPrice(0.95)
    	, m_ShirtsQuantity(0)
    	, m_ShirtsSubTotal(_T("0.00"))
    	, m_PantsUnitPrice(1.75)
    	, m_PantsQuantity(0)
    	, m_PantsSubTotal(_T("0.00"))
    	, m_Item1Name(_T(""))
    	, m_Item1UnitPrice(0)
    	, m_Item1Quantity(0)
    	, m_Item1SubTotal(_T("0.00"))
    	, m_Item2Name(_T(""))
    	, m_Item2UnitPrice(0)
    	, m_Item2Quantity(0)
    	, m_Item2SubTotal(_T("0.00"))
    	, m_Item3Name(_T(""))
    	, m_Item3UnitPrice(0)
    	, m_Item3Quantity(0)
    	, m_Item3SubTotal(_T("0.00"))
    	, m_Item4Name(_T(""))
    	, m_Item4UnitPrice(0)
    	, m_Item4Quantity(0)
    	, m_Item4SubTotal(_T("0.00"))
    	, m_CleaningTotal(_T("0.00"))
    	, m_TaxRate(7.75)
    	, m_TaxAmount(_T("0.00"))
    	, m_OrderTotal(_T("0.00"))
    {
    
    }
  24. In the Class View, click CCleaningOrderDlg
  25. In the Properties window, click the Overrides button and generate the OnInitDialog event
  26. Add the following code to it:
     
    BOOL CCleaningOrderDlg::OnInitDialog()
    {
    	CDialog::OnInitDialog();
    
    	// TODO: Add extra initialization here
    	m_DateLeft.Format     = TEXT("dddd dd MMM yyyy");
    	m_DateExpected.Format = TEXT("dddd dd MMM yyyy");
    
    	return TRUE; // return TRUE unless you set the focus to a control
    	// EXCEPTION: OCX Property Pages should return FALSE
    }
  27. Display the dialog box and double-click the Reset Cleaning Order button to implement it as follows:
     
    void CCleaningOrderDlg::OnBnClickedReset()
    {
    	// TODO: Add your control notification handler code here
    	m_CustomerName    = _T("");
    	m_CustomerPhone   = _T("");
    	m_DateLeft        = CTime::GetCurrentTime();
    	m_TimeLeft        = CTime::GetCurrentTime();
    	m_DateExpected    = CTime::GetCurrentTime();
    	m_TimeExpected    = CTime::GetCurrentTime();
    	m_ShirtsUnitPrice = 0.95;
    	m_ShirtsQuantity  = 0;
    	m_ShirtsSubTotal  = _T("0.00");
    	m_PantsUnitPrice  = 1.75;
    	m_PantsQuantity   = 0;
    	m_PantsSubTotal   = _T("0.00");
    	m_Item1Name       = _T("");
    	m_Item1UnitPrice  = 0;
    	m_Item1Quantity   = 0;
    	m_Item1SubTotal   = _T("0.00");
    	m_Item2Name       = _T("");
    	m_Item2UnitPrice  = 0;
    	m_Item2Quantity   = 0;
    	m_Item2SubTotal   = _T("0.00");
    	m_Item3Name       = _T("");
    	m_Item3UnitPrice  = 0;
    	m_Item3Quantity   = 0;
    	m_Item3SubTotal   = _T("0.00");
    	m_Item4Name       = _T("");
    	m_Item4UnitPrice  = 0;
    	m_Item4Quantity   = 0;
    	m_Item4SubTotal   = _T("0.00");
    	m_CleaningTotal   = _T("0.00");
    	m_TaxRate         = 7.75;
    	m_TaxAmount       = _T("0.00");
    	m_OrderTotal = _T("0.00");
    
    	UpdateData(FALSE);
    }
  28. In the Class View, right-click CCleaningOrderDlg -> Add -> Add Function…
  29. Set the Return Type to void, the Function Name to CalculateOrder, and and the Access to private
     
  30. Click Finish and implement the method as follows:
     
    void CCleaningOrderDlg::CalculateOrder(void)
    {
    	UpdateData();
    
    	double subTotalShirts = m_ShirtsUnitPrice * m_ShirtsQuantity;
    	m_ShirtsSubTotal.Format(TEXT("%.2f"), subTotalShirts);
    
    	double subTotalPants = m_PantsUnitPrice * m_PantsQuantity;
    	m_PantsSubTotal.Format(TEXT("%.2f"), subTotalPants);
    
    	double subTotalItem1;
    	if( (m_Item1Name == TEXT("None")) || (m_Item1Name == TEXT("")) )
    		subTotalItem1 = 0;
    	else
    		subTotalItem1 = m_Item1UnitPrice * m_Item1Quantity;
    
    	m_Item1SubTotal.Format(TEXT("%.2f"), subTotalItem1);
    
    	double subTotalItem2;
    	if( (m_Item2Name == TEXT("None")) || (m_Item2Name == TEXT("")) )
    		subTotalItem2 = 0;
    	else
    		subTotalItem2 = m_Item2UnitPrice * m_Item2Quantity;
    
    	m_Item2SubTotal.Format(TEXT("%.2f"), subTotalItem2);
    
    	double subTotalItem3;
    	if( (m_Item3Name == TEXT("None")) || (m_Item3Name == TEXT("")) )
    		subTotalItem3 = 0;
    	else
    		subTotalItem3 = m_Item3UnitPrice * m_Item3Quantity;
    
    	this->m_Item3SubTotal.Format(TEXT("%.2f"), subTotalItem3);
    
    	double subTotalItem4;
    	if( (m_Item4Name == TEXT("None")) || (m_Item4Name == TEXT("")) )
    		subTotalItem4 = 0;
    	else
    		subTotalItem4 = m_Item4UnitPrice * m_Item4Quantity;
    
    	m_Item4SubTotal.Format(TEXT("%.2f"), subTotalItem4);
    
    double cleaningTotal = subTotalShirts + subTotalPants + subTotalItem1 + 
    		subTotalItem2 + subTotalItem3 + subTotalItem4;
    
    	// Calculate the amount owed for the taxes
    	double taxAmount = cleaningTotal * m_TaxRate / 100;
    	// Add the tax amount to the cleaning total
    	double netPrice = cleaningTotal + taxAmount;
    
    	// Display the values of the order summary
    	this->m_CleaningTotal.Format(TEXT("%.2f"), cleaningTotal);
    	this->m_TaxAmount.Format(TEXT("%.2f"), taxAmount);
    	this->m_OrderTotal.Format(TEXT("%.2f"), netPrice);
    
    	strDateLeft = m_DateLeft.Format(TEXT("%A %d %b %Y"));
    	strTimeLeft = m_TimeLeft.Format(TEXT("%H:%M"));
    	strDateExpected = m_DateExpected.Format(TEXT("%A %d %b %Y"));
    	strTimeExpected = m_TimeExpected.Format(TEXT("%H:%M"));
    
    	UpdateData(FALSE);
    }
  31. Display the dialog box and double-click the Time Left control
  32. Implement its Chantge event as follows:
     
    void CCleaningOrderDlg::OnDtnDatetimechangeTimeleft(NMHDR *pNMHDR, 
    			LRESULT *pResult)
    {
        LPNMDATETIMECHANGE pDTChange = 
    		reinterpret_cast<LPNMDATETIMECHANGE>(pNMHDR);
        // TODO: Add your control notification handler code here
        *pResult = 0;
    
        UpdateData();
        CTime dateLeft, timeLeft;
    
        dateLeft = m_DateLeft;
        timeLeft = m_TimeLeft;
    
        CTime time9AM = CTime(timeLeft.GetYear(), timeLeft.GetMonth(), 
    				imeLeft.GetDay(), 9, 0, 0);
    
        // If the customer leaves clothes before 9AM...
        if( timeLeft <= time9AM )
        {
    	// ... then they should be ready the same day after 5PM
    	m_DateExpected = dateLeft;
    	CTime tm5PM = CTime(dateLeft.GetYear(), 
    			dateLeft.GetMonth(), dateLeft.GetDay(), 17, 0, 0);
    	m_TimeExpected = tm5PM;
        }
        else
        {
    	// If the clothes were left after 9AM, they will be 
    	// available the following day by 8AM
    	CTime tmTomorrowMorning = CTime(dateLeft.GetYear(), 
    	dateLeft.GetMonth(), dateLeft.GetDay() + 1, 8, 0, 0);
    	m_DateExpected = tmTomorrowMorning;
    	m_TimeExpected = tmTomorrowMorning;
        }
    
        UpdateData(FALSE);
    }
  33. Display the dialog box and click the Shirts Qty edit control
  34. In the Properties window, click the Contrl Events button, and generate an event for the EN_KILLFOCUS message
  35. Return to the dialog box and generate an event for the EN_KILLFOCUS message of all Qty edit controls
  36. Also, generate an event for the EN_KILLFOCUS message of the Tax Rate edit control
  37. In each event, simply call the CalculateOrder method:
     
    void CCleaningOrderDlg::OnEnKillfocusShirtsQty()
    {
    	// TODO: Add your control notification handler code here
    	CalculateOrder();
    }
    
    void CCleaningOrderDlg::OnEnKillfocusPantsQty()
    {
    	// TODO: Add your control notification handler code here
    	CalculateOrder();
    }
    
    void CCleaningOrderDlg::OnEnKillfocusItem1quantity()
    {
    	// TODO: Add your control notification handler code here
    	CalculateOrder();
    }
    
    void CCleaningOrderDlg::OnEnKillfocusItem2quantity()
    {
    	// TODO: Add your control notification handler code here
    	CalculateOrder();
    }
    
    void CCleaningOrderDlg::OnEnKillfocusItem3quantity()
    {
    	// TODO: Add your control notification handler code here
    	CalculateOrder();
    }
    
    void CCleaningOrderDlg::OnEnKillfocusItem4quantity()
    {
    	// TODO: Add your control notification handler code here
    	CalculateOrder();
    }
    
    void CCleaningOrderDlg::OnEnKillfocusTaxrate()
    {
    	// TODO: Add your control notification handler code here
    	CalculateOrder();
    }
  38. Save all

Fundamental Implementation of a View

If you don't want to write all of your primary code manually, you can use the MFC Application wizard that would create a skeleton functional application for you: it would derive a class from CView, would override the OnDraw() event, and would perform many cosmetic initializations for you.

The basic view displays as a rectangular box with a white background delimited on all four sides by a frame, sometimes assisted by its resources such as a toolbar and/or a status bar. What you can do with this white area can be as vague or complex as you want.

When an application is launched, it first draws the frame and (some of) its resources, if any. Then, a document is invoked to prepare what to display to the user. This document is transmitted to the view that is in charge of displaying or drawing it. Before the view actually “shows” the document, you may want to prepare it. To assist you with this, the CView class is equipped with the OnInitialUpdate() method (or event). Its syntax is:

virtual void OnInitialUpdate();

As you can see, this method takes no argument. For most CView-based applications, you will hardly need to implement this event. In fact, if you create your CView-based application using the MFC Application wizard, it would not generate the OnInitialUpdate event for you. This is because it assumes that you are likely not going to need this event.

The OnInitialUpdate event is mostly useful to the CView-derived classes as we will see in other sections.

Practical Learning Practical Learning: Implementing a View

  1. In the Class View, double-click the view node and declare the following variables in the header file:
     
    // GCS1View.h : interface of the CGCS1View class
    //
    
    #pragma once
    
    class CGCS1View : public CView
    {
    protected: // create from serialization only
    	CGCS1View();
    	DECLARE_DYNCREATE(CGCS1View)
    
    // Attributes
    public:
    	CGCS1Doc* GetDocument() const;
    
    // Operations
    public:
    
    // Overrides
    public:
    	virtual void OnDraw(CDC* pDC); // overridden to draw this view
    	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    protected:
    	virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
    	virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
    	virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
    
    // Implementation
    public:
    	virtual ~CGCS1View();
    #ifdef _DEBUG
    	virtual void AssertValid() const;
    	virtual void Dump(CDumpContext& dc) const;
    #endif
    
    protected:
    
    // Generated message map functions
    protected:
    	DECLARE_MESSAGE_MAP()
    
    private:
    	CString CustomerName;
    	CString CustomerPhone;
    
    	CString DateLeft;
    	CString TimeLeft;
    	CString DateExpected;
    	CString strTimeExpected;
    
    	CString ShirtsUnitPrice;
    	CString ShirtsQuantity;
    	CString ShirtsSubTotal;
    	CString PantsUnitPrice;
    
    	CString PantsQuantity;
    	CString PantsSubTotal;
    	CString Item1Name;
    
    	CString Item1UnitPrice;
    	CString Item1Quantity;
    	CString Item1SubTotal;
    
    	CString Item2Name;
    	CString Item2UnitPrice;
    	CString Item2Quantity;
    	CString Item2SubTotal;
    
    	CString Item3Name;
    	CString Item3UnitPrice;
    	CString Item3Quantity;
    	CString Item3SubTotal;
    
    	CString Item4Name;
    	CString Item4UnitPrice;
    	CString Item4Quantity;
    	CString Item4SubTotal;
    
    	CString CleaningTotal;
    	CString TaxRate;
    	CString TaxAmount;
    	CString OrderTotal;
    };
    
    #ifndef _DEBUG // debug version in GCS1View.cpp
    inline CGCS1Doc* CGCS1View::GetDocument() const
    { return reinterpret_cast<CGCS1Doc*>(m_pDocument); }
    #endif
  2. Display the IDR_MAINFRAME menu
  3. Change the caption (but not the ID) of the first menu item under file to
    &New Cleaning Order\tCtrl+N
  4. Right-click this menu item and click Add Event Handler
  5. In the Event Handler Wizard, make sure hat COMMAND is selected in the Message Type list. In the Class List, click the view class
  6. Change the Function Handler Name to OnNewCleaningOrder
     
  7. Click Add and Edit
  8. In the top section of the file, include the header file of the Cleaning Order dialog box
     
    // GCS1View.cpp : implementation of the CGCS1View class
    //
    
    #include "stdafx.h"
    #include "GCS1.h"
    
    #include "GCS1Doc.h"
    #include "GCS1View.h"
    
    #include "CleaningOrderDlg.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
  9. Edit the event as follows:
     
    void CGCS1View::OnNewCleaningOrder()
    {
        // TODO: Add your command handler code here
        CCleaningOrderDlg dlg;
    
        dlg.m_DateLeft = CTime::GetCurrentTime();
        dlg.m_TimeLeft = CTime::GetCurrentTime();
        dlg.m_DateExpected = CTime::GetCurrentTime();
        dlg.m_TimeExpected = CTime::GetCurrentTime();
    
        if( dlg.DoModal() == IDOK )
        {
    	CustomerName = dlg.m_CustomerName;
    	CustomerPhone = dlg.m_CustomerPhone;
    	DateLeft = dlg.strDateLeft;
    	TimeLeft = dlg.strTimeLeft;
    	DateExpected = dlg.strDateExpected;
    	TimeExpected = dlg.strTimeExpected;
    	ShirtsUnitPrice.Format(TEXT("%.2f"), dlg.m_ShirtsUnitPrice);
    	ShirtsQuantity.Format(TEXT("%d"), dlg.m_ShirtsQuantity);
    	ShirtsSubTotal = dlg.m_ShirtsSubTotal;
    	PantsUnitPrice.Format(TEXT("%.2f"), dlg.m_PantsUnitPrice);
    	PantsQuantity.Format(TEXT("%d"), dlg.m_PantsQuantity);
    	PantsSubTotal = dlg.m_PantsSubTotal;
    
    	Item1Name = dlg.m_Item1Name;
    	Item1UnitPrice.Format(TEXT("%.2f"), dlg.m_Item1UnitPrice);
    	Item1Quantity.Format(TEXT("%d"), dlg.m_Item1Quantity);
    	Item1SubTotal = dlg.m_Item1SubTotal;
    
    	Item2Name = dlg.m_Item2Name;
    	Item2UnitPrice.Format(TEXT("%.2f"), dlg.m_Item2UnitPrice);
    	Item2Quantity.Format(TEXT("%d"), dlg.m_Item2Quantity);
    	Item2SubTotal = dlg.m_Item2SubTotal;
    
    	Item3Name = dlg.m_Item3Name;
    	Item3UnitPrice.Format(TEXT("%.2f"), dlg.m_Item3UnitPrice);
    	Item3Quantity.Format(TEXT("%d"), dlg.m_Item3Quantity);
    	Item3SubTotal = dlg.m_Item3SubTotal;
    
    	Item4Name = dlg.m_Item4Name;
    	Item4UnitPrice.Format(TEXT("%.2f"), dlg.m_Item4UnitPrice);
    	Item4Quantity.Format(TEXT("%d"), dlg.m_Item4Quantity);
    	Item4SubTotal = dlg.m_Item4SubTotal;
    
    	CleaningTotal = dlg.m_CleaningTotal;
    	TaxRate.Format(TEXT("%.2f"), dlg.m_TaxRate);
    	TaxAmount = dlg.m_TaxAmount;
    	OrderTotal = dlg.m_OrderTotal;
    
    	Invalidate();
        }
    }
  10. Save all

Drawing the View

Most of the time, a CView object is used to draw something on the document. To make this possible, the CView class is equipped with the OnDraw() and we mentioned that you must always override it.

Practical Learning Practical Learning: Drawing a View

  1. In the Class View, click CGCS1View and double-click OnDraw
  2. Change the event as follows:
     
    void CGCS1View::OnDraw(CDC* pDC)
    {
    	CGCS1Doc* pDoc = GetDocument();
    	ASSERT_VALID(pDoc);
    	if (!pDoc)
    		return;
    
    	// TODO: add draw code for native data here
    	CFont fntMainTitle, fntSubTitle, 
    	      fntSectTitle, fntLabel, fntResult;
    	int left = 15, top = 10;
    	TEXTMETRIC tm;
    	CPen penLine2, penLine1;
    	CBrush brsGray;
    
    	VERIFY(fntMainTitle.CreateFont(40, 0, 0, 0, FW_BOLD,
    				FALSE, FALSE, 0, ANSI_CHARSET,
    				OUT_DEFAULT_PRECIS,
    				CLIP_DEFAULT_PRECIS,
    				DEFAULT_QUALITY,
    				DEFAULT_PITCH | FF_SWISS,
    				TEXT("Times New Roman")));
    	CFont *fntDefault = pDC->SelectObject(&fntMainTitle);
    	pDC->GetTextMetrics(&tm);
    
    	pDC->SetTextColor(RGB(0, 0, 0));
    	pDC->SetBkMode(TRANSPARENT);
    	pDC->SetTextCharacterExtra(1);
    
    	pDC->TextOut(left, top, TEXT("Georgetown Cleaning Services"));
    
    	top = top + tm.tmHeight;
    
    	pDC->SetTextCharacterExtra(4);
    	VERIFY(fntSubTitle.CreateFont(32, 0, 0, 0, FW_BOLD,
    				FALSE, FALSE, 0, ANSI_CHARSET,
    				OUT_DEFAULT_PRECIS,
    				CLIP_DEFAULT_PRECIS,
    				DEFAULT_QUALITY,
    				DEFAULT_PITCH | FF_SWISS,
    				TEXT("Times New Roman")));
    	fntDefault = pDC->SelectObject(&fntSubTitle);
    	pDC->TextOut(140, top, TEXT("Cleaning Order"));
    
    	penLine2.CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
    	CPen *penOld = pDC->SelectObject(&penLine2);
    
    	pDC->MoveTo(10, tm.tmHeight + 50);
    	pDC->LineTo(515, tm.tmHeight + 50);
    
    	VERIFY(fntSectTitle.CreateFont(14, 0, 0, 0, FW_BOLD,
    				FALSE, FALSE, 0, ANSI_CHARSET,
    				OUT_DEFAULT_PRECIS,
    				CLIP_DEFAULT_PRECIS,
    				DEFAULT_QUALITY,
    				DEFAULT_PITCH | FF_SWISS,
    				TEXT("Verdana")));
    
    	penLine1.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
    	penOld = pDC->SelectObject(&penLine1);
    	pDC->Rectangle(20, 120, 520, 210);
    	pDC->Rectangle(20, 230, 520, 400);
    	pDC->Rectangle(20, 420, 520, 520);
    
    	brsGray.CreateSolidBrush(RGB(128, 128, 128));
    	CBrush *oldBrush = pDC->SelectObject(&brsGray);
    
    	pDC->Rectangle(30, 110, 270, 130);
    	pDC->Rectangle(30, 220, 270, 240);
    	pDC->Rectangle(30, 410, 270, 430);
    
    	pDC->SetTextCharacterExtra(0);
    	fntDefault = pDC->SelectObject(&fntSectTitle);
    	pDC->SetTextColor(RGB(255, 255, 255));
    	pDC->TextOut(40, 112, TEXT("Order Identification"));
    	pDC->TextOut(40, 222, TEXT("Order Processing"));
    	pDC->TextOut(40, 412, TEXT("Order Summary"));
    
    	pDC->SetTextColor(RGB(0, 0, 0));
    	VERIFY(fntLabel.CreateFont(16, 0, 0, 0, FW_BOLD,
    				FALSE, FALSE, 0, ANSI_CHARSET,
    				OUT_DEFAULT_PRECIS,
    				CLIP_DEFAULT_PRECIS,
    				DEFAULT_QUALITY,
    				DEFAULT_PITCH | FF_SWISS,
    				TEXT("Times New Roman")));
    	VERIFY(fntResult.CreateFont(16, 0, 0, 0, FW_NORMAL,
    				FALSE, FALSE, 0, ANSI_CHARSET,
    				OUT_DEFAULT_PRECIS,
    				CLIP_DEFAULT_PRECIS,
    				DEFAULT_QUALITY,
    				DEFAULT_PITCH | FF_SWISS,
    				TEXT("Times New Roman")));
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(40, 140, TEXT("Customer Name: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->TextOut(150, 140, this->CustomerName);
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(300, 140, TEXT("Customer Phone: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->TextOut(420, 140, this->CustomerPhone);
    
    	pDC->MoveTo(30, 155);
    	pDC->LineTo(510, 155);
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(40, 160, TEXT("Date Left: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->TextOut(150, 160, this->DateLeft);
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(300, 160, TEXT("Time Left: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->TextOut(420, 160, this->TimeLeft);
    
    	pDC->MoveTo(30, 175);
    	pDC->LineTo(510, 175);
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(40, 180, TEXT("Date Expected: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->TextOut(150, 180, this->DateExpected);
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(300, 180, TEXT("Time Expected: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->TextOut(420, 180, this->TimeExpected);
    
    	pDC->MoveTo(30, 195);
    	pDC->LineTo(510, 195);
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut( 40, 250, TEXT("Item Type"));
    	pDC->TextOut(320, 250, TEXT("Unit Price"));
    	pDC->TextOut(400, 250, TEXT("Qty"));
    	pDC->TextOut(445, 250, TEXT("Sub-Total"));
    
    	pDC->MoveTo( 30, 265);
    	pDC->LineTo(510, 265);
    
    	pDC->TextOut( 40, 270, TEXT("Shirts"));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(365, 270, ShirtsUnitPrice);
    	pDC->TextOut(420, 270, ShirtsQuantity);
    	pDC->TextOut(495, 270, ShirtsSubTotal);
    
    	pDC->MoveTo( 30, 285);
    	pDC->LineTo(510, 285);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut( 40, 290, TEXT("Pants"));
    	pDC->SetTextAlign(TA_RIGHT);
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->TextOut(365, 290, PantsUnitPrice);
    	pDC->TextOut(420, 290, PantsQuantity);
    	pDC->TextOut(495, 290, PantsSubTotal);
    
    	pDC->MoveTo( 30, 305);
    	pDC->LineTo(510, 305);
    
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->SetTextAlign(TA_LEFT);
    	pDC->TextOut( 40, 310, Item1Name);
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(365, 310, Item1UnitPrice);
    	pDC->TextOut(420, 310, Item1Quantity);
    	pDC->TextOut(495, 310, Item1SubTotal);
    
    	pDC->MoveTo(30, 325);
    	pDC->LineTo(510, 325);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut( 40, 330, Item2Name);
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(365, 330, Item2UnitPrice);
    	pDC->TextOut(420, 330, Item2Quantity);
    	pDC->TextOut(495, 330, Item2SubTotal);
    
    	pDC->MoveTo( 30, 345);
    	pDC->LineTo(510, 345);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut( 40, 350, Item3Name);
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(365, 350, Item3UnitPrice);
    	pDC->TextOut(420, 350, Item3Quantity);
    	pDC->TextOut(495, 350, Item3SubTotal);
    
    	pDC->MoveTo( 30, 365);
    	pDC->LineTo(510, 365);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut( 40, 370, Item4Name);
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(365, 370, Item4UnitPrice);
    	pDC->TextOut(420, 370, Item4Quantity);
    	pDC->TextOut(495, 370, Item4SubTotal);
    
    	pDC->MoveTo( 30, 385);
    	pDC->LineTo(510, 385);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(300, 430, TEXT("Cleaning Total: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(470, 430, CleaningTotal);
    
    	pDC->MoveTo(290, 445);
    	pDC->LineTo(510, 445);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(300, 450, TEXT("Tax Rate: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(470, 450, TaxRate + TEXT(" %"));
    
    	pDC->MoveTo(290, 465);
    	pDC->LineTo(510, 465);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(300, 470, TEXT("Tax Amount: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(470, 470, TaxAmount);
    
    	pDC->MoveTo(290, 485);
    	pDC->LineTo(510, 485);
    
    	pDC->SetTextAlign(TA_LEFT);
    	fntDefault = pDC->SelectObject(&fntLabel);
    	pDC->TextOut(300, 490, TEXT("Order Total: "));
    	fntDefault = pDC->SelectObject(&fntResult);
    	pDC->SetTextAlign(TA_RIGHT);
    	pDC->TextOut(470, 490, OrderTotal);
    
    	pDC->MoveTo(290, 505);
    	pDC->LineTo(510, 505);
    
    	pDC->SelectObject(fntDefault);
    	pDC->SelectObject(penOld);
    }
  3. Execute the application and try processing an order by clicking File -> New Cleaning Order
     
  4. After creating an order, click OK
     
  5. Close the application
 
 

Home