Как получить TextBox, чтобы принимать только числовой ввод в WPF?

335

Я хочу принять цифры и десятичную точку, но без знака.

Я рассмотрел примеры использования элемента управления NumericUpDown для Windows Forms и этот пример пользовательского элемента управления NumericUpDown от Microsoft . Но до сих пор кажется, что NumericUpDown (поддерживается WPF или нет) не собирается предоставлять функциональность, которую я хочу. При разработке моего приложения никто в здравом уме не захочет возиться со стрелками. Они не имеют никакого практического смысла в контексте моего приложения.

Поэтому я ищу простой способ заставить стандартный WPF TextBox принимать только те символы, которые мне нужны. Это возможно? Это практично?

Giffyguy
источник

Ответы:

418

Добавьте предварительный просмотр ввода текста. Как так: <TextBox PreviewTextInput="PreviewTextInput" />.

Тогда внутри этого установите, e.Handledесли текст не разрешен.e.Handled = !IsTextAllowed(e.Text);

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

private static readonly Regex _regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
private static bool IsTextAllowed(string text)
{
    return !_regex.IsMatch(text);
}

Если вы хотите предотвратить вставку неверных данных, подключите DataObject.Pastingсобытие, DataObject.Pasting="TextBoxPasting"как показано здесь (фрагмент кода):

// Use the DataObject.Pasting Handler 
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
    if (e.DataObject.GetDataPresent(typeof(String)))
    {
        String text = (String)e.DataObject.GetData(typeof(String));
        if (!IsTextAllowed(text))
        {
            e.CancelCommand();
        }
    }
    else
    {
        e.CancelCommand();
    }
}
луч
источник
5
Ваше регулярное выражение не допускает научную запись (1e5), если это важно.
Рон Уорхолик
14
Обратите внимание, что этот ответ проверяет только то, что вы печатаете, поэтому вы можете ввести 3 -3
Дэвид Сайкс
153
Суть ответа заключалась не в том, чтобы указать идеальный Regex, а в том, чтобы показать, как использовать WPF для фильтрации того, что кто-то печатает.
Рэй,
28
[Space] не запускает событие PreviewTextInput.
peterG
5
Нечто подобное double.TryParse(), вероятно, будет реализовано с таким же количеством строк и будет более гибким.
Томас Веллер
190

Обработчик событий выполняет предварительный просмотр ввода текста. Здесь регулярное выражение соответствует вводу текста только в том случае, если оно не является числом, и тогда оно не вносится в текстовое поле ввода.

Если вы хотите только буквы, замените регулярное выражение на [^a-zA-Z].

XAML

<TextBox Name="NumberTextBox" PreviewTextInput="NumberValidationTextBox"/>

ФАЙЛ XAML.CS

using System.Text.RegularExpressions;
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
    Regex regex = new Regex("[^0-9]+");
    e.Handled = regex.IsMatch(e.Text);
}
Кишор
источник
1
Обработчик событий - предварительный ввод текста. Здесь регулярное выражение соответствует вводу текста, только если оно не является числом, то оно не вносится в текстовое поле ввода. Если вам нужны только алфавиты, замените регулярное выражение на [^ a-zA-Z].
Кишор
Как насчет чисел, десятичных дробей и операторов?
Джейсон Эберси
Пожалуйста, дайте мне знать, как использовать его, когда объявляет в каком-то другом классе STATIC и применяется к текстовому полю?
SHEKHAR SHETE
3
Мне это нравится больше, чем фактический ответ, короткий и простой. Фактический ответ требует двух методов, чтобы получить тот же результат.
Щепка
1
@Jagd Предложенный ответ - лучшее разделение проблем. Однако вы также можете установить столько текстовых полей для этой проверки. просто добавьте PreviewTextInput = "NumberValidationTextBox". (точно так же как другой ответ!)
Щепка
84

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

public class AllowableCharactersTextBoxBehavior : Behavior<TextBox>
{
    public static readonly DependencyProperty RegularExpressionProperty =
         DependencyProperty.Register("RegularExpression", typeof(string), typeof(AllowableCharactersTextBoxBehavior),
         new FrameworkPropertyMetadata(".*"));
    public string RegularExpression
    {
        get
        {
            return (string)base.GetValue(RegularExpressionProperty);
        }
        set
        {
            base.SetValue(RegularExpressionProperty, value);
        }
    }

    public static readonly DependencyProperty MaxLengthProperty =
        DependencyProperty.Register("MaxLength", typeof(int), typeof(AllowableCharactersTextBoxBehavior),
        new FrameworkPropertyMetadata(int.MinValue));
    public int MaxLength
    {
        get
        {
            return (int)base.GetValue(MaxLengthProperty);
        }
        set
        {
            base.SetValue(MaxLengthProperty, value);
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewTextInput += OnPreviewTextInput;
        DataObject.AddPastingHandler(AssociatedObject, OnPaste);
    }

    private void OnPaste(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(DataFormats.Text))
        {
            string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));

            if (!IsValid(text, true))
            {
                e.CancelCommand();
            }
        }
        else
        {
            e.CancelCommand();
        }
    }

    void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    {
        e.Handled = !IsValid(e.Text, false);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
        DataObject.RemovePastingHandler(AssociatedObject, OnPaste);
    }

    private bool IsValid(string newText, bool paste)
    {
        return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression);
    }

    private bool ExceedsMaxLength(string newText, bool paste)
    {
        if (MaxLength == 0) return false;

        return LengthOfModifiedText(newText, paste) > MaxLength;
    }

    private int LengthOfModifiedText(string newText, bool paste)
    {
        var countOfSelectedChars = this.AssociatedObject.SelectedText.Length;
        var caretIndex = this.AssociatedObject.CaretIndex;
        string text = this.AssociatedObject.Text;

        if (countOfSelectedChars > 0 || paste)
        {
            text = text.Remove(caretIndex, countOfSelectedChars);
            return text.Length + newText.Length;
        }
        else
        {
            var insert = Keyboard.IsKeyToggled(Key.Insert);

            return insert && caretIndex < text.Length ? text.Length : text.Length + newText.Length;
        }
    }
}

