Georgetown Cleaning Services

 

Introduction

Some times if you are asked to create an application for a store that sells a specific number of items based on a fixed list, you may request the list of items so you can create its elements in the application. Here is an example:
 

For such an application, when the list of items changes, you may have to modify the application, recompile, and redistribute (or re-install it). One solution to this problem would consist of providing placeholders for an employee to fill out and process the order with them. Another problem that can be common with this type of application is that, while the form may be crowded with too many objects, most of the time, some items are rarely ordered.

In this exercise, to make this type of application a little more flexible, we will use a few combo boxes that allow the user to simply select the items that are valid for the order.

Prerequisites:

Practical Learning Practical Learning: Introducing Buttons

  1. Start a new MFC Application named GCS1
  2. Create it as a Single Document
  3. Set the File Extension to gcs and the Filter Name to
    Georgetown Cleaning Services Files (*.gcs)
     
  4. Uncheck the Maximize Box check box
  5. Set the Base Class to CFormView and click Finish
  6. In the Resource View, expand GCS1, followed by GDC1.rc and the Dialog folder
  7. Double-click IDD_GCS1_Form and design it as follows:
     
    Control ID Caption Additional Properties
    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
    Button Button IDC_CALC_SHIRTS ...  
    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
    Button Button IDC_CALC_PANTS ...  
    Edit Control Edit Control IDC_PANTS_SUBTOTAL   Align Text: Right
    ComboBox ComboBox IDC_ITEM1   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_UNITPRICE1   Align Text: Right
    Edit Control Edit Control IDC_QUANTITY1   Align Text: Right
    Number: True
    Button Button IDC_CALC1 ...  
    Edit Control Edit Control IDC_SUBTOTAL1   Align Text: Right
    ComboBox ComboBox IDC_ITEM2   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_UNITPRICE2   Align Text: Right
    Edit Control Edit Control IDC_QUANTITY2   Align Text: Right
    Number: True
    Button Button IDC_CALC2    
    Edit Control Edit Control IDC_SUBTOTAL2   Align Text: Right
    ComboBox ComboBox IDC_ITEM3   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_UNITPRICE3   Align Text: Right
    Edit Control Edit Control IDC_QUANTITY3   Align Text: Right
    Number: True
    Button Button IDC_CALC3    
    Edit Control Edit Control IDC_SUBTOTAL3   Align Text: Right
    ComboBox ComboBox IDC_ITEM4   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_UNITPRICE4   Align Text: Right
    Edit Control Edit Control IDC_QUANTITY4   Align Text: Right
    Number: True
    Button Button IDC_CALC4    
    Edit Control Edit Control IDC_SUBTOTAL4   Align Text: Right
    Button Button IDC_CALCULATE Calculate  
    Group Box GroupBox   Order Summary Horizontal Alignment: Center
    Static Text Static Text   Cleaning Order:  
    Edit Control Edit Control IDC_CLEANINGORDER   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
    Group Box   Maintenance  
    Static Text Static Text   Receipt Number:  
    Edit Control Edit Control IDC_RECEIPTNBR    
    Button IDC_RESET Reset Order  
  8. If you are using MSVC 6, press Ctrl + W to access the ClassWizard.
    If you are using MSVC .NET, right-click each control and click Add Variable
  9. Add the control variables as follows:
     
    Identifier Value Variable Control Variable
    Type Name
    IDC_CUSTOMERNAME CString m_CustomerName  
    IDC_CUSTOMERPHONE CString m_CustomerPhone  
    IDC_DATELEFT     m_DateLeft
    IDC_TIMELEFT     m_TimeLeft
    IDC_DATEEXPECTED     m_DateExpected
    IDC_TIMEEXPECTED     m_TimeExpected
    IDC_SHIRTS_UNITPRICE double m_ShirtsUnitPrice  
    IDC_SHIRTS_QTY int m_ShirtsQuantity  
    IDC_SHIRTS_SUBTOTAL double m_ShirtsSubTotal  
    IDC_PANTS_UNITPRICE double m_PantsUnitPrice  
    IDC_PANTS_QTY int m_PantsQuantity  
    IDC_PANTS_SUBTOTAL double m_PantsSubTotal  
    IDC_ITEM1     m_Item1
    IDC_UNITPRICE1 double m_UnitPrice1  
    IDC_QUANTITY1 int m_Quantity1  
    IDC_SUBTOTAL1 double m_SubTotal1  
    IDC_ITEM2     m_Item2
    IDC_UNITPRICE2 double m_UnitPrice2  
    IDC_QUANTITY2 int m_Quantity2  
    IDC_SUBTOTAL2 double m_SubTotal2  
    IDC_ITEM3     m_Item3
    IDC_UNITPRICE3 double m_UnitPrice3  
    IDC_QUANTITY3 int m_Quantity3  
    IDC_SUBTOTAL3 double m_SubTotal3  
    IDC_ITEM4     m_Item4
    IDC_UNITPRICE4 double m_UnitPrice4  
    IDC_QUANTITY4 int m_Quantity4  
    IDC_SUBTOTAL4 double m_SubTotal4  
    IDC_CLEANINGORDER CString m_CleaningOrder  
    IDC_TAXRATE double m_TaxRate  
    IDC_TAXAMOUNT CString m_TaxAmount  
    IDC_ORDERTOTAL CString m_OrderTotal  
    IDC_RECEIPTNBR CString m_ReceiptNumber  
  10. Access the form view and double-click the Start New Cleaning Order button
  11. Implement its event as follows:
     
    void CGCS1View::OnBnClickedReset()
    {
    	// TODO: Add your control notification handler code here
    	UpdateData();
    
    	// Reset the form
    	m_CustomerName = "";
    	m_CustomerPhone = "";
    	CTime tmeCurrent = CTime::GetCurrentTime();
    	m_DateLeft.SetTime(&tmeCurrent);
    	m_TimeLeft.SetTime(&tmeCurrent);
    	m_DateExpected.SetTime(&tmeCurrent);
    	m_TimeExpected.SetTime(&tmeCurrent);
    	m_ShirtsUnitPrice = 0.95;
    	m_ShirtsQuantity = 0;
    	m_ShirtsSubTotal = 0.00;
    	m_PantsUnitPrice = 2.75;
    	m_PantsQuantity = 0;
    	m_PantsSubTotal = 0.00;
    	m_Item1.SetWindowText("None");
    	m_UnitPrice1 = 0.00;
    	m_Quantity1 = 0;
    	m_SubTotal1 = 0.00;
    	m_Item2.SetWindowText("None");
    	m_UnitPrice2 = 0.00;
    	m_Quantity2 = 0;
    	m_SubTotal2 = 0.00;
    	m_Item3.SetWindowText("None");
    	m_UnitPrice3 = 0.00;
    	m_Quantity3 = 0;
    	m_SubTotal3 = 0.00;
    	m_Item4.SetWindowText("None");
    	m_UnitPrice4 = 0;
    	m_Quantity4 = 0;
    	m_SubTotal4 = 0;
    	m_CleaningOrder = "0.00";
    	m_TaxRate = 5.75;
    	m_TaxAmount = "0.00";
    	m_OrderTotal = "0.00";
    	
    	CTime tmeLeft;
    	this->m_TimeLeft.GetTime(tmeLeft);
    	CString strReceiptNumber = tmeLeft.Format("%H%M%S");
    	this->m_ReceiptNumber = strReceiptNumber;
    
    	UpdateData(FALSE);
    }
  12. In the Class View, expand CGCS1View and double-click OnInitialUpdate
  13. To initialize the date pickers and other controls, make the following changes:
     
    void CGCS1View::OnInitialUpdate()
    {
    	CFormView::OnInitialUpdate();
    	GetParentFrame()->RecalcLayout();
    	ResizeParentToFit();
    
    	this->m_DateLeft.SetFormat(_T("dddd dd MMM yyyy"));
    	this->m_DateExpected.SetFormat(_T("dddd dd MMM yyyy"));
    
    	UpdateData(FALSE);
    
    	OnBnClickedReset();
    }
  14. In the Class View, expand the CMainFrame node and double-click PreCreateWindow
  15. To customize the title of the application and set the size of the frame, make the following changes:
     
    // MainFrm.cpp : implementation of the CMainFrame class
    //
    
    #include "stdafx.h"
    #include "GCS1.h"
    
    #include "MainFrm.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    
    // CMainFrame
    
    IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
    
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    	ON_WM_CREATE()
    END_MESSAGE_MAP()
    
    static UINT indicators[] =
    {
    	ID_SEPARATOR,           // status line indicator
    	ID_INDICATOR_CAPS,
    	ID_INDICATOR_NUM,
    	ID_INDICATOR_SCRL,
    };
    
    
    // CMainFrame construction/destruction
    
    CMainFrame::CMainFrame()
    {
    	// TODO: add member initialization code here
    }
    
    CMainFrame::~CMainFrame()
    {
    }
    
    
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
    		return -1;
    	
    	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
    		| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
    		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
    	{
    		TRACE0("Failed to create toolbar\n");
    		return -1;      // fail to create
    	}
    	m_wndToolBar.SetWindowText(_T("Standard Toolbar"));
    
    	if (!m_wndStatusBar.Create(this) ||
    		!m_wndStatusBar.SetIndicators(indicators,
    		  sizeof(indicators)/sizeof(UINT)))
    	{
    		TRACE0("Failed to create status bar\n");
    		return -1;      // fail to create
    	}
    	// 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);
    
    	this->CenterWindow();
    	this->SetWindowText(_T("Georgetown Cleaning Services - Customer Order"));
    
    	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.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE
    		 | WS_THICKFRAME | WS_MINIMIZEBOX | WS_SYSMENU;
    	
    	// The new width of the window's frame
    	cs.cx = 580;
    	// The new height of the window's frame
    	cs.cy = 620;
    	// Remove the Untitled thing
    	cs.style &= ~FWS_ADDTOTITLE;
    
    	return TRUE;
    }
    
    
    // CMainFrame diagnostics
    
    #ifdef _DEBUG
    void CMainFrame::AssertValid() const
    {
    	CFrameWnd::AssertValid();
    }
    
    void CMainFrame::Dump(CDumpContext& dc) const
    {
    	CFrameWnd::Dump(dc);
    }
    
    #endif //_DEBUG
    
    
    // CMainFrame message handlers
    
  16. Execute the application to preview it
     
  17. Close the window and return to MSVC

