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





вы можете использовать событие TextChanged / Keypress, использовать регулярное выражение для фильтрации по числам и предпринять некоторые действия.
Попробуйте MaskedTextBox. Он принимает простой формат маски, поэтому вы можете ограничить ввод числами, датами или чем-то еще.
Я специально не хочу использовать MaskedTextBox. Допустимые форматы могут быть очень ограниченными. Они работают в этом случае, но я хотел бы сделать что-нибудь более общее.
Из контекста и тегов, которые вы использовали, я предполагаю, что вы пишете приложение .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);
}
}
Разве это не даст очень странный эффект, если вы введете в середину числа?
а также должно быть: textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);
что, если первый символ сам по себе не является цифрой ... в этом случае вычитание 1 не вызовет ошибку ...
Кроме того, использование TextChanged вместо KeyPress создает небольшую рекурсию, так как код переходит во второе событие TextChanged после метода Remove.
Вы переключили параметры ввода и шаблона для своей функции IsMatch. Сначала должен быть ввод, затем узор. msdn.microsoft.com/en-us/library/sdx2bds0(v=vs.110).aspx
Удаление удалит весь текст, а не недопустимый символ. Я думаю, что будет лучше сохранить предыдущее действительное изменение и соответственно обновить его.
решить проблему с TextChangedEventArgs и использовать e.Changes -> Change.Offset
Два варианта:
Вместо этого используйте NumericUpDown. NumericUpDown выполняет фильтрацию за вас, что приятно. Конечно, это также дает вашим пользователям возможность нажимать стрелки вверх и вниз на клавиатуре, чтобы увеличивать и уменьшать текущее значение.
Обрабатывайте соответствующие события клавиатуры, чтобы предотвратить ввод любых чисел, кроме числовых. Я добился успеха с этими двумя обработчиками событий в стандартном 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.
Единственный недостаток NumericUpDown заключается в том, что он не обеспечивает обратной связи, когда вы вводите значение, выходящее за пределы максимального или минимального допустимых значений - он просто меняет то, что вы набираете. TextBox может, по крайней мере, допускать недопустимые значения, чтобы вы могли предупреждать пользователя, когда он отправляет форму.
Также можно получить значения в текстовое поле с помощью мыши.
Это правда - пользователь всегда может вставить какие-то нечисловые символы. Вы бы надеялись, что проверка формы уловит это, поскольку в какой-то момент вы захотите выполнить Int32.TryParse или что-то в этом роде.
Вам потребуются дополнительные усилия, чтобы сделать это глобальным путем замены проверок на '.' с проверками на CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparat или.
@ Мэтт, а почему тебя беспокоит клавиша Control? if (!char.IsControl(e.KeyChar)
@Hamish, если пользователь нажимает Ctrl + 3, я знаю, что мне не нужно продолжать.
@HamishGrubijan, IsControl не имеет ничего общего с клавишей Control; он возвращает, является ли символ управляющим символом. Разрешая управляющие символы, вы не нарушаете такие вещи, как backspace, delete или клавиши со стрелками.
Это TextChanged() вместо KeyPress()?
Это тоже полезно как расширение.
Между прочим, он по-прежнему принимает недопустимый ввод ctrl + v; ошибка, которая существует даже в официальном элементе управления NumericUpDown.
Похоже, этот код принимает 123-456 в качестве допустимого ввода. Мы также должны учитывать индекс, по которому - размещается
может быть мы можем изменить код 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; } `
Вы можете установить numericUpDown только для чтения. Тогда вы можете только увеличивать / уменьшать с помощью стрелок. Вставить не получится
Просто имейте в виду, что 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 может не подойти.
работает, но при вставке чего-то с control + v возникает ошибка, буквы все равно добавляются.
Взгляните на Обработка ввода в WinForm
Я опубликовал свое решение, которое использует события ProcessCmdKey и OnKeyPress в текстовом поле. В комментариях показано, как использовать Regex для проверки нажатия клавиш и блокировки / разрешения соответствующим образом.
Я кое-что для этого сделал на CodePlex.
Он работает путем перехвата события TextChanged. Если результат хороший, он будет сохранен. Если что-то не так, будет восстановлено последнее хорошее значение. Источник слишком велик для публикации здесь, но вот ссылка на класс обрабатывает ядро этой логики.
И просто потому, что всегда веселее делать что-то в одну строчку ...
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}
ПРИМЕЧАНИЕ. Это НЕ запрещает пользователю копировать / вставлять в это текстовое поле. Это небезопасный способ дезинфицировать ваши данные.
это не общие решения, так как он работает только для интергеров. Недавно мне пришлось реализовать такую вещь, и я закончил тем, что попытался проанализировать полученную строку до числа и разрешить ввод только в том случае, если синтаксический анализ прошел успешно.
Это может не работать, если несколько методов обрабатывают события KeyPress из одного текстового поля. Одно событие может установить для e.Handled значение «истина», а другое - вернуть значение «ложь». В общем лучше использовать if (...) e.Handled = true;
Вы можете отключить свойство ShortcutsEnabled, чтобы предотвратить копирование и вставку с клавиатуры или меню
ХАХА! Да! Один лайнер!
Эх. Событие TextChanged, которое переходит к нему с помощью регулярного выражения, может исправить копипасту;)
1-й: один вкладыш МОЖЕТ предотвратить копирование / вставку, если вы исправите в нем ошибку (&& ! должен быть ||). 2-й: вы можете исправить это, чтобы он предотвращал только вставку, но позволял копировать / вырезать / выбирать все.
для проблемы с вставкой я просто переопределил метод 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);
Извините, что разбудил мертвых, но я подумал, что кому-то это может пригодиться для использования в будущем.
Вот как я с этим справляюсь. Он обрабатывает числа с плавающей запятой, но может быть легко изменен для целых чисел.
В основном вы можете нажимать только 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;
}
Это решение изобретает колесо с оговорками. Например, локализация.
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.
Похоже, свойство TextChanged - это часть процедуры, которую вы хотите отменить (). У меня есть переменная для всего окна, и я использую public int txtBoxValue, и если tryParse не работает, я возвращаю текст в txtBox с помощью txtBox.Text = txtBoxValue.ToString();
Это может быть полезно. Он допускает "реальные" числовые значения, включая правильные десятичные точки и предшествующие знаки плюс или минус. Вызовите его из связанного события 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: У моего первого ответа есть некоторые проблемы:
Поэтому я придумал другую версию, более общую, которая по-прежнему поддерживает копирование / вставку, знак + и - и т. д.
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);
но что приятно, это работает с любой строкой и любой процедурой проверки.
Это замечательно, красиво и просто, легко используется и справляется с необычными попытками ввода. Спасибо!
Обратите внимание на версию 2017 года, когда есть одно значение, например 1, и вы нажимаете Backspace, он игнорируется, а если вы сказали 120 и трижды нажали Backspace, у нас останется 1.
Ваш ValidatingTextbox - безусловно, одна из лучших реализаций, которые я когда-либо видел. Просто и эффективно. Спасибо!
Как я могу реализовать это в своем проекте 2019?
Я бы обработал это в событии KeyDown.
void TextBox_KeyDown(object sender, KeyEventArgs e)
{
char c = Convert.ToChar(e.PlatformKeyCode);
if (!char.IsDigit(c))
{
e.Handled = true;
}
}
Как насчет таких клавиш, как «Backspace», «Удалить», «Стрелка-Клавиша-Влево», «Стрелка-Клавиша-Вправо», Копировать и Вставить, Цифры, введенные Numpad (они торгуются как! Цифра)
Просто добавьте еще несколько таких тестов: if (! Char.IsDigit (c) && c! = (Char) Keys.Back)
Именно для этого были разработаны события 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, похоже, требует, чтобы фокус был перемещен в другую форму / элемент управления, прежде чем он вступит в силу. .
При нажатии кнопки вы можете проверить текст текстового поля с помощью цикла 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;
}
Спасибо, очень полезно.
БЕЗОПАСНЫЙ и простой «рекурсивный» метод, который можно использовать с несколькими текстовыми полями.
Он блокирует неправильные символы, введенные с клавиатуры, а также вставленные значения и т. д. Он принимает только целые числа и максимальная длина числа - максимальная длина строкового типа (который имеет тип 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 то, что, как я думал, было изменено на текст, может вызвать сообщение об ошибке, даже если пользователь осознает ошибку и попытается ее исправить, поэтому я подумал, что буду работать лучше. Как вы думаете, какое мероприятие лучше всего подходит для этого случая?
@AlstonAntony, поздний комментарий, я знаю. Но простого события щелчка, которое активируется при щелчке правой кнопкой мыши, было бы достаточно, не так ли?
На нашей веб-странице с определением текстового поля мы можем добавить событие 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 на самом деле представляет собой набор элементов управления, содержащий текстовое поле и «счетчик» (кнопки «вверх-вниз»), а также некоторую проверку ввода для обработки кода.
Необходимо принимать как целые числа, так и числа с плавающей запятой, включая отрицательные числа.
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;
}
}
Это мой подход:
и номера любого размера
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
вы можете просто предотвратить добавление нечисловых символов с помощью этого простого кода
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. Но, конечно, это полезно только в том случае, если вы ожидаете, что пользователь будет использовать приличный и разумный набор целых чисел, а не пытаться коснуться бесконечности.
числа или цифры? большая разница: даже целые числа могут стать отрицательными