Jak utworzyć pole tekstowe, które akceptuje tylko liczby?

582

Mam aplikację formularzy systemu Windows z formantem pola tekstowego, który chcę akceptować tylko wartości całkowite. W przeszłości przeprowadzałem tego rodzaju walidację, przeciążając zdarzenie KeyPress i usuwając znaki, które nie pasowały do ​​specyfikacji. Patrzyłem na kontrolkę MaskedTextBox, ale chciałbym bardziej ogólne rozwiązanie, które może działać z być może wyrażeniem regularnym lub zależeć od wartości innych kontrolek.

Idealnie byłoby, gdyby naciśnięcie znaku nieliczbowego nie przyniosło rezultatu lub natychmiast zapewniło użytkownikowi informację zwrotną na temat nieprawidłowego znaku.

Mykroft
źródło
11
cyfry czy cyfry? duża różnica: nawet liczby całkowite mogą być ujemne
Joel Coehoorn
8
Pytanie było przeznaczone dla liczb obejmujących cały zestaw liczb wymiernych.
Mykroft,

Odpowiedzi:

797

Dwie opcje:

  1. NumericUpDownZamiast tego użyj . NumericUpDown wykonuje filtrowanie za Ciebie, co jest miłe. Oczywiście daje również użytkownikom możliwość uderzania w górę i w dół strzałki na klawiaturze, aby zwiększać i zmniejszać bieżącą wartość.

  2. Obsługuj odpowiednie zdarzenia na klawiaturze, aby nie wprowadzać niczego poza wprowadzaniem numerycznym. Odniosłem sukces z tymi dwoma modułami obsługi zdarzeń w standardowym TextBox:

    private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) &&
            (e.KeyChar != '.'))
        {
                e.Handled = true;
        }
    
        // only allow one decimal point
        if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
        {
            e.Handled = true;
        }
    }
    

Możesz usunąć czek dla '.'(i kolejne sprawdzenie dla więcej niż jednego '.'), jeśli TextBox nie zezwala na miejsca dziesiętne. Możesz również dodać sprawdzenie, '-'czy TextBox powinien zezwalać na wartości ujemne.

Jeśli chcesz ograniczyć liczbę cyfr użytkownika, użyj: textBox1.MaxLength = 2; // this will allow the user to enter only 2 digits

Matt Hamilton
źródło
5
Jedyną wadą NumericUpDown jest to, że nie zapewnia informacji zwrotnej po wprowadzeniu wartości poza maksymalnymi lub minimalnymi dozwolonymi wartościami - po prostu zmienia to, co wpisałeś. TextBox może co najmniej dopuszczać nieprawidłowe wartości, abyś mógł ostrzec użytkownika, gdy prześle formularz.
Matt Hamilton
7
To prawda - użytkownik zawsze może wkleić niektóre znaki nienumeryczne. Miałbyś nadzieję, że złapie to sprawdzanie poprawności formularza, ponieważ w pewnym momencie będziesz chciał zrobić Int32.TryParse lub coś takiego.
Matt Hamilton
52
Będziesz potrzebował dodatkowego wysiłku, aby zglobalizować to poprzez zastąpienie czeków „.” z kontrolami na CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator.
Jeff Yates
6
@HamishGrubijan, IsControl nie ma nic wspólnego z klawiszem Control; zwraca, czy znak jest znakiem kontrolnym, czy nie. Pozwalając na znaki kontrolne, nie psujesz rzeczy, takich jak backspace, delete lub klawisze strzałek
Thomas Levesque
13
Nawiasem mówiąc, to nadal akceptuje nielegalne dane ctrl + v; błąd, który istnieje nawet w oficjalnej kontrolce NumericUpDown.
Nyerguds,
149

I tylko dlatego, że zawsze fajniej jest robić rzeczy w jednym wierszu ...

 private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
    }

UWAGA: NIE Uniemożliwia to użytkownikowi kopiowania / wklejania w tym polu tekstowym. Nie jest to bezpieczny sposób na oczyszczenie danych.

BFree
źródło
nie jest to ogólne rozwiązanie, ponieważ działa tylko w przypadku intergerów. Ostatnio musiałem zaimplementować coś takiego i skończyło się na parsowaniu wynikowego łańcucha na liczbę i zezwalaniu na wprowadzanie danych tylko wtedy, gdy parsowanie się powiodło
grzegorz_p
1
Może to nie działać, gdy wiele metod obsługuje KeyPresszdarzenia z tego samego pola tekstowego. Jedno zdarzenie może mieć wartość e.Handledtrue, a następnie inne może ustawić z powrotem wartość false. Ogólnie lepiej jest używaćif (...) e.Handled = true;
Nathaniel Jones
2
Możesz wyłączyć właściwość ShortcutsEnabled, aby zapobiec kopiowaniu za pomocą klawiatury lub menu
Ahmad
3
HA HA! Tak! Jeden wkładka!
Jamie L.,
3
Eh TextChangedWydarzenie, które wykracza ponad to z regex można naprawić kopiuj-wklej;)
Nyerguds
51

