我希望接受数字和小数点,但没有符号。
我已经看过使用Windows窗体中的NumericUpDown控件的示例,以及来自微软的这个NumericUpDown自定义控件的示例。但到目前为止,似乎NumericUpDown (WPF是否支持)不会提供我想要的功能。我的应用程序是这样设计的,任何头脑正常的人都不会想弄乱箭头。在我的应用程序中,它们没有任何实际意义。
所以我正在寻找一个简单的方法,使一个标准的WPF文本框只接受我想要的字符。这可能吗?实用吗?
我希望接受数字和小数点,但没有符号。
我已经看过使用Windows窗体中的NumericUpDown控件的示例,以及来自微软的这个NumericUpDown自定义控件的示例。但到目前为止,似乎NumericUpDown (WPF是否支持)不会提供我想要的功能。我的应用程序是这样设计的,任何头脑正常的人都不会想弄乱箭头。在我的应用程序中,它们没有任何实际意义。
所以我正在寻找一个简单的方法,使一个标准的WPF文本框只接受我想要的字符。这可能吗?实用吗?
当前回答
对于那些寻找一个快速和非常简单的实现这种类型的问题,只使用整数和小数,在你的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() == '.');
其他回答
另一种方法将使用附加的行为,我实现了我的自定义TextBoxHelper类,它可以在我的项目中的所有文本框上使用。因为我认为为此目的订阅每个文本框和每个单独XAML文件中的事件会很耗时。
我实现的TextBoxHelper类有这些特性:
过滤和只接受Double, Int, Uint和自然格式的数字 只过滤和接受偶数或奇数 处理粘贴事件处理程序,以防止将无效文本粘贴到数值文本框中 可以设置一个默认值,这将被用来防止无效的数据作为最后一个镜头订阅的文本框TextChanged事件
下面是TextBoxHelper类的实现:
public static class TextBoxHelper
{
#region Enum Declarations
public enum NumericFormat
{
Double,
Int,
Uint,
Natural
}
public enum EvenOddConstraint
{
All,
OnlyEven,
OnlyOdd
}
#endregion
#region Dependency Properties & CLR Wrappers
public static readonly DependencyProperty OnlyNumericProperty =
DependencyProperty.RegisterAttached("OnlyNumeric", typeof(NumericFormat?), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetOnlyNumeric(TextBox element, NumericFormat value) =>
element.SetValue(OnlyNumericProperty, value);
public static NumericFormat GetOnlyNumeric(TextBox element) =>
(NumericFormat) element.GetValue(OnlyNumericProperty);
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.RegisterAttached("DefaultValue", typeof(string), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetDefaultValue(TextBox element, string value) =>
element.SetValue(DefaultValueProperty, value);
public static string GetDefaultValue(TextBox element) => (string) element.GetValue(DefaultValueProperty);
public static readonly DependencyProperty EvenOddConstraintProperty =
DependencyProperty.RegisterAttached("EvenOddConstraint", typeof(EvenOddConstraint), typeof(TextBoxHelper),
new PropertyMetadata(EvenOddConstraint.All, DependencyPropertiesChanged));
public static void SetEvenOddConstraint(TextBox element, EvenOddConstraint value) =>
element.SetValue(EvenOddConstraintProperty, value);
public static EvenOddConstraint GetEvenOddConstraint(TextBox element) =>
(EvenOddConstraint)element.GetValue(EvenOddConstraintProperty);
#endregion
#region Dependency Properties Methods
private static void DependencyPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox textBox))
throw new Exception("Attached property must be used with TextBox.");
switch (e.Property.Name)
{
case "OnlyNumeric":
{
var castedValue = (NumericFormat?) e.NewValue;
if (castedValue.HasValue)
{
textBox.PreviewTextInput += TextBox_PreviewTextInput;
DataObject.AddPastingHandler(textBox, TextBox_PasteEventHandler);
}
else
{
textBox.PreviewTextInput -= TextBox_PreviewTextInput;
DataObject.RemovePastingHandler(textBox, TextBox_PasteEventHandler);
}
break;
}
case "DefaultValue":
{
var castedValue = (string) e.NewValue;
if (castedValue != null)
{
textBox.TextChanged += TextBox_TextChanged;
}
else
{
textBox.TextChanged -= TextBox_TextChanged;
}
break;
}
}
}
#endregion
private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = (TextBox)sender;
string newText;
if (textBox.SelectionLength == 0)
{
newText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
}
else
{
var textAfterDelete = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength);
newText = textAfterDelete.Insert(textBox.SelectionStart, e.Text);
}
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.Handled = true;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
}
else
e.Handled = true;
break;
}
}
}
private static void TextBox_PasteEventHandler(object sender, DataObjectPastingEventArgs e)
{
var textBox = (TextBox)sender;
if (e.DataObject.GetDataPresent(typeof(string)))
{
var clipboardText = (string) e.DataObject.GetData(typeof(string));
var newText = textBox.Text.Insert(textBox.SelectionStart, clipboardText);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.CancelCommand();
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
}
else
{
e.CancelCommand();
}
break;
}
}
}
else
{
e.CancelCommand();
}
}
private static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
var defaultValue = GetDefaultValue(textBox);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(textBox.Text, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(textBox.Text, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(textBox.Text, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(textBox.Text, out uint number))
{
if(number == 0)
textBox.Text = defaultValue;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
}
else
{
textBox.Text = defaultValue;
}
break;
}
}
}
}
下面是一些简单用法的例子:
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double"
viewHelpers:TextBoxHelper.DefaultValue="1"/>
Or
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural"
viewHelpers:TextBoxHelper.DefaultValue="3"
viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>
注意,我的TextBoxHelper驻留在viewHelpers xmlns别名中。
我希望这个实现可以减轻其他人的工作:)
在使用这里的一些解决方案一段时间后,我开发了自己的解决方案,该解决方案适用于我的MVVM设置。注意,在允许用户输入错误字符的意义上,它不像其他一些那样动态,但它阻止用户按下按钮,从而阻止他们做任何事情。这很好地配合了我的主题,即当操作无法执行时将按钮变灰。
我有一个文本框,用户必须输入一些文档页面要打印:
<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属性控制行为。
添加一个验证规则,以便在文本更改时检查数据是否是数字,如果是,则允许继续处理,如果不是,则提示用户该字段中只接受数字数据。
请参阅Windows演示基础中的验证
我使用了一些已经在这里的东西,并使用一个行为把我自己的扭曲,这样我就不必在大量的视图中传播这段代码……
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>
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;
在预览文本框的按键事件。