FunctionX - Practical Learning Logo

Pledge Distribution

 
Pledge Distribution
 

Introduction

A spin button, also called an up down button is a small rectangular control divided in two buttons. Each button displays a small arrow pointing in the opposite direction of the other. To use the control, the user clicks one of the arrowed buttons to change the value of the control.

The spin button in MFC is based on the Up-Down control of the Windows API created from the UPDOWN_CLASS. Unfortunately, the spin button in MFC doesn't faithfully replicate the UPDOWN_CLASS. For example, the MFC's version cannot inherently fire most events of the API's equivalent control. In fact, in Visual C++ 6.0 (MFC 6), the control has only two events:

  • NM_OUTOFMEMORY: This message is sent not if the control could not be created for lack of memory. This message notifies you that the spin button could not complete an action because it ran out of memory. This is a necessary but hardly used event
  • UDN_DELTAPOS: This message is sent when the value of the spin button is about to be changed

Visual C++ .NET 2003 (MFC 7) added just two messages:

  • NM_RELEASEDCAPTURE: This message is sent when the user releases the mouse from using the control
  • NM_THEMECHANGED: This message is in connection with the themes of the new Windows operating system

Probably the most important event to consider on this control is when the user clicks one of the buttons. None of these messages really sends a clear message when the user clicks one of the buttons of the spin control. At the same time, using MFC, it is almost impossible to identify the exact button that the user clicked (what is Borland doing to us by leaving C++ Builder? The C++ Builder version of the control tells you exactly what button the user clicked; this is sometimes very important in some applications).

A dialog box or a form is equipped with scrolling events. These events can help you identifier the type of scrolling that occurred: what is a scroll bar on the form or dialog box, or a scroll control? To do this, the form or dialog box fires either the WM_VSCROLL message for vertical scrolling or the WM_HSCROLL message for the horizontal scroll. The good news about these events is that they fire for any type of scrolling that occurred on the form or dialog box; that is, for any control positioned on the form or dialog box. This includes any spin button on the form or dialog box. The bad news is that these events are primarily meant to identify only a scroll bar. Fortunately, in MFC, both the scroll bars and the spin button are derived from the CWnd class.

If you place only one spin button on a form or dialog box, when the user clicks one of its buttons, you can process the message in either the WM_VSCROLL or the WM_HSCROLL events, depending on how the spin control's buttons are oriented, which you can specify at design time using the Orientation property. If you position more than one spin button, you can still process their click events in either the WM_VSCROLL or the WM_HSCROLL events but you must be able to identify what particular spin button was clicked. If you used a scroll bar as done in our Color Changer example, there is no much to do, since the event is able to recognize the scroll bar that fired the event. If you use another control, you must cast it to a CScrollBar control to get a pointer to your control. Once you have done that, you can use the non-scroll bar control as if it were one.

In this exercise, we are asked to create an application used to raise money for three educational institutions. To proceed, we will call people on the phone or post the application in an intranet's directory. When the application starts, an amount of money will be entered. Then, the person who is pledging money will specify the percentage of money that can be allocated to each institution. We will use three up down buttons to set the percentages. We must synchronize them so that the total values of the up down buttons must not exceed 100. The amount pledged is then converted with regards to each up down.

 
 