Application Implementation

As a dry cleaning application, there are some behaviors we will apply. About dates, if a customer leaves his or her clothes in the morning before 9AM, we will promise that the clothes can be ready on the same day after 5PM. If a customer leaves the clothes after 9AM, they will be ready only the following day.

To process an order, we provided the clerk with 4 combo boxes to select some of the most regularly ordered cleaning items. The combo boxes also allow the user to type an item that is not in the list. Also, because we can't or we won't predict the prices of these items, we let the user specify their prices.

Once an order is ready, we will calculate it using classic operations.

Practical Learning Practical Learning: Implementing the Application

  1. If you are using MSVC 6, display the ClassWizard (Ctrl + W), generate an event for the DTN_DATETIMECHANGE message of the IDC_TIMELEFT control and name it OnTimeLeftChanged. Associate it with the CGCS1View class
    If you are using MSVC .NET, right-click the form, click Add Event Handler, generate an event for the DTN_DATETIMECHANGE message of the IDC_TIMELEFT control and name it OnTimeLeftChanged. Associate it with the CGCS1View class
  2. Implement the event as follows:
     
    void CGCS1View::OnTimeLeftChanged(NMHDR *pNMHDR, LRESULT *pResult)
    {
    	LPNMDATETIMECHANGE pDTChange = reinterpret_cast<LPNMDATETIMECHANGE>(pNMHDR);
    	// TODO: Add your control notification handler code here
    	*pResult = 0;
    	
    	CTime dateLeft, timeLeft;
    	
    	this->m_DateLeft.GetTime(dateLeft);
    	this->m_TimeLeft.GetTime(timeLeft);
    
    	CTime time9AM = CTime(timeLeft.GetYear(), timeLeft.GetMonth(), timeLeft.GetDay(), 9, 0, 0);
    
    	 // If the customer leaves clothes before 9AM...
    	 if( timeLeft <= time9AM )
    	 {
    		 // ... then they should be ready the same day after 5PM
    		 this->m_DateExpected.SetTime(&dateLeft);
    		 CTime tm5PM = CTime(dateLeft.GetYear(), dateLeft.GetMonth(), dateLeft.GetDay(), 17, 0, 0);
    		 this->m_TimeExpected.SetTime(&tm5PM);
    	 }
    	 else
    	 {
    		 // If the clothes were left after 9AM, they will be available the following by 8AM
    	CTime tmTomorrowMorning = CTime(dateLeft.GetYear(), dateLeft.GetMonth(), dateLeft.GetDay() + 1, 8, 0, 0);
    		 this->m_DateExpected.SetTime(&tmTomorrowMorning);
    		 this->m_TimeExpected.SetTime(&tmTomorrowMorning);
    	 }
    }
  3. Return to the form. Double-click the button on the right side of the Shirts text box and implement its event as follows:
     
    void CGCS1View::OnBnClickedCalcShirts()
    {
    	// TODO: Add your control notification handler code here
    	UpdateData();
    	double unitPrice = this->m_ShirtsUnitPrice;
    	int quantity = this->m_ShirtsQuantity;
    	double subTotal = unitPrice * quantity;
    
    	// Display the sub-total in the corresponding text box
    	this->m_ShirtsSubTotal = subTotal;
    	UpdateData(FALSE);
    }
  4. Return to the form. Double-click the button on the right side of the Pants text box and implement its Click event as follows:
     
    void CGCS1View::OnBnClickedCalcPants()
    {
    	UpdateData();
    	double unitPrice = this->m_PantsUnitPrice;
    	int quantity = this->m_PantsQuantity;
    	double subTotal = unitPrice * quantity;
    
    	this->m_PantsSubTotal = subTotal;
    	UpdateData(FALSE);
    }
  5. Return to the form. Double-click the button for the first combo box and implement its event as follows:
     
    void CGCS1View::OnBnClickedCalc1()
    {
    	// Process this item only if the user specified what it is
    	UpdateData();
    	if( this->m_Item1.GetCurSel() < 0 )
    		return;
    		
    	double unitPrice = this->m_UnitPrice1;
    	int quantity  = this->m_Quantity1;
    	double	subTotal  = quantity * unitPrice;
    
    	this->m_SubTotal1 = subTotal;
    	UpdateData(FALSE);
    }
  6. Return to the form. Double-click the button for the second combo box and implement its event as follows:
     
    void CGCS1View::OnBnClickedCalc2()
    {
    	// Process this item only if the user specified what it is
    	UpdateData();
    	if( this->m_Item2.GetCurSel() < 0 )
    		return;
    		
    	double unitPrice = this->m_UnitPrice2;
    	int quantity  = this->m_Quantity2;
    	double	subTotal  = quantity * unitPrice;
    
    	this->m_SubTotal2 = subTotal;
    	UpdateData(FALSE);
    }
  7. Return to the form. Double-click the button for the third combo box and implement its event as follows:
     
    void CGCS1View::OnBnClickedCalc3()
    {
    	// Process this item only if the user specified what it is
    	UpdateData();
    	if( this->m_Item3.GetCurSel() < 0 )
    		return;
    		
    	double unitPrice = this->m_UnitPrice3;
    	int quantity  = this->m_Quantity3;
    	double	subTotal  = quantity * unitPrice;
    
    	this->m_SubTotal3 = subTotal;
    	UpdateData(FALSE);
    }
  8. Return to the form. Double-click the button for the fourth combo box and implement its event as follows:
     
    void CGCS1View::OnBnClickedCalc4()
    {
    	// Process this item only if the user specified what it is
    	UpdateData();
    	if( this->m_Item4.GetCurSel() < 0 )
    		return;
    		
    	double unitPrice = this->m_UnitPrice4;
    	int quantity  = this->m_Quantity4;
    	double	subTotal  = quantity * unitPrice;
    
    	this->m_SubTotal4 = subTotal;
    	UpdateData(FALSE);
    }
  9. Return to the form. Double-click the Calculate button and implement its Click event as follows:
     
    void CGCS1View::OnBnClickedCalculate()
    {
    	// TODO: Add your control notification handler code here
    	// Just in case the user forgot to calculate the sub totals, do it now
    	this->OnBnClickedCalcShirts();
    	this->OnBnClickedCalcPants();
    	this->OnBnClickedCalc1();
    	this->OnBnClickedCalc2();
    	this->OnBnClickedCalc3();
    	this->OnBnClickedCalc4();
    
    	UpdateData();
    	// Retrieve the value of the sub-total for each category of items
    	double priceShirts = this->m_ShirtsSubTotal;
    	double pricePants  = this->m_PantsSubTotal;
    	double priceItem1  = this->m_SubTotal1;
    	double priceItem2  = this->m_SubTotal1;
    	double priceItem3  = this->m_SubTotal1;
    	double priceItem4  = this->m_SubTotal1;
    		   
    	// Calculate the total cleaning
    	double cleaningOrder = priceShirts + pricePants + priceItem1 + 
    		                   priceItem2  + priceItem3 + priceItem4;
    
    	// Retrieve the value of the tax rate
    	double taxRate = this->m_TaxRate;
    
    	// Calculate the amount owed for the taxes
    	double taxAmount = cleaningOrder * taxRate / 100;
    	// Add the tax amount to the cleaning total
    	double netPrice  = cleaningOrder + taxAmount;
    			
    	// Display the values of the order summary
    	this->m_CleaningOrder.Format("$%.2f", cleaningOrder);
    	this->m_TaxAmount.Format("$%.2f", taxAmount);
    	this->m_OrderTotal.Format("$%.2f", netPrice);
    	UpdateData(FALSE);
    }
  10. Execute the application to test it by processing an order:
     
  11. Close the form and return to your programming environment

