如何让 TextBox 只接受 WPF 中的数字输入?

发布于 2024-08-02 03:49:32 字数 374 浏览 15 评论 0 原文

我希望接受数字和小数点,但没有符号。

我查看了使用 Windows 窗体 NumericUpDown 控件的示例,以及 此示例来自 Microsoft 的 NumericUpDown 自定义控件。 但到目前为止,NumericUpDown(是否受 WPF 支持)似乎无法提供我想要的功能。 按照我的应用程序的设计方式,任何头脑正常的人都不会想要弄乱箭头。 在我的应用程序中,它们没有任何实际意义。

因此,我正在寻找一种简单的方法来使标准 WPF TextBox 仅接受我想要的字符。 这可能吗? 实用吗?

I'm looking to accept digits and the decimal point, but no sign.

I've looked at samples using the NumericUpDown control for Windows Forms, and this sample of a NumericUpDown custom control from Microsoft. But so far it seems like NumericUpDown (supported by WPF or not) is not going to provide the functionality that I want. The way my application is designed, nobody in their right mind is going to want to mess with the arrows. They don't make any practical sense, in the context of my application.

So I'm looking for a simple way to make a standard WPF TextBox accept only the characters that I want. Is this possible? Is it practical?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

木緿 2024-08-09 03:49:32

添加预览文本输入事件。 就像这样:

如果不允许文本,则在其中设置 e.Handlede.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();
    }
}

Add a preview text input event. Like so: <TextBox PreviewTextInput="PreviewTextInput" />.

Then inside that set the e.Handled if the text isn't allowed. e.Handled = !IsTextAllowed(e.Text);

I use a simple regex in IsTextAllowed method to see if I should allow what they've typed. In my case I only want to allow numbers, dots and dashes.

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

If you want to prevent pasting of incorrect data hook up the DataObject.Pasting event DataObject.Pasting="TextBoxPasting" as shown here (code excerpted):

// 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();
    }
}
北音执念 2024-08-09 03:49:32

事件处理程序正在预览文本输入。 这里,正则表达式仅在文本输入不是数字时才匹配它,并且不会将其写入输入文本框。

如果您只需要字母,请将正则表达式替换为 [^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);
}

The event handler is previewing text input. Here a regular expression matches the text input only if it is not a number, and then it is not made to entry textbox.

If you want only letters then replace the regular expression as [^a-zA-Z].

XAML

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

XAML.CS FILE

using System.Text.RegularExpressions;
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
    Regex regex = new Regex("[^0-9]+");
    e.Handled = regex.IsMatch(e.Text);
}
慕巷 2024-08-09 03:49:32

我使用了这里已经存在的一些内容,并使用一种行为对其进行了自己的修改,因此我不必在大量视图中传播此代码...

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>

I used some of what was already here and put my own twist on it using a behavior so I don't have to propagate this code throughout a ton of Views...

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

Here is the relevant view code:

<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>
情话墙 2024-08-09 03:49:32

这是 WilP 答案的改进解决方案。
我的改进是:

  • 改进了 DelBackspace 按钮的行为
  • 添加了 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>

This is an improved solution of WilPs answer.
My improvements are:

  • Improved behaviour on Del and Backspace buttons
  • Added EmptyValue property, if empty string is inappropriate
  • Fixed some minor typos
/// <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] --------------------------------
}

Usage is pretty straightforward:

<i:Interaction.Behaviors>
    <behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>
陌若浮生 2024-08-09 03:49:32

这是使用 MVVM 执行此操作的一种非常简单且容易的方法。

将文本框与视图模型中的整数属性绑定,这将像 gem 一样工作......当在文本框中输入非整数时,它甚至会显示验证。

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

Here is a very simple and easy way to do this using MVVM.

Bind your textBox with an integer property in the view model, and this will work like a gem ... it will even show validation when a non-integer is entered in the textbox.

XAML code:

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

View model code:

private long _contactNo;
public long contactNo
{
    get { return _contactNo; }
    set
    {
        if (value == _contactNo)
            return;
        _contactNo = value;
        OnPropertyChanged();
    }
}
心碎无痕… 2024-08-09 03:49:32

这里我有一个简单的解决方案,灵感来自 雷的回答。 这应该足以识别任何形式的数字。

如果您只需要正数、整数值或精确到最大小数位数的值等,也可以轻松修改此解决方案。


Ray的回答,需要先添加一个PreviewTextInput event:

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