Вот соответствующий код представления:

<TextBox MaxLength="50" TextWrapping="Wrap" MaxWidth="150" Margin="4"
 Text="{Binding Path=FileNameToPublish}" >
     <interactivity:Interaction.Behaviors>
         <v:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9.\-]+$" MaxLength="50" />
     </interactivity:Interaction.Behaviors>
</TextBox>
Вил П
источник
1
Вдохновленный этим потрясающим решением, я реализовал некоторые улучшения. Пожалуйста, проверьте это ниже в теме.
Алекс Клаус
2
Здравствуй. Я знаю, что уже немного поздно, но я пытаюсь это реализовать, но продолжаю получать ошибки. Я предполагаю, что мне не хватает некоторых ссылок. Есть ли какие-либо, которые предназначены для ввода, кроме значений по умолчанию после того, как вы создали класс?
Предложение
1
@ Предложение Да, обязательно включите xmlns: interactivity = " schemas.microsoft.com/expression/2010/interactivity " в верхней части окна xaml.
WiteCastle
Выражение теперь устарело. Хотя этот подход является чистым, он использует код, который больше не поддерживается.
Роберт Бейкер
1
Так что если вы редактируете функцию IsValid для возврата! ExceedsMaxLength (newText, paste) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), RegularExpression); тогда это будет оценивать всю строку. Кстати - люблю этот вариант с поведением!
Рогала
59

Это улучшенное решение ответа WilP . Мои улучшения:

  • Улучшено поведение кнопок Del и Backspace
  • Добавленное EmptyValueсвойство, если пустая строка не подходит
  • Исправлены некоторые мелкие опечатки
/// <summary>
///     Regular expression for Textbox with properties: 
///         <see cref="RegularExpression"/>, 
///         <see cref="MaxLength"/>,
///         <see cref="EmptyValue"/>.
/// </summary>
public class TextBoxInputRegExBehaviour : Behavior<TextBox>
{
    #region DependencyProperties
    public static readonly DependencyProperty RegularExpressionProperty =
        DependencyProperty.Register("RegularExpression", typeof(string), typeof(TextBoxInputRegExBehaviour), new FrameworkPropertyMetadata(".*"));

    public string RegularExpression
    {
        get { return (string)GetValue(RegularExpressionProperty); }
        set { SetValue(RegularExpressionProperty, value); }
    }

    public static readonly DependencyProperty MaxLengthProperty =
        DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBoxInputRegExBehaviour),
                                        new FrameworkPropertyMetadata(int.MinValue));

    public int MaxLength
    {
        get { return (int)GetValue(MaxLengthProperty); }
        set { SetValue(MaxLengthProperty, value); }
    }

    public static readonly DependencyProperty EmptyValueProperty =
        DependencyProperty.Register("EmptyValue", typeof(string), typeof(TextBoxInputRegExBehaviour), null);

    public string EmptyValue
    {
        get { return (string)GetValue(EmptyValueProperty); }
        set { SetValue(EmptyValueProperty, value); }
    }
    #endregion

    /// <summary>
    ///     Attach our behaviour. Add event handlers
    /// </summary>
    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.PreviewTextInput += PreviewTextInputHandler;
        AssociatedObject.PreviewKeyDown += PreviewKeyDownHandler;
        DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
    }

    /// <summary>
    ///     Deattach our behaviour. remove event handlers
    /// </summary>
    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.PreviewTextInput -= PreviewTextInputHandler;
        AssociatedObject.PreviewKeyDown -= PreviewKeyDownHandler;
        DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
    }

    #region Event handlers [PRIVATE] --------------------------------------

    void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
    {
        string text;
        if (this.AssociatedObject.Text.Length < this.AssociatedObject.CaretIndex)
            text = this.AssociatedObject.Text;
        else
        {
            //  Remaining text after removing selected text.
            string remainingTextAfterRemoveSelection;

            text = TreatSelectedText(out remainingTextAfterRemoveSelection)
                ? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text)
                : AssociatedObject.Text.Insert(this.AssociatedObject.CaretIndex, e.Text);
        }

        e.Handled = !ValidateText(text);
    }

    /// <summary>
    ///     PreviewKeyDown event handler
    /// </summary>
    void PreviewKeyDownHandler(object sender, KeyEventArgs e)
    {
        if (string.IsNullOrEmpty(this.EmptyValue))
            return;

        string text = null;

        // Handle the Backspace key
        if (e.Key == Key.Back)
        {
            if (!this.TreatSelectedText(out text))
            {
                if (AssociatedObject.SelectionStart > 0)
                    text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart - 1, 1);
            }
        }
        // Handle the Delete key
        else if (e.Key == Key.Delete)
        {
            // If text was selected, delete it
            if (!this.TreatSelectedText(out text) && this.AssociatedObject.Text.Length > AssociatedObject.SelectionStart)
            {
                // Otherwise delete next symbol
                text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, 1);
            }
        }

        if (text == string.Empty)
        {
            this.AssociatedObject.Text = this.EmptyValue;
            if (e.Key == Key.Back)
                AssociatedObject.SelectionStart++;
            e.Handled = true;
        }
    }

    private void PastingHandler(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(DataFormats.Text))
        {
            string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));

            if (!ValidateText(text))
                e.CancelCommand();
        }
        else
            e.CancelCommand();
    }
    #endregion Event handlers [PRIVATE] -----------------------------------

    #region Auxiliary methods [PRIVATE] -----------------------------------

    /// <summary>
    ///     Validate certain text by our regular expression and text length conditions
    /// </summary>
    /// <param name="text"> Text for validation </param>
    /// <returns> True - valid, False - invalid </returns>
    private bool ValidateText(string text)
    {
        return (new Regex(this.RegularExpression, RegexOptions.IgnoreCase)).IsMatch(text) && (MaxLength == int.MinValue || text.Length <= MaxLength);
    }

    /// <summary>
    ///     Handle text selection
    /// </summary>
    /// <returns>true if the character was successfully removed; otherwise, false. </returns>
    private bool TreatSelectedText(out string text)
    {
        text = null;
        if (AssociatedObject.SelectionLength <= 0) 
            return false;

        var length = this.AssociatedObject.Text.Length;
        if (AssociatedObject.SelectionStart >= length)
            return true;

        if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length)
            AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart;

        text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength);
        return true;
    }
    #endregion Auxiliary methods [PRIVATE] --------------------------------
}

