Как сделать текстовое поле, которое принимает только числа?

У меня есть приложение Windows Forms с элементом управления текстовым полем, которое я хочу принимать только целочисленные значения. Раньше я проводил такую ​​проверку, перегружая событие KeyPress и просто удаляя символы, не соответствующие спецификации. Я посмотрел на элемент управления MaskedTextBox, но мне хотелось бы более общее решение, которое могло бы работать, возможно, с регулярным выражением или зависеть от значений других элементов управления.

В идеале это должно вести себя так, что нажатие нечислового символа либо не приведет к результату, либо немедленно предоставит пользователю обратную связь о недопустимом символе.

числа или цифры? большая разница: даже целые числа могут стать отрицательными

Joel Coehoorn 21.01.2009 00:59

Вопрос был предназначен для чисел, включающих весь набор рациональных чисел.

Mykroft 08.06.2009 03:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
613
2
1 190 775
39
Перейти к ответу Данный вопрос помечен как решенный

Ответы 39

вы можете использовать событие TextChanged / Keypress, использовать регулярное выражение для фильтрации по числам и предпринять некоторые действия.

Попробуйте MaskedTextBox. Он принимает простой формат маски, поэтому вы можете ограничить ввод числами, датами или чем-то еще.

Я специально не хочу использовать MaskedTextBox. Допустимые форматы могут быть очень ограниченными. Они работают в этом случае, но я хотел бы сделать что-нибудь более общее.

Mykroft 21.01.2009 01:03

Из контекста и тегов, которые вы использовали, я предполагаю, что вы пишете приложение .NET C#. В этом случае вы можете подписаться на событие изменения текста и подтверждать каждое нажатие клавиши.

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);
    }
}

Разве это не даст очень странный эффект, если вы введете в середину числа?

Colin Pickard 25.10.2010 23:14

а также должно быть: textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);

Pieniadz 24.08.2011 13:59

что, если первый символ сам по себе не является цифрой ... в этом случае вычитание 1 не вызовет ошибку ...

manu_dilip_shah 26.03.2012 12:29

Кроме того, использование TextChanged вместо KeyPress создает небольшую рекурсию, так как код переходит во второе событие TextChanged после метода Remove.

WEFX 21.05.2013 18:29

Вы переключили параметры ввода и шаблона для своей функции IsMatch. Сначала должен быть ввод, затем узор. msdn.microsoft.com/en-us/library/sdx2bds0(v=vs.110).aspx

Mibou 17.02.2014 13:52

Удаление удалит весь текст, а не недопустимый символ. Я думаю, что будет лучше сохранить предыдущее действительное изменение и соответственно обновить его.

ErAcube 27.06.2018 13:42

решить проблему с TextChangedEventArgs и использовать e.Changes -> Change.Offset

Lenor 05.09.2019 17:14
Ответ принят как подходящий

Два варианта:

  1. Вместо этого используйте NumericUpDown. NumericUpDown выполняет фильтрацию за вас, что приятно. Конечно, это также дает вашим пользователям возможность нажимать стрелки вверх и вниз на клавиатуре, чтобы увеличивать и уменьшать текущее значение.

  2. Обрабатывайте соответствующие события клавиатуры, чтобы предотвратить ввод любых чисел, кроме числовых. Я добился успеха с этими двумя обработчиками событий в стандартном 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;
        }
    }
    

Вы можете удалить проверку для '.' (и последующую проверку для нескольких '.'), если в текстовом поле не должно быть десятичных знаков. Вы также можете добавить проверку для '-', если ваш TextBox должен допускать отрицательные значения.

Если вы хотите ограничить пользователя по количеству цифр, используйте: textBox1.MaxLength = 2; // this will allow the user to enter only 2 digits

Я забыл, что существует числовой элемент управления вверх-вниз. На самом деле это то, что я должен использовать здесь вместо текстового поля. В будущем, когда у меня будет более сложная проверка, я буду использовать свойство handled с событием KeyPress.

Mykroft 21.01.2009 01:11

Единственный недостаток NumericUpDown заключается в том, что он не обеспечивает обратной связи, когда вы вводите значение, выходящее за пределы максимального или минимального допустимых значений - он просто меняет то, что вы набираете. TextBox может, по крайней мере, допускать недопустимые значения, чтобы вы могли предупреждать пользователя, когда он отправляет форму.

