我如何得到一个文本框只接受数字输入在WPF?

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

我已经看过使用Windows窗体的NumericUpDown控件和这个例子的数字向上向下自定义控件从微软的示例。但到目前为止,似乎NumericUpDown (WPF是否支持)不会提供我想要的功能。我的应用程序是这样设计的,任何头脑正常的人都不会想弄乱箭头。在我的应用程序中,它们没有任何实际意义。

所以我正在寻找一个简单的方法,使一个标准的WPF文本框只接受我想要的字符。这可能吗?实用吗?

558086 次浏览

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

Windows Presentation Foundation中的验证中阅读更多

添加预览文本输入事件。像这样:<TextBox PreviewTextInput="PreviewTextInput" />

然后在里面设置e.Handled如果文本不被允许。e.Handled = !IsTextAllowed(e.Text);

我在IsTextAllowed方法中使用一个简单的正则表达式来查看是否应该允许他们键入的内容。在我的例子中,我只允许数字、点和破折号。

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

如果你想防止粘贴不正确的数据,连接DataObject.Pasting事件DataObject.Pasting="TextBoxPasting",如在这里所示(代码摘录):

// Use the DataObject.Pasting Handler
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
String text = (String)e.DataObject.GetData(typeof(String));
if (!IsTextAllowed(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}

扩展的WPF工具包有一个:NumericUpDown enter image description here

我假设:

  1. 您希望允许数字输入的文本框的文本属性最初设置为一些有效的数字值(例如,2.7172)。

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

  3. 你的主窗口是Window1类

  4. 您的文本框名称是numericTB

基本思想:

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

  2. 在主窗口构造函数中添加:previousText = numericTB.Text;

  3. 为数字tb创建处理程序。TextChanged事件是这样的:

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

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.

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

在预览文本框的按键事件。

使用:

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

我使用了一些已经在这里的东西,并使用一个行为把我自己的扭曲,这样我就不必在大量的视图中传播这段代码……

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>

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

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

事件处理程序正在预览文本输入。在这里,正则表达式只在文本输入不是数字时才匹配文本输入,然后它就不会进入文本框。

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

我在一个简单的项目中使用的是未绑定的盒子,所以我不能使用标准的绑定方法。因此,我创建了一个简单的黑客,其他人可能会发现非常方便,只需扩展现有的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" >

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

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

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

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

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

这是唯一需要的代码:

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

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

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

在使用这里的一些解决方案一段时间后,我开发了自己的解决方案,该解决方案适用于我的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属性控制行为。

当检查一个数值时,你可以使用VisualBasic。IsNumeric函数。

在WPF应用程序中,你可以通过处理TextChanged事件来处理这个问题:

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


if (i == -1)
this.SelectionStart = this.Text.Length;
else
this.SelectionStart = cursor;
}
}

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

它具有诸如NumberStyles和__abc1之类的属性用于验证。

WPF的子类TextBox

NuGet

也可以简单地实现一个验证规则,并将其应用到TextBox:

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

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

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


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


return validationResult;
}
}

在Windows窗体中,这很容易;你可以添加一个事件按键和一切工作轻松。然而,在WPF中该事件不存在。但是有一个更简单的方法。

WPF文本框有TextChanged事件,这是一般的一切。它包括粘贴,输入和任何你能想到的东西。

所以你可以这样做:

XAML:

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

背后的代码:

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

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

请注意:此事件可以在许多文本框上使用,因为它使用sender对象的文本。你只写事件一次,可以为多个文本框使用它。

如果你不想写很多代码来做一个基本的函数(我不知道为什么人们要写很长的方法),你可以这样做:

  1. < p >添加命名空间:

    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]+", "");
    }
    

这里有一个非常简单和容易的方法来做到这一点使用MVVM。

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

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();
}
}
PreviewTextInput += (s, e) =>
{
e.Handled = !e.Text.All(char.IsDigit);
};

这里我有一个受雷的回答启发的简单解决方案。这应该足以识别任何形式的数字。

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


正如雷的回答中所建议的,你需要首先添加一个PreviewTextInput事件:

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

然后把下面的代码放在后面:

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


double val;
// If parsing is successful, set Handled to false
e.Handled = !double.TryParse(fullText, out val);
}

对于无效的空格,可以添加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);
}

现在我知道这个问题有一个公认的答案,但就我个人而言,我觉得它有点混乱,我相信它应该比这更容易。所以我会试着演示我是如何做到最好的:

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

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

  1. 使用XAML告诉程序调用哪个函数:<PreviewTextInput="textBox_PreviewTextInput/>
  2. 在窗体(文本框所在)的Loaded事件中执行: 李textBox.PreviewTextInput += onlyNumeric; < / >

我认为第二种方法更好,因为在这种情况下,你通常会被要求对多个TextBox你不想重复你说的话!应用相同的条件(regex)。

最后,你可以这样做:

private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$";
Regex regex = new Regex(onlyNumeric);
e.Handled = !regex.IsMatch(e.Text);
}