Использование довольно просто:

<i:Interaction.Behaviors>
    <behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>
Алекс Клаус
источник
1
Это решение довольно лучше. Но вы допустили небольшую ошибку: когда вы не устанавливаете настройку, MaxLengthваше условие (this.MaxLength == 0 || text.Length <= this.MaxLength)всегда возвращается falseпри тестировании нового текста. Это должно быть лучше, (this.MaxLength == int.MinValue || text.Length <= this.MaxLength)так как вы установили int.MinValueзначение по умолчанию для MaxLength.
Кристоф
1
Спасибо @Christoph, да, ты прав. Я изменил свой ответ.
Алекс Клаус
@AlexKlaus это прекрасно работает, пока я не добавлю UpdateSourceTrigger=PropertyChangedпривязку. Любая идея , как получить этот код на работу при изменении UpdateSourceTriggerустановлено значение PropertyChanged? Спасибо, что поделились этим кодом.
младший
32

Вот очень простой и легкий способ сделать это с помощью MVVM.

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

Код XAML:

<TextBox x:Name="contactNoTxtBox"  Text="{Binding contactNo}" />

Посмотреть код модели:

private long _contactNo;
public long contactNo
{
    get { return _contactNo; }
    set
    {
        if (value == _contactNo)
            return;
        _contactNo = value;
        OnPropertyChanged();
    }
}
Снзив Гупта
источник
Но вопрос содержал: «Я хочу принять цифры и десятичную точку» . Десятичная точка принята для этого ответа?
Питер Мортенсен
Я попытался перейти longна float, но это не сработало с немедленной проверкой. Я добавил UpdateSourceTrigger="PropertyChanged"к привязке, чтобы она выполняла проверку при вводе каждого символа и больше не могла набирать '.' в TextBox, если не было недопустимого символа (пришлось ввести «1x.234», а затем удалить «х»). Он также чувствует себя немного вялым в этом режиме. Похоже, это используется System.Number.ParseSingle()для выполнения работы, поэтому он принимает различные обозначения.
Fadden
@wolle, вероятно, не проголосовал, потому что он не объясняет, как работает проверка.
Пол Маккарти
26

Добавьте в ПРАВИЛО ВАЛИДАЦИИ, чтобы при изменении текста проверялось, чтобы определить, являются ли данные числовыми, и, если это так, позволяет продолжить обработку, а если нет, то предлагает пользователю принять в этом поле только числовые данные.

Подробнее читайте в валидации в Windows Presentation Foundation

Стивен Райтон
источник
6
Это не совсем ответ для стандартов SO.
Роберт Бейкер
Кажется, это .net способ сделать это.
Телемат
1
Является правильным ответом: проверка должна быть на Viene модели или на уровне модели. Более того, вы можете просто привязать числовой тип, например, doubleи он уже предоставляет вам стандартную проверку.
24

У расширенного набора инструментов WPF есть один: NumericUpDown введите описание изображения здесь

Брайан Лагунас
источник
Я пробовал этот элемент управления, но он вызывает проблемы при использовании счетчика с UpdateSourceTrigger = PropertyChanged и, как правило, пользователю сложно ввести научную нотацию.
Менно Дей - ван Рейсвейк
5
Обратите внимание, что NumericUpDownсейчас устарел. вы можете использовать DecimalUpDownиз обновленного инструментария Extended WPF Toolkit ™ Community Edition
itsho
20

Может также просто реализовать правило проверки и применить его к TextBox:

  <TextBox>
    <TextBox.Text>
      <Binding Path="OnyDigitInput" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
          <conv:OnlyDigitsValidationRule />
        </Binding.ValidationRules>
      </Binding>
    </TextBox.Text>

С реализацией правила следующим образом (используя тот же Regex, как предложено в других ответах):

public class OnlyDigitsValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        var validationResult = new ValidationResult(true, null);

        if(value != null)
        {
            if (!string.IsNullOrEmpty(value.ToString()))
            {
                var regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
                var parsingOk = !regex.IsMatch(value.ToString());
                if (!parsingOk)
                {
                    validationResult = new ValidationResult(false, "Illegal Characters, Please Enter Numeric Value");
                }
            }
        }

        return validationResult;
    }
}
Goul
источник
Если вы хотите ввести десятичные цифры, не возвращайте «valid», когда текст оканчивается на «.» Пожалуйста, обратитесь к stackoverflow.com/a/27838893/417939
YantingChen
15

