Movatterモバイル変換


[0]ホーム

URL:


Wayback Machine
3 captures
09 Aug 2009 - 28 Mar 2012
AugJANMar
Previous capture04Next capture
200920122013
success
fail
COLLECTED BY
Organization:Alexa Crawls
Starting in 1996,Alexa Internet has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to theWayback Machine after an embargo period.
Collection:Alexa Crawls
Starting in 1996,Alexa Internet has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to theWayback Machine after an embargo period.
TIMESTAMPS
loading
The Wayback Machine - https://web.archive.org/web/20120104205209/http://www.codeproject.com:80/KB/edit/decimal_support.aspx
Click here to Skip to main content
8,384,724 members and growing!
EmailPassword Lost password?
Home
Search within:




Licence CPOL
First Posted 10 Nov 2007
Views 17,009
Downloads 331
Bookmarked 17 times

Decimal number support for your controls

ByAlexander Uckun | 18 Nov 2007
The WTL way of adding number support to your controls.
 
See Also
Print Article
add
Add to your CodeProject bookmarks
Discuss
Discuss this article
2
  3.86 (6 votes)

1
1 vote, 16.7%
2
1 vote, 16.7%
3
1 vote, 16.7%
4
3 votes, 50.0%
5
3.86/5 - 6 votes
μ 3.86, σa 2.21 [?]
Sponsored Links

Screenshot - decimal_support.jpg

Introduction

In this article, I will describe how to extend the edit control for validating decimal numbers (like in the above picture), and the problems of converting a string to double and vice versa.

Instead of creating a new class that is derived fromCEdit, I use multiple inheritance and templates to achieve the goal. This design allows you to combine the number support class with your favorite control, not justCEdit, and it is even possible to use theCDecimalSupport class for both WTL and MFC.

The problem

An edit control with theES_NUMBER style allows only digits to be entered into the edit control. On Windows XP (and above), the edit control shows a balloon tip (if enabled) in an attempt to enter a non-digit.

Unfortunately, an edit control with theES_NUMBER style set doesn't accept a decimal point or a negative sign. To get rid of this restriction, you have to subclass the edit control and handle theWM_CHAR message yourself.

The WTL solution

WTL and ATL use thecuriously recurring template pattern (CRTP) extensively. This makes it possible to combine different extensions for a class via multiple inheritance.

TheCDecimalSupport class, which handles theWM_CHAR message, is a class template without any base class.