Zakładam, że z kontekstu i użytych tagów piszesz aplikację .NET C #. W takim przypadku możesz zasubskrybować zdarzenie o zmienionym tekście i sprawdzić poprawność każdego naciśnięcia klawisza.

private void textBox1_TextChanged(object sender, EventArgs e)
{
    if (System.Text.RegularExpressions.Regex.IsMatch(textBox1.Text, "[^0-9]"))
    {
        MessageBox.Show("Please enter only numbers.");
        textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);
    }
}
Anthony D.
źródło
22
Czy to nie da dziwnego efektu, jeśli wpiszesz w środku liczby?
Colin Pickard,
5
a także powinno być:textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);
Pieniadz
3
co jeśli sam pierwszy znak nie jest cyfrą ... nie odejmowałby 1 w tym przypadku generowałby błąd ....
manu_dilip_shah
6
Ponadto użycie TextChanged zamiast KeyPress tworzy odrobinę rekurencji, ponieważ kod przeskoczy do drugiego zdarzenia TextChanged po metodzie Remove.
WEFX
2
Zmieniłeś parametry wejściowe i wzorce dla swojej funkcji IsMatch. Wejście powinno być najpierw, a następnie wzorzec. msdn.microsoft.com/en-us/library/sdx2bds0(v=vs.110).aspx
Mibou
36

Oto prosta, niezależna kontrolka Winforms, pochodząca ze standardowego TextBox, która pozwala tylko na wejście System.Int32 (można ją łatwo dostosować do innych typów, takich jak System.Int64 itp.). Obsługuje operacje kopiuj / wklej i liczby ujemne:

public class Int32TextBox : TextBox
{
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        base.OnKeyPress(e);

        NumberFormatInfo fi = CultureInfo.CurrentCulture.NumberFormat;

        string c = e.KeyChar.ToString();
        if (char.IsDigit(c, 0))
            return;

        if ((SelectionStart == 0) && (c.Equals(fi.NegativeSign)))
            return;

        // copy/paste
        if ((((int)e.KeyChar == 22) || ((int)e.KeyChar == 3))
            && ((ModifierKeys & Keys.Control) == Keys.Control))
            return;

        if (e.KeyChar == '\b')
            return;

        e.Handled = true;
    }

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        const int WM_PASTE = 0x0302;
        if (m.Msg == WM_PASTE)
        {
            string text = Clipboard.GetText();
            if (string.IsNullOrEmpty(text))
                return;

            if ((text.IndexOf('+') >= 0) && (SelectionStart != 0))
                return;

            int i;
            if (!int.TryParse(text, out i)) // change this for other integer types
                return;

            if ((i < 0) && (SelectionStart != 0))
                return;
        }
        base.WndProc(ref m);
    }

Aktualizacja 2017 : Moja pierwsza odpowiedź ma pewne problemy:

  • możesz wpisać coś, co jest dłuższe niż liczba całkowita danego typu (na przykład 2147483648 jest większa niż Int32.MaxValue);
  • bardziej ogólnie, nie ma prawdziwej weryfikacji wyniku tego, co zostało wpisane;
  • obsługuje tylko int32, będziesz musiał napisać specyficzną kontrolę pochodnej TextBox dla każdego typu (Int64 itp.)

Więc wymyśliłem inną wersję, która jest bardziej ogólna, która nadal obsługuje kopiowanie / wklejanie, znak + i - itp.

public class ValidatingTextBox : TextBox
{
    private string _validText;
    private int _selectionStart;
    private int _selectionEnd;
    private bool _dontProcessMessages;

    public event EventHandler<TextValidatingEventArgs> TextValidating;

    protected virtual void OnTextValidating(object sender, TextValidatingEventArgs e) => TextValidating?.Invoke(sender, e);

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (_dontProcessMessages)
            return;

        const int WM_KEYDOWN = 0x100;
        const int WM_ENTERIDLE = 0x121;
        const int VK_DELETE = 0x2e;

        bool delete = m.Msg == WM_KEYDOWN && (int)m.WParam == VK_DELETE;
        if ((m.Msg == WM_KEYDOWN && !delete) || m.Msg == WM_ENTERIDLE)
        {
            DontProcessMessage(() =>
            {
                _validText = Text;
                _selectionStart = SelectionStart;
                _selectionEnd = SelectionLength;
            });
        }

        const int WM_CHAR = 0x102;
        const int WM_PASTE = 0x302;
        if (m.Msg == WM_CHAR || m.Msg == WM_PASTE || delete)
        {
            string newText = null;
            DontProcessMessage(() =>
            {
                newText = Text;
            });

            var e = new TextValidatingEventArgs(newText);
            OnTextValidating(this, e);
            if (e.Cancel)
            {
                DontProcessMessage(() =>
                {
                    Text = _validText;
                    SelectionStart = _selectionStart;
                    SelectionLength = _selectionEnd;
                });
            }
        }
    }

    private void DontProcessMessage(Action action)
    {
        _dontProcessMessages = true;
        try
        {
            action();
        }
        finally
        {
            _dontProcessMessages = false;
        }
    }
}