Matt Hamilton 21.01.2009 01:12

Также можно получить значения в текстовое поле с помощью мыши.

GvS 21.01.2009 01:24

Это правда - пользователь всегда может вставить какие-то нечисловые символы. Вы бы надеялись, что проверка формы уловит это, поскольку в какой-то момент вы захотите выполнить Int32.TryParse или что-то в этом роде.

Matt Hamilton 21.01.2009 01:41

Вам потребуются дополнительные усилия, чтобы сделать это глобальным путем замены проверок на '.' с проверками на CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparat‌ или.

Jeff Yates 16.04.2009 22:08

@ Мэтт, а почему тебя беспокоит клавиша Control? if (!char.IsControl(e.KeyChar)

Hamish Grubijan 10.08.2011 20:42

@Hamish, если пользователь нажимает Ctrl + 3, я знаю, что мне не нужно продолжать.

Matt Hamilton 11.08.2011 00:55

@HamishGrubijan, IsControl не имеет ничего общего с клавишей Control; он возвращает, является ли символ управляющим символом. Разрешая управляющие символы, вы не нарушаете такие вещи, как backspace, delete или клавиши со стрелками.

Thomas Levesque 31.10.2011 06:22

Это TextChanged() вместо KeyPress()?

SearchForKnowledge 13.04.2015 20:43

Это тоже полезно как расширение.

Jack 01.07.2015 23:52

Между прочим, он по-прежнему принимает недопустимый ввод ctrl + v; ошибка, которая существует даже в официальном элементе управления NumericUpDown.

Nyerguds 11.08.2015 01:33

Похоже, этот код принимает 123-456 в качестве допустимого ввода. Мы также должны учитывать индекс, по которому - размещается

Ramu 29.02.2016 15:23

может быть мы можем изменить код From: // разрешить только одну десятичную точку `if ((e.KeyChar == '.') && ((sender as TextBox) .Text.IndexOf ('.')> -1)) { e.Handled = true; } to: // разрешить только одну десятичную точку только в начале if (e.KeyChar == '-') {if ((sender as TextBox) .Text.IndexOf ('-')> -1) return true; if ((отправитель как TextBox) .Text.Length> 0) return true; } `

Ramu 29.02.2016 15:53

Вы можете установить numericUpDown только для чтения. Тогда вы можете только увеличивать / уменьшать с помощью стрелок. Вставить не получится

Koxo 07.12.2018 09:34

Просто имейте в виду, что numericUpDown.Value является десятичным и имеет другой диапазон, чем float или double (от ± 1,5 x E-45 до ± 3,4 x E38 и от ± 5,0 x E-324 до ± 1,7 x E308, соответственно) по сравнению с этим десятичного типа (от ± 1,0 x E-28 до ± 7,9228 x E28). Итак, если вам нужны очень большие или очень маленькие числа, numericUpDown может не подойти.

m_a_s 15.01.2019 04:41

работает, но при вставке чего-то с control + v возникает ошибка, буквы все равно добавляются.

Eduardo Herrera 24.10.2020 01:00

Взгляните на Обработка ввода в WinForm

Я опубликовал свое решение, которое использует события ProcessCmdKey и OnKeyPress в текстовом поле. В комментариях показано, как использовать Regex для проверки нажатия клавиш и блокировки / разрешения соответствующим образом.

Я кое-что для этого сделал на CodePlex.

Он работает путем перехвата события TextChanged. Если результат хороший, он будет сохранен. Если что-то не так, будет восстановлено последнее хорошее значение. Источник слишком велик для публикации здесь, но вот ссылка на класс обрабатывает ядро ​​этой логики.

И просто потому, что всегда веселее делать что-то в одну строчку ...

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

ПРИМЕЧАНИЕ. Это НЕ запрещает пользователю копировать / вставлять в это текстовое поле. Это небезопасный способ дезинфицировать ваши данные.

это не общие решения, так как он работает только для интергеров. Недавно мне пришлось реализовать такую ​​вещь, и я закончил тем, что попытался проанализировать полученную строку до числа и разрешить ввод только в том случае, если синтаксический анализ прошел успешно.

grzegorz_p 04.01.2012 19:03

Это может не работать, если несколько методов обрабатывают события KeyPress из одного текстового поля. Одно событие может установить для e.Handled значение «истина», а другое - вернуть значение «ложь». В общем лучше использовать if (...) e.Handled = true;

Nathaniel Jones 27.05.2014 20:55

Вы можете отключить свойство ShortcutsEnabled, чтобы предотвратить копирование и вставку с клавиатуры или меню

Ahmad 11.12.2014 16:25

ХАХА! Да! Один лайнер!

Jamie L. 23.07.2015 21:20

Эх. Событие TextChanged, которое переходит к нему с помощью регулярного выражения, может исправить копипасту;)