Practical Learning: Creating the Application

  1. If you want to follow this exercise, create a new Dialog-Based Application named it Pledge1
  2. Change the design of the IDR_MAINFRAME icon as follows:
     
     
  3. Design the dialog box as follows:
     
     
    Control ID Caption Additional Properties
    Static Text Static Text   Amount Pledged   $:  
    Edit Box IDC_AMOUNT_PLEDGED   Alignment: Right
    Static Text Static Text   Rotherham College:  
    Edit Box Edit Box IDC_RATE_VALUE1   Alignment: Right
    Spin Spin IDC_SPIN_RATE1   Alignment: Right
    Auto Buddy: True
    Set Buddy Integer: True
    Edit Text IDC_AMOUNT1    
    Static Text Static Text   Leicester University:  
    Edit Box IDC_RATE_VALUE2   Alignment: Right
    Spin Spin IDC_SPIN_RATE2   Alignment: Right
    Auto Buddy: True
    Set Buddy Integer: True
    Edit Box IDC_AMOUNT2   Alignment: Right
    Static Text Static Text   Lars Union Academy:
    Edit Box IDC_RATE_VALUE3   Alignment: Right
    Spin IDC_SPIN_RATE3   Alignment: Right
    Auto Buddy: True
    Set Buddy Integer: True
    Edit Box IDC_AMOUNT3   Align Text
    Static Text Static Text IDC_MESSAGE  
    Button Button IDCANCEL Close  
  4. Add a Member Variable for the following controls:
     
     
  5. Initiate the WM_VSCROLL message of the CPledge1 class to fire the ON_WM_VSCROLL event
     
  6. Implement the event and change the source file as follows:
     
    // Pledge1Dlg.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "Pledge1.h"
    #include "Pledge1Dlg.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    
    /////////////////////////////////////////////////////////////////////////////
    // CPledge1Dlg dialog
    
    CPledge1Dlg::CPledge1Dlg(CWnd* pParent /*=NULL*/)
    	: CDialog(CPledge1Dlg::IDD, pParent)
    {
    	//{{AFX_DATA_INIT(CPledge1Dlg)
    	m_AmountPledged = _T("1000");
    	m_Amount1 = _T("$500.00");
    	m_Amount2 = _T("$250.00");
    	m_Amount3 = _T("$250.00");
    	m_Message = _T("");
    	m_RateValue1 = _T("");
    	m_RateValue2 = _T("");
    	m_RateValue3 = _T("");
    	//}}AFX_DATA_INIT
    	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
    
    void CPledge1Dlg::DoDataExchange(CDataExchange* pDX)
    {
    	CDialog::DoDataExchange(pDX);
    	//{{AFX_DATA_MAP(CPledge1Dlg)
    	DDX_Control(pDX, IDC_SPIN_RATE3, m_Rate3);
    	DDX_Control(pDX, IDC_SPIN_RATE2, m_Rate2);
    	DDX_Control(pDX, IDC_SPIN_RATE1, m_Rate1);
    	DDX_Text(pDX, IDC_AMOUNT_PLEDGED, m_AmountPledged);
    	DDX_Text(pDX, IDC_AMOUNT1, m_Amount1);
    	DDX_Text(pDX, IDC_AMOUNT2, m_Amount2);
    	DDX_Text(pDX, IDC_AMOUNT3, m_Amount3);
    	DDX_Text(pDX, IDC_MESSAGE, m_Message);
    	DDX_Text(pDX, IDC_RATE_VALUE1, m_RateValue1);
    	DDX_Text(pDX, IDC_RATE_VALUE2, m_RateValue2);
    	DDX_Text(pDX, IDC_RATE_VALUE3, m_RateValue3);
    	//}}AFX_DATA_MAP
    }
    
    BEGIN_MESSAGE_MAP(CPledge1Dlg, CDialog)
    	//{{AFX_MSG_MAP(CPledge1Dlg)
    	ON_WM_PAINT()
    	ON_WM_QUERYDRAGICON()
    	ON_WM_VSCROLL()
    	//}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    
    /////////////////////////////////////////////////////////////////////////////
    // CPledge1Dlg message handlers
    
    BOOL CPledge1Dlg::OnInitDialog()
    {
    	CDialog::OnInitDialog();
    
    	SetIcon(m_hIcon, TRUE);			// Set big icon
    	SetIcon(m_hIcon, FALSE);		// Set small icon
    	
    	// TODO: Add extra initialization here
    	this->m_Rate1.SetRange(0, 100);
    	this->m_Rate1.SetPos(50);
    	this->m_Rate2.SetRange(0, 100);
    	this->m_Rate2.SetPos(25);
    	this->m_Rate3.SetRange(0, 100);
    	this->m_Rate3.SetPos(25);
    
    	return TRUE;  // return TRUE  unless you set the focus to a control
    }
    
    // If you add a minimize button to your dialog, you will need the code below
    //  to draw the icon.  For MFC applications using the document/view model,
    //  this is automatically done for you by the framework.
    
    void CPledge1Dlg::OnPaint() 
    {
    	if (IsIconic())
    	{
    		CPaintDC dc(this); // device context for painting
    
    		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
    
    		// Center icon in client rectangle
    		int cxIcon = GetSystemMetrics(SM_CXICON);
    		int cyIcon = GetSystemMetrics(SM_CYICON);
    		CRect rect;
    		GetClientRect(&rect);
    		int x = (rect.Width() - cxIcon + 1) / 2;
    		int y = (rect.Height() - cyIcon + 1) / 2;
    
    		// Draw the icon
    		dc.DrawIcon(x, y, m_hIcon);
    	}
    	else
    	{
    		CDialog::OnPaint();
    	}
    }
    
    HCURSOR CPledge1Dlg::OnQueryDragIcon()
    {
    	return (HCURSOR) m_hIcon;
    }
    
    void CPledge1Dlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
    {
    	// TODO: Add your message handler code here and/or call default
    	UpdateData();
    
    	double AmountPledged,
    		   Value1,
    		   Value2,
    		   Value3,
    		   Amount1,
    		   Amount2,
    		   Amount3,
    		   Rest;
    	int Rate1, Rate2, Rate3;
    
    	AmountPledged = atof(this->m_AmountPledged);
    
    	CSpinButtonCtrl *spnRate1, *spnRate2, *spnRate3;
    
    	// Identify the spin buttons of the dialog box using their IDentifiers
    	spnRate1 = reinterpret_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN_RATE1));
    	spnRate2 = reinterpret_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN_RATE2));
    	spnRate3 = reinterpret_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN_RATE3));
    
    	// Find out what spin button fired the event
    	if( reinterpret_cast<CSpinButtonCtrl *>(pScrollBar) == spnRate1 )
    	{
    		Value2      = atof(this->m_RateValue2);
    		Value3      = atof(this->m_RateValue3);
    
    		Rate2 = this->m_Rate2.GetPos();
    		Rate3 = this->m_Rate3.GetPos();
    	
    		this->m_Rate1.SetRange(0, 100 - Rate3 - Rate2);
    	
    		Value1 = atof(this->m_RateValue1);
    		Rate1 = this->m_Rate1.GetPos();
    	
    	}
    	else if( reinterpret_cast<CSpinButtonCtrl *>(pScrollBar) == spnRate2 )
    	{
    		Value1 = atof(this->m_RateValue1);
    		Value3 = atof(this->m_RateValue3);
    	
    		Rate1  = this->m_Rate1.GetPos();
    		Rate3  = this->m_Rate3.GetPos();
    
    		this->m_Rate2.SetRange(0, 100 - Rate1 - Rate3);
    	
    		Value2 = atof(this->m_RateValue2);
    
    		Rate2  = this->m_Rate2.GetPos();
    	}
    	else if( reinterpret_cast<CSpinButtonCtrl *>(pScrollBar) == spnRate3 )
    	{
    		Value1 = atof(this->m_RateValue1);
    		Rate1  = this->m_Rate1.GetPos();
    		Value2 = atof(this->m_RateValue2);
    		Rate2  = this->m_Rate2.GetPos();
    	
    		this->m_Rate3.SetRange(0, 100 - Rate1 - Rate2);
    	
    		Value3 = atof(this->m_RateValue3);
    		Rate3 = this->m_Rate3.GetPos();
    	}
    	
    	Amount1 = AmountPledged * Value1 / 100;
    	Amount2 = AmountPledged * Value2 / 100;
    	Amount3 = AmountPledged * Value3 / 100;
    
    	Rest = AmountPledged - Amount1 - Amount2 - Amount3;
    
    	this->m_Amount1.Format("$%.2f", Amount1);
    	this->m_Amount2.Format("$%.2f", Amount2);
    	this->m_Amount3.Format("$%.2f", Amount3);
        
    	if( Rest > 0 )
    		this->m_Message.Format("$%.2f still to be used", Rest);
    	else
    		this->m_Message = "";
    
    	UpdateData(FALSE);
    	
    	CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
    }
  7. Test the application
 

Copyright © 2004 FunctionX, Inc.