Click here to Skip to main content
15,901,001 members
Articles / Programming Languages / C#
Article

Numeric TextBox : Allow your users to enter numeric data the easy way

Rate me:
Please Sign up or sign in to vote.
4.38/5 (25 votes)
20 Jun 20041 min read 463.2K   5.4K   56   44
A TextBox control for numeric input, with the ability to set precision, scale (when focused or not)

Introduction

Numeric input has always been one of the key-features in many applications (CRM, Accounting,...). Although MS provides a masked edit control, many developers find it to have some limitations.

The Numeric TextBox tries to provide some features to allow the user to input numeric data in a natural way. Let's have a look at some of the key features.

  1. Allow/Disallow negative input.
  2. Specify the maximum precision and scale.
  3. Specify a different scale when the control hasn't got the focus.
  4. Allow/Disallow zero as a valid input.
  5. The control displays grouping characters.
  6. The control adapts the regional settings (grouping char, decimal char).

Using the code

To use the control simply reference the Shorty.Windows.Forms assembly in your project. Create (or drag and drop on the form) a NumericTextBox and set the properties. The properties can be set trough code or via the property pane (Numeric settings section). See below for an example.

C#
// 
// numericTextBox1
// 
Shorty.Windows.Forms.NumericTextBox() numericTextBox1 = 
  new Shorty.Windows.Forms.NumericTextBox();

this.numericTextBox1.AllowNegative = true;
this.numericTextBox1.DataBindings.Add(
  new System.Windows.Forms.Binding("NumericValue", 
  this, "Table1.test"));
this.numericTextBox1.NumericPrecision = 8;
this.numericTextBox1.NumericScaleOnFocus = 5;
this.numericTextBox1.NumericScaleOnLostFocus = 3;
this.numericTextBox1.NumericValue = 0;
this.numericTextBox1.ZeroIsValid = false;

The control automatically checks if the combination NumericPrecision NumericScaleOn(Lost)Focus is valid. As you can see in the above sample to retrieve the numeric value displayed in the NumericTextBox you should use the NumericValue property. The control is also capable of databinding it's NumericValue property. (See above code sample).

History

  • Initial release version 0.1.0.0 (20/06/2004)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Belgium Belgium
3 years ago I started out with VB 6 and ASP. After seeing .Net in Beta in 2001 I haven't done anything else. I moved from VB6 over VB.Net to C#.
In 2004 I earned my MCAD. Currently I am employed as a Analyst/Developer and I'm working towards my MCDBA (1 exam to go Smile | :) ).

You can follow me at my blog http://radio.weblogs.com/0132279/