Nyerguds 11.08.2015 01:34

1-й: один вкладыш МОЖЕТ предотвратить копирование / вставку, если вы исправите в нем ошибку (&& ! должен быть ||). 2-й: вы можете исправить это, чтобы он предотвращал только вставку, но позволял копировать / вырезать / выбирать все.

Bitterblue 20.03.2020 12:18

для проблемы с вставкой я просто переопределил метод WndProc и проигнорировал его (вставьте код = 0x0302), если он не содержит цифр: string text = Text; PasteEventArgs e = new PasteEventArgs (текст, Clipboard.GetText (), SelectionStart, SelectionLength); TextPasted (this, e); if (! e.Handled && text.Equals (Text)) Text = text.Substring (0, e.SelectionStart) + e.PastedText + text.Substring (e.SelectionStart + e.SelectionLength);

Guiorgy 29.09.2020 19:11

Извините, что разбудил мертвых, но я подумал, что кому-то это может пригодиться для использования в будущем.

Вот как я с этим справляюсь. Он обрабатывает числа с плавающей запятой, но может быть легко изменен для целых чисел.

В основном вы можете нажимать только 0 - 9 и .

У вас может быть только один 0 перед .

Все остальные символы игнорируются, а позиция курсора сохраняется.

    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;
    }

Вот быстро измененная версия 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;
    }

Это решение изобретает колесо с оговорками. Например, локализация.

Julien Guertault 08.05.2012 12:05
int Number;
bool isNumber;
isNumber = int32.TryPase(textbox1.text, out Number);

if (!isNumber)
{ 
    (code if not an integer);
}
else
{
    (code if an integer);
}

Вы можете использовать событие TextChanged

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();
    }
}

Похоже, он должен хорошо работать, если вы использовали Undo(), но в результате получается StackOverflowException.

Drew Chapin 19.10.2015 06:44

Похоже, свойство TextChanged - это часть процедуры, которую вы хотите отменить (). У меня есть переменная для всего окна, и я использую public int txtBoxValue, и если tryParse не работает, я возвращаю текст в txtBox с помощью txtBox.Text = txtBoxValue.ToString();

L. Zeda 22.12.2016 11:41

Это может быть полезно. Он допускает "реальные" числовые значения, включая правильные десятичные точки и предшествующие знаки плюс или минус. Вызовите его из связанного события 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;
    }

3 решение

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) другое решение от 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;
    }
}

источник http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keypress(v=VS.90).aspx

3) используя MaskedTextBox: http://msdn.microsoft.com/en-us/library/system.windows.forms.maskedtextbox.aspx

Вот простой автономный настраиваемый элемент управления Winforms, производный от стандартного TextBox, который позволяет вводить только System.Int32 (его можно легко адаптировать для других типов, таких как System.Int64 и т. д.). Он поддерживает операции копирования / вставки и отрицательные числа:

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);
    }

Обновление 2017: У моего первого ответа есть некоторые проблемы:

  • вы можете ввести что-то более длинное, чем целое число данного типа (например, 2147483648 больше, чем Int32.MaxValue);
  • в более общем смысле, нет никакой реальной проверки результат того, что было набрано;
  • он обрабатывает только int32, вам нужно будет написать конкретный элемент управления TextBox для каждого типа (Int64 и т. д.)

Поэтому я придумал другую версию, более общую, которая по-прежнему поддерживает копирование / вставку, знак + и - и т. д.

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; }
}

Для Int32 вы можете получить его, например:

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

или без деривации используйте новое событие TextValidating следующим образом:

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

но что приятно, это работает с любой строкой и любой процедурой проверки.