Здесь у меня есть простое решение, вдохновленное ответом Рэя . Этого должно быть достаточно для идентификации любой формы номера.

Это решение также можно легко изменить, если вам нужны только положительные числа, целочисленные значения или значения с точностью до максимального количества десятичных знаков и т. Д.


Как указано в ответе Рэя , вам нужно сначала добавить PreviewTextInputсобытие:

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

Затем поместите в код следующее:

private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
    var textBox = sender as TextBox;
    // Use SelectionStart property to find the caret position.
    // Insert the previewed text into the existing text in the textbox.
    var fullText = textBox.Text.Insert(textBox.SelectionStart, e.Text);

    double val;
    // If parsing is successful, set Handled to false
    e.Handled = !double.TryParse(fullText, out val);
}
Энтони
источник
4
мне очень нравится этот ответ, простой и эффективный +
Pulle
Боже и просто, но это уродливо, что это позволяет места
Момо
2
Это все еще позволяет кому-то просто вставить строку в текстовое поле
FCin
8

Я позволил числовые цифры клавиатуры и Backspace:

    private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        int key = (int)e.Key;

        e.Handled = !(key >= 34 && key <= 43 || 
                      key >= 74 && key <= 83 || 
                      key == 2);
    }
Хамзех Собох
источник
8
Я бы рекомендовал использовать значения enum вместо Magic Numbers :var keyEnum = (System.Windows.Input.Key) e.Key; e.Handled = !(keyEnum >= System.Windows.Input.Key.D0 && keyEnum <= System.Windows.Input.Key.D9 || keyEnum >= System.Windows.Input.Key.NumPad0 && keyEnum <= System.Windows.Input.Key.NumPad9 || keyEnum == System.Windows.Input.Key.Back);
itho
7

Я буду считать, что:

  1. Ваш TextBox, для которого вы хотите разрешить только числовой ввод, имеет свойство Text, изначально установленное в какое-то допустимое числовое значение (например, 2.7172).

  2. Ваше текстовое поле является дочерним элементом вашего главного окна

  3. Ваше главное окно класса Window1

  4. Ваше имя в TextBox является числовым

Основная идея:

  1. Добавить: private string previousText;в свой класс главного окна (Window1)

  2. Добавьте: previousText = numericTB.Text;в ваш главный конструктор окон

  3. Создайте обработчик для события numericTB.TextChanged, чтобы он был примерно таким:

    private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
    {
        double num = 0;
        bool success = double.TryParse(((TextBox)sender).Text, out num);
        if (success & num >= 0)
            previousText = ((TextBox)sender).Text;
        else
            ((TextBox)sender).Text = previousText;
    }

При этом для предыдущего тега будет установлено значение numericTB.Text, пока оно действительно, и для numericTB.Text будет установлено его последнее действительное значение, если пользователь пишет что-то, что вам не нравится. Конечно, это просто базовая идея, и она просто «устойчива к идиотам», а не «защищена от идиотов». Это не относится к случаю, когда пользователь, например, путается с пробелами. Итак, вот полное решение, которое я считаю «доказательством идиота», и если я ошибаюсь, скажите мне:

  1. Содержимое вашего файла Window1.xaml:

    <Window x:Class="IdiotProofNumericTextBox.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <TextBox Height="30" Width="100" Name="numericTB" TextChanged="numericTB_TextChanged"/>
        </Grid>
    </Window>
  2. Содержимое вашего файла Window.xaml.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace IdiotProofNumericTextBox
    {
        public partial class Window1 : Window
        {
            private string previousText;
    
            public Window1()
            {
                InitializeComponent();
                previousText = numericTB.Text;
            }
    
            private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
            {
                if (string.IsNullOrEmpty(((TextBox)sender).Text))
                    previousText = "";
                else
                {
                    double num = 0;
                    bool success = double.TryParse(((TextBox)sender).Text, out num);
                    if (success & num >= 0)
                    {
                        ((TextBox)sender).Text.Trim();
                        previousText = ((TextBox)sender).Text;
                    }
                    else
                    {
                        ((TextBox)sender).Text = previousText;
                        ((TextBox)sender).SelectionStart = ((TextBox)sender).Text.Length;
                    }
                }
            }
        }
    }

И это все. Если у вас много TextBox-ов, то я рекомендую создать CustomControl, который наследуется от TextBox, чтобы вы могли обернуть предыдущиеText и numericTB_TextChanged в отдельный файл.

user666535
источник
Вау, это здорово! Как я мог допустить отрицательный символ спереди?
theNoobGuy
6

Это единственный необходимый код:

void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = new Regex("[^0-9]+").IsMatch(e.Text);
}

Это позволяет только вводить числа в текстовое поле.

Чтобы разрешить десятичную точку или знак минус, вы можете изменить регулярное выражение на [^0-9.-]+.

Дэнни Беккет
источник
1
Очень хорошее решение, за исключением одного незначительного случая: оно не остановит вас от ввода пробелов, поскольку они не вызывают событие PreviewTextInput.
Тим Полманн
Backspace не запускает его также.
Вингер Сендон
6

Если вы не хотите писать много кода для выполнения основной функции (я не знаю, почему люди делают длинные методы), вы можете просто сделать это:

  1. Добавить пространство имен:

    using System.Text.RegularExpressions;
  2. В XAML установите свойство TextChanged:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
  3. В WPF под методом txt1_TextChanged добавьте Regex.Replace:

    private void txt1_TextChanged(object sender, TextChangedEventArgs e)
    {
        txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", "");
    }
