Як змусити TextBox приймати лише числовий вхід у WPF?


335

Я хочу прийняти цифри і десяткову точку, але жодної ознаки.

Я розглянув зразки, що використовують керування NumericUpDown для Windows Forms, і цей зразок спеціального управління NumericUpDown від Microsoft . Але поки здається, що NumericUpDown (підтримується WPF чи ні) не збирається надавати потрібну мені функціональність. Те, як розроблено мою програму, ніхто з розумом не збирається возитися зі стрілками. Вони не мають жодного практичного сенсу в контексті моєї заяви.

Тому я шукаю простий спосіб змусити стандартний WPF TextBox приймати лише потрібні мені символи. Чи можливо це? Це практично?

Відповіді:


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 для фільтрації того, що хтось набирає.
Рей

27
[Пробіл] не запускає подію 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
@Offer Так, обов’язково включіть xmlns: interactivity = " schemas.microsoft.com/expression/2010/interactivity " у верхній частині вікна xaml.
WiteCastle

Вираз зараз застарілий. Хоча цей підхід чистий, він використовує код, який більше не підтримується.
Роберт Бейкер

1
Отже, якщо ви редагуєте функцію IsValid для повернення! ExceedsMaxLength (newText, вставити) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), RegularExpression); тоді це дасть оцінку всій рядку. Btw - Любіть цей варіант з поведінкою !!
Рогала

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", а потім видалити "x"). Також в цьому режимі відчувається трохи мляво. Здається, це System.Number.ParseSingle()робиться для роботи, тому вона приймає різні позначення.
вівден

@wolle, ймовірно, не отримує голосування, оскільки це не пояснює, як працює перевірка.
Пол Маккарті

26

Додайте в Правило валідації так, що коли текст змінюється, перевірте, чи є дані числовими, а якщо вони є, дозволяє обробці продовжуватись, а якщо ні, то спонукає користувача до того, що у цьому полі приймаються лише числові дані.

Детальніше читайте у розділі Валідація у Фонді презентації Windows


6
Це насправді не є відповіддю для стандартів SO.
Роберт Бейкер

Це, мабуть, є .net способом зробити це.
Telemat

1
Є правильним відповіддю: перевірка повинна бути на Viene моделі або на рівні моделі. Крім того, ви можете просто прив’язати до числового типу типу, doubleі це вже надає вам стандартну перевірку.

24

Розширений інструментарій WPF має один: NumericUpDown введіть тут опис зображення


Я спробував це управління, але він створює проблеми при використанні спінера з UpdateSourceTrigger = PropertyChanged і в цілому користувачеві важко ввести наукові позначення.
Menno Deij - van Rijswijk

5
Зауважте, що NumericUpDownце тепер застаріло. ви можете скористатися DecimalUpDownоновленим набором інструментів Розширене WPF Toolkit ™ Community Edition
його

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

Якщо ви хочете ввести десяткові цифри, не повертайте "дійсним", коли текст закінчується на ". Будь ласка , зверніться до stackoverflow.com/a/27838893/417939
YantingChen

14

Тут у мене є просте рішення, натхнене відповіддю Рея . Цього має бути достатньо для виявлення будь-якої форми числа.

Це рішення також може бути легко модифіковане, якщо ви хочете, щоб тільки додатні числа, цілі значення чи значення були точними до максимальної кількості десяткових знаків тощо.


Як запропоновано у відповіді Рея , спочатку потрібно додати 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

Я дозволив числові цифри клавіатури та зворотну область:

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

7

Я буду вважати, що:

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

  2. Ваш Textbox - це дитина головного вікна

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

Це дозволить продовжувати встановлення попередньогоText на 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;
                    }
                }
            }
        }
    }

І це все. Якщо у вас багато TextBoxes, то я рекомендую створити CustomControl, який успадковується від TextBox, щоб ви могли загортати попереднійText і numericTB_TextChanged в окремий файл.


Ух, це чудово! Як я можу дозволити негативний символ спереду?
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]+", "");
    }

2
Набагато чистіше використовувати поведінку або властивість AttachedProperty. Немає жодного коду за кожним переглядом / для кожного текстового поля
сер Руфо,

Може працювати, але некрасиво, оскільки морква стрибне в передньому положенні текстового поля
Momo

6

Іншим підходом буде використання прикріпленої поведінки, я реалізував свій власний клас TextBoxHelper , який можна використовувати в текстових полях по всьому проекту. Тому що я зрозумів, що передплата на події для кожного текстового поля та кожного окремого XAML-файлу для цієї мети може зайняти багато часу.

Клас TextBoxHelper, який я реалізував, має такі особливості:

  • Фільтрація та прийняття лише чисел у Double , Int , Uint та форматі Natural
  • Фільтрування та прийняття лише парного чи непарного чисел
  • Обробка обробника подій вставки для вставки, щоб запобігти вставленню недійсного тексту в наші числові текстові поля
  • Можна встановити значення за замовчуванням, яке буде використовуватися для запобігання недійсних даних як останній кадр, підписавшись на текстові поля 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;

у події попереднього перегляду клавіатури в текстовому полі.


3
Хоча не дозволяє повернути назад.
sventevit

2
Backspace 2, вкладка 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'es, оскільки вона використовує senderText об'єкта. Ви пишете подію лише один раз і можете використовувати її для декількох TextBox'es.


2

Тепер я знаю, що на це питання є прийнята відповідь , але особисто я вважаю це трохи заплутаним і вважаю, що це повинно бути легше. Тому я спробую продемонструвати, як я змусив його працювати якнайкраще:

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

Ще одна річ, перш ніж ми зануримось у святий кодекс . Щоб звільнити подію, можна зробити дві речі:

  1. Використовуйте XAML, щоб вказати програмі, яку функцію викликати: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Зробіть це у Loadedвипадку форми (у якій є текстовий бокс): 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;
            }
        }
    }
}

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

Потім у файл 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" >

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(), але це неважливо для цієї теми. У моєму випадку це добре працює, тому що мені не потрібно додавати жодний код у файл із кодовим видом View, і це дозволяє мені контролювати поведінку, використовуючи Commandвластивість.



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

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.


1
Дивіться більш просте РІШНЕ простіше рішення за допомогою NumberValidationTextBox та регулярних виразів. Це смішно.
Скотт Шоу-Сміт

@ ScottShaw-Smith Можливо, прийняте рішення менше коду, але це не швидше, ніж це. Завжди є проекти, для яких потрібна велика потужність обробки, а не використання регулярного виразу.
Beyondo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.