NumberBox

通常你会想要一个只接受数字的输入框。再次通过从标准控件派生,这很容易实现,例如:

using System;
using System.Windows.Forms;
using System.Globalization;

namespace StackOverflowDocumentation
{
    public class SONumberBox : SOTextBox
    {
        private int decPlaces;
        private int extraDecPlaces;
        private bool perCent;
        private bool useThouSep = true;
        private string decSep = ".";
        private string thouSep = ",";
        private double numVal;

        public SONumberBox() : base()

    {
    }

    public bool PerCent
    {
        get
        {
            return perCent;
        }
        set
        {
            perCent = value;
        }
    }

    public double Value
    {
        get
        {
            return numVal;
        }
        set
        {
            numVal = value;
            if (perCent)
            {
                double test = numVal * 100.0;
                this.Text = FormatNumber(test) + "%";
            }
            else
            {
                this.Text = FormatNumber(value);
            }
        }
    }
    public bool UseThousandSeparator
    {
        get
        {
            return useThouSep;
        }
        set
        {
            useThouSep = value;
        }
    }
    public int DecimalPlaces
    {
        get
        {
            return decPlaces;
        }
        set
        {
            decPlaces = value;
        }
    }
    public int ExtraDecimalPlaces
    {
        get
        {
            return extraDecPlaces;
        }
        set
        {
            extraDecPlaces = value;
        }
    }
    protected override void OnTextChanged(EventArgs e)
    {
        string newVal = this.Text;
        int len = newVal.Length;
        if (len == 0)
        {
            return;
        }
        bool neg = false;
        if (len > 1)
        {
            if (newVal.Substring(0, 1) == "-")
            {
                newVal = newVal.Substring(1, len - 1);
                len = newVal.Length;
                neg = true;
            }
        }
        double val = 1.0;
        string endChar = newVal.Substring(newVal.Length - 1);
        switch (endChar)
        {
            case "M":
            case "m":
                if (len > 1)
                {
                    val = double.Parse(newVal.Substring(0, len - 1)) * 1000000.0;
                }
                else
                {
                    val *= 1000000.0;
                }
                if (neg)
                {
                    val = -val;
                }
                this.Text = FormatNumber(val);
                break;
            case "T":
            case "t":
                if (len > 1)
                {
                    val = double.Parse(newVal.Substring(0, len - 1)) * 1000.0;
                }
                else
                {
                    val *= 1000.0;
                }
                if (neg)
                {
                    val = -val;
                }
                this.Text = FormatNumber(val);
                break;
        }

        base.OnTextChanged(e);
    }
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        bool handled = false;
        switch (e.KeyChar)
        {
            case '-':
                if (this.Text.Length == 0)
                {
                    break;
                }
                else if (this.SelectionStart == 0)
                {
                    //negative being inserted first
                    break;
                }
                else
                {
                    handled = true;
                    break;
                }
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '0':
            case (char)Keys.Back:
                break;
            case 'M':
            case 'm':
            case 'T':
            case 't':
            case '%':
                //check last pos
                int l = this.Text.Length;
                int sT = this.SelectionStart;
                int sL = this.SelectionLength;
                if ((sT + sL) != l)
                {
                    handled = true;
                }
                break;
            default:
                string thisChar = e.KeyChar.ToString();
                if (thisChar == decSep)
                {
                    char[] txt = this.Text.ToCharArray();
                    for (int i = 0; i < txt.Length; i++)
                    {
                        if (txt[i].ToString() == decSep)
                        {
                            handled = true;
                            break;
                        }
                    }
                    break;
                }
                else if (thisChar != thouSep)
                {
                    handled = true;
                }
                break;
        }