public class TextValidatingEventArgs : CancelEventArgs
{
    public TextValidatingEventArgs(string newText) => NewText = newText;
    public string NewText { get; }
}

W przypadku Int32 możesz albo z niego wywnioskować, jak poniżej:

public class Int32TextBox : ValidatingTextBox
{
    protected override void OnTextValidating(object sender, TextValidatingEventArgs e)
    {
        e.Cancel = !int.TryParse(e.NewText, out int i);
    }
}

lub bez pochodnej, użyj nowego zdarzenia TextValidating w następujący sposób:

var vtb = new ValidatingTextBox();
...
vtb.TextValidating += (sender, e) => e.Cancel = !int.TryParse(e.NewText, out int i);

ale fajne jest to, że działa z dowolnym łańcuchem i dowolną procedurą sprawdzania poprawności.

Simon Mourier
źródło
Jest to świetne, ładne i proste, łatwe w użyciu i zajmuje się nietypowymi próbami wprowadzania danych. Dzięki!
WiredEarp
1
Uwaga na temat wersji 2017, gdy jest jedna wartość, np. 1, a ty wciśniesz backspace, zostanie to zignorowane, a jeśli powiesz 120 i trzy razy w backspace, zostaniemy z 1.
Karen Payne
1
Twój ValidatingTextbox jest zdecydowanie najlepszą implementacją, jaką widziałem od dłuższego czasu. Prosty i skuteczny. Dzięki!
Samuel
19

Właśnie do tego zostały zaprojektowane zdarzenia sprawdzania poprawności / sprawdzania poprawności.

Oto artykuł MSDN na ten temat: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.validating.aspx

Wersja TL; DR: sprawdź właściwość .Text w zdarzeniu Sprawdzanie poprawności i ustaw, e.Cancel=Truegdy dane są nieprawidłowe.

Po ustawieniu e.Cancel = True użytkownik nie może opuścić pola, ale musisz przekazać mu informację zwrotną, że coś jest nie tak. Zmieniam kolor tła pudełka na jasnoczerwony, aby wskazać problem. Pamiętaj, aby ustawić go z powrotem, SystemColors.Windowgdy sprawdzanie poprawności jest wywoływane z dobrą wartością.

TomXP411
źródło
1
+1 za wzmiankę o bardzo idiomatycznym podejściu do API. Jestem względnie nowy w Windows Forms, i jest to całkiem dżungla funkcjonalności i dokumentów MSDN, więc dziękuję również za konkretny wskaźnik do dokumentu Validating. <nitpick>OP wspomina, że ​​natychmiastowe niedopuszczenie / wskazanie unieważniającej postaci jest idealne, ale Validatingwydaje się, że wymaga przeniesienia fokusu na inną formę / kontrolę, zanim zacznie działać. </nitpick>Jest to jednak świetne podejście i zawsze warte rozważenia w bardziej ogólnym przypadku.
William
13

Wypróbuj MaskedTextBox . Ma prosty format maski, dzięki czemu można ograniczyć wprowadzanie do liczb lub dat lub cokolwiek innego.

Andrew Kennan
źródło
2
W szczególności nie chcę używać MaskedTextBox. Formaty, na które zezwalają, mogą być bardzo ograniczające. Działają w tej sprawie, ale chciałbym zrobić coś bardziej ogólnego.
Mykroft
12

Możesz użyć TextChangedwydarzenia

private void textBox_BiggerThan_TextChanged(object sender, EventArgs e)
{
    long a;
    if (! long.TryParse(textBox_BiggerThan.Text, out a))
    {
        // If not int clear textbox text or Undo() last operation
        textBox_LessThan.Clear();
    }
}
Davit Tvildiani
źródło
Wygląda na to, że powinien działać dobrze, jeśli używałeś Undo(), ale powoduje to StackOverflowException.
Drew Chapin,
Wygląda na to, że właściwość TextChanged jest częścią rutyny, którą chcesz cofnąć (). Mam zmienną dla całego okna i używam public int txtBoxValue, a jeśli tryParse nie działa, przywracam tekst w txtBox przeztxtBox.Text = txtBoxValue.ToString();
L. Zeda
8