Comments and Discussions

 
GeneralRe: Drop Control on to form Pin
Mahmood Dehghan28-Mar-08 23:08
Mahmood Dehghan28-Mar-08 23:08 
GeneralException Pin
naalexander27-Jul-06 3:12
naalexander27-Jul-06 3:12 
GeneralRe: Exception Pin
Carlo Bos11-Jan-07 8:11
Carlo Bos11-Jan-07 8:11 
GeneralRe: Exception Pin
richm12234513234128-Apr-07 21:49
richm12234513234128-Apr-07 21:49 
GeneralThanks, very helpful . Pin
Roman Lerman23-Jul-06 0:54
Roman Lerman23-Jul-06 0:54 
GeneralWhat's about Cut, Copy and Paste Pin
carloqueirolo22-Jul-06 7:28
carloqueirolo22-Jul-06 7:28 
GeneralRe: What's about Cut, Copy and Paste Pin
Ciupaz12-Apr-11 0:11
Ciupaz12-Apr-11 0:11 
Generalmy numtextbox control Pin
byshome23-Mar-06 17:04
byshome23-Mar-06 17:04 
using System;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
namespace NumTextBox
{
[System.ComponentModel.DefaultEvent("TextBoxChanged"),
System.ComponentModel.DefaultProperty("Text"),
ToolboxBitmap(typeof(System.Windows.Forms.TextBox))]
///
/// NumTextBox 的摘要说明。
///

public class TextBoxNumEx : System.Windows.Forms.TextBox
{
///
/// 必需的设计器变量。
///

private System.ComponentModel.Container components = null;

//以下是消息号,参考WinUser.h中的申明
public const int WM_SETFOCUS = 0x0007; //得到焦点
public const int WM_KILLFOCUS = 0x0008; //失去焦点
public const int WM_CONTEXTMENU = 0x007b; //右键菜单消息
public const int WM_CHAR = 0x0102; //输入字符消息(键盘输入的,输入法输入的好像不是这个消息)
public const int WM_CUT = 0x0300; //程序发送此消息给一个编辑框或combobox来删除当前选择的文本
public const int WM_COPY = 0x0301; //程序发送此消息给一个编辑框或combobox来复制当前选择的文本到剪贴板
public const int WM_PASTE = 0x0302; //程序发送此消息给editcontrol或combobox从剪贴板中得到数据
public const int WM_CLEAR = 0x0303; //程序发送此消息给editcontrol或combobox清除当前选择的内容;
public const int WM_UNDO = 0x0304; //程序发送此消息给editcontrol或combobox撤消最后一次操作

#region 属性
private string strFormat = "G";
///
/// 数值格式
///

[System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.All),
System.ComponentModel.Category("数值相关设置"),
System.ComponentModel.Description("数值格式")]
public virtual string Format
{
set
{
string format;
string strPrecision;
format = value;
decimal decTest = 325456245.021564M;
try
{
decTest.ToString(format);
}
catch(Exception)
{
format = "G"; //无效的格式时采用G
}
strFormat = format;
strPrecision = decTest.ToString(format);
//设置最大的精度
this.NumericPrecision = strPrecision.IndexOf('.') < 0 ? 0 : strPrecision.Length - strPrecision.IndexOf('.') - 1;
this.Text = this.Text;
}
get
{
return strFormat;
}
}
///
/// 当前数值格式的最大精度
///

private int NumericPrecision = 2;
private bool bZeroIsValid = true;
///
/// 0值是否显示有效
///

[System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.All),
System.ComponentModel.Category("数值相关设置"),
System.ComponentModel.Description("0值是否显示有效")]
public virtual bool ZeroIsValid
{
set
{
bZeroIsValid = value;
this.Text = this.Text;
}
get
{
return bZeroIsValid;
}
}
private bool bAllowNegative = true;
///
/// 是否允许输入负数
///

[System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.All),
System.ComponentModel.Category("数值相关设置"),
System.ComponentModel.Description("是否允许输入负数")]
public virtual bool AllowNegative
{
set
{
bAllowNegative = value;
if (!value)
this.Text = this.Text;
}
get
{
return bAllowNegative;
}
}
#endregion

public TextBoxNumEx()
{
InitializeComponent();
//
// TODO: 在此处添加构造函数逻辑
//
base.Text = "0";
this.SelectionStart = 0;
//目前无法找到按下Delete键的消息值,所以采用事件来处理按下Delete的情况
this.KeyDown += new KeyEventHandler(TextBoxNumEx_KeyDown);
this.KeyUp += new KeyEventHandler(TextBoxNumEx_KeyUp);
this.TextAlign = HorizontalAlignment.Right;
}
///
/// 清理所有正在使用的资源。
///

protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region 控件设计器生成的代码
///
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
///

private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
#region 重载的方法与属性
///
/// 重载的Text属性
///

[System.ComponentModel.DefaultValue(0)]
public override string Text
{
get
{
string strReturn = "0";
string strBaseText = base.Text;
while(strBaseText.IndexOf(',') >= 0)
{
strBaseText = strBaseText.Replace(",","");
}
if (strBaseText.Length > 0 && strBaseText.LastIndexOf('.') == strBaseText.Length - 1)
strBaseText = strBaseText.Substring(0,strBaseText.LastIndexOf('.'));
if (strBaseText == string.Empty || strBaseText == "-")
strBaseText = "0";
if (strBaseText.IndexOf('.') >= 0) //如果显示的是.0001或-.90的显示时,自动在.前面补0
if (strBaseText.Substring(0,strBaseText.IndexOf('.')) == "-" || strBaseText.Substring(0,strBaseText.IndexOf('.')) == string.Empty)
strBaseText = strBaseText.Substring(0,strBaseText.IndexOf('.')) + "0" + strBaseText.Substring(strBaseText.IndexOf('.'));
try
{
decimal.Parse(strBaseText).ToString(this.Format);
}
catch(Exception)
{
//应该不会出错!出错不处理
strBaseText = "0";
}
strReturn = strBaseText;
return strReturn;
}
set
{
decimal decValue;
if (value == null || value == string.Empty)
value = "0";
try
{
decValue = decimal.Parse(value);
}
catch(Exception)
{
//MessageBox.Show("输入的值不是有效的值或超出范围!"+ex.Message, "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
try
{
decValue = decimal.Parse(this.Text);
}
catch(Exception)
{
decValue = 0;
}
}
if (decValue == 0 && !this.ZeroIsValid)
base.Text = string.Empty;
else
base.Text = decValue.ToString(this.Format);
}
}
///
/// 不允许多行显示
///