IATO
источник
2
Гораздо чище использовать Behavior или AttachedProperty. Нет кодов позади, в каждом представлении / для каждого текстового
сэр Руфо
Может работать, но безобразно, так как морковь прыгнет впереди текстового поля
Момо
6

Другой подход будет использовать прикрепленное поведение, я реализовал свой кастом класс TextBoxHelper , который можно использовать в текстовых полях по всему моему проекту. Потому что я полагал, что подписка на события для каждого текстового поля и в каждом отдельном файле XAML для этой цели может занимать много времени.

Реализованный мной класс TextBoxHelper имеет следующие особенности:

  • Фильтрация и прием только чисел в Double , Int , Uint и природный формате
  • Фильтрация и принятие только четных или нечетных чисел
  • Обработка обработчика события вставки для предотвращения вставки недопустимого текста в наши числовые текстовые поля
  • Можно установить значение по умолчанию, которое будет использоваться для предотвращения неверных данных в качестве последнего снимка путем подписки на событие TextChanged для текстовых полей.

Вот реализация класса TextBoxHelper:

public static class TextBoxHelper
{
    #region Enum Declarations

    public enum NumericFormat
    {
        Double,
        Int,
        Uint,
        Natural
    }

    public enum EvenOddConstraint
    {
        All,
        OnlyEven,
        OnlyOdd
    }

    #endregion

    #region Dependency Properties & CLR Wrappers

    public static readonly DependencyProperty OnlyNumericProperty =
        DependencyProperty.RegisterAttached("OnlyNumeric", typeof(NumericFormat?), typeof(TextBoxHelper),
            new PropertyMetadata(null, DependencyPropertiesChanged));
    public static void SetOnlyNumeric(TextBox element, NumericFormat value) =>
        element.SetValue(OnlyNumericProperty, value);
    public static NumericFormat GetOnlyNumeric(TextBox element) =>
        (NumericFormat) element.GetValue(OnlyNumericProperty);


    public static readonly DependencyProperty DefaultValueProperty =
        DependencyProperty.RegisterAttached("DefaultValue", typeof(string), typeof(TextBoxHelper),
            new PropertyMetadata(null, DependencyPropertiesChanged));
    public static void SetDefaultValue(TextBox element, string value) =>
        element.SetValue(DefaultValueProperty, value);
    public static string GetDefaultValue(TextBox element) => (string) element.GetValue(DefaultValueProperty);


    public static readonly DependencyProperty EvenOddConstraintProperty =
        DependencyProperty.RegisterAttached("EvenOddConstraint", typeof(EvenOddConstraint), typeof(TextBoxHelper),
            new PropertyMetadata(EvenOddConstraint.All, DependencyPropertiesChanged));
    public static void SetEvenOddConstraint(TextBox element, EvenOddConstraint value) =>
        element.SetValue(EvenOddConstraintProperty, value);
    public static EvenOddConstraint GetEvenOddConstraint(TextBox element) =>
        (EvenOddConstraint)element.GetValue(EvenOddConstraintProperty);

    #endregion

    #region Dependency Properties Methods