对于那些只使用整数和小数快速而简单地实现这类问题的人来说,在你的XAML文件中,向你的__abc1添加一个PreviewTextInput属性,然后在你的XAML .cs文件中使用:

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

每次都检查整个字符串有点多余,除非像其他人提到的那样,你正在使用科学符号(尽管,如果你添加某些字符,如'e',一个添加符号/字符的简单正则表达式非常简单,并在其他答案中说明)。但是对于简单的浮点值,这个解决方案就足够了。

编写为带有lambda表达式的一行代码:

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

这是我的版本。它基于一个基类ValidatingTextBox,如果它不“有效”,它只会撤销已做的操作。它支持粘贴,剪切,删除,退格,+,-等。

对于32位整型,有一个Int32TextBox类用于与整型进行比较。我还添加了浮点验证类。

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方法。

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

我实现的TextBoxHelper类有这些特性:

  • 只过滤和接受Int使用Uint自然格式的数字
  • 只过滤和接受甚至奇怪的数字
  • 处理粘贴事件处理程序来防止无效文本粘贴到我们的数字文本框
  • 可以设置一个默认值,这将被用来防止无效的数据作为最后一个镜头订阅文本框TextChanged事件

下面是TextBoxHelper类的实现:

public static class TextBoxHelper
{
#region Enum Declarations


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


public enum EvenOddConstraint
{
All,
OnlyEven,
OnlyOdd
}


#endregion


#region Dependency Properties & CLR Wrappers


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




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




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


#endregion


#region Dependency Properties Methods


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


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


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


break;
}


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


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


break;
}
}
}


#endregion


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


string newText;


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


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


var evenOddConstraint = GetEvenOddConstraint(textBox);


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


break;
}
}
else
e.Handled = true;


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


break;
}
}
else
e.Handled = true;


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


break;
}
}
else
e.Handled = true;


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


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


break;
}
}
}


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


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


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


var evenOddConstraint = GetEvenOddConstraint(textBox);


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


break;
}
}
else
e.CancelCommand();


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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




break;
}
}
else
e.CancelCommand();


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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




break;
}
}
else
e.CancelCommand();


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


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


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


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


var defaultValue = GetDefaultValue(textBox);


var evenOddConstraint = GetEvenOddConstraint(textBox);


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


break;
}
}
else
textBox.Text = defaultValue;


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


break;
}
}
else
textBox.Text = defaultValue;


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


break;
}
}
else
textBox.Text = defaultValue;


break;
}


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


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


break;


case EvenOddConstraint.OnlyOdd:


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


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


break;
}
}
}
}

下面是一些简单用法的例子:

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

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

请注意,我的TextBoxHelper驻留在viewHelpers xmlns别名。

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

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

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

这个怎么样?很适合我。希望我没有漏掉任何边缘情况……

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

下面的代码创建了一个控件,你将能够像正常的文本框一样使用,但它只会把一个正的双精度作为输入:

在XAML中,你可以这样使用这个控件:

<local:UnsignedDoubleBox/>

在c#代码中,在当前命名空间中添加以下内容:

public class UnsignedDoubleBox : TextBox
{
public UnsignedDoubleBox()
{
this.PreviewTextInput += defaultPreviewTextInput;
DataObject.AddPastingHandler(this, defaultTextBoxPasting);
}


private bool IsTextAllowed(TextBox textBox, String text)
{
//source: https://stackoverflow.com/questions/23397195/in-wpf-does-previewtextinput-always-give-a-single-character-only#comment89374810_23406386
String newText = textBox.Text.Insert(textBox.CaretIndex, text);
double res;
return double.TryParse(newText, out res) && res >= 0;
}
//source: https://stackoverflow.com/a/1268648/13093413
private void defaultTextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
String text = (String)e.DataObject.GetData(typeof(String));


if (!IsTextAllowed((TextBox)sender, text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}


private void defaultPreviewTextInput(object sender, TextCompositionEventArgs e)
{


if (IsTextAllowed((TextBox)sender, e.Text))
{
e.Handled = false;
}
else
{
e.Handled = true;
}
}


}

我修改了射线的答案以在检查正则表达式之前处理突出显示的文本。我还将正则表达式调整为只允许小数点后两位(货币)。

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" />

最好和最优雅的解决方案,只允许整数在文本框(甚至在某些范围)是:

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).Name来调整max (min)的最小和最大可接受数。

此解决方案不允许输入的前导零或复制粘贴。在每个场景中,文本框中都有一个正确的数字。

它适用于我在Xamarin Forms移动应用程序中的情况。

XAML

    <Entry Text="{Binding CustomerMobileNo, Mode=TwoWay}" Keyboard="Numeric" MaxLength="11" HorizontalOptions="FillAndExpand"/>

视图模型

     private string _customerMobileNo;
public string CustomerMobileNo
{
get
{
return _customerMobileNo;
}
set
{
SetProperty(ref _customerMobileNo, value);
base.RaisePropertyChanged(nameof(CustomerMobileNo));
}
}