public override bool Multiline
{
get
{
return base.Multiline;
}
set
{
base.Multiline = false;
}
}

///
/// 重载消息处理
///

/// <param name="m" />
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case WM_SETFOCUS: //得到焦点时,重新设置光标位置
if (base.Text == string.Empty)
base.Text = (0.0M).ToString(this.Format);
base.WndProc(ref m);
this.SelectionStart = 0;
break;
case WM_KILLFOCUS: //焦点移开时,重新设置显示
this.Text = base.Text;
base.WndProc(ref m);
break;
case WM_CHAR:
bool isSign = ((int)m.WParam == 45); //减号
bool isNum = ((int)m.WParam >= 48) && ((int)m.WParam <= 57); //数字
bool isBack = (int)m.WParam == (int)Keys.Back; //退格键
bool isDelete = (int)m.WParam == (int)Keys.Delete;//实际上这是一个"."键
bool isCtr = ((int)m.WParam == 24) || ((int)m.WParam == 22) || ((int)m.WParam == 26) ||((int)m.WParam == 3);

if( isNum || isBack || isCtr)
{
if (isBack)
{
//处理不能删除 .
if (this.SelectionStart == base.Text.IndexOf('.') + 1)
break;
}
if (isNum)
{
//减号前面不能输入数字
if (this.SelectionStart == 0 && base.Text.IndexOf('-') >= 0)
break;
if (base.Text.IndexOf('.') >= 0 && this.SelectionStart - base.Text.IndexOf('.') > this.NumericPrecision)
{
return;
}

}
base.WndProc (ref m);
}
if (isSign)
{
if (!this.AllowNegative) //不允许输入负数
break;
if (this.SelectionStart != 0) //当前光标不在第一位时不允许输入减号
break;
if (this.Text.IndexOf('-') < 0)
base.WndProc (ref m);
else
this.SelectionStart = 1; //第一位已经是减号时,光标重新定位
break;
}
if (isDelete && this.NumericPrecision > 0) //只有当前的精度大于0才能输入点
{
if (base.Text.IndexOf('.') < 0)
{
base.WndProc (ref m);
}
else
{
if (base.Text.IndexOf('.') < this.SelectionStart)
break;
base.Text = base.Text.Substring(0,this.SelectionStart) + "." + base.Text.Substring(base.Text.LastIndexOf('.') + 1);
this.SelectionStart = base.Text.IndexOf('.') + 1;
}
}
if ((int)m.WParam == 1)
{
this.SelectAll();
}
if (!isSign) //输入的不是 - 的话,重新格式化一次
{
bool bNegative = false; //是不是负数
string strBaseText = base.Text;
while(strBaseText.IndexOf(',') >= 0)
{
strBaseText = strBaseText.Replace(",","");
}
if (strBaseText.Length > 0 && strBaseText.LastIndexOf('.') == strBaseText.Length - 1)
strBaseText = strBaseText.Substring(0,strBaseText.LastIndexOf('.'));
if (strBaseText.Length > 0)
bNegative = strBaseText.Substring(0,1) == "-";
if (strBaseText == string.Empty || strBaseText == "-")
strBaseText = "0";
if (strBaseText.IndexOf('.') >= 0) //如果显示的是.0001或-.90的显示时,自动在.前面补0
if (strBaseText.Substring(0,strBaseText.IndexOf('.')) == "-" || strBaseText.Substring(0,strBaseText.IndexOf('.')) == string.Empty)
strBaseText = strBaseText.Substring(0,strBaseText.IndexOf('.')) + "0" + strBaseText.Substring(strBaseText.IndexOf('.'));
//如果有精度控制的话,判断是否越过了精度
if (strBaseText.IndexOf('.') >= 0)
if (strBaseText.Substring(strBaseText.IndexOf('.') + 1).Length > this.NumericPrecision)
strBaseText = strBaseText.Substring(0,strBaseText.IndexOf('.') + 1) + strBaseText.Substring(strBaseText.IndexOf('.') + 1, this.NumericPrecision);
try
{
strBaseText = decimal.Parse(strBaseText).ToString(this.Format);
}
catch(Exception)
{
//应该不会出错!出错不处理
}
if (bNegative && strBaseText.Length > 0)
{
if (strBaseText.Substring(0,1) != "-")
strBaseText = "-" + strBaseText;
}
//记录光标位置。小数点左边显示正数,否则显示负数,整数以长度为小数点位置
int iCursorPosition = base.Text.IndexOf('.') >= 0 ? base.Text.IndexOf('.') - this.SelectionStart : base.Text.Length - this.SelectionStart;
//设置显示时先清空显示,然后再加载.否则会显示不正确
base.Text = string.Empty;
base.Text = strBaseText;
//重新定位
this.SetCursorPosition(iCursorPosition);
}
break;
case WM_PASTE:
IDataObject iData = Clipboard.GetDataObject();//取剪贴板对象
if(iData.GetDataPresent(DataFormats.Text)) //判断是否是Text
{
bool bNegative = false;
string str = (string)iData.GetData(DataFormats.Text);//取数据
//取消空格
str = str.Trim();
if (MatchNumber(str))
{
string strBaseText = str;
while(strBaseText.IndexOf(',') >= 0)
{
strBaseText = strBaseText.Replace(",","");
}
if (strBaseText.Length > 0 && strBaseText.LastIndexOf('.') == strBaseText.Length - 1)
strBaseText = strBaseText.Substring(0,strBaseText.LastIndexOf('.'));
if (strBaseText.Length > 0)
bNegative = strBaseText.Substring(0,1) == "-";
if (strBaseText == string.Empty || strBaseText == "-")
strBaseText = "0";
if (strBaseText.IndexOf('.') >= 0) //如果显示的是.0001或-.90的显示时,自动在.前面补0
if (strBaseText.Substring(0,strBaseText.IndexOf('.')) == "-" || strBaseText.Substring(0,strBaseText.IndexOf('.')) == string.Empty)
strBaseText = strBaseText.Substring(0,strBaseText.IndexOf('.')) + "0" + strBaseText.Substring(strBaseText.IndexOf('.'));
try
{
strBaseText = decimal.Parse(strBaseText).ToString(this.Format);
}
catch(Exception)
{
//应该不会出错!出错不处理
}
if (bNegative && strBaseText.Length > 0)
{
if (strBaseText.Substring(0,1) != "-")
strBaseText = "-" + strBaseText;
}
//设置显示时先清空显示,然后再加载.否则会显示不正确
base.Text = string.Empty;
base.Text = strBaseText;
this.SelectionStart = 0;
break;
}
}
m.Result = (IntPtr)0;//不可以粘贴
break;
default:
base.WndProc (ref m);
break;
}
}
///
/// 由于消息处理中无法得到Delete的消息。所以采用事件处理按下Delete键执行了删除后显示的问题
///