对于无效的空格,我们可以添加 NumberStyles

using System.Globalization;

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, 
                                 NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, 
                                 CultureInfo.InvariantCulture,
                                 out val);
}

Here I have a simple solution inspired by Ray's answer. This should be sufficient to identify any form of number.

This solution can also be easily modified if you want only positive numbers, integer values or values accurate to a maximum number of decimal places, etc.


As suggested in Ray's answer, you need to first add a PreviewTextInput event:

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

Then put the following in the code behind:

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

To invalid whitespace, we can add NumberStyles:

using System.Globalization;

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, 
                                 NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, 
                                 CultureInfo.InvariantCulture,
                                 out val);
}
一向肩并 2024-08-09 03:49:32

添加验证规则,以便当文本更改时,检查以确定数据是否为数字,如果是,则允许继续处理,如果不是,则提示用户该字段中仅接受数字数据。

详细信息请参阅Windows Presentation Foundation 中的验证

Add in a VALIDATION RULE so that when the text changes, check to determine if the data is numeric, and if it is, allows processing to continue, and if it is not, prompts the user that only numeric data is accepted in that field.

Read more in Validation in Windows Presentation Foundation

你げ笑在眉眼 2024-08-09 03:49:32

扩展 WPF 工具包有一个: NumericUpDown
在此处输入图像描述

The Extented WPF Toolkit has one: NumericUpDown
enter image description here

记忆里有你的影子 2024-08-09 03:49:32

还可以简单地实现验证规则并将其应用于文本框:

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

规则的实现如下(使用与其他答案中建议的相同的正则表达式):

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

Could also simply implement a validation rule and apply it to the TextBox:

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

With the implementation of the rule as follow (using the same Regex as proposed in other answers):

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;
    }
}
枕梦 2024-08-09 03:49:32

另一种方法是使用附加行为,我实现了自定义 TextBoxHelper 类,它可以在我的项目中的所有文本框上使用。 因为我认为为此目的订阅每个文本框和每个单独的 XAML 文件中的事件可能非常耗时。

我实现的 TextBoxHelper 类具有以下功能:

  • 过滤并仅接受 DoubleIntUintNatural 中的数字格式
  • 过滤并仅接受偶数奇数数字
  • 处理粘贴事件处理程序以防止将无效文本粘贴到我们的数字文本框中
  • 可以设置默认值Value 将用于通过订阅文本框 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 别名中。

我希望这个实现可以减轻其他人的工作:)

Another approach will be using an attached behavior, I implemented my custom TextBoxHelper class, which can be used on textboxes all over my project. Because I figured that subscribing to the events for every textboxes and in every individual XAML file for this purpose can be time consuming.

The TextBoxHelper class I implemented has these features:

  • Filtering and accepting only numbers in Double, Int, Uint and Natural format
  • Filtering and accepting only Even or Odd numbers
  • Handling paste event handler to prevent pasting invalid text into our numeric textboxes
  • Can set a Default Value which will be used to prevent invalid data as the last shot by subscribing to the textboxes TextChanged event

Here is the implementation of TextBoxHelper class:

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

And here is some example of its easy usage:

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

Or

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

Note that my TextBoxHelper resides in the viewHelpers xmlns alias.

I hope that this implementation eases someone else's work :)

倒数 2024-08-09 03:49:32

我允许数字键盘数字和退格键:

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

I allowed numeric keypad numbers and 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);
    }
小忆控 2024-08-09 03:49:32

这是唯一需要的代码:

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

这只允许在文本框中输入数字。

要允许使用小数点或减号,您可以将正则表达式更改为 [^0-9.-]+

This is the only code needed:

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

This only allows numbers to be inputted into the text box.

To allow a decimal point or minus sign, you can change the regular expression to [^0-9.-]+.

只是我以为 2024-08-09 03:49:32

我将假设:

  1. 您希望允许数字输入的文本框仅将其 Text 属性初始设置为某个有效的数字值(例如 2.7172)。

  2. 您的文本框是主窗口的子窗口

  3. 您的主窗口属于 Window1 类

  4. 您的TextBox 名称为 numericTB

基本思想:

  1. 添加:private string previousText; 到您的主窗口类 (Window1)

  2. 添加:previousText = numericTB.Text; 到您的主窗口构造函数

  3. 为 numericTB.TextChanged 事件创建一个处理程序,如下所示:

    private void numericTB_TextChanged(对象发送者,TextChangedEventArgs e) 
      { 
          双数 = 0; 
          bool success = double.TryParse(((TextBox)sender).Text, out num); 
          if(成功&num>=0) 
              以前的文本 = ((TextBox)sender).Text; 
          别的 
              ((TextBox)sender).Text = previousText; 
      } 
      