    private static void DependencyPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is TextBox textBox))
            throw new Exception("Attached property must be used with TextBox.");

        switch (e.Property.Name)
        {
            case "OnlyNumeric":
            {
                var castedValue = (NumericFormat?) e.NewValue;

                if (castedValue.HasValue)
                {
                    textBox.PreviewTextInput += TextBox_PreviewTextInput;
                    DataObject.AddPastingHandler(textBox, TextBox_PasteEventHandler);
                }
                else
                {
                    textBox.PreviewTextInput -= TextBox_PreviewTextInput;
                    DataObject.RemovePastingHandler(textBox, TextBox_PasteEventHandler);
                }

                break;
            }

            case "DefaultValue":
            {
                var castedValue = (string) e.NewValue;

                if (castedValue != null)
                {
                    textBox.TextChanged += TextBox_TextChanged;
                }
                else
                {
                    textBox.TextChanged -= TextBox_TextChanged;
                }

                break;
            }
        }
    }

    #endregion

    private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        var textBox = (TextBox)sender;

        string newText;

        if (textBox.SelectionLength == 0)
        {
            newText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
        }
        else
        {
            var textAfterDelete = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength);

            newText = textAfterDelete.Insert(textBox.SelectionStart, e.Text);
        }

        var evenOddConstraint = GetEvenOddConstraint(textBox);

        switch (GetOnlyNumeric(textBox))
        {
            case NumericFormat.Double:
            {
                if (double.TryParse(newText, out double number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;
                    }
                }
                else
                    e.Handled = true;

                break;
            }

            case NumericFormat.Int:
            {
                if (int.TryParse(newText, out int number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;
                    }
                }
                else
                    e.Handled = true;

                break;
            }

            case NumericFormat.Uint:
            {
                if (uint.TryParse(newText, out uint number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;
                    }
                }
                else
                    e.Handled = true;

                break;
            }

            case NumericFormat.Natural:
            {
                if (uint.TryParse(newText, out uint number))
                {
                    if (number == 0)
                        e.Handled = true;
                    else
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.Handled = true;
                                else
                                    e.Handled = false;

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.Handled = true;
                                else
                                    e.Handled = false;

                                break;
                        }
                    }
                }
                else
                    e.Handled = true;

                break;
            }
        }
    }

    private static void TextBox_PasteEventHandler(object sender, DataObjectPastingEventArgs e)
    {
        var textBox = (TextBox)sender;

        if (e.DataObject.GetDataPresent(typeof(string)))
        {
            var clipboardText = (string) e.DataObject.GetData(typeof(string));

            var newText = textBox.Text.Insert(textBox.SelectionStart, clipboardText);

            var evenOddConstraint = GetEvenOddConstraint(textBox);

            switch (GetOnlyNumeric(textBox))
            {
                case NumericFormat.Double:
                {
                    if (double.TryParse(newText, out double number))
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.CancelCommand();

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.CancelCommand();

                                break;
                        }
                    }
                    else
                        e.CancelCommand();

                    break;
                }

                case NumericFormat.Int:
                {
                    if (int.TryParse(newText, out int number))
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.CancelCommand();

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.CancelCommand();


                                break;
                        }
                    }
                    else
                        e.CancelCommand();

                    break;
                }

                case NumericFormat.Uint:
                {
                    if (uint.TryParse(newText, out uint number))
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.CancelCommand();

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.CancelCommand();


                                break;
                        }
                    }
                    else
                        e.CancelCommand();

                    break;
                }

                case NumericFormat.Natural:
                {
                    if (uint.TryParse(newText, out uint number))
                    {
                        if (number == 0)
                            e.CancelCommand();
                        else
                        {
                            switch (evenOddConstraint)
                            {
                                case EvenOddConstraint.OnlyEven:

                                    if (number % 2 != 0)
                                        e.CancelCommand();

                                    break;

                                case EvenOddConstraint.OnlyOdd:

                                    if (number % 2 == 0)
                                        e.CancelCommand();

                                    break;
                            }
                        }
                    }
                    else
                    {
                        e.CancelCommand();
                    }

                    break;
                }
            }
        }
        else
        {
            e.CancelCommand();
        }
    }

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

        var defaultValue = GetDefaultValue(textBox);

        var evenOddConstraint = GetEvenOddConstraint(textBox);

        switch (GetOnlyNumeric(textBox))
        {
            case NumericFormat.Double:
            {
                if (double.TryParse(textBox.Text, out double number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                textBox.Text = defaultValue;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                textBox.Text = defaultValue;

                            break;
                    }
                }
                else
                    textBox.Text = defaultValue;

                break;
            }

            case NumericFormat.Int:
            {
                if (int.TryParse(textBox.Text, out int number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                textBox.Text = defaultValue;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                textBox.Text = defaultValue;

                            break;
                    }
                }
                else
                    textBox.Text = defaultValue;

                break;
            }

            case NumericFormat.Uint:
            {
                if (uint.TryParse(textBox.Text, out uint number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                textBox.Text = defaultValue;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                textBox.Text = defaultValue;

                            break;
                    }
                }
                else
                    textBox.Text = defaultValue;

                break;
            }

            case NumericFormat.Natural:
            {
                if (uint.TryParse(textBox.Text, out uint number))
                {
                    if(number == 0)
                        textBox.Text = defaultValue;
                    else
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    textBox.Text = defaultValue;

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    textBox.Text = defaultValue;

                                break;
                        }
                    }
                }
                else
                {
                    textBox.Text = defaultValue;
                }

                break;
            }
        }
    }
}

И вот несколько примеров его легкого использования:

<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double"
         viewHelpers:TextBoxHelper.DefaultValue="1"/>

Или

<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural"
         viewHelpers:TextBoxHelper.DefaultValue="3"
         viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>

Обратите внимание, что мой TextBoxHelper находится в псевдониме viewHelpers xmlns.

Я надеюсь, что эта реализация облегчит работу некоторых других :)

Амир Махди Нассири
источник
1
Это замечательно, когда вы используете текстовое поле внутри DataTemplate, спасибо!
NucS
3
Отличный ответ, но мне трудно читать ваши методы. Вероятно, вы должны разбить их на более мелкие. См. Какова идеальная длина метода для вас?
Энтони
Спасибо за конструктивный отзыв @Anthony
Амир Махди Нассири
4
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;

в предпросмотре события keydown текстового поля.

послушник
источник
3
Не допускает возврата, хотя.
Свентевит
2
Backspace - 2, tab - 3
Daniel
6
-1 потому что по моему опыту такие умные трюки в конечном итоге кусают тебя в задницу, как отмечали некоторые другие комментаторы.
DonkeyMaster
Стрелка влево 23, Стрелка вправо 25.
Аарон
4
PreviewTextInput += (s, e) =>
{
    e.Handled = !e.Text.All(char.IsDigit);
};
Джулиан Ковальчук
источник
2
это также не примет точку ., так как e.Textвозвращает только последний введенный символ и точка не пройдет IsDigitпроверку.
Энтони
4

Для тех, кто ищет быструю и очень простую реализацию проблемы такого типа, используя только целые и десятичные числа, добавьте в свой файл XAML PreviewTextInputсвойство, TextBoxа затем в свой файл xaml.cs:

private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !char.IsDigit(e.Text.Last()) && !e.Text.Last() == '.';
}

Излишне проверять всю строку каждый раз, если, как уже упоминали другие, вы делаете что-то с научной нотацией (хотя, если вы добавляете определенные символы, такие как 'e', ​​простое регулярное выражение, добавляющее символы / символы, действительно просто и проиллюстрировано другими ответами). Но для простых значений с плавающей точкой этого решения будет достаточно.

Написано как однострочный с лямбда-выражением:

private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) => e.Handled = !char.IsDigit(e.Text.Last() && !e.Text.Last() == '.');
Крис
источник
3

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

private void textBoxNumeric_TextChanged(object sender, TextChangedEventArgs e) 
{         
      TextBox textBox = sender as TextBox;         
      Int32 selectionStart = textBox.SelectionStart;         
      Int32 selectionLength = textBox.SelectionLength;         
      String newText = String.Empty;         
      int count = 0;         
      foreach (Char c in textBox.Text.ToCharArray())         
      {             
         if (Char.IsDigit(c) || Char.IsControl(c) || (c == '.' && count == 0))             
         {                 
            newText += c;                 
            if (c == '.')                     
              count += 1;             
         }         
     }         
     textBox.Text = newText;         
     textBox.SelectionStart = selectionStart <= textBox.Text.Length ? selectionStart :        textBox.Text.Length;     
} 
Кумар Гурав
источник
3