Это замечательно, красиво и просто, легко используется и справляется с необычными попытками ввода. Спасибо!

WiredEarp 28.09.2015 08:14

Обратите внимание на версию 2017 года, когда есть одно значение, например 1, и вы нажимаете Backspace, он игнорируется, а если вы сказали 120 и трижды нажали Backspace, у нас останется 1.

Karen Payne 02.12.2017 17:04

Ваш ValidatingTextbox - безусловно, одна из лучших реализаций, которые я когда-либо видел. Просто и эффективно. Спасибо!

Samuel 11.09.2019 23:28

Как я могу реализовать это в своем проекте 2019?

Eduardo Herrera 25.10.2020 00:48

Я бы обработал это в событии KeyDown.

void TextBox_KeyDown(object sender, KeyEventArgs e)
        {
            char c = Convert.ToChar(e.PlatformKeyCode);
            if (!char.IsDigit(c))
            {
                e.Handled = true;
            }
        }

Как насчет таких клавиш, как «Backspace», «Удалить», «Стрелка-Клавиша-Влево», «Стрелка-Клавиша-Вправо», Копировать и Вставить, Цифры, введенные Numpad (они торгуются как! Цифра)

user799821 27.05.2013 14:19

Просто добавьте еще несколько таких тестов: if (! Char.IsDigit (c) && c! = (Char) Keys.Back)

dnennis 29.12.2015 13:59

Именно для этого были разработаны события Validated / Validating.

Вот статья MSDN по этой теме: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.validating.aspx

Версия TL; DR: проверьте свойство .Text в событии Validating и установите e.Cancel=True, если данные недействительны.

Когда вы устанавливаете e.Cancel = True, пользователь не может покинуть поле, но вам нужно будет дать ему какую-то обратную связь, что что-то не так. Я меняю цвет фона окна на светло-красный, чтобы указать на проблему. Обязательно установите его обратно на SystemColors.Window, когда Validating вызывается с хорошим значением.

+1 за упоминание об очень API-идиоматическом подходе. Я относительно новичок в Windows Forms, и это довольно много функциональных возможностей и документов MSDN, поэтому также спасибо за конкретный указатель документов на Validating. <nitpick> OP упоминает, что немедленное запрещение / указание недействительного символа является идеальным, но Validating, похоже, требует, чтобы фокус был перемещен в другую форму / элемент управления, прежде чем он вступит в силу. .

William 23.07.2014 19:37

При нажатии кнопки вы можете проверить текст текстового поля с помощью цикла for:

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
    }

Этот работает с копированием и вставкой, перетаскиванием, клавишей вниз, предотвращает переполнение и довольно прост

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);
        }
    }       
}

Более простой ответ:

_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());
};
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;
        }
    }
}

попробуйте это очень просто

Привет, вы можете сделать что-то подобное в событии textchanged текстового поля.

вот демо

    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;
    }

Спасибо, очень полезно.

Kiran RS 18.09.2015 09:56

БЕЗОПАСНЫЙ и простой «рекурсивный» метод, который можно использовать с несколькими текстовыми полями.

Он блокирует неправильные символы, введенные с клавиатуры, а также вставленные значения и т. д. Он принимает только целые числа и максимальная длина числа - максимальная длина строкового типа (который имеет тип int, очень длинный!)

public void Check_If_Int_On_TextChanged(object sender, EventArgs e)
{
   // This method checks that each inputed character is a number. Any non-numeric
   // characters are removed from the text

   TextBox textbox = (TextBox)sender;

   // If the text is empty, return
   if (textbox.Text.Length == 0) { return; }

   // Check the new Text value if it's only numbers
   byte parsedValue;
   if (!byte.TryParse(textbox.Text[(textbox.Text.Length - 1)].ToString(), out parsedValue))
   {
      // Remove the last character as it wasn't a number
      textbox.Text = textbox.Text.Remove((textbox.Text.Length - 1));

      // Move the cursor to the end of text
      textbox.SelectionStart = textbox.Text.Length;
    }
 }

Похоже, что многие из текущих ответов на этот вопрос анализируют вводимый текст вручную. Если вы ищете определенный встроенный числовой тип (например, int или double), почему бы просто не делегировать работу методу TryParse этого типа? Например:

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;
        }
    }
}