只要它有效,这将继续将 previousText 设置为 numericTB.Text,并在以下情况下将 numericTB.Text 设置为其最后一个有效值:用户写了一些你不喜欢的东西。 当然,这只是基本思想,只是“抗白痴”,而不是“防白痴”。 例如,它不处理用户弄乱空格的情况。 因此,这是一个完整的解决方案,我认为这是“白痴证明”,如果我错了,请告诉我:

  1. 您的 Window1.xaml 文件的内容:

     
          <网格> 
               
           
       
      
  2. Window.xaml.cs 文件的内容:

    使用系统; 
      使用 System.Collections.Generic; 
      使用 System.Linq; 
      使用系统文本; 
      使用系统.Windows; 
      使用 System.Windows.Controls; 
      使用 System.Windows.Data; 
      使用 System.Windows.Documents; 
      使用系统.Windows.输入; 
      使用 System.Windows.Media; 
      使用 System.Windows.Media.Imaging; 
      使用系统.Windows.导航; 
      使用 System.Windows.Shapes; 
    
      名称空间 IdiotProofNumericTextBox 
      { 
          公共部分类 Window1 : 窗口 
          { 
              私有字符串 previousText; 
    
              公共窗口1() 
              { 
                  初始化组件(); 
                  previousText = numericTB.Text; 
              } 
    
              私人无效数字TB_TextChanged(对象发送者,TextChangedEventArgs e) 
              { 
                  if (string.IsNullOrEmpty(((TextBox)sender).Text)) 
                      上一个文本=“”; 
                  别的 
                  { 
                      双数 = 0; 
                      bool success = double.TryParse(((TextBox)sender).Text, out num); 
                      if(成功&num>=0) 
                      { 
                          ((TextBox)sender).Text.Trim(); 
                          以前的文本 = ((TextBox)sender).Text; 
                      } 
                      别的 
                      { 
                          ((TextBox)sender).Text = previousText; 
                          ((TextBox)sender).SelectionStart = ((TextBox)sender).Text.Length; 
                      } 
                  } 
              } 
          } 
      } 
      

就是这样。 如果您有许多 TextBox,那么我建议创建一个继承自 TextBox 的 CustomControl,以便您可以将 previousText 和 numericTB_TextChanged 包装在单独的文件中。

I will assume that:

  1. Your TextBox for which you want to allow numeric input only has its Text property initially set to some valid number value (for example, 2.7172).

  2. Your Textbox is a child of your main window

  3. Your main window is of class Window1

  4. Your TextBox name is numericTB

Basic idea:

  1. Add: private string previousText; to your main window class (Window1)

  2. Add: previousText = numericTB.Text; to your main window constructor

  3. Create a handler for the numericTB.TextChanged event to be something like this:

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

This will keep setting previousText to numericTB.Text as long as it is valid, and set numericTB.Text to its last valid value if the user writes something that you don't like. Of course, this is just basic idea, and it is just "idiot resistant", not "idiot proof". It doesn't handle the case in which the user messes with spaces, for example. So here is a complete solution which I think is "idiot proof", and if I'm wrong please tell me:

  1. Content of your Window1.xaml file:

    <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. Content of your Window.xaml.cs file:

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

And that's it. If you have many TextBoxes then I recommend creating a CustomControl that inherits from TextBox, so you can wrap previousText and numericTB_TextChanged up in a separate file.

秉烛思 2024-08-09 03:49:32

如果您不想编写大量代码来执行基本功能(我不知道为什么人们会编写很长的方法),您可以这样做:

  1. 添加命名空间:

    使用 System.Text.RegularExpressions; 
      
  2. 在 XAML 中,设置 TextChanged 属性:

     
      
  3. 在 WPF 中的 txt1_TextChanged 方法下,添加 Regex.Replace

    private void txt1_TextChanged(object sender, TextChangedEventArgs e) 
      { 
          txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", ""); 
      } 
      

