仅包含数字的 C# 文本框
C# Textbox with numbers only
我只想在文本框中对数字进行数学处理,为此我使用“System.Text.RegularExpressions”。
我现在使用的掩码是"^ [0-9] + \,? [0-9] * $"
。我不能在这个使用中输入负数。
如何创建仅支持以下数字格式的掩码。
它只适用于所有地方的整数。不幸的是,我找不到可以进行数学运算的数字格式。
我的代码
public static class Masking
{
private static readonly DependencyPropertyKey _maskExpressionPropertyKey = DependencyProperty.RegisterAttachedReadOnly("MaskExpression",
typeof(Regex),
typeof(Masking),
new FrameworkPropertyMetadata());
/// <summary>
/// Identifies the <see cref="Mask"/> dependency property.
/// </summary>
public static readonly DependencyProperty MaskProperty = DependencyProperty.RegisterAttached("Mask",
typeof(string),
typeof(Masking),
new FrameworkPropertyMetadata(OnMaskChanged));
/// <summary>
/// Identifies the <see cref="MaskExpression"/> dependency property.
/// </summary>
public static readonly DependencyProperty MaskExpressionProperty = _maskExpressionPropertyKey.DependencyProperty;
/// <summary>
/// Gets the mask for a given <see cref="TextBox"/>.
/// </summary>
/// <param name="textBox">
/// The <see cref="TextBox"/> whose mask is to be retrieved.
/// </param>
/// <returns>
/// The mask, or <see langword="null"/> if no mask has been set.
/// </returns>
public static string GetMask(TextBox textBox)
{
if (textBox == null)
{
throw new ArgumentNullException("textBox");
}
return textBox.GetValue(MaskProperty) as string;
}
/// <summary>
/// Sets the mask for a given <see cref="TextBox"/>.
/// </summary>
/// <param name="textBox">
/// The <see cref="TextBox"/> whose mask is to be set.
/// </param>
/// <param name="mask">
/// The mask to set, or <see langword="null"/> to remove any existing mask from <paramref name="textBox"/>.
/// </param>
public static void SetMask(TextBox textBox, string mask)
{
if (textBox == null)
{
throw new ArgumentNullException("textBox");
}
textBox.SetValue(MaskProperty, mask);
}
/// <summary>
/// Gets the mask expression for the <see cref="TextBox"/>.
/// </summary>
/// <remarks>
/// This method can be used to retrieve the actual <see cref="Regex"/> instance created as a result of setting the mask on a <see cref="TextBox"/>.
/// </remarks>
/// <param name="textBox">
/// The <see cref="TextBox"/> whose mask expression is to be retrieved.
/// </param>
/// <returns>
/// The mask expression as an instance of <see cref="Regex"/>, or <see langword="null"/> if no mask has been applied to <paramref name="textBox"/>.
/// </returns>
public static Regex GetMaskExpression(TextBox textBox)
{
if (textBox == null)
{
throw new ArgumentNullException("textBox");
}
return textBox.GetValue(MaskExpressionProperty) as Regex;
}
private static void SetMaskExpression(TextBox textBox, Regex regex)
{
textBox.SetValue(_maskExpressionPropertyKey, regex);
}
private static void OnMaskChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var textBox = dependencyObject as TextBox;
var mask = e.NewValue as string;
textBox.PreviewTextInput -= textBox_PreviewTextInput;
textBox.PreviewKeyDown -= textBox_PreviewKeyDown;
textBox.GotFocus += textBox_GotFocus;
DataObject.RemovePastingHandler(textBox, Pasting);
if (mask == null)
{
textBox.ClearValue(MaskProperty);
textBox.ClearValue(MaskExpressionProperty);
}
else
{
textBox.SetValue(MaskProperty, mask);
SetMaskExpression(textBox, new Regex(mask, RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace));
textBox.PreviewTextInput += textBox_PreviewTextInput;
textBox.PreviewKeyDown += textBox_PreviewKeyDown;
textBox.GotFocus += textBox_GotFocus;
DataObject.AddPastingHandler(textBox, Pasting);
}
}
private static void textBox_GotFocus(object sender, RoutedEventArgs e)
{
if(Globals.KeyboardActive.IsChecked == true)
{
Process process = Process.Start(new ProcessStartInfo(
((Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\osk.exe"))));
}
}
private static void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
if (maskExpression == null)
{
return;
}
var proposedText = GetProposedText(textBox, e.Text);
if (!maskExpression.IsMatch(proposedText))
{
e.Handled = true;
}
}
private static void textBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
if (maskExpression == null)
{
return;
}
if (e.Key == Key.Space)
{
var proposedText = GetProposedText(textBox, " ");
if (!maskExpression.IsMatch(proposedText))
{
e.Handled = true;
}
}
if (e.Key == Key.Enter)
{
var proposedText = GetProposedText(textBox, " ");
if (!maskExpression.IsMatch(proposedText))
{
e.Handled = true;
}
}
}
private static void Pasting(object sender, DataObjectPastingEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
if (maskExpression == null)
{
return;
}
if (e.DataObject.GetDataPresent(typeof(string)))
{
var pastedText = e.DataObject.GetData(typeof(string)) as string;
var proposedText = GetProposedText(textBox, pastedText);
if (!maskExpression.IsMatch(proposedText))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
private static string GetProposedText(TextBox textBox, string newText)
{
var text = textBox.Text;
if (textBox.SelectionStart != -1)
{
text = text.Remove(textBox.SelectionStart, textBox.SelectionLength);
}
text = text.Insert(textBox.CaretIndex, newText);
return text;
}
}
Xaml代码
<TextBox Name="Text" VerticalAlignment="Top" HorizontalAlignment="Left"
Foreground="{DynamicResource ForegroundColor}" Height="25" Width="90"
materialDesign:TextFieldAssist.UnderlineBrush="{DynamicResource UnderlineColor}"
materialDesign:HintAssist.Foreground="#244886"
AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"
materialDesign:HintAssist.Hint="Elevetion Angle" Margin="172,15,0,0"
materialDesign:TextFieldAssist.SuffixText="°"
b:Masking.Mask="^-?[0-9]+(?:\,[0-9]+)?$"
LostFocus="textboxsLostFocus"/>
我不推荐正则表达式来完成这个简单的任务。使用简单字符串比较时的性能要好得多。
一般情况下应该使用double.TryParse()
来判断一个字符串是否为纯数值。
以下示例扩展 TextBox
并覆盖 OnTextInput
以取消非数字输入。过滤器允许使用单个小数点分隔符。它是 dynamic/customizable,因为在比较当前分隔符或其他数字符号时,它取决于当前文化的 NumberFormatInfo
。
class NumericTextBox : TextBox
{
#region Overrides of TextBoxBase
/// <inheritdoc />
protected override void OnTextInput(TextCompositionEventArgs e)
{
CultureInfo culture = CultureInfo.CurrentCulture;
if (TryHandleSpecialNonNumericCharacter(e, culture))
{
base.OnTextInput(e);
return;
}
// Input is not a special character.
// Cancel text input if non-numeric.
e.Handled = !IsInputNumeric(e.Text, culture);
base.OnTextInput(e);
}
private bool IsInputNumeric(string input, IFormatProvider culture) =>
double.TryParse(input, NumberStyles.Number, culture, out _) ;
private bool TryHandleSpecialNonNumericCharacter(TextCompositionEventArgs inputArgs, CultureInfo culture)
{
string input = inputArgs.Text;
switch (input)
{
case var _ when input.Equals(culture.NumberFormat.NegativeSign, StringComparison.CurrentCultureIgnoreCase):
case var _ when input.Equals(culture.NumberFormat.PositiveSign, StringComparison.CurrentCultureIgnoreCase):
break;
// Allow single decimal separator
case var _ when input.Equals(culture.NumberFormat.NumberDecimalSeparator, StringComparison.CurrentCultureIgnoreCase):
inputArgs.Handled = this.Text.Contains(culture.NumberFormat.NumberDecimalSeparator);
break;
default:
return false;
}
return true;
}
#endregion
}
我只想在文本框中对数字进行数学处理,为此我使用“System.Text.RegularExpressions”。
我现在使用的掩码是"^ [0-9] + \,? [0-9] * $"
。我不能在这个使用中输入负数。
如何创建仅支持以下数字格式的掩码。
它只适用于所有地方的整数。不幸的是,我找不到可以进行数学运算的数字格式。
我的代码
public static class Masking
{
private static readonly DependencyPropertyKey _maskExpressionPropertyKey = DependencyProperty.RegisterAttachedReadOnly("MaskExpression",
typeof(Regex),
typeof(Masking),
new FrameworkPropertyMetadata());
/// <summary>
/// Identifies the <see cref="Mask"/> dependency property.
/// </summary>
public static readonly DependencyProperty MaskProperty = DependencyProperty.RegisterAttached("Mask",
typeof(string),
typeof(Masking),
new FrameworkPropertyMetadata(OnMaskChanged));
/// <summary>
/// Identifies the <see cref="MaskExpression"/> dependency property.
/// </summary>
public static readonly DependencyProperty MaskExpressionProperty = _maskExpressionPropertyKey.DependencyProperty;
/// <summary>
/// Gets the mask for a given <see cref="TextBox"/>.
/// </summary>
/// <param name="textBox">
/// The <see cref="TextBox"/> whose mask is to be retrieved.
/// </param>
/// <returns>
/// The mask, or <see langword="null"/> if no mask has been set.
/// </returns>
public static string GetMask(TextBox textBox)
{
if (textBox == null)
{
throw new ArgumentNullException("textBox");
}
return textBox.GetValue(MaskProperty) as string;
}
/// <summary>
/// Sets the mask for a given <see cref="TextBox"/>.
/// </summary>
/// <param name="textBox">
/// The <see cref="TextBox"/> whose mask is to be set.
/// </param>
/// <param name="mask">
/// The mask to set, or <see langword="null"/> to remove any existing mask from <paramref name="textBox"/>.
/// </param>
public static void SetMask(TextBox textBox, string mask)
{
if (textBox == null)
{
throw new ArgumentNullException("textBox");
}
textBox.SetValue(MaskProperty, mask);
}
/// <summary>
/// Gets the mask expression for the <see cref="TextBox"/>.
/// </summary>
/// <remarks>
/// This method can be used to retrieve the actual <see cref="Regex"/> instance created as a result of setting the mask on a <see cref="TextBox"/>.
/// </remarks>
/// <param name="textBox">
/// The <see cref="TextBox"/> whose mask expression is to be retrieved.
/// </param>
/// <returns>
/// The mask expression as an instance of <see cref="Regex"/>, or <see langword="null"/> if no mask has been applied to <paramref name="textBox"/>.
/// </returns>
public static Regex GetMaskExpression(TextBox textBox)
{
if (textBox == null)
{
throw new ArgumentNullException("textBox");
}
return textBox.GetValue(MaskExpressionProperty) as Regex;
}
private static void SetMaskExpression(TextBox textBox, Regex regex)
{
textBox.SetValue(_maskExpressionPropertyKey, regex);
}
private static void OnMaskChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var textBox = dependencyObject as TextBox;
var mask = e.NewValue as string;
textBox.PreviewTextInput -= textBox_PreviewTextInput;
textBox.PreviewKeyDown -= textBox_PreviewKeyDown;
textBox.GotFocus += textBox_GotFocus;
DataObject.RemovePastingHandler(textBox, Pasting);
if (mask == null)
{
textBox.ClearValue(MaskProperty);
textBox.ClearValue(MaskExpressionProperty);
}
else
{
textBox.SetValue(MaskProperty, mask);
SetMaskExpression(textBox, new Regex(mask, RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace));
textBox.PreviewTextInput += textBox_PreviewTextInput;
textBox.PreviewKeyDown += textBox_PreviewKeyDown;
textBox.GotFocus += textBox_GotFocus;
DataObject.AddPastingHandler(textBox, Pasting);
}
}
private static void textBox_GotFocus(object sender, RoutedEventArgs e)
{
if(Globals.KeyboardActive.IsChecked == true)
{
Process process = Process.Start(new ProcessStartInfo(
((Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\osk.exe"))));
}
}
private static void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
if (maskExpression == null)
{
return;
}
var proposedText = GetProposedText(textBox, e.Text);
if (!maskExpression.IsMatch(proposedText))
{
e.Handled = true;
}
}
private static void textBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
if (maskExpression == null)
{
return;
}
if (e.Key == Key.Space)
{
var proposedText = GetProposedText(textBox, " ");
if (!maskExpression.IsMatch(proposedText))
{
e.Handled = true;
}
}
if (e.Key == Key.Enter)
{
var proposedText = GetProposedText(textBox, " ");
if (!maskExpression.IsMatch(proposedText))
{
e.Handled = true;
}
}
}
private static void Pasting(object sender, DataObjectPastingEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
if (maskExpression == null)
{
return;
}
if (e.DataObject.GetDataPresent(typeof(string)))
{
var pastedText = e.DataObject.GetData(typeof(string)) as string;
var proposedText = GetProposedText(textBox, pastedText);
if (!maskExpression.IsMatch(proposedText))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
private static string GetProposedText(TextBox textBox, string newText)
{
var text = textBox.Text;
if (textBox.SelectionStart != -1)
{
text = text.Remove(textBox.SelectionStart, textBox.SelectionLength);
}
text = text.Insert(textBox.CaretIndex, newText);
return text;
}
}
Xaml代码
<TextBox Name="Text" VerticalAlignment="Top" HorizontalAlignment="Left"
Foreground="{DynamicResource ForegroundColor}" Height="25" Width="90"
materialDesign:TextFieldAssist.UnderlineBrush="{DynamicResource UnderlineColor}"
materialDesign:HintAssist.Foreground="#244886"
AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"
materialDesign:HintAssist.Hint="Elevetion Angle" Margin="172,15,0,0"
materialDesign:TextFieldAssist.SuffixText="°"
b:Masking.Mask="^-?[0-9]+(?:\,[0-9]+)?$"
LostFocus="textboxsLostFocus"/>
我不推荐正则表达式来完成这个简单的任务。使用简单字符串比较时的性能要好得多。
一般情况下应该使用double.TryParse()
来判断一个字符串是否为纯数值。
以下示例扩展 TextBox
并覆盖 OnTextInput
以取消非数字输入。过滤器允许使用单个小数点分隔符。它是 dynamic/customizable,因为在比较当前分隔符或其他数字符号时,它取决于当前文化的 NumberFormatInfo
。
class NumericTextBox : TextBox
{
#region Overrides of TextBoxBase
/// <inheritdoc />
protected override void OnTextInput(TextCompositionEventArgs e)
{
CultureInfo culture = CultureInfo.CurrentCulture;
if (TryHandleSpecialNonNumericCharacter(e, culture))
{
base.OnTextInput(e);
return;
}
// Input is not a special character.
// Cancel text input if non-numeric.
e.Handled = !IsInputNumeric(e.Text, culture);
base.OnTextInput(e);
}
private bool IsInputNumeric(string input, IFormatProvider culture) =>
double.TryParse(input, NumberStyles.Number, culture, out _) ;
private bool TryHandleSpecialNonNumericCharacter(TextCompositionEventArgs inputArgs, CultureInfo culture)
{
string input = inputArgs.Text;
switch (input)
{
case var _ when input.Equals(culture.NumberFormat.NegativeSign, StringComparison.CurrentCultureIgnoreCase):
case var _ when input.Equals(culture.NumberFormat.PositiveSign, StringComparison.CurrentCultureIgnoreCase):
break;
// Allow single decimal separator
case var _ when input.Equals(culture.NumberFormat.NumberDecimalSeparator, StringComparison.CurrentCultureIgnoreCase):
inputArgs.Handled = this.Text.Contains(culture.NumberFormat.NumberDecimalSeparator);
break;
default:
return false;
}
return true;
}
#endregion
}