Если вам нужно что-то более общее, но все же совместимое с конструктором 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;
        }
    }
}

И, наконец, если вы хотите что-то полностью универсальное и не заботитесь о поддержке 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;
        }
    }
}

просто используйте этот код в текстовом поле:

private void textBox1_TextChanged(object sender, EventArgs e)
{

    double parsedValue;

    if (!double.TryParse(textBox1.Text, out parsedValue))
    {
        textBox1.Text = "";
    }
}

Я работал над набором компонентов, чтобы восполнить недостающие вещи в WinForms, вот он: Расширенные формы

В частности, это класс для 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);
    }
}

Достаточно просто добавить что-то вроде myNumbericTextBox.RegexString = "^(\\d+|)$";.

мне нравится лаконичный код

    private void xmm_textbox_KeyPress(object sender, KeyPressEventArgs e) {
        double x;
        e.Handled = !double.TryParse(((TextBox)sender).Text, out x);
    }

Я также искал лучший способ проверить только числа в текстовом поле и проблему с нажатием клавиши, потому что он не поддерживает копирование и вставку щелчком правой кнопкой мыши или буфером обмена, поэтому придумал этот код, который проверяет, когда курсор покидает текстовое поле, а также проверяет наличие пустое поле. (адаптированная версия 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();
    }
}

MouseLeave кажется действительно плохим выбором для использования события.

LarsTech 08.10.2015 23:20

@LarsTech то, что, как я думал, было изменено на текст, может вызвать сообщение об ошибке, даже если пользователь осознает ошибку и попытается ее исправить, поэтому я подумал, что буду работать лучше. Как вы думаете, какое мероприятие лучше всего подходит для этого случая?

Alston Antony 09.10.2015 00:00

@AlstonAntony, поздний комментарий, я знаю. Но простого события щелчка, которое активируется при щелчке правой кнопкой мыши, было бы достаточно, не так ли?

Takarii 03.05.2016 10:41

На нашей веб-странице с определением текстового поля мы можем добавить событие onkeypress для приема только чисел. Он не покажет никаких сообщений, но предотвратит неправильный ввод. У меня это сработало, пользователь не мог вводить ничего, кроме номера.

<asp:TextBox runat = "server" ID = "txtFrom"
     onkeypress = "if (isNaN(String.fromCharCode(event.keyCode))) return false;">

Не забывайте, что пользователь может вставить неверный текст в TextBox.

Если вы хотите ограничить это, следуйте приведенному ниже коду:

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;
}   
Here is a simple solution that works for me.

public static bool numResult;
    public static bool checkTextisNumber(string numberVal)
    {
        try
        {
            if (numberVal.Equals("."))
            {
                numResult = true;
            }
            else if (numberVal.Equals(""))
            {
                numResult = true;
            }
            else
            {
                decimal number3 = 0;
                bool canConvert = decimal.TryParse(numberVal, out number3);
                if (canConvert == true)
                {
                    numResult = true;
                }
                else
                    numResult = false;
            }

        }
        catch (System.Exception ex)
        {
            numResult = false;
        }
        return numResult;
    }
    string correctNum;
    private void tBox_NumTester_TextChanged(object sender, TextChangedEventArgs e)
    {


        if (checkTextisNumber(tBox_NumTester.Text))
        {
            correctNum = tBox_NumTester.Text;
        }
        else
        {
            tBox_NumTester.Text = correctNum;
        }

    }

Просто используйте элемент управления NumericUpDown и установите видимость этих уродливых кнопок вверх-вниз на false.

numericUpDown1.Controls[0].Visible = false;

NumericUpDown на самом деле представляет собой набор элементов управления, содержащий «счетчик» (кнопки «вверх-вниз»), текстовое поле и некоторый код для проверки и изменения всего этого вместе.

Маркировка:

YourNumericUpDown.Controls[0].visible = false 

скроет кнопки, сохраняя активным базовый код.

Хотя это и не является очевидным решением, оно простое и эффективное. .Controls[1] скроет часть текстового поля, если вы захотите это сделать.

В принятом ответе не было никакой информации о том, как удалить кнопки вверх-вниз, как это сделать неочевидно, поскольку нет удобочитаемых интерфейсов для их включения или отключения. NumericUpDown на самом деле представляет собой набор элементов управления, содержащий текстовое поле и «счетчик» (кнопки «вверх-вниз»), а также некоторую проверку ввода для обработки кода.

