Page 1 of 1

wxNumberCtrl - a flexible wxTextCtrl accepting numbers only

Posted: Sun Sep 16, 2007 9:49 am
by C_Bastian
Hi,

I came up to the point that I need a control for the input of numbers which should be highly flexible. Perhaps someone finds it usual, so I put it here.

This control behaves like a wxTextCtrl (which it is derived from), but accepts numbers only. You can define min and max values, you can define if the number should be int only (no decimal point accepted) or that certain numbers should not be shown (eg. if you want to have a blank field instead of showing a "0").

If you find any errors or write any extensions, please post them here.

Tested under Windows with VC only.

So here we go:

wxNumberCtrl.h

Code: Select all



#ifndef _WXNUMBERCTRL_H_
#define _WXNUMBERCTRL_H_

#ifdef __GNUG__
#pragma interface "wxNumberCtrl.cpp"
#endif

// includes
#include "wx/wx.h"
#include "wx/textctrl.h"

class wxNumberCtrl: public wxTextCtrl
{
    DECLARE_CLASS( wxNumberCtrl )
    public:
        wxNumberCtrl( ){};

        wxNumberCtrl(wxWindow* parent, wxWindowID id, const int value = 0, 
            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
            long style = 0, const wxValidator& validator = wxDefaultValidator, 
            const wxString& idname = wxTextCtrlNameStr)
            {
                Create( parent, id, value, pos, size, style, validator, idname);
            };
            
        wxNumberCtrl(wxWindow* parent, wxWindowID id, wxString s, 
            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
            long style = 0, const wxValidator& validator = wxDefaultValidator, 
            const wxString& idname = wxTextCtrlNameStr)
            {
                Create( parent, id, 0, pos, size, style, validator, idname);
            };
        
        /// Creation
        bool Create(wxWindow* parent, wxWindowID id, const int value = 0, 
            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
            long style = 0 , const wxValidator& validator = wxDefaultValidator, 
            const wxString& idname = wxTextCtrlNameStr);
            
        // Methoden zum Zugriff auf interne Variablen
        
        //Lesen
        int GetInt();
        double GetDouble();
        
        //Setzen
        
        void SetRingBell(bool ring);
        void SetInt(int i);
        void SetDouble(double d, wxString s=wxT("%0.9g"));
        void SetRange(double minimum, double maximum);
        void SetNoShow(double ns, bool b=true);
        void AcceptIntOnly(bool b);
       
    private:
        
        DECLARE_EVENT_TABLE()

        void OnKillFocus(wxFocusEvent& event);        
        void OnChar(wxKeyEvent& event);
        void OnKeyUp(wxKeyEvent& event);
        
        
        wxString oldvalue;
        int min, max;
        
        double noshownumber;
                
        bool ringbell, noshow, intonly;
};
#endif
wxNumberCtrl.cpp:

Code: Select all


#ifdef __GNUG__
#pragma implementation "wxNumberCtrl.h"
#endif

#include "wx/wx.h"
#include "wx/textctrl.h"
#include "wxNumberCtrl.h"

IMPLEMENT_CLASS( wxNumberCtrl, wxTextCtrl )

BEGIN_EVENT_TABLE( wxNumberCtrl, wxTextCtrl )
    EVT_KEY_DOWN(wxNumberCtrl::OnChar )
    EVT_KEY_UP(wxNumberCtrl::OnKeyUp)
    EVT_KILL_FOCUS(wxNumberCtrl::OnKillFocus )
END_EVENT_TABLE()


bool wxNumberCtrl::Create(wxWindow* parent, wxWindowID id, const int value, 
            const wxPoint& pos, const wxSize& size, 
            long style, const wxValidator& validator, 
            const wxString& idname)
{
    wxString s;
    if (value!=0)
        s.Printf("%i",value);
    max=INT_MAX;
    min=-(INT_MAX-1);  
    noshow=false;
    
    if (!wxTextCtrl::Create(parent, id, s, pos, size, style, validator, idname))
        return false;
    else
        return true;
    

}

void wxNumberCtrl::SetRange(double minimum, double maximum)
{
    double d;
    if (minimum>maximum)
    {
        d=minimum;
        minimum=maximum;
        maximum=d;
    }
    min=minimum;
    max=maximum;
   
}

void wxNumberCtrl::SetRingBell(bool ring)
{
    ringbell=ring;
}

int wxNumberCtrl::GetInt() 
{
    wxString s;
    long val;
    s=GetValue();
    if (noshow)
    {
        if (GetLastPosition()==0)
            val=noshownumber;
        else
            s.ToLong(&val, 10);
    }
    else
    {
        s.ToLong(&val, 10);
    }
    return (int) val;
}

void wxNumberCtrl::SetInt(int i)
{
    wxString s;
    s.Printf("%i",i);
    SetValue(s);
    if ((noshow)&&(i==noshownumber))
        Clear();

}

double wxNumberCtrl::GetDouble()
{
    wxString s; 
    s=wxTextCtrl::GetValue();
    double val;   
    if (noshow)
    {
        if (GetLastPosition()==0)
            val=noshownumber;
        else
            s.ToDouble(&val);
    }
    else
    {
        s.ToDouble(&val);
    }
    return val;
}

