§ April 15, 2005

Numeric Text Box: A number only text box control in C#

Update: (Oct 24 2008) Carlos Montiers has graciously provided an improved version which you can view here. Thanks Carlos!

Here's my number only text control. I'll admit there's not much too it, and Widbey has a masked edit control, but for those who cant use that, or need something right now, this will do.

/******************************************************/
/*          NULLFX FREE SOFTWARE LICENSE              */
/******************************************************/
/*  NumericTextBox Library                            */
/*  by: Steve Whitley                                 */
/*  © 2005 NullFX Software                            */
/*                                                    */
/* NULLFX SOFTWARE DISCLAIMS ALL WARRANTIES,          */
/* RESPONSIBILITIES, AND LIABILITIES ASSOCIATED WITH  */
/* USE OF THIS CODE IN ANY WAY, SHAPE, OR FORM        */
/* REGARDLESS HOW IMPLICIT, EXPLICIT, OR OBSCURE IT   */
/* IS. IF THERE IS ANYTHING QUESTIONABLE WITH REGARDS */
/* TO THIS SOFTWARE BREAKING AND YOU GAIN A LOSS OF   */
/* ANY NATURE, WE ARE NOT THE RESPONSIBLE PARTY. USE  */
/* OF THIS SOFTWARE CREATES ACCEPTANCE OF THESE TERMS */
/*                                                    */
/* USE OF THIS CODE MUST RETAIN ALL COPYRIGHT NOTICES */
/* AND LICENSES (MEANING THIS TEXT).                  */
/*                                                    */
/******************************************************/

namespace NullFX.Controls {
    using System;
    using System.Windows.Forms;
    public class NumericTextBox : TextBox {
        int WM_KEYDOWN = 0x0100,
            WM_PASTE = 0x0302;
        public override bool PreProcessMessage(ref Message msg) {
            if(msg.Msg == WM_KEYDOWN) {
                Keys keys = (Keys)msg.WParam.ToInt32();
                bool numbers = ((keys >= Keys.D0 && keys <= Keys.D9)
                    || (keys >= Keys.NumPad0 && keys <= Keys.NumPad9)) && ModifierKeys != Keys.Shift;
                bool ctrl = keys == Keys.Control;
                bool ctrlZ = keys == Keys.Z && ModifierKeys == Keys.Control,
                    ctrlX = keys == Keys.X && ModifierKeys == Keys.Control,
                    ctrlC = keys == Keys.C && ModifierKeys == Keys.Control,
                    ctrlV = keys == Keys.V && ModifierKeys == Keys.Control,
                    del = keys == Keys.Delete,
                    bksp = keys == Keys.Back,
                    arrows = (keys == Keys.Up)
                    |(keys == Keys.Down)
                    |(keys == Keys.Left)
                    |(keys == Keys.Right);
                if(numbers | ctrl | del | bksp
                                 | arrows | ctrlC | ctrlX | ctrlZ)
                    return false;
                else if(ctrlV) {
                    IDataObject obj = Clipboard.GetDataObject();
                    string input = (string)obj.GetData(typeof(string));
                    foreach(char c in input) {
                        if(!char.IsDigit(c)) return true;
                    }
                    return false;
                }else
                    return true;
            }else


                return base.PreProcessMessage(ref msg);
        }
        protected override void WndProc(ref Message m) {
            if(m.Msg == WM_PASTE) {
                IDataObject obj = Clipboard.GetDataObject();               
                string input = (string)obj.GetData(typeof(string));
                foreach(char c in input) {
                    if(!char.IsDigit(c)) {
                        m.Result = (IntPtr)0;
                        return;
                    }
                }               
            }
            base.WndProc (ref m);
        }
    }
}

What this does, is watch the Windows Message Queue for incoming messages (specifically WM_KEYDOWN and WM_PASTE). If a key has been pressed, it will look to see if the key is actually a number. It also looks to see if a paste command has been sent. if a key has been pressed and it is a numeric key (there are a few more keys for convenience sake) or the paste operation has been invoked (the contents of the paste operation are checked to make sure that anything that gets pasted is a number, else it fails to paste in the data) and its contents are numeric, allows that data to be pasted into the textbox.

For Clarity sake, PreProcessMessage is invoked before the Message is sent into the message queue. If the control handles this message, it returns true, and the message ends there. If it returns false, it will send the Message into the message pump for further processing. WndProc is akin to WindowProc (but for .NET), and one can filter message sent here like they used to in standard windows programming. In this sample, we're peeking at the KEYDOWN message as mentioned, in the PreProcessMessage function. Commands are not sent through here, but are passed in WndProc, where we can listen, and handle this Message ourselves (because we need to check the contents of the clip board for numeric only data).

If PreProcessMessage's Message contains a number key, it returns false (which forces the base TextBox to handle the instruction like normal), and if not, it returns true (meaning we've handled it and it doesn't get passed into the base TextBox control), and nothing is entered into the textbox. If WndProc receives a numeric only string in the clip board, then it sends the message to the base TextBox
base.WndProc(ref m);
if the cliboard contains alpha or other data, it sets the LResult to 0, and returns without passing the message to the base TextBox (effectively saying we've handled the message, and there's no need to process it any further).



Posted 13 years, 1 month ago on April 15, 2005

 Comments can be posted in the forums.

© 2003 - 2017 NullFX
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License