/// <param name="sender" />
/// <param name="e" />
private void TextBoxNumEx_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
{
if (this.SelectionStart == base.Text.IndexOf('.'))
e.Handled = true;
}
}
///
/// 由于消息处理中无法得到Delete的消息。所以采用事件处理按下Delete键执行了删除后显示的问题
///

/// <param name="sender" />
/// <param name="e" />
private void TextBoxNumEx_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete) //按删除时执行
{
bool bNegative = false;
string strBaseText = base.Text;
while(strBaseText.IndexOf(',') >= 0)
{
strBaseText = strBaseText.Replace(",","");
}
if (strBaseText.Length > 0 && strBaseText.LastIndexOf('.') == strBaseText.Length - 1)
strBaseText = strBaseText.Substring(0,strBaseText.LastIndexOf('.'));
if (strBaseText.Length > 0)
bNegative = strBaseText.Substring(0,1) == "-";
if (strBaseText == string.Empty || strBaseText == "-")
strBaseText = "0";
if (strBaseText.IndexOf('.') >= 0) //如果显示的是.0001或-.90的显示时,自动在.前面补0
if (strBaseText.Substring(0,strBaseText.IndexOf('.')) == "-" || strBaseText.Substring(0,strBaseText.IndexOf('.')) == string.Empty)
strBaseText = strBaseText.Substring(0,strBaseText.IndexOf('.')) + "0" + strBaseText.Substring(strBaseText.IndexOf('.'));
try
{
strBaseText = decimal.Parse(strBaseText).ToString(this.Format);
}
catch(Exception)
{
//应该不会出错!出错不处理
}
if (bNegative && strBaseText.Length > 0)
{
if (strBaseText.Substring(0,1) != "-")
strBaseText = "-" + strBaseText;
}
//记录光标位置
//记录光标位置。小数点左边显示正数,否则显示负数,整数以长度为小数点位置
int iCursorPosition = base.Text.IndexOf('.') >= 0 ? base.Text.IndexOf('.') - this.SelectionStart : base.Text.Length - this.SelectionStart;
//设置显示时先清空显示,然后再加载.否则会显示不正确
base.Text = string.Empty;
base.Text = strBaseText;
//重新定位
this.SetCursorPosition(iCursorPosition);
}
}
#endregion
#region 方法
protected virtual bool MatchNumber(string ClipboardText)
{
int index = 0;
string strNum = "-0.123456789,";

if (ClipboardText.Length == 0)
return false;
if (ClipboardText == "-" || ClipboardText == "." || ClipboardText == ",")
return false;
index = ClipboardText.IndexOf(strNum[0]);
if (index >= 0)
{
if (index > 0)
{
return false;
}
}

index = ClipboardText.IndexOf(strNum[2]);
if (index != -1 && index != ClipboardText.Length - 1)
{
index = ClipboardText.IndexOf(strNum[2], index + 1);
if (index != -1)
{
return false;
}
}

for(int i = 0; i < ClipboardText.Length; i++)
{
index = strNum.IndexOf(ClipboardText[i]);
if (index < 0)
{
return false;
}
}
return true;
}
///
/// 重新设置光标位置
///