void wxNumberCtrl::SetDouble(double d, wxString s)
{
    wxString t;
    t.Printf(s,d);
    SetValue(t);   
    if ((noshow)&&(d==noshownumber))
        Clear();
}

void wxNumberCtrl::OnChar(wxKeyEvent& event)
{
    oldvalue=GetValue();

    switch (event.GetKeyCode())
    {
    
        case '0':
        case WXK_NUMPAD0:  
            if (!((GetDouble()==0)&&(oldvalue.Find('.')==-1))) //keine doppelte Null
                event.Skip();
            break;
        
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case WXK_NUMPAD1:
        case WXK_NUMPAD2:
        case WXK_NUMPAD3:
        case WXK_NUMPAD4:
        case WXK_NUMPAD5:
        case WXK_NUMPAD6:
        case WXK_NUMPAD7:
        case WXK_NUMPAD8:
        case WXK_NUMPAD9:
            if(GetDouble()==0) Clear();
            
        case WXK_DELETE:    
        case WXK_RIGHT:
        case WXK_LEFT:        
        case WXK_BACK:
        case WXK_TAB: 
        case WXK_NUMPAD_END:
        case WXK_NUMPAD_BEGIN:
        case WXK_HOME:
        case WXK_END:
        case WXK_RETURN:
            event.Skip();  
            break;
        
        case WXK_NUMPAD_DECIMAL:
        case '.':
        case ',':
            if (!intonly)
                if (oldvalue.Find('.')==-1) //keine doppelten Punkte zulassen
                    WriteText(".");  
            break;
        
        case WXK_NUMPAD_SUBTRACT:
        case '-':
            if (min<0)
                event.Skip();
            break;
            
        default:
            if (ringbell) wxBell();
                event.Skip(false);
            break;
    }

}

void wxNumberCtrl::OnKeyUp(wxKeyEvent& event)
{
    double d=GetDouble();
    if ((d>max)||(d<min))
    {
        SetValue(oldvalue);
        if (ringbell) wxBell();
    }

}

void wxNumberCtrl::SetNoShow(double ns, bool b)
{
    noshow=b;
    noshownumber=ns;
}


void wxNumberCtrl::OnKillFocus(wxFocusEvent& event)
{    
    if (noshow)
    {
        if (GetDouble()==noshownumber)
            Clear();
    }
}

void wxNumberCtrl::AcceptIntOnly(bool b)
{
    intonly=b;
}

Have fun.

Sebastian

Posted: Mon Sep 17, 2007 7:43 am
by priyank_bolia
You can also use validators and wxRegExValidator

Posted: Fri Jun 27, 2008 10:06 am
by kingkamg
wxTextValidator validator(wxFILTER_INCLUDE_CHAR_LIST);

wxArrayString list;

wxString valid_chars(wxT(" 0123456789"));
size_t len = valid_chars.Length();
for (size_t i=0; i<len; i++)
list.Add(wxString(valid_chars.GetChar(i)));

validator.SetIncludes(list);
wxTextCtrl* textCtrl = new wxTextCtrl( this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, 0,validator );

Posted: Wed Nov 19, 2008 9:45 am
by nuvola_notturna
kingkamg wrote:wxTextValidator validator(wxFILTER_INCLUDE_CHAR_LIST);

wxArrayString list;

wxString valid_chars(wxT(" 0123456789"));
size_t len = valid_chars.Length();
for (size_t i=0; i<len; i++)
list.Add(wxString(valid_chars.GetChar(i)));

validator.SetIncludes(list);
wxTextCtrl* textCtrl = new wxTextCtrl( this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, 0,validator );
I also was looking for a kind of "number validator", and I run into this.
Using SetIncludes() is ok to set valid characters, but it doesn't consider the actual value inserted. So, how can I set something like max and min values, etc..
Actually, I was thinking about writing my own Validator, rather than deriving the control itself. Do you think there's a simpler way?

Posted: Wed Nov 19, 2008 11:10 am
by priyank_bolia
I guess you can write an regular expression to check for the min and max range: like between 100 and 200
something like: [1][0-9][0-9]|200
Deriving a new control, won't be good idea just for validation, that the validator job.

Posted: Wed Nov 19, 2008 11:35 am
by nuvola_notturna
priyank_bolia wrote:I guess you can write an regular expression to check for the min and max range: like between 100 and 200
something like: [1][0-9][0-9]|200
Deriving a new control, won't be good idea just for validation, that the validator job.
Thanks for your help. Regex if fine but I think I'll try to derive a new validator (I agree with you about deriving a new control), just in order to have something more flexible (for example I would like to simply pass the range values, in a dynamic way).

Posted: Thu Nov 20, 2008 7:34 am
by priyank_bolia
Just a thought:
You can write a customizable validator, so that it can check not only for integers but also floating point values, and the number of digits after the decimal.
Also in European languages, the symbol for decimal and thousand separators are different, and also the location of separator, like in US its thousand separator but in India its power of 100 like: 9,99,000.50 Rs.
This would enable a generic validator that can be used by all without mush hassle.