template<class T>class CDecimalSupport{public://stores the decimal point returned by GetLocaleInfo  TCHAR m_DecimalSeparator[5];//stores the negative sign returned by GetLocaleInfo  TCHAR m_NegativeSign[6];  BEGIN_MSG_MAP(CDecimalSupport)  ALT_MSG_MAP(8)    MESSAGE_HANDLER(WM_CHAR, OnChar)  END_MSG_MAP()...

But, how is such a class, which isn't derived fromCEdit orCWindow, able to handle theWM_CHAR message?

LRESULT OnChar(UINT/*uMsg*/, WPARAM wParam, LPARAM/*lParam*/, BOOL& bHandled){if (wParam == m_DecimalSeparator[0])//The '.' key was pressed   {this->ReplaceSel(m_DecimalSeparator,true);//error C3861   }else   {   bHandled =false;   }return0;}

This is a first try of theOnChar function. TheOnChar function will replace the current selection with the decimal point, whenever the point key is pressed. Otherwise, it sets thebHandled flag tofalse, so the edit control can handle theWM_CHAR message itself. SinceCDecimalSupport doesn't have aReplaceSel member function and isn't derived from a base class with aReplaceSel member, this code generates an error (C3861 identifier not found).

Fortunately,CDecimalSupport is a base class in the CRTP, so we are able to use the template parameterT.

LRESULT OnChar(UINT/*uMsg*/, WPARAM wParam, LPARAM/*lParam*/, BOOL& bHandled){if (wParam == m_DecimalSeparator[0])//The '.' key was pressed  {//works if CDecimalSupport<T> is a base class of T    T* pT =static_cast<T*>(this);//compiles if T has a RelpaceSel function    pT->ReplaceSel(m_DecimalSeparator,true);  }else  {    bHandled =false;  }return0;}

WTL uses this technique, calledsimulated dynamic binding, as a replacement for virtual functions.

Using the code (WTL)

First, create a new edit control class that is derived fromCDecimalSupport, as shown below.

TheCHAIN_MSG_MAP_ALT enables theCDecimalSupport class to handle theWM_CHAR message.

class CNumberEdit :public CWindowImpl<CNumberEdit, CEdit>                  ,public CDecimalSupport<CNumberEdit>{public:    BEGIN_MSG_MAP(CNumberEdit)        CHAIN_MSG_MAP_ALT(CDecimalSupport<CNumberEdit>,8)    END_MSG_MAP()};

Now, you need to subclass the edit control in the dialog'sOnInit function (and don't forget to set theES_NUMBER style).

class CMainDlg :public ...{  ...  CNumberEdit myNumberEdit;  ...};LRESULT CMainDlg::OnInitDialog(UINT/*uMsg*/, WPARAM/*wParam*/,         LPARAM/*lParam*/, BOOL&/*bHandled*/){  ...  myNumberEdit.SublassWindow(GetDlgItem(IDC_NUMBER));  ...}

CDecimalSupport also has some member functions for converting from text to double and vice versa.

LRESULT CMainDlg::OnInitDialog(UINT/*uMsg*/, WPARAM/*wParam*/,         LPARAM/*lParam*/, BOOL&/*bHandled*/){  ...  myNumberEdit.SublassWindow(GetDlgItem(IDC_NUMBER));  myNumberEdit.LimitText(12);  myNumberEdit.SetDecimalValue(3.14159265358979323846);  ...}LRESULT CMainDlg::OnOK(WORD/*wNotifyCode*/, WORD wID, HWND/*hWndCtl*/, BOOL&/*bHandled*/){double d;bool ok = myNumberEdit.GetDecimalValue(d);  ...}

Using the code (MFC)

The main difference between the WTL and the MFC usage is the message handling.

In the MFC code, you have to write your ownOnChar function that callsCDecimalSupport::OnChar.

class CNumberEdit :public CEdit ,public CDecimalSupport<CNumberEdit>{protected:    afx_msgvoid OnChar( UINT nChar, UINT nRepCnt, UINT nFlags );    DECLARE_MESSAGE_MAP()};BEGIN_MESSAGE_MAP(CNumberEdit, CEdit)    ON_WM_CHAR()//}}AFX_MSG_MAPEND_MESSAGE_MAP()afx_msgvoid CNumberEdit::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ){  BOOL bHandled =false;  CDecimalSupport<CNumberEdit>::OnChar(0, nChar,0, bHandled);if (!bHandled) CEdit::OnChar(nChar , nRepCnt,  nFlags);}

Converting the control's text to double

A conversion function from string to double has to deal with two problems:

  • The string may contain invalid characters.
  • The string format could be locale-dependent.

The first problem is solved by using thestrtod instead of theatof function (atof simply returns 0.0 if the input cannot be converted). Thestrtod function is affected by the program's locale settings, which are changeable via thesetlocale function. TheTextToDouble function changes the decimal separator and the negative sign before it calls_tcstod. I recommend that you leave the locale unchanged in your program.

bool GetDecimalValue(double& d)const{  TCHAR szBuff[limit];static_cast<const T*>(this)->GetWindowText(szBuff, limit);return TextToDouble(szBuff , d);}bool TextToDouble(TCHAR* szBuff , double& d)const{//replace the decimal separator with .  TCHAR* point = _tcschr(szBuff , m_DecimalSeparator[0]);if (point)  {    *point = localeconv()->decimal_point[0];if (_tcslen(m_DecimalSeparator)>1)      _tcscpy(point  +1, point + _tcslen(m_DecimalSeparator));   }//replace the negative sign with -if (szBuff[0] == m_NegativeSign[0])   {    szBuff[0] = _T('-');if (_tcslen(m_NegativeSign)>1)      _tcscpy(szBuff  +1, szBuff + _tcslen(m_NegativeSign));   }  TCHAR* endPtr;  d = _tcstod(szBuff, &endPtr);return *endPtr == _T('\0');}

TheGetDecimalValue function converts the controls text to a double value. The result istrue, if the conversion was successful.

Display a decimal value in a control

If you wish to display a decimal value, you have to make some decisions first:

  • Is the string format locale-dependent? Yes.
  • Is the text length limited? Yes, callSetDecimalValue.
  • Are the digits after the decimal print limited? Yes, callSetFixedValue.
  • Is the result truncated or rounded? Rounded.
  • Display a leading zero (0.5 or .5)? A leading zero is always displayed.
  • Display trailing zeros (2.5 or 2.5000)? Trailing zeros aren't displayed.
  • Support scientific formatting (1.05e6 or 1050000)? Not supported.
  • Display a thousands separator (1,000,000)? Not supported.

I chose to use the_fcvt and the_ecvt functions instead ofsprintf, because these functions are unaffected by locale settings.

int SetFixedValue(double d,int count){int decimal_pos;int sign;char* digits = _fcvt(d,count,&decimal_pos,&sign);  TCHAR szBuff[limit];  DigitsToText(szBuff, limit , digits, decimal_pos, sign);returnstatic_cast<T*>(this)->SetWindowText(szBuff);}int SetDecimalValue(double d,int count){int decimal_pos;int sign;char* digits = _ecvt(d,count,&decimal_pos,&sign);  TCHAR szBuff[limit];  DigitsToText(szBuff, limit , digits, decimal_pos, sign);returnstatic_cast<T*>(this)->SetWindowText(szBuff);}int SetDecimalValue(double d){return SetDecimalValue(d , min(limit ,static_cast<const />(this)->GetLimitText()) -2);}

CDecimalSupport has two member functions,SetDecimalValue andSetFixedValue, to display a double value in the control. The only difference is the interpretation of thecount parameter.

  • SetFixedValue: Number of digits after decimal point.
  • SetDecimalValue: Number of digits stored.

The magic of templates

Maybe you wish to display a double value in a button control, is it possible to use theCDecimalSupport for this purpose? Yes, just create a new button class as shown below:

class CNumberButton :public CButton ,public CDecimalSupport<CNumberButton>{};

Did you ask: "Is this code legal C++, theCNumberButton class doesn't have aReplaceSel member function?"

In the template world, the code for theOnChar function is only generated when it is really needed. So, if nobody calls theOnChar function, no code is generated, and the compiler doesn't complain about a missingReplaceSel function.

You can use theCNumberButton class to set a button's text.

CNumberButton btn;btn.Attach(GetDlgItem(ID_APP_ABOUT));btn.SetDecimalValue(2.75,3);//compiles finebtn.SetDecimalValue(2.75);//error C2039: GetLimitText is not a member of CNumberButton

History

  • 18 Nov 2007: TheWM_PASTE message handler added.
  • 10 Nov 2007: Initial version.

License

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

About the Author

Alexander Uckun

Web Developer

Germany Germany

Member


loading...
Sign Up to vote  PoorExcellent
Add a reason or comment to your vote:x
Votes of 3 or less require a comment

Comments and Discussions

 
 RefreshFirstPrevNext
QuestionWM_CHAR?memberPJ Arends15:20 10 Nov '07  
AnswerRe: WM_CHAR?memberAlexander Uckun6:52 11 Nov '07  
Last Visit: 19:00 31 Dec '99     Last Update: 10:52 4 Jan '121

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

Permalink |Advertise |Privacy |Mobile
Web03 |2.5.120104.1 |Last Updated 18 Nov 2007
Article Copyright 2007 by Alexander Uckun
Everything elseCopyright ©CodeProject, 1999-2012
Terms of Use
Layout:fixed|fluid

The Daily Insider

[8]ページ先頭

©2009-2025 Movatter.jp