To może być przydatne. Pozwala na „rzeczywiste” wartości liczbowe, w tym odpowiednie kropki dziesiętne i poprzedzające znaki plus lub minus. Zadzwoń z odpowiedniego zdarzenia KeyPress.

       private bool IsOKForDecimalTextBox(char theCharacter, TextBox theTextBox)
    {
        // Only allow control characters, digits, plus and minus signs.
        // Only allow ONE plus sign.
        // Only allow ONE minus sign.
        // Only allow the plus or minus sign as the FIRST character.
        // Only allow ONE decimal point.
        // Do NOT allow decimal point or digits BEFORE any plus or minus sign.

        if (
            !char.IsControl(theCharacter)
            && !char.IsDigit(theCharacter)
            && (theCharacter != '.')
            && (theCharacter != '-')
            && (theCharacter != '+')
        )
        {
            // Then it is NOT a character we want allowed in the text box.
            return false;
        }



        // Only allow one decimal point.
        if (theCharacter == '.'
            && theTextBox.Text.IndexOf('.') > -1)
        {
            // Then there is already a decimal point in the text box.
            return false;
        }

        // Only allow one minus sign.
        if (theCharacter == '-'
            && theTextBox.Text.IndexOf('-') > -1)
        {
            // Then there is already a minus sign in the text box.
            return false;
        }

        // Only allow one plus sign.
        if (theCharacter == '+'
            && theTextBox.Text.IndexOf('+') > -1)
        {
            // Then there is already a plus sign in the text box.
            return false;
        }

        // Only allow one plus sign OR minus sign, but not both.
        if (
            (
                (theCharacter == '-')
                || (theCharacter == '+')
            )
            && 
            (
                (theTextBox.Text.IndexOf('-') > -1)
                ||
                (theTextBox.Text.IndexOf('+') > -1)
            )
            )
        {
            // Then the user is trying to enter a plus or minus sign and
            // there is ALREADY a plus or minus sign in the text box.
            return false;
        }

        // Only allow a minus or plus sign at the first character position.
        if (
            (
                (theCharacter == '-')
                || (theCharacter == '+')
            )
            && theTextBox.SelectionStart != 0
            )
        {
            // Then the user is trying to enter a minus or plus sign at some position 
            // OTHER than the first character position in the text box.
            return false;
        }

        // Only allow digits and decimal point AFTER any existing plus or minus sign
        if  (
                (
                    // Is digit or decimal point
                    char.IsDigit(theCharacter)
                    ||
                    (theCharacter == '.')
                )
                &&
                (
                    // A plus or minus sign EXISTS
                    (theTextBox.Text.IndexOf('-') > -1)
                    ||
                    (theTextBox.Text.IndexOf('+') > -1)
                )
                &&
                    // Attempting to put the character at the beginning of the field.
                    theTextBox.SelectionStart == 0
            )
        {
            // Then the user is trying to enter a digit or decimal point in front of a minus or plus sign.
            return false;
        }

        // Otherwise the character is perfectly fine for a decimal value and the character
        // may indeed be placed at the current insertion position.
        return true;
    }
Roger Garrett
źródło
6

Pracowałem nad kolekcją komponentów, aby uzupełnić brakujące elementy w WinForm, oto: Zaawansowane formularze

W szczególności jest to klasa dla Regex TextBox

/// <summary>Represents a Windows text box control that only allows input that matches a regular expression.</summary>
public class RegexTextBox : TextBox
{
    [NonSerialized]
    string lastText;

    /// <summary>A regular expression governing the input allowed in this text field.</summary>
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public virtual Regex Regex { get; set; }

    /// <summary>A regular expression governing the input allowed in this text field.</summary>
    [DefaultValue(null)]
    [Category("Behavior")]
    [Description("Sets the regular expression governing the input allowed for this control.")]
    public virtual string RegexString {
        get {
            return Regex == null ? string.Empty : Regex.ToString();
        }
        set {
            if (string.IsNullOrEmpty(value))
                Regex = null;
            else
                Regex = new Regex(value);
        }
    }

    protected override void OnTextChanged(EventArgs e) {
        if (Regex != null && !Regex.IsMatch(Text)) {
            int pos = SelectionStart - Text.Length + (lastText ?? string.Empty).Length;
            Text = lastText;
            SelectionStart = Math.Max(0, pos);
        }

        lastText = Text;

        base.OnTextChanged(e);
    }
}

Wystarczy dodać coś podobnego myNumbericTextBox.RegexString = "^(\\d+|)$";.

Fabio Iotti
źródło
5

Wystarczy użyć NumericUpDownkontrolki i ustawić te brzydkie przyciski widoczności w górę na false.

numericUpDown1.Controls[0].Visible = false;

NumericUpDown jest właściwie zbiorem elementów sterujących zawierających „pole przewijania” (przyciski w górę w dół), pole tekstowe i trochę kodu do sprawdzania poprawności i łączenia go w całość.

Cechowanie:

YourNumericUpDown.Controls[0].visible = false 

ukryje przyciski, utrzymując aktywny kod podstawowy.

Chociaż nie jest to oczywiste rozwiązanie, jest proste i skuteczne. .Controls[1]ukryłby część pola tekstowego, jeśli chcesz to zrobić zamiast tego.