If you do not want to write a lot of code to do a basic function (I don't know why people make long methods) you can just do this:

  1. Add namespace:

    using System.Text.RegularExpressions;
    
  2. In XAML, set a TextChanged property:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
    
  3. In WPF under txt1_TextChanged method, add Regex.Replace:

    private void txt1_TextChanged(object sender, TextChangedEventArgs e)
    {
        txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", "");
    }
    
为人所爱 2024-08-09 03:49:32

在 Windows 窗体中这很容易; 您可以为按键添加一个事件,一切都会轻松进行。 然而,在 WPF 中,该事件不存在。 但有一种更简单的方法。

WPF TextBox 具有适用于所有内容的通用 TextChanged 事件。 它包括粘贴、打字以及任何你能想到的事情。

所以你可以这样做:

XAML:

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

CODE BEHIND:

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

这也接受 . ,如果你不想要它,只需将其删除将 regex 语句改为 @[^\d]

注意:此事件可用于许多 TextBox 元素,因为它使用 sender 对象的 Text。 您只需编写该事件一次,即可将其用于多个 TextBox 元素。

In Windows Forms it was easy; you can add an event for KeyPress and everything works easily. However, in WPF that event isn't there. But there is a much easier way for it.

The WPF TextBox has the TextChanged event which is general for everything. It includes pasting, typing and whatever that can come up to your mind.

So you can do something like this:

XAML:

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

CODE BEHIND:

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

This also accepts . , if you don't want it, just remove it from the regex statement to be @[^\d].

Note: This event can be used on many TextBox elements as it uses the sender object's Text. You only write the event once and can use it for multiple TextBox elements.

岁月打碎记忆 2024-08-09 03:49:32

对于那些寻求仅使用整数和小数来快速且非常简单地实现此类问题的人,请在 XAML 文件中向 TextBox 添加一个 PreviewTextInput 属性,然后在您的 xaml.cs 文件使用:

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

每次都检查整个字符串有点多余,除非,正如其他人提到的,您正在使用科学记数法做一些事情(尽管,如果您添加某些字符,例如“e”,a添加符号/字符的简单正则表达式非常简单,并在其他答案中进行了说明)。 但对于简单的浮点值,这个解决方案就足够了。

写成带有 lambda 表达式的单行代码:

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

For those looking for a quick and very simple implementation for this type of problem using only integers and decimal, in your XAML file, add a PreviewTextInput property to your TextBoxand then in your xaml.cs file use:

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

It's kind of redundant to keep checking the entire string every time unless, as others have mentioned, you're doing something with scientific notation (albeit, if you're adding certain characters like 'e', a simple regex adding symbols/characters is really simple and illustrated in other answers). But for simple floating point values, this solution will suffice.

Written as a one-liner with a lambda expression:

private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) => e.Handled = !char.IsDigit(e.Text.Last() && !e.Text.Last() == '.');
刘备忘录 2024-08-09 03:49:32

对于希望其文本字段仅接受无符号数字(例如套接字端口等)的开发人员:

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

For developers who want their text fields to accept unsigned numbers only such as socket ports and so on:

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);
}
挽心 2024-08-09 03:49:32
PreviewTextInput += (s, e) =>
{
    e.Handled = !e.Text.All(char.IsDigit);
};
PreviewTextInput += (s, e) =>
{
    e.Handled = !e.Text.All(char.IsDigit);
};
向日葵 2024-08-09 03:49:32

只允许文本框中的整数(即使在某些范围内)的最佳和最优雅的解决方案是:

XAML:

<TextBox PreviewTextInput="ValidationTextBox" TextChanged="TextBox_TextChanged"/>

C#:

private void ValidationTextBox(object sender, TextCompositionEventArgs e)
{
    int max = 100;

    //do not allow futher incorrect typing
    e.Handled = !(int.TryParse(((TextBox)sender).Text + e.Text, out int i) && i >= 1 && i <= max);
}

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    int max = 100;

    if (!int.TryParse(((TextBox)sender).Text, out int j) || j < 1 || j > max)
    {
        //delete incoret input
        ((TextBox)sender).Text = "";
    }
    else
    {
        //delete leading zeros
        ((TextBox)sender).Text = j.ToString();
    }
}

您可以通过打开 ((TextBox)sender 来调整 max (min) 的最小和最大可接受数字).名称

此解决方案不允许前导零或复制粘贴输入。 在每种情况下,文本框中都会有正确的数字。

The best and most elegant solution to only allow integer numbers in textbox (even in some range) is:

XAML:

<TextBox PreviewTextInput="ValidationTextBox" TextChanged="TextBox_TextChanged"/>

C#:

