Click here to Skip to main content
15,892,072 members
Articles / Desktop Programming / MFC
Article

CNumSpinCtrl - a simple numeric spin control

Rate me:
Please Sign up or sign in to vote.
4.90/5 (45 votes)
11 Jul 2002CPOL3 min read 201.4K   7.9K   49   33
Numeric spin control for working with real numbers

Introduction

When I needed a numeric spin control, I searched both CodeGuru and CodeProject. Surprisingly I only found one article by T. VU KHAC on CodeGuru, but as it turned out his CNumSpinCtrl could only be used in pair with CNumEdit which was too restrictive for my purposes. So I ended up writing my own numeric spin control.

CNumSpinCtrl allows you to work with non-integer numbers quite easily. It provides methods to set up value range, increment, and current position. It also lets you format the output value either by specifying number of decimal places or by providing your own formatting string.

Usage

Using the control is not much different from using CSpinButtonCtrl. You would subclass a dialog item or create the control dynamically. Then all you need to do is to set up range and position and, if necessary, the output formatting. Here’s an example from the demo project:

BOOL CCNumSpinCtrlDemoDlg::OnInitDialog()
{
        CDialog::OnInitDialog();
        
        m_spinValue.SetDecimalPlaces (3);
        m_spinValue.SetTrimTrailingZeros (FALSE);
        m_spinValue.SetRangeAndDelta (0.1, 1.0, 0.05);
        m_spinValue.SetPos (0.5);
...

There one thing to watch out however - style. Make sure you uncheck the "Set buddy integer" style or, if you are creating control dynamically, do not add UDS_SETBUDDYINT style. Otherwise the Windows automatically resets buddy's value to some integer when user clicks up/down arrows.

Formatting

There are two ways to format the output value: by specifying a number of decimal places or by providing a formatting string. With the first method you will specify number of decimal places with a call to SetDecimalPlaces function. Passing -1 to this function will turn off rounding to decimal places (the value will be output with "%g" format string). You can also specify whether you want to trim any trailing zeros with a call to SetTrimTrailingZeros. With a second method you simply provide your own formatting string, which later will be used with CString’s Format function. Example:

SetFormatString ("%.2e")

Remarks

Be sure to set appropriate formatting settings. If your formatting settings are inadequate for the current range and increment, the text in the buddy control may not change at all. For example, if you set the increment to be 0.005, but the number of decimal places only to 1, the value in buddy control will not change when a user clicks on up and down arrows. This is because the control does not keep the current value internally. The value is obtained from the buddy control before it is incremented or decremented. Then it is formatted and passed back to the buddy control. So the formatting settings are quite important.

Revisions

2002-07-03 Commented out ModifyStyle in InitControl - it didn't work, didn't remove UDS_SETBUDDYINT style. Put ASSERT instead. So now program will ASSERT if you forget to remove UDS_SETBUDDYINT style in resources.
Fixed wrapping. In some cases due to poor machine precision the wrapping condition didn't evaluate properly.
2002-06-05 Warren Stevens added wrapping ability. If the style of the spin control is set to "wrap", the value will wrap around if increased or decreased past the range limits.
2002-04-17 Fixed bug with trimming zeros. If number of decimal places was set to zero, it was still trimming zeros (e.g. 100 would become 1).
2001-08-24 Changed ON_NOTIFY_REFLECT to ON_NOTIFY_REFLECT_EX so that parent can also handle notification message from spin control. Thanks to Emmanuil Tsagarakis for this one.
2001-07-06 Original version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
oleg6327-Jun-13 6:03
professionaloleg6327-Jun-13 6:03 
GeneralGreat Job! Pin
Paxmatic20-Jun-10 3:30
Paxmatic20-Jun-10 3:30 
GeneralConvert numbers to text Pin
Anonymous23-May-05 0:52
Anonymous23-May-05 0:52 
GeneralRe: Convert numbers to text Pin
ROCKISDEAD23-May-05 0:54
ROCKISDEAD23-May-05 0:54 
GeneralRe: Convert numbers to text Pin
Damir Valiulin24-May-05 4:51
Damir Valiulin24-May-05 4:51 
QuestionHow about a draggable spin control? Pin
eleroux27-Apr-05 8:30
eleroux27-Apr-05 8:30 
First of all, congratulations for this job.

I needed a spin control who accepted and validated float values, so this has been very useful for me. Also I needed that the button had a "tracking" or "dragging" feature. This control is like the Spin Ctrl that can be pressed up or down, but also you can hold the mouse and drag it up/down so the value changes rapidly.

Many applications have this kind of control. Some implementations have a little middle "thumb" button between the arrows and the cursor will change to an up/down cursor while hovering over this button.

Now, after digging in this and other c++ sites, I wonder why it is so difficult to find any reference to this kind of control.

I added a small modification on this code - yet it is simple and does not include a middle "thumb" button nor custom redraw. I'm afraid I'm not an experienced programmer, so i think my code is awkward - but still, there is it.



<br />
void CNumSpinCtrl::OnLButtonDown(UINT nFlags, CPoint point) <br />
{	<br />
	m_bLButtonHold = TRUE;<br />
	m_ctrlCursorPos = point;<br />
	GetCursorPos(&m_scrCursorPos);<br />
	CSpinButtonCtrl::OnLButtonDown(nFlags, point);<br />
}<br />
<br />
void CNumSpinCtrl::OnLButtonUp(UINT nFlags, CPoint point) <br />
{<br />
	m_bLButtonHold = FALSE;<br />
<br />
	if (m_bTrack)<br />
	{<br />
		m_bTrack = FALSE;<br />
		ShowCursor(TRUE);<br />
<br />
		ClipCursor(NULL);<br />
		SetCursorPos(m_scrCursorPos.x,m_scrCursorPos.y);<br />
		SetCursor(LoadCursor(NULL, IDC_ARROW));<br />
		ReleaseCapture();<br />
	}<br />
<br />
	CSpinButtonCtrl::OnLButtonUp(nFlags, point);<br />
}<br />
<br />
<br />
<br />
void CNumSpinCtrl::OnMouseMove(UINT nFlags, CPoint point) <br />
{<br />
	if (m_bLButtonHold)<br />
	{<br />
		int delta = m_ctrlCursorPos.y-point.y;<br />
		if (delta || (m_ctrlCursorPos.x-point.x) )<br />
		{<br />
			if (!m_bTrack)<br />
			{<br />
				m_bTrack = TRUE;<br />
				SetCapture();<br />
			}<br />
			else<br />
			{<br />
				SetCursor(LoadCursor(NULL, IDC_SIZENS));<br />
				SetCursorPos(m_scrCursorPos.x,m_scrCursorPos.y); <br />
<br />
				double val = GetPos () + m_Delta * delta;<br />
				const bool can_wrap = (UDS_WRAP & GetStyle());<br />
<br />
				if (can_wrap)<br />
				{<br />
<br />
					if (delta < 0) // wrap down<br />
					{<br />
						double abs_eps = fabs(EPS * max (val, m_MinVal));<br />
						if (abs_eps < EPS) <br />
							abs_eps = EPS;<br />
						<br />
						if (m_MinVal - val > abs_eps)<br />
								val = m_MaxVal;<br />
					}<br />
					else  // wrap up<br />
					{<br />
						double abs_eps = fabs(EPS * max (val, m_MaxVal));<br />
						if (abs_eps < EPS) <br />
							abs_eps = EPS;<br />
					<br />
						if (val - m_MaxVal > abs_eps)  <br />
								val = m_MinVal;<br />
					}<br />
				}<br />
<br />
				//After 'warp', limit values<br />
				if (val > m_MaxVal)<br />
					val=m_MaxVal;<br />
<br />
				if (val < m_MinVal)<br />
					val=m_MinVal;<br />
<br />
<br />
				SetValueForBuddy (val);<br />
			}<br />
		}<br />
	}	<br />
<br />
	CSpinButtonCtrl::OnMouseMove(nFlags, point);<br />
}<br />



Most of this last member was taken from OnDeltaPos - but to be applied when 'tracking' the mouse movement.

Now, It would be nice if any REAL programmer made real "CSpinTrackCtrl" for us Smile | :)