użytkownik2163234
źródło
Przyjęta odpowiedź nie zawierała żadnych informacji na temat usuwania przycisków góra-dół, jak to zrobić nie jest oczywiste, ponieważ nie ma interfejsów czytelnych dla człowieka, aby je włączyć lub wyłączyć. NumericUpDown jest w rzeczywistości zbiorem kontrolek zawierających pole tekstowe i „pole spin” (przyciski góra-dół) oraz sprawdzanie poprawności danych wejściowych do obsługi kodu.
user2163234
4

Zrobiłem coś do tego na CodePlex .

Działa poprzez przechwytywanie zdarzenia TextChanged. Jeśli wynik jest dobry, zostanie zapisany. Jeśli coś jest nie tak, ostatnia dobra wartość zostanie przywrócona. Źródło jest nieco za duże, by je tutaj opublikować, ale tutaj jest link do klasy, która obsługuje rdzeń tej logiki.

GvS
źródło
4

po prostu użyj tego kodu w polu tekstowym:

private void textBox1_TextChanged(object sender, EventArgs e)
{

    double parsedValue;

    if (!double.TryParse(textBox1.Text, out parsedValue))
    {
        textBox1.Text = "";
    }
}
saurabh27
źródło
4

Na naszej stronie internetowej z definicją pola tekstowego możemy dodać onkeypresszdarzenie, aby akceptować tylko liczby. Nie wyświetli żadnego komunikatu, ale zapobiegnie błędnemu wprowadzeniu danych. To działało dla mnie, użytkownik nie mógł wprowadzić niczego oprócz numeru.

<asp:TextBox runat="server" ID="txtFrom"
     onkeypress="if(isNaN(String.fromCharCode(event.keyCode))) return false;">
ssah
źródło
2

możesz użyć zdarzenia TextChanged / Keypress , użyć wyrażenia regularnego do filtrowania liczb i podjąć pewne działania.

Perpetualcoder
źródło
2

Poradziłbym sobie z tym w przypadku KeyDown.

void TextBox_KeyDown(object sender, KeyEventArgs e)
        {
            char c = Convert.ToChar(e.PlatformKeyCode);
            if (!char.IsDigit(c))
            {
                e.Handled = true;
            }
        }
Shaz
źródło
2
Co z klawiszami takimi jak „Backspace”, „Delete”, „Arrow-Key-Left”, „Arrow-Key-Right”, Copy and Paste, Cyfry wprowadzane przez Numpad (są sprzedawane jako! Cyfra)
user799821
Wystarczy dodać jeszcze kilka takich testów: if (! Char.IsDigit (c) && c! = (Char) Keys.Back)
dnennis
2
private void txt3_KeyPress(object sender, KeyPressEventArgs e)
{
    for (int h = 58; h <= 127; h++)
    {
        if (e.KeyChar == h)             //58 to 127 is alphabets tat will be         blocked
        {
            e.Handled = true;
        }
    }
    for(int k=32;k<=47;k++)
    {
        if (e.KeyChar == k)              //32 to 47 are special characters tat will 
        {                                  be blocked
            e.Handled = true;
        }
    }
}

spróbuj to jest bardzo proste

rithish
źródło
2

Spójrz na obsługę danych wejściowych w WinForm

W polu tekstowym opublikowałem moje rozwiązanie wykorzystujące zdarzenia ProcessCmdKey i OnKeyPress. Komentarze pokazują, jak używać Regex do weryfikacji naciśnięcia klawisza i odpowiednio zablokować / zezwolić.

benPearce
źródło
2

Cześć, możesz zrobić coś takiego w zdarzeniu zmienionym tekstem w polu tekstowym.

tutaj jest demo

    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        string actualdata = string.Empty;
        char[] entereddata = textBox1.Text.ToCharArray();
        foreach (char aChar in entereddata.AsEnumerable())
        {
            if (Char.IsDigit(aChar))
            {
                actualdata = actualdata + aChar;
                // MessageBox.Show(aChar.ToString());
            }
            else
            {
                MessageBox.Show(aChar + " is not numeric");
                actualdata.Replace(aChar, ' ');
                actualdata.Trim();
            }
        }
        textBox1.Text = actualdata;
    }
Chandan Kumar
źródło
Dzięki, to bardzo przydatne.
Kiran RS,
2

Wygląda na to, że wiele obecnych odpowiedzi na to pytanie ręcznie analizuje tekst wejściowy. Jeśli szukasz określonego wbudowanego typu numerycznego (np. intLub double), dlaczego po prostu nie delegować pracy do TryParsemetody tego typu ? Na przykład:

public class IntTextBox : TextBox
{
    string PreviousText = "";
    int BackingResult;

    public IntTextBox()
    {
        TextChanged += IntTextBox_TextChanged;
    }

    public bool HasResult { get; private set; }

    public int Result
    {
        get
        {
            return HasResult ? BackingResult : default(int);
        }
    }