private void ValidationTextBox(object sender, TextCompositionEventArgs e)
{
    int max = 100;

    //do not allow futher incorrect typing
    e.Handled = !(int.TryParse(((TextBox)sender).Text + e.Text, out int i) && i >= 1 && i <= max);
}

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    int max = 100;

    if (!int.TryParse(((TextBox)sender).Text, out int j) || j < 1 || j > max)
    {
        //delete incoret input
        ((TextBox)sender).Text = "";
    }
    else
    {
        //delete leading zeros
        ((TextBox)sender).Text = j.ToString();
    }
}

You can adjust the minimal and maximal acceptable number with max (min) by switch on ((TextBox)sender).Name.

This solution does not allow leading zeros or copy pasting the input. In every scenario you will have a correct number in the textbox.

离鸿 2024-08-09 03:49:32
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;

在文本框的预览 keydown 事件中。

e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;

in preview keydown event of textbox.

虐人心 2024-08-09 03:49:32

我们可以对文本框更改事件进行验证。 以下实现阻止除数字和一位小数点之外的按键输入。

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

We can do validation on text box changed event. The following implementation prevents keypress input other than numeric and one decimal point.

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;     
} 
浮世清欢 2024-08-09 03:49:32

这个怎么样? 对我来说效果很好。 希望我没有错过任何边缘情况......

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

How about this? Works well for me. Hope I didn't miss any edge cases...

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();
    }
});
三月梨花 2024-08-09 03:49:32

现在我知道这个问题有一个可接受的答案,但就我个人而言,我发现它有点令人困惑,我相信它应该更容易比起那个来说。 因此,我将尝试演示如何让它尽可能地工作:

Windows 窗体 中,有一个名为 KeyPress 的事件,它非常适合这种情况任务。 但 WPF 中不存在这种情况,因此我们将使用 PreviewTextInput 事件。 另外,对于验证,我相信可以使用 foreach 循环遍历 textbox.Text 并检查它是否匹配;)条件,但老实说,这就是 正则表达式 的用途。

在我们深入探讨神圣代码之前,还有一件事。 对于要触发的事件,可以做两件事:

  1. 使用 XAML 告诉程序要调用哪个函数:
  2. Loaded 中执行此操作code> 表单的事件(文本框位于其中): 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);
}

Now I know this question has an accepted answer, but personally, I find it a bit confusing and I believe it should be easier than that. So I'll try to demonstrate how I got it to work as best as I can:

In Windows Forms, there's an event called KeyPress which is perfectly good for this kind of task. But that doesn't exist in WPF, so instead, we'll be using the PreviewTextInput event. Also, for the validation, I believe one can use a foreach to loop through the textbox.Text and check if it matches ;) the condition, but honestly, this is what regular expressions are for.

One more thing before we dive into the holy code. For the event to be fired, one can do two things:

  1. Use XAML to tell the program which function to call: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Do it in the Loaded event of the form (which the textBox is in):
    textBox.PreviewTextInput += onlyNumeric;

I think the second method is better because in situations like this, you'll mostly be required to apply the same condition (regex) to more than one TextBox and you don't want to repeat yourself!.

Finally, here's how you'd do it:

private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
    string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$";
    Regex regex = new Regex(onlyNumeric);
    e.Handled = !regex.IsMatch(e.Text);
}
终止放荡 2024-08-09 03:49:32

这是我的版本。 它基于 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 方法。

Here is my version of it. It's based on a base ValidatingTextBox class that just undoes what has been done if it's not "valid". It supports paste, cut, delete, backspace, +, - etc.

For 32-bit integer, there is a Int32TextBox class that just compares with an int. I have also added floating point validation classes.

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

Note 1: When using WPF binding, you must make sure you use the class that fits the bound property type otherwise, it may lead to strange results.

Note 2: When using floating point classes with WPF binding, make sure the binding uses the current culture to match the TryParse method I've used.

扛起拖把扫天下 2024-08-09 03:49:32

我修改了 Rays answer 以在检查正则表达式之前处理突出显示的文本。 我还调整了正则表达式,只允许两位小数(货币)。

private static readonly Regex _regex = new Regex(@"^[0-9]\d*(\.\d{0,2})?$");
private static bool IsTextAllowed(string text)
{
    return _regex.IsMatch(text);
}

private bool IsAllowed(TextBox tb, string text)
{
    bool isAllowed = true;
    if (tb != null)
    {
        string currentText = tb.Text;
        if (!string.IsNullOrEmpty(tb.SelectedText))
            currentText = currentText.Remove(tb.CaretIndex, tb.SelectedText.Length);
        isAllowed = IsTextAllowed(currentText.Insert(tb.CaretIndex, text));
    }
    return isAllowed;
}