Regards..




Emilio Le Roux

www.moscafilms.com.br/emilioleroux

"It's never too late to get some sleep"
AnswerRe: How about a draggable spin control? Pin
Damir Valiulin24-May-05 5:23
Damir Valiulin24-May-05 5:23 
GeneralRe: How about a draggable spin control? Pin
eleroux24-May-05 6:44
eleroux24-May-05 6:44 
GeneralRe: How about a draggable spin control? Pin
gizmocuz19-Nov-06 9:24
gizmocuz19-Nov-06 9:24 
GeneralFixes when buddy control is manually edited Pin
Hard Hat12-Oct-03 1:27
Hard Hat12-Oct-03 1:27 
GeneralRe: Fixes when buddy control is manually edited Pin
Damir Valiulin14-Oct-03 8:38
Damir Valiulin14-Oct-03 8:38 
GeneralExcellent stuff.... Pin
adrian cooper9-Sep-03 5:40
adrian cooper9-Sep-03 5:40 
GeneralThe CNumSpinCtrl can't display on toolbar!!! Pin
ChinaThink28-Aug-03 16:14
ChinaThink28-Aug-03 16:14 
GeneralRe: The CNumSpinCtrl can't display on toolbar!!! Pin
Damir Valiulin2-Sep-03 4:10
Damir Valiulin2-Sep-03 4:10 
GeneralYou can do this with a regular CEdit and Spin control Pin
Roger Allen23-Aug-02 4:20
Roger Allen23-Aug-02 4:20 
GeneralRe: You can do this with a regular CEdit and Spin control Pin
Damir Valiulin23-Aug-02 4:31
Damir Valiulin23-Aug-02 4:31 
GeneralRe: You can do this with a regular CEdit and Spin control Pin
Roger Allen23-Aug-02 4:46
Roger Allen23-Aug-02 4:46 
GeneralNice Job! Pin
Jim Hawkins29-Jul-02 5:35
Jim Hawkins29-Jul-02 5:35 
GeneralIt can't work in dialog bar ! Pin
lijshuchina13-Jul-02 16:25
lijshuchina13-Jul-02 16:25 
GeneralRe: It can't work in dialog bar ! Pin
Damir Valiulin15-Jul-02 3:29
Damir Valiulin15-Jul-02 3:29 
Generalstack overflow software exception Pin
suma553-Jul-02 7:54
suma553-Jul-02 7:54 
GeneralSmall bug found -> correction Pin
13-Jun-02 9:29
suss13-Jun-02 9:29 
GeneralRe: Small bug found -> correction Pin
3-Jul-02 8:31
suss3-Jul-02 8:31 
GeneralRe: Small bug found -&gt; correction Pin
tattalevieuxgrincheux16-May-03 11:29
tattalevieuxgrincheux16-May-03 11:29 
GeneralRe: Small bug found -&gt; correction Pin
tattalevieuxgrincheux16-May-03 11:30
tattalevieuxgrincheux16-May-03 11:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.