/// <param name="iCursorPosition" />原先的光标位置
private void SetCursorPosition(int iCursorPosition)
{
if (iCursorPosition < 0) //光标在小数点的右边
{
if (base.Text.IndexOf('.') < 0)
this.SelectionStart = base.Text.Length;
else
this.SelectionStart = System.Math.Abs(base.Text.IndexOf('.') - iCursorPosition);
}
else //光标在小数点的左边
{
if (base.Text.IndexOf('.') < 0)
this.SelectionStart = base.Text.Length - iCursorPosition;
else
this.SelectionStart = System.Math.Abs(base.Text.IndexOf('.') - iCursorPosition);
}
}
#endregion
}
}




bys_home
Generalbug II Pin
lethedon1-Mar-06 5:15
lethedon1-Mar-06 5:15 
GeneralVery good numeric textbox - simple and efficient Pin
clody5-Dec-05 23:44
clody5-Dec-05 23:44 
GeneralRe: Very good numeric textbox - simple and efficient Pin
drjoju15-Mar-06 9:20
drjoju15-Mar-06 9:20 
GeneralExcellent Pin
Kia Rahimzadeh26-Oct-04 23:02
Kia Rahimzadeh26-Oct-04 23:02 
GeneralCursor position goes wrong when the comma appears Pin
Thuc.Nguyen7-Sep-04 17:43
professionalThuc.Nguyen7-Sep-04 17:43 
GeneralRe: Cursor position goes wrong when the comma appears Pin
asterixnz19-Sep-06 16:18
asterixnz19-Sep-06 16:18 
GeneralRe: Cursor position goes wrong when the comma appears Pin
R. van der Pal17-Dec-06 12:10
R. van der Pal17-Dec-06 12:10 
GeneralNice! Pin
Marc Clifton22-Aug-04 11:40
mvaMarc Clifton22-Aug-04 11:40 
GeneralRe: Nice! Pin
Hannes Decorte22-Aug-04 19:01
Hannes Decorte22-Aug-04 19:01 
Questionanother one used in web ? Pin
´ó±Ç×Ó¹·27-Jun-04 16:11
´ó±Ç×Ó¹·27-Jun-04 16:11 
GeneralComponent not in Toolbox Pin
adwilcox25-Jun-04 10:44
adwilcox25-Jun-04 10:44 
Questionbug?? Pin
Selvin25-Jun-04 8:18
Selvin25-Jun-04 8:18 
AnswerRe: bug?? Pin
Hannes Decorte26-Jun-04 0:55
Hannes Decorte26-Jun-04 0:55 

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.