user2163234 28.11.2017 02:19

Необходимо принимать как целые числа, так и числа с плавающей запятой, включая отрицательные числа.

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));
}

Я создал класс Повторно используемое расширение текстового поля для всевозможных проверок и подумал о том, чтобы поделиться им.

Все, что вам нужно сделать, это поднять TextChange событие и затем вызвать метод Validate. Выглядит это так:

private void tbxAmount_TextChanged(object sender, EventArgs e)
{ 
    tbxAmount.Validate(TextValidator.ValidationType.Amount);
}

Вот класс расширения:

public static class TextValidator
{
    public enum ValidationType
    {
        Amount,
        Integer
    }

    /// <summary>
    /// Validate a textbox on text change.
    /// </summary>
    /// <param name = "tbx"></param>
    /// <param name = "validationType"></param>
    public static void Validate(this TextBox tbx, ValidationType validationType)
    {
        PerformValidation(tbx, validationType);
        tbx.Select(tbx.Text.Length, 0);
    }


    private static void PerformValidation(this TextBox tbx, ValidationType validationType)
    {
        char[] enteredString = tbx.Text.ToCharArray();
        switch (validationType)
        {
            case ValidationType.Amount:
                tbx.Text = AmountValidation(enteredString);
                break;

            case ValidationType.Integer:
                tbx.Text = IntegerValidation(enteredString);
                break;

            default:
                break;
        }

        tbx.SelectionStart = tbx.Text.Length;
    }



    private static string AmountValidation(char[] enteredString)
    {
        string actualString = string.Empty;
        int count = 0;
        foreach (char c in enteredString.AsEnumerable())
        {
            if (count >= 1 && c == '.')
            { actualString.Replace(c, ' '); actualString.Trim(); }
            else
            {
                if (Char.IsDigit(c))
                {
                    actualString = actualString + c;
                }

                if (c == '.')
                {
                    actualString = actualString + c; count++;
                }

                else
                {
                    actualString.Replace(c, ' ');
                    actualString.Trim();
                }
            }
        }
        return actualString;
    }


    private static string IntegerValidation(char[] enteredString)
    {
        string actualString = string.Empty;
        foreach (char c in enteredString.AsEnumerable())
        {
            if (Char.IsDigit(c))
            {
                actualString = actualString + c;
            }
            else
            {
                actualString.Replace(c, ' ');
                actualString.Trim();
            }
        }
        return actualString;
    }
}

Вы можете найти полный код здесь

Это мой подход:

  1. с помощью linq (легко изменить фильтр)
  2. скопировать / вставить код подтверждения
  3. сохраняет положение каретки при нажатии запрещенного символа
  4. принимает левые нули
  5. и номера любого размера

    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;
        }
    }
    

Используя подход, описанный в Ответ Фабио Йотти, я создал более общее решение:

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», который содержит все нетривиальное поведение проверки. Все, что осталось сделать, это унаследовать от этого класса и переопределить метод «IsValid» любой необходимой логикой проверки. Например, с помощью этого класса можно создать «RegexedTextBox», который будет принимать только строки, соответствующие определенному регулярному выражению:

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);
    }
}

После этого, унаследовав от класса «RegexedTextBox», мы можем легко создать элементы управления «PositiveNumberTextBox» и «PositiveFloatingPointNumberTextBox»:

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

public sealed class PositiveFloatingPointNumberTextBox : RegexedTextBox {
    public PositiveFloatingPointNumberTextBox()
        : base(@"^(\d+\" + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + @")?\d*$") { }
}

Рабочее решение для WPF и простой TextChangedEventArgs.

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    var TextBox = (sender as TextBox);

    // if not a numeric value, remove news characters
    if (Regex.IsMatch(TextBox.Text, "[^0-9]"))
    {
        foreach (TextChange Change in e.Changes)
        {
            TextBox.Text = TextBox.Text.Remove(Change.Offset, Change.AddedLength);

            TextBox.CaretIndex = Change.Offset;
        }
    }
}

Речь идет о WinForms. В событии TextChanged WinForms нет TextChangedEventArgs

elshev 26.03.2020 07:13