Как насчет этого? Хорошо работает для меня. Надеюсь, я не пропустил ни одного крайнего случая ...

MyTextBox.PreviewTextInput += (sender, args) =>
{
    if (!int.TryParse(args.Text, out _))
    {
        args.Handled = true;
    }
};

DataObject.AddPastingHandler(MyTextBox, (sender, args) =>
{
    var isUnicodeText = args.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
    if (!isUnicodeText)
    {
        args.CancelCommand();
    }

    var data = args.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
    if (!int.TryParse(data, out _))
    {
        args.CancelCommand();
    }
});
Шахин Дохан
источник
2

В Windows Forms это было легко; Вы можете добавить событие для KeyPress, и все работает легко. Однако в WPF этого события нет. Но есть гораздо более простой способ для этого.

WPF TextBox имеет событие TextChanged, которое является общим для всего. Это включает в себя вставку, ввод текста и все, что может прийти в голову.

Так что вы можете сделать что-то вроде этого:

XAML:

<TextBox name="txtBox1" ... TextChanged="TextBox_TextChanged"/>

КОД позади:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
    string s = Regex.Replace(((TextBox)sender).Text, @"[^\d.]", "");
    ((TextBox)sender).Text = s;
}

Это также принимает ., если вы не хотите этого, просто удалите его из regexоператора, чтобы быть @[^\d].

Примечание : это событие может использоваться во многих TextBox'ах, так как оно использует senderText объекта. Вы пишете событие только один раз и можете использовать его для нескольких TextBox'ов.

Все
источник
2

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

В Windows Forms есть событие, KeyPressкоторое отлично подходит для такого рода задач. Но это не существует в WPF , поэтому вместо этого мы будем использовать PreviewTextInputсобытие. Кроме того , для проверки, я считаю , можно использовать foreachв цикле через textbox.Textи проверить , если он соответствует ;) состояние, но , честно говоря, это то , что регулярные выражения для.

Еще одна вещь, прежде чем мы погрузимся в священный кодекс . Для запуска события можно сделать две вещи:

  1. Используйте XAML, чтобы сообщить программе, какую функцию вызывать: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Сделайте это в Loadedслучае формы (в которой находится textBox): textBox.PreviewTextInput += onlyNumeric;

Я думаю, что второй метод лучше, потому что в подобных ситуациях вам, скорее всего, потребуется применить одно и то же условие ( regex) к нескольким, TextBoxи вы не захотите повторяться!,

Наконец, вот как вы это сделаете:

private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
    string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$";
    Regex regex = new Regex(onlyNumeric);
    e.Handled = !regex.IsMatch(e.Text);
}
Амир А. Шабани
источник
2

Вот моя версия этого. Он основан на базовом ValidatingTextBoxклассе, который просто отменяет то, что было сделано, если он не «допустим». Он поддерживает вставку, вырезание, удаление, возврат, +, - и т. Д.

Для 32-разрядного целого числа существует класс Int32TextBox, который просто сравнивается с int. Я также добавил классы проверки с плавающей запятой.

public class ValidatingTextBox : TextBox
{
    private bool _inEvents;
    private string _textBefore;
    private int _selectionStart;
    private int _selectionLength;

    public event EventHandler<ValidateTextEventArgs> ValidateText;

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (_inEvents)
            return;

        _selectionStart = SelectionStart;
        _selectionLength = SelectionLength;
        _textBefore = Text;
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        if (_inEvents)
            return;

        _inEvents = true;
        var ev = new ValidateTextEventArgs(Text);
        OnValidateText(this, ev);
        if (ev.Cancel)
        {
            Text = _textBefore;
            SelectionStart = _selectionStart;
            SelectionLength = _selectionLength;
        }
        _inEvents = false;
    }

    protected virtual void OnValidateText(object sender, ValidateTextEventArgs e) => ValidateText?.Invoke(this, e);
}

public class ValidateTextEventArgs : CancelEventArgs
{
    public ValidateTextEventArgs(string text) => Text = text;

    public string Text { get; }
}

public class Int32TextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !int.TryParse(e.Text, out var value);
}

public class Int64TextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !long.TryParse(e.Text, out var value);
}

public class DoubleTextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !double.TryParse(e.Text, out var value);
}

public class SingleTextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !float.TryParse(e.Text, out var value);
}

public class DecimalTextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !decimal.TryParse(e.Text, out var value);
}

Примечание 1: При использовании привязки WPF необходимо убедиться, что вы используете класс, который соответствует связанному типу свойства, в противном случае это может привести к странным результатам.

Примечание 2. При использовании классов с плавающей точкой с привязкой WPF убедитесь, что привязка использует текущую культуру для соответствия методу TryParse, который я использовал.

Саймон Мурье
источник
1

Использование:

Private Sub DetailTextBox_PreviewTextInput( _
  ByVal sender As Object, _
  ByVal e As System.Windows.Input.TextCompositionEventArgs) _
  Handles DetailTextBox.PreviewTextInput

    If _IsANumber Then
        If Not Char.IsNumber(e.Text) Then
            e.Handled = True
        End If
    End If
End Sub
Джонни
источник
Объяснение будет в порядке.
Питер Мортенсен
1

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

namespace MyApplication.InterfaceSupport
{
    public class NumericTextBox : TextBox
    {