    void IntTextBox_TextChanged(object sender, EventArgs e)
    {
        HasResult = int.TryParse(Text, out BackingResult);

        if (HasResult || string.IsNullOrEmpty(Text))
        {
            // Commit
            PreviousText = Text;
        }
        else
        {
            // Revert
            var changeOffset = Text.Length - PreviousText.Length;
            var previousSelectionStart =
                Math.Max(0, SelectionStart - changeOffset);

            Text = PreviousText;
            SelectionStart = previousSelectionStart;
        }
    }
}

Jeśli chcesz czegoś bardziej ogólnego, ale nadal zgodnego z Projektantem Visual Studio:

public class ParsableTextBox : TextBox
{
    TryParser BackingTryParse;
    string PreviousText = "";
    object BackingResult;

    public ParsableTextBox()
        : this(null)
    {
    }

    public ParsableTextBox(TryParser tryParse)
    {
        TryParse = tryParse;

        TextChanged += ParsableTextBox_TextChanged;
    }

    public delegate bool TryParser(string text, out object result);

    public TryParser TryParse
    {
        set
        {
            Enabled = !(ReadOnly = value == null);

            BackingTryParse = value;
        }
    }

    public bool HasResult { get; private set; }

    public object Result
    {
        get
        {
            return GetResult<object>();
        }
    }

    public T GetResult<T>()
    {
        return HasResult ? (T)BackingResult : default(T);
    }

    void ParsableTextBox_TextChanged(object sender, EventArgs e)
    {
        if (BackingTryParse != null)
        {
            HasResult = BackingTryParse(Text, out BackingResult);
        }

        if (HasResult || string.IsNullOrEmpty(Text))
        {
            // Commit
            PreviousText = Text;
        }
        else
        {
            // Revert
            var changeOffset = Text.Length - PreviousText.Length;
            var previousSelectionStart =
                Math.Max(0, SelectionStart - changeOffset);

            Text = PreviousText;
            SelectionStart = previousSelectionStart;
        }
    }
}

I wreszcie, jeśli chcesz czegoś w pełni ogólnego i nie zależy ci na wsparciu Designer:

public class ParsableTextBox<T> : TextBox
{
    TryParser BackingTryParse;
    string PreviousText;
    T BackingResult;

    public ParsableTextBox()
        : this(null)
    {
    }

    public ParsableTextBox(TryParser tryParse)
    {
        TryParse = tryParse;

        TextChanged += ParsableTextBox_TextChanged;
    }

    public delegate bool TryParser(string text, out T result);

    public TryParser TryParse
    {
        set
        {
            Enabled = !(ReadOnly = value == null);

            BackingTryParse = value;
        }
    }

    public bool HasResult { get; private set; }

    public T Result
    {
        get
        {
            return HasResult ? BackingResult : default(T);
        }
    }

    void ParsableTextBox_TextChanged(object sender, EventArgs e)
    {
        if (BackingTryParse != null)
        {
            HasResult = BackingTryParse(Text, out BackingResult);
        }

        if (HasResult || string.IsNullOrEmpty(Text))
        {
            // Commit
            PreviousText = Text;
        }
        else
        {
            // Revert
            var changeOffset = Text.Length - PreviousText.Length;
            var previousSelectionStart =
                Math.Max(0, SelectionStart - changeOffset);

            Text = PreviousText;
            SelectionStart = previousSelectionStart;
        }
    }
}
William
źródło
2

Należy zaakceptować zarówno liczby całkowite, jak i zmiennoprzecinkowe, w tym liczby ujemne.

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    // Text
    string text = ((Control) sender).Text;

    // Is Negative Number?
    if (e.KeyChar == '-' && text.Length == 0)
    {
        e.Handled = false;
        return;
    }

    // Is Float Number?
    if (e.KeyChar == '.' && text.Length > 0 && !text.Contains("."))
    {
        e.Handled = false;
        return;
    }

    // Is Digit?
    e.Handled = (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar));
}
nabeghe
źródło
2

Oto moje podejście:

  1. za pomocą linq (filtr łatwy do modyfikacji)
  2. skopiuj / wklej kod sprawdzający
  3. utrzymuje pozycję karetki po naciśnięciu zabronionej postaci
  4. akceptuje lewe zera
  5. i dowolne numery rozmiarów

    private void numeroCuenta_TextChanged(object sender, EventArgs e)
    {
        string org = numeroCuenta.Text;
        string formated = string.Concat(org.Where(c => (c >= '0' && c <= '9')));
        if (formated != org)
        {
            int s = numeroCuenta.SelectionStart;
            if (s > 0 && formated.Length > s && org[s - 1] != formated[s - 1]) s--;
            numeroCuenta.Text = formated;
            numeroCuenta.SelectionStart = s;
        }
    }
Ing. Gerardo Sánchez
źródło
2

Korzystając z podejścia opisanego w odpowiedzi Fabio Iottiego, stworzyłem bardziej ogólne rozwiązanie:

public abstract class ValidatedTextBox : TextBox {
    private string m_lastText = string.Empty;
    protected abstract bool IsValid(string text);
    protected sealed override void OnTextChanged(EventArgs e) {
        if (!IsValid(Text)) {
            var pos = SelectionStart - Text.Length + m_lastText.Length;
            Text = m_lastText;
            SelectionStart = Math.Max(0, pos);
        }
        m_lastText = Text;
        base.OnTextChanged(e);
    }
}

„ValidatedTextBox”, który zawiera wszystkie nietrywialne zachowania sprawdzania poprawności. Pozostało tylko odziedziczyć tę klasę i zastąpić metodę „IsValid” dowolną wymaganą logiką sprawdzania poprawności. Na przykład za pomocą tej klasy można utworzyć „RegexedTextBox”, który będzie akceptował tylko ciągi pasujące do określonego wyrażenia regularnego:

public abstract class RegexedTextBox : ValidatedTextBox {
    private readonly Regex m_regex;
    protected RegexedTextBox(string regExpString) {
        m_regex = new Regex(regExpString);
    }
    protected override bool IsValid(string text) {
        return m_regex.IsMatch(Text);
    }
}

Następnie, dziedzicząc po klasie „RegexedTextBox”, możemy łatwo utworzyć formanty „PositiveNumberTextBox” i „PositiveFloatingPointNumberTextBox”:

public sealed class PositiveNumberTextBox : RegexedTextBox {
    public PositiveNumberTextBox() : base(@"^\d*$") { }
}

public sealed class PositiveFloatingPointNumberTextBox : RegexedTextBox {
    public PositiveFloatingPointNumberTextBox()
        : base(@"^(\d+\" + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + @")?\d*$") { }
}
Niedźwiedź polarny
źródło
1

Przepraszam, że obudziłem umarłych, ale pomyślałem, że ktoś może uznać to za przydatne w przyszłości.

Oto jak sobie z tym poradzić. Obsługuje liczby zmiennoprzecinkowe, ale można je łatwo modyfikować dla liczb całkowitych.

Zasadniczo można tylko nacisnąć przycisk 0 - 9 oraz .

Możesz mieć tylko jedno 0 przed .

Wszystkie pozostałe znaki są ignorowane, a pozycja kursora zostaje zachowana.

    private bool _myTextBoxChanging = false;

    private void myTextBox_TextChanged(object sender, EventArgs e)
    {
        validateText(myTextBox);
    }

    private void validateText(TextBox box)
    {
        // stop multiple changes;
        if (_myTextBoxChanging)
            return;
        _myTextBoxChanging = true;

        string text = box.Text;
        if (text == "")
            return;
        string validText = "";
        bool hasPeriod = false;
        int pos = box.SelectionStart;
        for (int i = 0; i < text.Length; i++ )
        {
            bool badChar = false;
            char s = text[i];
            if (s == '.')
            {
                if (hasPeriod)
                    badChar = true;
                else
                    hasPeriod = true;
            }
            else if (s < '0' || s > '9')
                badChar = true;

            if (!badChar)
                validText += s;
            else
            {
                if (i <= pos)
                    pos--;
            }
        }

        // trim starting 00s
        while (validText.Length >= 2 && validText[0] == '0')
        {
            if (validText[1] != '.')
            {
                validText = validText.Substring(1);
                if (pos < 2)
                    pos--;
            }
            else
                break;
        }

        if (pos > validText.Length)
            pos = validText.Length;
        box.Text = validText;
        box.SelectionStart = pos;
        _myTextBoxChanging = false;
    }

Oto szybko zmodyfikowana wersja int:

    private void validateText(TextBox box)
    {
        // stop multiple changes;
        if (_myTextBoxChanging)
            return;
        _myTextBoxChanging = true;

        string text = box.Text;
        if (text == "")
            return;
        string validText = "";
        int pos = box.SelectionStart;
        for (int i = 0; i < text.Length; i++ )
        {
            char s = text[i];
            if (s < '0' || s > '9')
            {
                if (i <= pos)
                    pos--;
            }
            else
                validText += s;
        }

        // trim starting 00s 
        while (validText.Length >= 2 && validText.StartsWith("00")) 
        { 
            validText = validText.Substring(1); 
            if (pos < 2) 
                pos--; 
        } 

        if (pos > validText.Length)
            pos = validText.Length;
        box.Text = validText;
        box.SelectionStart = pos;
        _myTextBoxChanging = false;
    }
yardape
źródło
2
To rozwiązanie na nowo wymyśla koło z zastrzeżeniami. Lokalizacja na przykład.
Julien Guertault
1

Ten działa z kopiowaniem i wklejaniem, przeciąganiem i upuszczaniem, klawiszem w dół, zapobiega przepełnieniu i jest dość prosty