вы можете просто предотвратить добавление нечисловых символов с помощью этого простого кода

 if (long.TryParse(TextBox.Text,out long isparsable))
        {
          // your code to handle numbers
        }
        else
        {
            TextBox.Text = "Only Numbers Allowed";
            TextBox.Focus();
            TextBox.SelectAll();
        }

Используйте RegularExpressionValidator:

<asp:TextBox ID = "TextBoxNum" runat = "server"></asp:TextBox><br />
<asp:RegularExpressionValidator ID = "RegularExpressionValidator1"
                        ControlToValidate = "TextBoxNum" runat = "server"
                        ErrorMessage = "Only numbers allowed"
                        ValidationExpression = "\d+">
</asp:RegularExpressionValidator>

Вот более 30 ответов, и многие ответы будут полезными. Но хочу поделиться обобщенной формой для System.Windows.Forms.TextBox и System.Windows.Controls.TextBox.

В System.Windows.Controls.TextBox нет доступного события Нажатие клавиши. Этот ответ предназначен для тех людей, которые хотят реализовать ту же логику для System.Windows.Forms.TextBox и System.Windows.Controls.TextBox.

Это код NumberTextBox. Используйте закомментированную строку вместо предыдущей для System.Windows.Controls.TextBox.

public class NumberTextBox : System.Windows.Forms.TextBox
//public class NumberTextBox : System.Windows.Controls.TextBox
{
    private double _maxValue;
    private double _minValue;
    private bool _flag;
    private string _previousValue;

    public NumberTextBox()
    {
        this.TextAlign = HorizontalAlignment.Right;
        //TextAlignment = TextAlignment.Right;
        KeyDown += TextBox_KeyDown;
        TextChanged += TextBox_TextChanged;
        _minValue = double.MinValue;
        _maxValue = double.MaxValue;
    }

    private void TextBox_KeyDown(object sender, KeyEventArgs e)
    {
        _previousValue = this.Text;
        _flag = this.SelectedText.Length > 0;
    }

    private void TextBox_TextChanged(object sender, EventArgs e)
    //private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        var text = this.Text;
        if (text.Length < 1) return;
        var cursorPosition = SelectionStart == 0 ? SelectionStart : SelectionStart - 1;
        var insertedChar = text[cursorPosition];
        if (IsInvalidInput(insertedChar, cursorPosition, text))
        {
            HandleText(text, cursorPosition);
        }
        ValidateRange(text, cursorPosition);
    }

    private bool IsInvalidInput(char insertedChar, int cursorPosition, string text)
    {
        return !char.IsDigit(insertedChar) && insertedChar != '.' && insertedChar != '-' ||
               insertedChar == '-' && cursorPosition != 0 ||
               text.Count(x => x == '.') > 1 ||
               text.Count(x => x == '-') > 1;
    }

    private void HandleText(string text, int cursorPosition)
    {
        this.Text = _flag ? _previousValue : text.Remove(cursorPosition, 1);
        this.SelectionStart = cursorPosition;
        this.SelectionLength = 0;
    }

    private void ValidateRange(string text, int cursorPosition)
    {
        try
        {
            if (text == "." || _minValue < 0 && text == "-") return;
            var doubleValue = Convert.ToDouble(text);
            if (doubleValue > _maxValue || doubleValue < _minValue)
            {
                HandleText(text, cursorPosition);
            }
        }
        catch (Exception)
        {
            HandleText(text, cursorPosition);
        }
    }

    protected void SetProperties(double minValue = double.MinValue, double maxValue = double.MaxValue)
    {
        _minValue = minValue;
        _maxValue = maxValue;
    }       

}

Код PositiveNumberTextBox:

public class PositiveNumberTextBox : NumberTextBox
{
    public PositiveNumberTextBox()
    {
        SetProperties(0);
    }
}

Код FractionNumberTextBox:

public class FractionNumberTextBox : NumberTextBox
{
    public FractionNumberTextBox()
    {
        SetProperties(0, 0.999999);
    }
}

Самый простой и грубый способ - использовать поле со списком вместо текстового поля с ReadOnly. Но, конечно, это полезно только в том случае, если вы ожидаете, что пользователь будет использовать приличный и разумный набор целых чисел, а не пытаться коснуться бесконечности.

Другие вопросы по теме