private void Txt_PreviewCurrencyTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !IsAllowed(sender as TextBox, e.Text);            
}


private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
    if (e.DataObject.GetDataPresent(typeof(String)))
    {
        String text = (String)e.DataObject.GetData(typeof(String));
        if (!IsAllowed(sender as TextBox, text))
            e.CancelCommand();
    }
    else
        e.CancelCommand();
}

还有xaml

<TextBox Name="Txt_Textbox" PreviewTextInput="Txt_PreviewCurrencyTextInput"  DataObject.Pasting="TextBoxPasting" />

I modified Rays answer to handle the highlighted text prior to checking the regular expression. I also adjusted the regular expression to only allow for two decimal places (currency).

private static readonly Regex _regex = new Regex(@"^[0-9]\d*(\.\d{0,2})?
quot;);
private static bool IsTextAllowed(string text)
{
    return _regex.IsMatch(text);
}

private bool IsAllowed(TextBox tb, string text)
{
    bool isAllowed = true;
    if (tb != null)
    {
        string currentText = tb.Text;
        if (!string.IsNullOrEmpty(tb.SelectedText))
            currentText = currentText.Remove(tb.CaretIndex, tb.SelectedText.Length);
        isAllowed = IsTextAllowed(currentText.Insert(tb.CaretIndex, text));
    }
    return isAllowed;
}

private void Txt_PreviewCurrencyTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !IsAllowed(sender as TextBox, e.Text);            
}


private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
    if (e.DataObject.GetDataPresent(typeof(String)))
    {
        String text = (String)e.DataObject.GetData(typeof(String));
        if (!IsAllowed(sender as TextBox, text))
            e.CancelCommand();
    }
    else
        e.CancelCommand();
}

And the xaml

<TextBox Name="Txt_Textbox" PreviewTextInput="Txt_PreviewCurrencyTextInput"  DataObject.Pasting="TextBoxPasting" />
我的影子我的梦 2024-08-09 03:49:32

这是一个用于 WPF 中数字输入的库

它具有诸如 NumberStylesRegexPattern 用于验证。

WPF 子类 TextBox

NuGet

Here is a library for numeric input in WPF

It has properties like NumberStyles and RegexPatternfor validation.

Subclasses WPF TextBox

NuGet

温暖的光 2024-08-09 03:49:32

使用:

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

Use:

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
反话 2024-08-09 03:49:32

我正在为一个简单的项目使用未绑定的盒子,因此我无法使用标准绑定方法。 因此,我创建了一个简单的技巧,其他人可能会发现通过简单地扩展现有的 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" >

I was working with an unbound box for a simple project I was working on, so I couldn't use the standard binding approach. Consequently I created a simple hack that others might find quite handy by simply extending the existing TextBox control:

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

Obviously, for a floating type, you would want to parse it as a float and so on. The same principles apply.

Then in the XAML file you need to include the relevant namespace:

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

After that you can use it as a regular control:

<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >
故笙诉离歌 2024-08-09 03:49:32

这就是我用来获取接受数字和小数点的 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 控件随后将出现在工具箱的顶部。

This is what I would use to get a WPF textbox that accept digits and the decimal point:

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

Put the code in a new class file, add

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

at the top of the file and build the solution. The numericTextBox control will then appear at the top of the toolbox.

扮仙女 2024-08-09 03:49:32

使用这里的一些解决方案一段时间后,我开发了自己的解决方案,它非常适合我的 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() 方法,但对于本主题来说并不重要。 它在我的情况下效果很好,因为我不必将任何代码添加到视图的代码隐藏文件中,并且它允许我使用 Command 属性控制行为。

After using some of the solutions here for some time, I developed my own that works well for my MVVM setup. Note that it's not as dynamic as some of the other ones in a sense of still allowing users to enter erroneous characters, but it blocks them from pressing the button and thus doing anything. This goes well with my theme of graying out buttons when actions cannot be performed.

I have a TextBox that a user must enter a number of document pages to be printed:

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

...with this binding property:

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

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

I also have a button:

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

...with this command binding:

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

        return _setNumberPagesCommand;
    }
}

And then there's the method of SetNumberOfPages(), but it's unimportant for this topic. It works well in my case because I don't have to add any code into the View's code-behind file and it allows me to control behavior using the Command property.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文