public partial class IntegerBox : TextBox 
{
    public IntegerBox()
    {
        InitializeComponent();
        this.Text = 0.ToString();
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);
    }

    private String originalValue = 0.ToString();

    private void Integerbox_KeyPress(object sender, KeyPressEventArgs e)
    {
        originalValue = this.Text;
    }

    private void Integerbox_TextChanged(object sender, EventArgs e)
    {
        try
        {
            if(String.IsNullOrWhiteSpace(this.Text))
            {
                this.Text = 0.ToString();
            }
            this.Text = Convert.ToInt64(this.Text.Trim()).ToString();
        }
        catch (System.OverflowException)
        {
            MessageBox.Show("Value entered is to large max value: " + Int64.MaxValue.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            this.Text = originalValue;
        }
        catch (System.FormatException)
        {                
            this.Text = originalValue;
        }
        catch (System.Exception ex)
        {
            this.Text = originalValue;
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK , MessageBoxIcon.Error);
        }
    }       
}
użytkownik1626874
źródło
1

Nie zapominaj, że użytkownik może wkleić nieprawidłowy tekst w pliku TextBox .

Jeśli chcesz to ograniczyć, postępuj zgodnie z poniższym kodem:

private void ultraTextEditor1_TextChanged(object sender, EventArgs e)
{
    string append="";
    foreach (char c in ultraTextEditor1.Text)
    {
        if ((!Char.IsNumber(c)) && (c != Convert.ToChar(Keys.Back)))
        {

        }
        else
        {
            append += c;
        }
    }

    ultraTextEditor1.Text = append;
}   
Divya
źródło
1

Szukałem również najlepszego sposobu sprawdzania tylko liczb w polu tekstowym i problem z naciśnięciem klawisza polegał na tym, że nie obsługuje on wklejania kopii prawym przyciskiem myszy ani schowka, więc wymyśliłem ten kod, który sprawdza, kiedy kursor opuszcza pole tekstowe, a także sprawdza, czy puste pole. (dostosowana wersja newguy)

private void txtFirstValue_MouseLeave(object sender, EventArgs e)
{
    int num;
    bool isNum = int.TryParse(txtFirstValue.Text.Trim(), out num);

    if (!isNum && txtFirstValue.Text != String.Empty)
    {
        MessageBox.Show("The First Value You Entered Is Not a Number, Please Try Again", "Invalid Value Detected", MessageBoxButtons.OK, MessageBoxIcon.Error);
        txtFirstValue.Clear();
    }
}
Alston Antony
źródło
MouseLeave wydaje się być naprawdę złym wyborem na wydarzenie.
LarsTech,
@LarsTech, co moim zdaniem zostało zmienione, może nawet spowodować komunikat o błędzie, nawet jeśli użytkownik zauważy błąd i spróbuje go naprawić, więc pomyślałem, że będę działać lepiej. Jak myślisz, co jest najlepszym wydarzeniem w tej sprawie?
Alston Antony
@AlstonAntony późny komentarz, wiem. Ale wystarczy zwykłe kliknięcie, które aktywuje się po kliknięciu prawym przyciskiem myszy, prawda?
Takarii
0
int Number;
bool isNumber;
isNumber = int32.TryPase(textbox1.text, out Number);

if (!isNumber)
{ 
    (code if not an integer);
}
else
{
    (code if an integer);
}
nowy chłopak
źródło
0

3 rozwiązanie

1)

//Add to the textbox's KeyPress event
//using Regex for number only textBox

private void txtBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), "\\d+"))
e.Handled = true;
}

2) inne rozwiązanie z msdn

// Boolean flag used to determine when a character other than a number is entered.
private bool nonNumberEntered = false;
// Handle the KeyDown event to determine the type of character entered into the     control.
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
// Initialize the flag to false.
nonNumberEntered = false;
// Determine whether the keystroke is a number from the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
    // Determine whether the keystroke is a number from the keypad.
    if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9)
    {
        // Determine whether the keystroke is a backspace.
        if (e.KeyCode != Keys.Back)
        {
            // A non-numerical keystroke was pressed.
            // Set the flag to true and evaluate in KeyPress event.
            nonNumberEntered = true;
        }
    }
}

}

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    if (nonNumberEntered == true)
    {
       MessageBox.Show("Please enter number only..."); 
       e.Handled = true;
    }
}

źródło http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keypress(v=VS.90).aspx

3) przy użyciu MaskedTextBox: http://msdn.microsoft.com/en-us/library/system.windows.forms.maskedtextbox.aspx

skromna i urocza dziewczyna
źródło
0

Kliknij przycisk, aby sprawdzić tekst pola tekstowego za pomocą pętli:

char[] c = txtGetCustomerId.Text.ToCharArray();
bool IsDigi = true;

for (int i = 0; i < c.Length; i++)
     {
       if (c[i] < '0' || c[i] > '9')
      { IsDigi = false; }
     }
 if (IsDigi)
    { 
     // do something
    }
Shaahin
źródło
0

Prostsza odpowiedź:

_textBox.TextChanged += delegate(System.Object o, System.EventArgs e)
{
    TextBox _tbox = o as TextBox;
    _tbox.Text = new string(_tbox.Text.Where(c => (char.IsDigit(c)) || (c == '.')).ToArray());
};
micahhoover
źródło