        public NumericTextBox() : base()
        {
            TextChanged += OnTextChanged;
        }


        public void OnTextChanged(object sender, TextChangedEventArgs changed)
        {
            if (!String.IsNullOrWhiteSpace(Text))
            {
                try
                {
                    int value = Convert.ToInt32(Text);
                }
                catch (Exception e)
                {
                    MessageBox.Show(String.Format("{0} only accepts numeric input.", Name));
                    Text = "";
                }
            }
        }


        public int? Value
        {
            set
            {
                if (value != null)
                {
                    this.Text = value.ToString();
                }
                else 
                    Text = "";
            }
            get
            {
                try
                {
                    return Convert.ToInt32(this.Text);
                }
                catch (Exception ef)
                {
                    // Not numeric.
                }
                return null;
            }
        }
    }
}

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

Затем в файл XAML необходимо включить соответствующее пространство имен:

<UserControl x:Class="MyApplication.UserControls.UnParameterisedControl"
             [ Snip ]
             xmlns:interfaceSupport="clr-namespace:MyApplication.InterfaceSupport"
             >

После этого вы можете использовать его как обычный элемент управления:

<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >
glenatron
источник
1

После использования некоторых решений здесь в течение некоторого времени я разработал свое собственное решение, которое хорошо работает для моей установки MVVM. Обратите внимание, что он не такой динамичный, как некоторые другие, в том смысле, что он по-прежнему позволяет пользователям вводить ошибочные символы, но он блокирует их от нажатия кнопки и, таким образом, выполнения каких-либо действий. Это хорошо согласуется с моей темой «закрашивание кнопок», когда действия не могут быть выполнены.

У меня есть, TextBoxчто пользователь должен ввести количество страниц документа для печати:

<TextBox Text="{Binding NumberPagesToPrint, UpdateSourceTrigger=PropertyChanged}"/>

... с этим обязательным свойством:

private string _numberPagesToPrint;
public string NumberPagesToPrint
{
    get { return _numberPagesToPrint; }
    set
    {
        if (_numberPagesToPrint == value)
        {
            return;
        }

        _numberPagesToPrint = value;
        OnPropertyChanged("NumberPagesToPrint");
    }
}

У меня также есть кнопка:

<Button Template="{DynamicResource CustomButton_Flat}" Content="Set"
        Command="{Binding SetNumberPagesCommand}"/>

... с этой привязкой команды:

private RelayCommand _setNumberPagesCommand;
public ICommand SetNumberPagesCommand
{
    get
    {
        if (_setNumberPagesCommand == null)
        {
            int num;
            _setNumberPagesCommand = new RelayCommand(param => SetNumberOfPages(),
                () => Int32.TryParse(NumberPagesToPrint, out num));
        }

        return _setNumberPagesCommand;
    }
}

И тогда есть метод SetNumberOfPages(), но это неважно для этой темы. В моем случае это работает хорошо, потому что мне не нужно добавлять какой-либо код в файл кода code View, и это позволяет мне контролировать поведение с помощью Commandсвойства.

BK
источник
1

В приложении WPF вы можете справиться с этим, обработав TextChangedсобытие:

void arsDigitTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
    Regex regex = new Regex("[^0-9]+");
    bool handle = regex.IsMatch(this.Text);
    if (handle)
    {
        StringBuilder dd = new StringBuilder();
        int i = -1;
        int cursor = -1;
        foreach (char item in this.Text)
        {
            i++;
            if (char.IsDigit(item))
                dd.Append(item);
            else if(cursor == -1)
                cursor = i;
        }
        this.Text = dd.ToString();

        if (i == -1)
            this.SelectionStart = this.Text.Length;
        else
            this.SelectionStart = cursor;
    }
}
Мехди Хадемлу
источник
1

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

WPF

<TextBox PreviewTextInput="Port_PreviewTextInput" MaxLines="1"/>

C #

private void Port_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !int.TryParse(e.Text, out int x);
}
Beyondo
источник
2
Обратите внимание, что если вы действительно хотите использовать этот метод с полем порта сокета; Вам нужно проверить, если целое число меньше или равно 65535. Если он больше, то это не допустимый порт. Кроме того, установка TextBox.MaxLengthto 5поможет либо программно, либо в XAML .
Бейондо
0

Вот что я хотел бы использовать, чтобы получить текстовое поле WPF, которое принимает цифры и десятичную точку:

class numericTextBox : TextBox
{
    protected override void OnKeyDown(KeyEventArgs e)
    {
        bool b = false;
        switch (e.Key)
        {
            case Key.Back: b = true; break;
            case Key.D0: b = true; break;
            case Key.D1: b = true; break;
            case Key.D2: b = true; break;
            case Key.D3: b = true; break;
            case Key.D4: b = true; break;
            case Key.D5: b = true; break;
            case Key.D6: b = true; break;
            case Key.D7: b = true; break;
            case Key.D8: b = true; break;
            case Key.D9: b = true; break;
            case Key.OemPeriod: b = true; break;
        }
        if (b == false)
        {
            e.Handled = true;
        }
        base.OnKeyDown(e);
    }
}

Поместите код в новый файл класса, добавьте

using System.Windows.Controls;
using System.Windows.Input;

в верхней части файла и построить решение. Элемент управления numericTextBox появится в верхней части панели инструментов.

matsolof
источник
1
Смотрите более раннее более простое решение с использованием NumberValidationTextBox и регулярных выражений. Это смешно.
Скотт Шоу-Смит
@ ScottShaw-Smith Может быть, принятое решение - это меньше кода, но оно не быстрее этого. Всегда есть проекты, для которых требуется большая вычислительная мощность, а не использование регулярных выражений.
Бейондо