我希望接受数字和小数点,但没有符号。
我查看了使用 Windows 窗体的 NumericUpDown 控件和 this sample of a NumericUpDown custom control from Microsoft 的示例。但到目前为止,似乎 NumericUpDown(无论是否受 WPF 支持)都不会提供我想要的功能。我的应用程序的设计方式,没有人会想弄乱箭头。在我的应用程序的上下文中,它们没有任何实际意义。
所以我正在寻找一种简单的方法来使标准的 WPF TextBox 只接受我想要的字符。这可能吗?实用吗?
添加预览文本输入事件。像这样:<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"
,如 here 所示(代码摘录):
// 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();
}
}
事件处理程序正在预览文本输入。此处的正则表达式仅在不是数字时才匹配文本输入,然后才不会输入文本框。
如果您只想要字母,请将正则表达式替换为 [^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);
}
我使用了一些已经在这里的东西,并使用一种行为对其进行了修改,因此我不必在大量视图中传播此代码......
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>
这是WilP答案的改进解决方案。我的改进是:
改进了 Del 和 Backspace 按钮的行为
添加 EmptyValue 属性,如果空字符串不合适
修正了一些小错别字
/// <summary>
/// Regular expression for Textbox with properties:
/// <see cref="RegularExpression"/>,
/// <see cref="MaxLength"/>,
/// <see cref="EmptyValue"/>.
/// </summary>
public class TextBoxInputRegExBehaviour : Behavior<TextBox>
{
#region DependencyProperties
public static readonly DependencyProperty RegularExpressionProperty =
DependencyProperty.Register("RegularExpression", typeof(string), typeof(TextBoxInputRegExBehaviour), new FrameworkPropertyMetadata(".*"));
public string RegularExpression
{
get { return (string)GetValue(RegularExpressionProperty); }
set { SetValue(RegularExpressionProperty, value); }
}
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBoxInputRegExBehaviour),
new FrameworkPropertyMetadata(int.MinValue));
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
public static readonly DependencyProperty EmptyValueProperty =
DependencyProperty.Register("EmptyValue", typeof(string), typeof(TextBoxInputRegExBehaviour), null);
public string EmptyValue
{
get { return (string)GetValue(EmptyValueProperty); }
set { SetValue(EmptyValueProperty, value); }
}
#endregion
/// <summary>
/// Attach our behaviour. Add event handlers
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += PreviewTextInputHandler;
AssociatedObject.PreviewKeyDown += PreviewKeyDownHandler;
DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
}
/// <summary>
/// Deattach our behaviour. remove event handlers
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= PreviewTextInputHandler;
AssociatedObject.PreviewKeyDown -= PreviewKeyDownHandler;
DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
}
#region Event handlers [PRIVATE] --------------------------------------
void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
{
string text;
if (this.AssociatedObject.Text.Length < this.AssociatedObject.CaretIndex)
text = this.AssociatedObject.Text;
else
{
// Remaining text after removing selected text.
string remainingTextAfterRemoveSelection;
text = TreatSelectedText(out remainingTextAfterRemoveSelection)
? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text)
: AssociatedObject.Text.Insert(this.AssociatedObject.CaretIndex, e.Text);
}
e.Handled = !ValidateText(text);
}
/// <summary>
/// PreviewKeyDown event handler
/// </summary>
void PreviewKeyDownHandler(object sender, KeyEventArgs e)
{
if (string.IsNullOrEmpty(this.EmptyValue))
return;
string text = null;
// Handle the Backspace key
if (e.Key == Key.Back)
{
if (!this.TreatSelectedText(out text))
{
if (AssociatedObject.SelectionStart > 0)
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart - 1, 1);
}
}
// Handle the Delete key
else if (e.Key == Key.Delete)
{
// If text was selected, delete it
if (!this.TreatSelectedText(out text) && this.AssociatedObject.Text.Length > AssociatedObject.SelectionStart)
{
// Otherwise delete next symbol
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, 1);
}
}
if (text == string.Empty)
{
this.AssociatedObject.Text = this.EmptyValue;
if (e.Key == Key.Back)
AssociatedObject.SelectionStart++;
e.Handled = true;
}
}
private void PastingHandler(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(DataFormats.Text))
{
string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));
if (!ValidateText(text))
e.CancelCommand();
}
else
e.CancelCommand();
}
#endregion Event handlers [PRIVATE] -----------------------------------
#region Auxiliary methods [PRIVATE] -----------------------------------
/// <summary>
/// Validate certain text by our regular expression and text length conditions
/// </summary>
/// <param name="text"> Text for validation </param>
/// <returns> True - valid, False - invalid </returns>
private bool ValidateText(string text)
{
return (new Regex(this.RegularExpression, RegexOptions.IgnoreCase)).IsMatch(text) && (MaxLength == int.MinValue || text.Length <= MaxLength);
}
/// <summary>
/// Handle text selection
/// </summary>
/// <returns>true if the character was successfully removed; otherwise, false. </returns>
private bool TreatSelectedText(out string text)
{
text = null;
if (AssociatedObject.SelectionLength <= 0)
return false;
var length = this.AssociatedObject.Text.Length;
if (AssociatedObject.SelectionStart >= length)
return true;
if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length)
AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart;
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength);
return true;
}
#endregion Auxiliary methods [PRIVATE] --------------------------------
}
用法非常简单:
<i:Interaction.Behaviors>
<behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>
MaxLength
,您的条件 (this.MaxLength == 0 || text.Length <= this.MaxLength)
在测试新文本时总是返回 false
。这最好是 (this.MaxLength == int.MinValue || text.Length <= this.MaxLength)
,因为您将 int.MinValue
设置为 MaxLength
的默认值。
UpdateSourceTrigger=PropertyChanged
添加到绑定中。知道如何在将 UpdateSourceTrigger
设置为 PropertyChanged
时使此代码正常工作吗?感谢您分享此代码。
这是使用 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();
}
}
long
更改为 float
,但立即验证效果不佳。我将 UpdateSourceTrigger="PropertyChanged"
添加到绑定中,因此它将在键入每个字符时执行验证,并且不能再键入“。”在 TextBox 中,除非存在非法字符(必须键入“1x.234”然后删除“x”)。在这种模式下也感觉有点迟钝。这似乎使用 System.Number.ParseSingle()
来完成这项工作,因此它接受各种符号。
TargetNullValue
,这应该不是问题,例如:<TextBox Text="{Binding SomeNullableProperty, TargetNullValue={x:Static sys:String.Empty}}"/>
不要忘记系统命名空间xmlns:sys="clr-namespace:System;assembly=System.Runtime
添加验证规则,以便在文本更改时检查以确定数据是否为数字,如果是,则允许继续处理,如果不是,则提示用户在该字段中仅接受数字数据。
在 Validation in Windows Presentation Foundation 中阅读更多信息
double
这样的数字类型,这已经为您提供了标准验证。
在这里,我有一个受 Ray's answer 启发的简单解决方案。这应该足以识别任何形式的数字。
如果您只想要正数、整数值或精确到最大小数位数等的值,也可以轻松修改此解决方案。
正如 Ray's answer 中所建议的,您需要先添加一个 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);
}
TextChanged
事件的处理程序方法开始时摆脱它们来解决空格问题。可能不是最优雅的解决方案,但有效。
https://i.stack.imgur.com/KbVoa.png
也可以简单地实现一个验证规则并将其应用于 TextBox:
<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;
}
}
另一种方法是使用附加行为,我实现了我的自定义 TextBoxHelper 类,它可以用于我项目中的所有文本框。因为我认为为此目的为每个文本框和每个单独的 XAML 文件订阅事件可能会很耗时。
我实现的 TextBoxHelper 类具有以下特性:
仅过滤和接受 Double、Int、Uint 和 Natural 格式的数字
仅过滤和接受偶数或奇数
处理粘贴事件处理程序以防止将无效文本粘贴到我们的数字文本框中
可以设置一个默认值,通过订阅文本框 TextChanged 事件来防止无效数据作为最后一次拍摄
下面是 TextBoxHelper 类的实现:
public static class TextBoxHelper
{
#region Enum Declarations
public enum NumericFormat
{
Double,
Int,
Uint,
Natural
}
public enum EvenOddConstraint
{
All,
OnlyEven,
OnlyOdd
}
#endregion
#region Dependency Properties & CLR Wrappers
public static readonly DependencyProperty OnlyNumericProperty =
DependencyProperty.RegisterAttached("OnlyNumeric", typeof(NumericFormat?), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetOnlyNumeric(TextBox element, NumericFormat value) =>
element.SetValue(OnlyNumericProperty, value);
public static NumericFormat GetOnlyNumeric(TextBox element) =>
(NumericFormat) element.GetValue(OnlyNumericProperty);
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.RegisterAttached("DefaultValue", typeof(string), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetDefaultValue(TextBox element, string value) =>
element.SetValue(DefaultValueProperty, value);
public static string GetDefaultValue(TextBox element) => (string) element.GetValue(DefaultValueProperty);
public static readonly DependencyProperty EvenOddConstraintProperty =
DependencyProperty.RegisterAttached("EvenOddConstraint", typeof(EvenOddConstraint), typeof(TextBoxHelper),
new PropertyMetadata(EvenOddConstraint.All, DependencyPropertiesChanged));
public static void SetEvenOddConstraint(TextBox element, EvenOddConstraint value) =>
element.SetValue(EvenOddConstraintProperty, value);
public static EvenOddConstraint GetEvenOddConstraint(TextBox element) =>
(EvenOddConstraint)element.GetValue(EvenOddConstraintProperty);
#endregion
#region Dependency Properties Methods
private static void DependencyPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox textBox))
throw new Exception("Attached property must be used with TextBox.");
switch (e.Property.Name)
{
case "OnlyNumeric":
{
var castedValue = (NumericFormat?) e.NewValue;
if (castedValue.HasValue)
{
textBox.PreviewTextInput += TextBox_PreviewTextInput;
DataObject.AddPastingHandler(textBox, TextBox_PasteEventHandler);
}
else
{
textBox.PreviewTextInput -= TextBox_PreviewTextInput;
DataObject.RemovePastingHandler(textBox, TextBox_PasteEventHandler);
}
break;
}
case "DefaultValue":
{
var castedValue = (string) e.NewValue;
if (castedValue != null)
{
textBox.TextChanged += TextBox_TextChanged;
}
else
{
textBox.TextChanged -= TextBox_TextChanged;
}
break;
}
}
}
#endregion
private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = (TextBox)sender;
string newText;
if (textBox.SelectionLength == 0)
{
newText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
}
else
{
var textAfterDelete = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength);
newText = textAfterDelete.Insert(textBox.SelectionStart, e.Text);
}
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.Handled = true;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
}
else
e.Handled = true;
break;
}
}
}
private static void TextBox_PasteEventHandler(object sender, DataObjectPastingEventArgs e)
{
var textBox = (TextBox)sender;
if (e.DataObject.GetDataPresent(typeof(string)))
{
var clipboardText = (string) e.DataObject.GetData(typeof(string));
var newText = textBox.Text.Insert(textBox.SelectionStart, clipboardText);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.CancelCommand();
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
}
else
{
e.CancelCommand();
}
break;
}
}
}
else
{
e.CancelCommand();
}
}
private static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
var defaultValue = GetDefaultValue(textBox);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(textBox.Text, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(textBox.Text, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(textBox.Text, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(textBox.Text, out uint number))
{
if(number == 0)
textBox.Text = defaultValue;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
}
else
{
textBox.Text = defaultValue;
}
break;
}
}
}
}
以下是其简单用法的一些示例:
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double"
viewHelpers:TextBoxHelper.DefaultValue="1"/>
或者
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural"
viewHelpers:TextBoxHelper.DefaultValue="3"
viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>
请注意,我的 TextBoxHelper 位于 viewHelpers xmlns 别名中。
我希望这个实现可以减轻其他人的工作:)
我允许数字键盘数字和退格:
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);
}
var keyEnum = (System.Windows.Input.Key) e.Key; e.Handled = !(keyEnum >= System.Windows.Input.Key.D0 && keyEnum <= System.Windows.Input.Key.D9 || keyEnum >= System.Windows.Input.Key.NumPad0 && keyEnum <= System.Windows.Input.Key.NumPad9 || keyEnum == System.Windows.Input.Key.Back);
这是唯一需要的代码:
void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = new Regex("[^0-9]+").IsMatch(e.Text);
}
这仅允许将数字输入到文本框中。
要允许小数点或减号,您可以将正则表达式更改为 [^0-9.-]+
。
我会假设:
您希望仅允许数字输入的 TextBox 的 Text 属性最初设置为某个有效的数字值(例如,2.7172)。您的文本框是主窗口的子窗口您的主窗口属于 Window1 类您的文本框名称是 numericTB
基本思路:
添加:私有字符串previousText;到您的主窗口类 (Window1) 添加:previousText = numericTB.Text;到您的主窗口构造函数为 numericTB.TextChanged 事件创建一个处理程序,如下所示: private void numericTB_TextChanged(object sender, TextChangedEventArgs e) { double num = 0; bool 成功 = double.TryParse(((TextBox)sender).Text, out num); if (success & num >= 0) previousText = ((TextBox)sender).Text; else ((TextBox)sender).Text = previousText; }
只要它有效,这将继续将 previousText 设置为 numericTB.Text,如果用户写了您不喜欢的内容,则将 numericTB.Text 设置为其最后一个有效值。当然,这只是基本思想,只是“防白痴”,而不是“防白痴”。例如,它不处理用户弄乱空格的情况。所以这是一个完整的解决方案,我认为它是“白痴证明”,如果我错了,请告诉我:
Window1.xaml 文件的内容:
就是这样。如果您有许多 TextBox,那么我建议您创建一个继承自 TextBox 的 CustomControl,这样您就可以将 previousText 和 numericTB_TextChanged 包装在一个单独的文件中。
如果您不想编写大量代码来执行基本功能(我不知道人们为什么要制作长方法),您可以这样做:
添加命名空间:使用 System.Text.RegularExpressions;在 XAML 中,设置一个 TextChanged 属性:
PreviewTextInput += (s, e) =>
{
e.Handled = !e.Text.All(char.IsDigit);
};
.
,因为 e.Text
只返回最后一个输入字符,而点将无法通过 IsDigit
检查。
对于那些寻找仅使用整数和小数的此类问题的快速且非常简单的实施方式的人,请在您的 XAML 文件中,将 PreviewTextInput
属性添加到您的 TextBox
,然后在您的 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() == '.');
对于希望其文本字段仅接受无符号数字(例如套接字端口等)的开发人员:
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);
}
65535
。如果它更大,那么它不是一个有效的端口。此外,将 TextBox.MaxLength
设置为 5
将有助于以编程方式或在 XAML 中。
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;
在文本框的预览 keydown 事件中。
我们可以对文本框更改事件进行验证。以下实现可防止除数字和小数点以外的按键输入。
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;
}
在 Windows 窗体中,这很容易;您可以为 KeyPress 添加一个事件,一切都很容易。但是,在 WPF 中,该事件不存在。但是有一个更简单的方法。
WPF TextBox 具有通用的 TextChanged 事件。它包括粘贴、打字和任何你能想到的东西。
所以你可以做这样的事情:
XAML:
<TextBox name="txtBox1" ... TextChanged="TextBox_TextChanged"/>
代码背后:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
string s = Regex.Replace(((TextBox)sender).Text, @"[^\d.]", "");
((TextBox)sender).Text = s;
}
这也接受 .
,如果您不想要它,只需将其从 regex
语句中删除为 @[^\d]
。
注意:此事件可用于许多 TextBox,因为它使用 sender
对象的文本。您只需编写一次事件,就可以将它用于多个 TextBox'es。
这个怎么样?对我来说效果很好。希望我没有错过任何边缘情况......
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();
}
});
现在我知道这个问题有 an accepted answer,但就我个人而言,我觉得它有点令人困惑,我相信它应该比这更容易。因此,我将尝试展示我是如何让它发挥最大作用的:
在 Windows 窗体 中,有一个名为 KeyPress
的事件非常适合此类任务。但这在 WPF 中不存在,因此我们将使用 PreviewTextInput
事件。此外,对于验证,我相信可以使用 foreach
循环遍历 textbox.Text
并检查它是否 matches ;) 条件,但老实说,这就是 regular expressions 的用途.
在我们深入研究神圣代码之前,还有一件事。对于要触发的事件,可以做两件事:
使用 XAML 告诉程序调用哪个函数:
我认为第二种方法更好,因为在这种情况下,您通常需要将相同的条件 (regex
) 应用于多个 TextBox
和 you don't want to repeat yourself!。
最后,这是你的做法:
private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$";
Regex regex = new Regex(onlyNumeric);
e.Handled = !regex.IsMatch(e.Text);
}
这是我的版本。它基于一个基 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 方法。
在检查正则表达式之前,我修改了 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" />
在文本框中只允许整数(即使在某个范围内)的最佳和最优雅的解决方案是:
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) 调整最小和最大可接受的数字。
此解决方案不允许前导零或复制粘贴输入。在每种情况下,您都会在文本框中有一个正确的数字。
Here is a library for numeric input in WPF
它具有用于验证的 NumberStyles
和 RegexPattern
等属性。
子类 WPF TextBox
https://img.shields.io/nuget/v/Gu.Wpf.NumericInput.svg
利用:
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
我正在为我正在处理的一个简单项目使用未绑定的盒子,所以我不能使用标准绑定方法。因此,我创建了一个简单的 hack,其他人可能会通过简单地扩展现有的 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" >
在这里使用了一些解决方案一段时间后,我开发了自己的解决方案,它适用于我的 MVVM 设置。请注意,在仍然允许用户输入错误字符的意义上,它不像其他一些那样动态,但它阻止他们按下按钮并因此做任何事情。这与我在无法执行操作时将按钮变灰的主题相得益彰。
我有一个 TextBox
,用户必须输入要打印的文档页数:
<TextBox Text="{Binding NumberPagesToPrint, UpdateSourceTrigger=PropertyChanged}"/>
...使用此绑定属性:
private string _numberPagesToPrint;
public string NumberPagesToPrint
{
get { return _numberPagesToPrint; }
set
{
if (_numberPagesToPrint == value)
{
return;
}
_numberPagesToPrint = value;
OnPropertyChanged("NumberPagesToPrint");
}
}
我还有一个按钮:
<Button Template="{DynamicResource CustomButton_Flat}" Content="Set"
Command="{Binding SetNumberPagesCommand}"/>
...使用此命令绑定:
private RelayCommand _setNumberPagesCommand;
public ICommand SetNumberPagesCommand
{
get
{
if (_setNumberPagesCommand == null)
{
int num;
_setNumberPagesCommand = new RelayCommand(param => SetNumberOfPages(),
() => Int32.TryParse(NumberPagesToPrint, out num));
}
return _setNumberPagesCommand;
}
}
然后是SetNumberOfPages()
的方法,但对于本主题并不重要。它在我的情况下运行良好,因为我不必将任何代码添加到 View 的代码隐藏文件中,它允许我使用 Command
属性控制行为。
double.TryParse()
这样的东西可能会以相同的行数实现,并且更加灵活。