Saving Cleaning Orders

When an application has been processed, the user may need to save the order before starting a new one. As always, there are various ways you can do this. Probably, if this were a real assignment, you would ask the store owner how he or she would like the cleaning orders to be saved. For our example, here is how we will do. We will create a parent folder on the C drive and we will call it Georgetown Cleaning Service. Every time the user decides to save a new order, we will use a sub-folder under the Georgetown Cleaning Service folder. The name of this sub-folder will be made of the year that the order was saved and the name of the month. If the folder exists already, we will save the order in it. If the folder doesn't exist, we will automatically create it. Our files will have the extension .gcs and by default, a order will have a receipt number, also used as the file name.

Practical Learning Practical Learning: Saving Cleaning Orders

  1. Display the CGCS1Doc.h header file and declare a variable that corresponds to each of the control variables you created for a cleaning order:
     
    // GCS1Doc.h : interface of the CGCS1Doc class
    //
    
    
    #pragma once
    
    class CGCS1Doc : public CDocument
    {
    protected: // create from serialization only
    	CGCS1Doc();
    	DECLARE_DYNCREATE(CGCS1Doc)
    
    // Attributes
    public:
    	CString CustomerName;
    	CString CustomerPhone;
    	CTime DateLeft;
    	CTime TimeLeft;
    	CTime DateExpected;
    	CTime TimeExpected;
    	double ShirtsUnitPrice;
    	int ShirtsQuantity;
    	double ShirtsSubTotal;
    	double PantsUnitPrice;
    	int PantsQuantity;
    	double PantsSubTotal;
    	CString strItem1;
    	double UnitPrice1;
    	int Quantity1;
    	double SubTotal1;
    	CString strItem2;
    	double UnitPrice2;
    	int Quantity2;
    	double SubTotal2;
    	CString strItem3;
    	double UnitPrice3;
    	int Quantity3;
    	double SubTotal3;
    	CString strItem4;
    	double UnitPrice4;
    	int Quantity4;
    	double SubTotal4;
    	CString CleaningOrder;
    	double TaxRate;
    	CString TaxAmount;
    	CString OrderTotal;
    	CString ReceiptNumber;
    
    // Operations
    public:
    
    // Overrides
    	public:
    	virtual BOOL OnNewDocument();
    	virtual void Serialize(CArchive& ar);
    
    // Implementation
    public:
    	virtual ~CGCS1Doc();
    #ifdef _DEBUG
    	virtual void AssertValid() const;
    	virtual void Dump(CDumpContext& dc) const;
    #endif
    
    protected:
    
    // Generated message map functions
    protected:
    	DECLARE_MESSAGE_MAP()
    };
  2. Display the CGCS1Doc.cpp source file and access its Serialize method
  3. Change it as follows:
     
    void CGCS1Doc::Serialize(CArchive& ar)
    {
    	if (ar.IsStoring())
    	{
    		// TODO: add storing code here
    		ar << CustomerName
    		   << CustomerPhone
    		   << DateLeft
    		   << TimeLeft
    		   << DateExpected
    		   << TimeExpected
    		   << ShirtsUnitPrice
    		   << ShirtsQuantity
    		   << ShirtsSubTotal
    		   << PantsUnitPrice
    		   << PantsQuantity
    		   << PantsSubTotal
    		   << strItem1
    		   << UnitPrice1
    		   << Quantity1
    		   << SubTotal1
    		   << strItem2
    		   << UnitPrice2
    		   << Quantity2
    		   << SubTotal2
    		   << strItem3
    		   << UnitPrice3
    		   << Quantity3
    		   << SubTotal3
    		   << strItem4
    		   << UnitPrice4
    		   << Quantity4
    		   << SubTotal4
    		   << CleaningOrder
    		   << TaxRate
    		   << TaxAmount
    		   << OrderTotal
    		   << ReceiptNumber;
    	}
    	else
    	{
    		// TODO: add loading code here
    		ar >> CustomerName
    		   >> CustomerPhone
    		   >> DateLeft
    		   >> TimeLeft
    		   >> DateExpected
    		   >> TimeExpected
    		   >> ShirtsUnitPrice
    		   >> ShirtsQuantity
    		   >> ShirtsSubTotal
    		   >> PantsUnitPrice
    		   >> PantsQuantity
    		   >> PantsSubTotal
    		   >> strItem1
    		   >> UnitPrice1
    		   >> Quantity1
    		   >> SubTotal1
    		   >> strItem2
    		   >> UnitPrice2
    		   >> Quantity2
    		   >> SubTotal2
    		   >> strItem3
    		   >> UnitPrice3
    		   >> Quantity3
    		   >> SubTotal3
    		   >> strItem4
    		   >> UnitPrice4
    		   >> Quantity4
    		   >> SubTotal4
    		   >> CleaningOrder
    		   >> TaxRate
    		   >> TaxAmount
    		   >> OrderTotal
    		   >> ReceiptNumber;
    	}
    }
  4. If you are using MSVC6, display the ClassWizard (Ctrl+W). Generate an event for the ID_FILE_SAVE menu item and associate it with the view class
    If you are using MSVC .NET, from the Resource View, display the IDR_MAINFRAME. Right-click the Save menu item and click Add Event Handler. create the COMMAND message and associate it with the view class
     
  5. Click OK or Add and Edit
  6. Implement the event as follows:
     
    void CGCS1View::OnFileSave()
    {
    	// TODO: Add your command handler code here
    	UpdateData();
    	// Get a reference to the document class that will save the file
    	CGCS1Doc *pDoc = GetDocument();
    	// Before saving the file, check if there is a folder called
    	// "Georgetown Cleaning Services" on the C drive
    	// If there is no such a folder, create it
    	CreateDirectory(_T("C:\\Georgetown Cleaning Services"), NULL);
    
    	// Get the date that this cleaning order was/is being saved
    	CTime DateLeft, TimeLeft, DateExpected, TimeExpected;
    	this->m_DateLeft.GetTime(DateLeft);
    	this->m_TimeLeft.GetTime(TimeLeft);
    	this->m_DateExpected.GetTime(DateExpected);
    	this->m_TimeExpected.GetTime(TimeExpected);
    
    	// Create a string made of the year and the month of the Date Left
    	CString strDate = DateLeft.Format(_T("%Y %B"));
    
    	// Check inside of the Georgetown Cleaning Services folder for
    	// a sub-folder made of this year and month.
    	// If there is such a folder, fine.
    	// If that folder doesn't exist, then create it
    	CString strPath = CString("C:\\Georgetown Cleaning Services\\") + strDate;
    	CreateDirectory(strPath, NULL);
    
    	// Create a receipt number based on the time the order was processed
    	CString strReceiptNumber = this->m_ReceiptNumber;
    	// Create a file name based on it
    	CString strFilename = this->m_ReceiptNumber + ".gcs";
    	// Create the complete path of the file
    	CString strFilePath = strPath + CString("\\") + strFilename;
    
    	// Check if that file exists already
    	CFileFind fndFile;
    	BOOL exists = fndFile.FindFile(strFilePath);
    
    	if( exists == TRUE )
    	{
    		AfxMessageBox("A file with that receipt number exists already\n"
    			          "Please enter a different receipt number");
    		return;
    	}
    	else
    	{
    		CString strItem1, strItem2, strItem3, strItem4;
    		this->m_Item1.GetWindowText(strItem1);
    		this->m_Item2.GetWindowText(strItem2);
    		this->m_Item3.GetWindowText(strItem3);
    		this->m_Item4.GetWindowText(strItem4);
    
    		pDoc->CustomerName = m_CustomerName;
    		pDoc->CustomerPhone = m_CustomerPhone;
    		pDoc->DateLeft = DateLeft;
    		pDoc->TimeLeft = TimeLeft;
    		pDoc->DateExpected = DateExpected;
    		pDoc->TimeExpected = TimeExpected;
    		pDoc->ShirtsUnitPrice = m_ShirtsUnitPrice;
    		pDoc->ShirtsQuantity = m_ShirtsQuantity;
    		pDoc->ShirtsSubTotal = m_ShirtsSubTotal;
    		pDoc->PantsUnitPrice = m_PantsUnitPrice;
    		pDoc->PantsQuantity = m_PantsQuantity;
    		pDoc->PantsSubTotal = m_PantsSubTotal;
    		pDoc->strItem1 = strItem1;
    		pDoc->UnitPrice1 = m_UnitPrice1;
    		pDoc->Quantity1 = m_Quantity1;
    		pDoc->SubTotal1 = m_SubTotal1;
    		pDoc->strItem2 = strItem2;
    		pDoc->UnitPrice2 = m_UnitPrice2;
    		pDoc->Quantity2 =  m_Quantity2;
    		pDoc->SubTotal2 = m_SubTotal2;
    		pDoc->strItem3 = strItem3;
    		pDoc->UnitPrice3 = m_UnitPrice3;
    		pDoc->Quantity3 = m_Quantity3;
    		pDoc->SubTotal3 = m_SubTotal3;
    		pDoc->strItem4 = strItem4;
    		pDoc->UnitPrice4 = m_UnitPrice4;
    		pDoc->Quantity4 = m_Quantity4;
    		pDoc->SubTotal4 = m_SubTotal4;
    		pDoc->CleaningOrder = m_CleaningOrder;
    		pDoc->TaxRate = m_TaxRate;
    		pDoc->TaxAmount = m_TaxAmount;
    		pDoc->OrderTotal = m_OrderTotal;
    		pDoc->ReceiptNumber = m_ReceiptNumber;
    
    		CFile fleOrder;
    
    		fleOrder.Open(strFilePath, CFile::modeCreate | CFile::modeWrite);
    		CArchive ar(&fleOrder, CArchive::store);
    		pDoc->Serialize(ar);
    
    		ar.Close();
    		fleOrder.Close();
    		
    		UpdateData(FALSE);
    
    		this->OnBnClickedReset();
    	}
    }
  7. In the same way, generate a COMMAND message for the ID_FILE_OPEN menu item and associate it with the view class
  8. Implement it as follows:
     
    void CGCS1View::OnFileOpen()
    {
    	// TODO: Add your command handler code here
    	UpdateData();
    	// Get a reference to the document class that will save the file
    	CGCS1Doc *pDoc = GetDocument();
    	char strFilter[] = { "Cleaning Orders (*.gcs)|*.gcs|All Files (*.*)|*.*||" };
    	CFileDialog dlgFile(FALSE, ".gcs", NULL, 0, strFilter);
    
    	if( dlgFile.DoModal() == IDOK )
    	{
    		CFile fleOrder(dlgFile.GetFileName(), CFile::modeRead);
    		CArchive ar(&fleOrder, CArchive::load);
    		pDoc->Serialize(ar);
    	
    		this->m_CustomerName = pDoc->CustomerName;
    		this->m_CustomerPhone = pDoc->CustomerPhone;
    		this->m_DateLeft.SetTime(&pDoc->DateLeft);
    		this->m_TimeLeft.SetTime(&pDoc->TimeLeft);
    		this->m_DateExpected.SetTime(&pDoc->DateExpected);
    		this->m_TimeExpected.SetTime(&pDoc->TimeExpected);
    		this->m_ShirtsUnitPrice = pDoc->ShirtsUnitPrice;
    		this->m_ShirtsQuantity  = pDoc->ShirtsQuantity;
    		this->m_ShirtsSubTotal = pDoc->ShirtsSubTotal;
    		this->m_PantsUnitPrice = pDoc->PantsUnitPrice;
    		this->m_PantsQuantity = pDoc->PantsQuantity;
    		this->m_PantsSubTotal = pDoc->PantsSubTotal;
    		this->m_Item1.SetWindowText(pDoc->strItem1);
    		this->m_UnitPrice1 = pDoc->UnitPrice1;
    		this->m_Quantity1 = pDoc->Quantity1;
    		this->m_SubTotal1 = pDoc->SubTotal1;
    		this->m_Item2.SetWindowText(pDoc->strItem2);
    		this->m_UnitPrice2 = pDoc->UnitPrice2;
    		this->m_Quantity2 = pDoc->Quantity2;
    		this->m_SubTotal2 = pDoc->SubTotal2;
    		this->m_Item3.SetWindowText(pDoc->strItem3);
    		this->m_UnitPrice3 = pDoc->UnitPrice3;
    		this->m_Quantity3 = pDoc->Quantity3;
    		this->m_SubTotal3 = pDoc->SubTotal3;
    		this->m_Item4.SetWindowText(pDoc->strItem4);
    		this->m_UnitPrice4 = pDoc->UnitPrice4;
    		this->m_Quantity4 = pDoc->Quantity4;
    		this->m_SubTotal4 = pDoc->SubTotal4;
    		this->m_CleaningOrder = pDoc->CleaningOrder;
    		this->m_TaxRate = pDoc->TaxRate;
    		this->m_TaxAmount = pDoc->TaxAmount;
    		this->m_OrderTotal = pDoc->OrderTotal;
    		this->m_ReceiptNumber = pDoc->ReceiptNumber;
    	
    		ar.Close();
    		fleOrder.Close();
    	}
    
    	UpdateData(FALSE);
    }
  9. Execute the application to test it
  10. Create a cleaning order and save it
  11. Create another cleaning order and save it
  12. Open the first cleaning order
  13. Close the application
 

Home Copyright © 2005-2016, FunctionX