        if (!handled)
        {
            base.OnKeyPress(e);
        }
        else
        {
            e.Handled = true;
        }

    }
    protected override void OnLeave(EventArgs e)
    {
        string tmp = this.Text;
        if (tmp == "")
        {
            tmp = "0";
            numVal = NumberLostFocus(ref tmp);
            this.Text = tmp;
        }
        if (tmp.Substring(tmp.Length - 1) == "%")
        {
            tmp = tmp.Substring(0, tmp.Length - 1);
            numVal = 0.0;
            numVal = NumberLostFocus(ref tmp) / 100.0;
            double test = numVal * 100.0;
            this.Text = FormatNumber(test) + "%";
        }
        else if (perCent)
        {
            numVal = NumberLostFocus(ref tmp);
            double test = numVal * 100.0;
            this.Text = FormatNumber(test) + "%";
        }
        else
        {
            numVal = NumberLostFocus(ref tmp);
            this.Text = tmp;
        }
        base.OnLeave(e);
    }
    private string FormatNumber(double amount)
    {
        NumberFormatInfo nF = new NumberFormatInfo();
        nF.NumberDecimalSeparator = decSep;
        nF.NumberGroupSeparator = thouSep;

        string decFormat;
        if (useThouSep)
        {
            decFormat = "#,##0";
        }
        else
        {
            decFormat = "#0";
        }
        if (decPlaces > 0)
        {
            decFormat += ".";
            for (int i = 0; i < decPlaces; i++)
            {
                decFormat += "0";
            }
            if (extraDecPlaces > 0)
            {
                for (int i = 0; i < extraDecPlaces; i++)
                {
                    decFormat += "#";
                }
            }
        }
        else if (extraDecPlaces > 0)
        {
            decFormat += ".";
            for (int i = 0; i < extraDecPlaces; i++)
            {
                decFormat += "#";
            }
        }
        return (amount.ToString(decFormat, nF));
    }
    private double NumberLostFocus(ref string amountBox)
    {
        if (amountBox.Substring(0, 1) == decSep)
            amountBox = "0" + amountBox;
        NumberFormatInfo nF = new NumberFormatInfo();
        nF.NumberDecimalSeparator = decSep;
        nF.NumberGroupSeparator = thouSep;

        try
        {
            double d = 0.0;
            int l = amountBox.Length;
            if (l > 0)
            {

                char[] c = amountBox.ToCharArray();
                char endChar = c[l - 1];

                switch (endChar)
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        {
                            stripNonNumerics(ref amountBox);
                            d = Double.Parse(amountBox, nF);
                            break;
                        }
                    case 'm':
                    case 'M':
                        {
                            if (amountBox.Length == 1)
                                d = 1000000.0;
                            else
                            {
                                string s = amountBox.Substring(0, l - 1);
                                stripNonNumerics(ref s);
                                d = Double.Parse(s, nF) * 1000000.0;
                            }
                            break;

                        }
                    case 't':
                    case 'T':
                        {
                            if (amountBox.Length == 1)
                                d = 1000.0;
                            else
                            {
                                string s = amountBox.Substring(0, l - 1);
                                stripNonNumerics(ref s);
                                d = Double.Parse(s, nF) * 1000.0;
                            }
                            break;
                        }
                    default:
                        {
                            //remove offending char
                            string s = amountBox.Substring(0, l - 1);
                            if (s.Length > 0)
                            {
                                stripNonNumerics(ref s);
                                d = Double.Parse(s, nF);
                            }
                            else
                                d = 0.0;
                            break;
                        }
                }
            }
            amountBox = FormatNumber(d);
            return (d);
        }
        catch (Exception e)
        {
            //handle exception here;
            return 0.0;
        }
    }
    private void stripNonNumerics(ref string amountBox)
    {
        bool dSFound = false;
        char[] tmp = decSep.ToCharArray();
        char dS = tmp[0];
        string cleanNum = "";
        int l = amountBox.Length;
        if (l > 0)
        {
            char[] c = amountBox.ToCharArray();
            for (int i = 0; i < l; i++)
            {
                char b = c[i];
                switch (b)
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        cleanNum += b;
                        break;
                    case '-':
                        if (i == 0)
                            cleanNum += b;
                        break;
                    default:
                        if ((b == dS) && (!dSFound))
                        {
                            dSFound = true;
                            cleanNum += b;
                        }
                        break;
                }
            }
        }
        amountBox = cleanNum;
    }
}

除了限制数字输入外,此类还有一些特殊功能。它公开了一个属性 Value 来表示数字的 double 值,它格式化文本,可选地有千位分隔符,它提供大数字的简写输入:10M 扩展到 10,000,000.00(小数位数是属性) )。为简洁起见,十进制和千位分隔符已经过硬编码。在生产系统中,这些也是用户偏好。