wpf 如何定义TextBox输入限制?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1103765/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to define TextBox input restrictions?
提问by iLemming
How can I restrict TextBox to accept only capital letters, or for example digits, or forbid to put any special character?
如何限制 TextBox 只接受大写字母,例如数字,或禁止放置任何特殊字符?
Sure it's a piece of cake to catch TextInput event and handle the text here, but is it the proper way to do this?
当然,捕获 TextInput 事件并在此处处理文本是小菜一碟,但这是正确的方法吗?
回答by Kent Boogaart
I've done this in the past with an attached behavior, which can be used like this:
我过去曾使用附加行为完成此操作,可以这样使用:
<TextBox b:Masking.Mask="^\p{Lu}*$"/>
The attached behavior code looks like this:
附加的行为代码如下所示:
/// <summary>
/// Provides masking behavior for any <see cref="TextBox"/>.
/// </summary>
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;
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;
DataObject.AddPastingHandler(textBox, Pasting);
}
}
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;
}
//pressing space doesn't raise PreviewTextInput - no idea why, but we need to handle
//explicitly here
if (e.Key == Key.Space)
{
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;
}
}
回答by VitalyB
I've improved Kent Boogaart's answer by also handling the following actions that previously could cause the pattern to be violated:
我还处理了以前可能导致违反模式的以下操作,从而改进了 Kent Boogaart 的回答:
- Backspace
- Selecting and dragging text in a way that can violate the pattern
- Cut command
- 退格
- 以可能违反模式的方式选择和拖动文本
- 剪切命令
For example, Kent Boogaart's answer allowed the user to enter "ac" by first entering "abc" and afterwards delete the "b" with the backspace which violates the following regex
例如,Kent Boogaart 的回答允许用户通过先输入“abc”然后删除“b”来输入“ac”,并使用退格键删除“b”,这违反了以下正则表达式
^(a|ab|abc)$
Usage (unchanged):
用法(不变):
<TextBox b:Masking.Mask="^\p{Lu}*$"/>
<TextBox b:Masking.Mask="^\p{Lu}*$"/>
Mask class:
面膜类:
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;
DataObject.RemovePastingHandler(textBox, Pasting);
DataObject.RemoveCopyingHandler(textBox, NoDragCopy);
CommandManager.RemovePreviewExecutedHandler(textBox, NoCutting);
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;
DataObject.AddPastingHandler(textBox, Pasting);
DataObject.AddCopyingHandler(textBox, NoDragCopy);
CommandManager.AddPreviewExecutedHandler(textBox, NoCutting);
}
}
private static void NoCutting(object sender, ExecutedRoutedEventArgs e)
{
if(e.Command == ApplicationCommands.Cut)
{
e.Handled = true;
}
}
private static void NoDragCopy(object sender, DataObjectCopyingEventArgs e)
{
if (e.IsDragDrop)
{
e.CancelCommand();
}
}
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;
}
string proposedText = null;
//pressing space doesn't raise PreviewTextInput, reasons here http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/446ec083-04c8-43f2-89dc-1e2521a31f6b?prof=required
if (e.Key == Key.Space)
{
proposedText = GetProposedText(textBox, " ");
}
// Same story with backspace
else if(e.Key == Key.Back)
{
proposedText = GetProposedTextBackspace(textBox);
}
if (proposedText != null && proposedText != string.Empty && !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 GetProposedTextBackspace(TextBox textBox)
{
var text = GetTextWithSelectionRemoved(textBox);
if (textBox.SelectionStart > 0 && textBox.SelectionLength == 0)
{
text = text.Remove(textBox.SelectionStart-1, 1);
}
return text;
}
private static string GetProposedText(TextBox textBox, string newText)
{
var text = GetTextWithSelectionRemoved(textBox);
text = text.Insert(textBox.CaretIndex, newText);
return text;
}
private static string GetTextWithSelectionRemoved(TextBox textBox)
{
var text = textBox.Text;
if (textBox.SelectionStart != -1)
{
text = text.Remove(textBox.SelectionStart, textBox.SelectionLength);
}
return text;
}
}
回答by Nathan Tornquist
I changed VitalyB's code to support Color Themes. Instead of blocking user input if it doesn't meet the RegEx script, it just highlights the text box. The text box will be the theme default without interaction, and then defaults to a light green or red depending on the value after the input is set. You can also set the fail and pass colors programatically with:
我更改了 VitalyB 的代码以支持颜色主题。如果它不符合 RegEx 脚本,它不会阻止用户输入,它只是突出显示文本框。文本框将是主题默认不交互,然后根据输入设置后的值默认为浅绿色或红色。您还可以通过以下方式以编程方式设置失败和通过颜色:
b:ColorMasking.PassColor = "Hexadecimal Value"
b:ColorMasking.FailColor = "Hexadecimal Value"
The class is below:
课程如下:
public class ColorMasking : DependencyObject
{
private static readonly DependencyPropertyKey _maskExpressionPropertyKey = DependencyProperty.RegisterAttachedReadOnly("MaskExpression",
typeof(Regex),
typeof(ColorMasking),
new FrameworkPropertyMetadata());
/// <summary>
/// Identifies the <see cref="Mask"/> dependency property.
/// </summary>
///
public static readonly DependencyProperty PassColorProperty = DependencyProperty.RegisterAttached("PassColor",
typeof(string),
typeof(ColorMasking),
new PropertyMetadata("#99FF99"));
public static void SetPassColor(DependencyObject obj, string passColor)
{
obj.SetValue(PassColorProperty, passColor);
}
public static string GetPassColor(DependencyObject obj)
{
return (string)obj.GetValue(PassColorProperty);
}
public static readonly DependencyProperty FailColorProperty = DependencyProperty.RegisterAttached("FailColor",
typeof(string),
typeof(ColorMasking),
new PropertyMetadata("#FFCCFF"));
public static void SetFailColor(DependencyObject obj, string failColor)
{
obj.SetValue(FailColorProperty, failColor);
}
public static string GetFailColor(DependencyObject obj)
{
return (string)obj.GetValue(FailColorProperty);
}
public static readonly DependencyProperty MaskProperty = DependencyProperty.RegisterAttached("Mask",
typeof(string),
typeof(ColorMasking),
new FrameworkPropertyMetadata(OnMaskChanged));
private static void OnPassColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var textBox = dependencyObject as TextBox;
var color = e.NewValue as string;
textBox.SetValue(PassColorProperty, color);
}
/// <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;
DataObject.RemovePastingHandler(textBox, Pasting);
DataObject.RemoveCopyingHandler(textBox, NoDragCopy);
CommandManager.RemovePreviewExecutedHandler(textBox, NoCutting);
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;
DataObject.AddPastingHandler(textBox, Pasting);
DataObject.AddCopyingHandler(textBox, NoDragCopy);
CommandManager.AddPreviewExecutedHandler(textBox, NoCutting);
}
}
private static void NoCutting(object sender, ExecutedRoutedEventArgs e)
{
if (e.Command == ApplicationCommands.Cut)
{
e.Handled = true;
}
}
private static void NoDragCopy(object sender, DataObjectCopyingEventArgs e)
{
if (e.IsDragDrop)
{
e.CancelCommand();
}
}
private static void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
string passHex = (string)textBox.GetValue(PassColorProperty);
string failHex = (string)textBox.GetValue(FailColorProperty);
Color passColor = Extensions.ToColorFromHex(passHex);
Color failColor = Extensions.ToColorFromHex(failHex);
if (maskExpression == null)
{
return;
}
var proposedText = GetProposedText(textBox, e.Text);
if (!maskExpression.IsMatch(proposedText))
{
textBox.Background = new SolidColorBrush(failColor);
}
else
{
textBox.Background = new SolidColorBrush(passColor);
}
}
private static void textBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
var textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
string passHex = (string)textBox.GetValue(PassColorProperty);
string failHex = (string)textBox.GetValue(FailColorProperty);
Color passColor = Extensions.ToColorFromHex(passHex);
Color failColor = Extensions.ToColorFromHex(failHex);
if (maskExpression == null)
{
return;
}
string proposedText = null;
//pressing space doesn't raise PreviewTextInput, reasons here http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/446ec083-04c8-43f2-89dc-1e2521a31f6b?prof=required
if (e.Key == Key.Space)
{
proposedText = GetProposedText(textBox, " ");
}
// Same story with backspace
else if (e.Key == Key.Back)
{
proposedText = GetProposedTextBackspace(textBox);
}
if (proposedText != null && !maskExpression.IsMatch(proposedText))
{
textBox.Background = new SolidColorBrush(failColor);
}
else
{
textBox.Background = new SolidColorBrush(passColor);
}
}
private static void Pasting(object sender, DataObjectPastingEventArgs e)
{
TextBox textBox = sender as TextBox;
var maskExpression = GetMaskExpression(textBox);
string passHex = (string)textBox.GetValue(PassColorProperty);
string failHex = (string)textBox.GetValue(FailColorProperty);
Color passColor = Extensions.ToColorFromHex(passHex);
Color failColor = Extensions.ToColorFromHex(failHex);
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))
{
textBox.Background = new SolidColorBrush(failColor);
}
else
{
textBox.Background = new SolidColorBrush(passColor);
}
}
else
{
textBox.Background = new SolidColorBrush(failColor);
}
}
private static string GetProposedTextBackspace(TextBox textBox)
{
var text = GetTextWithSelectionRemoved(textBox);
if (textBox.SelectionStart > 0)
{
text = text.Remove(textBox.SelectionStart - 1, 1);
}
return text;
}
private static string GetProposedText(TextBox textBox, string newText)
{
var text = GetTextWithSelectionRemoved(textBox);
text = text.Insert(textBox.CaretIndex, newText);
return text;
}
private static string GetTextWithSelectionRemoved(TextBox textBox)
{
var text = textBox.Text;
if (textBox.SelectionStart != -1)
{
text = text.Remove(textBox.SelectionStart, textBox.SelectionLength);
}
return text;
}
}
To run, the script requires a class written by Aaron C, explained here: Silverlight/WPF sets ellipse with hexadecimal colourshown here: http://www.wiredprairie.us/blog/index.php/archives/659
要运行,该脚本需要一个由 Aaron C 编写的类,解释如下:Silverlight/WPF 设置带有十六进制颜色的椭圆,此处显示:http: //www.wiredprairie.us/blog/index.php/archives/659
The code is below in case the website is ever moved:
如果网站被移动,代码如下:
public static class Extensions
{
public static void SetFromHex(this Color c, string hex)
{
Color c1 = ToColorFromHex(hex);
c.A = c1.A;
c.R = c1.R;
c.G = c1.G;
c.B = c1.B;
}
public static Color ToColorFromHex(string hex)
{
if (string.IsNullOrEmpty(hex))
{
throw new ArgumentNullException("hex");
}
// remove any "#" characters
while (hex.StartsWith("#"))
{
hex = hex.Substring(1);
}
int num = 0;
// get the number out of the string
if (!Int32.TryParse(hex, System.Globalization.NumberStyles.HexNumber, null, out num))
{
throw new ArgumentException("Color not in a recognized Hex format.");
}
int[] pieces = new int[4];
if (hex.Length > 7)
{
pieces[0] = ((num >> 24) & 0x000000ff);
pieces[1] = ((num >> 16) & 0x000000ff);
pieces[2] = ((num >> 8) & 0x000000ff);
pieces[3] = (num & 0x000000ff);
}
else if (hex.Length > 5)
{
pieces[0] = 255;
pieces[1] = ((num >> 16) & 0x000000ff);
pieces[2] = ((num >> 8) & 0x000000ff);
pieces[3] = (num & 0x000000ff);
}
else if (hex.Length == 3)
{
pieces[0] = 255;
pieces[1] = ((num >> 8) & 0x0000000f);
pieces[1] += pieces[1] * 16;
pieces[2] = ((num >> 4) & 0x000000f);
pieces[2] += pieces[2] * 16;
pieces[3] = (num & 0x000000f);
pieces[3] += pieces[3] * 16;
}
return Color.FromArgb((byte)pieces[0], (byte)pieces[1], (byte)pieces[2], (byte)pieces[3]);
}
}
回答by Oungawak
Here is yet another version of the existing solutions here. It's a Behavior simulating a "TextChanging" event providing the old text, the new text and a flag to cancel the change. This way we can implement whatever filter we want.
这是现有解决方案的另一个版本。这是一种模拟“TextChanging”事件的行为,提供旧文本、新文本和取消更改的标志。这样我们就可以实现我们想要的任何过滤器。
I got rid of the "PreviewKeyDown" handler used only for the space character. TextBox seems to manage everything with routed commands and "Space" does have its own commmand, though it's not public. I also added support for additionals keyboard shortcuts like "Ctrl+Backspace" (delete previous word).
我摆脱了仅用于空格字符的“PreviewKeyDown”处理程序。TextBox 似乎使用路由命令管理所有内容,而“Space”确实有自己的命令,尽管它不是公开的。我还添加了对附加键盘快捷键的支持,如“Ctrl+Backspace”(删除前一个单词)。
public class TextChangingBehavior : Behavior<TextBox>
{
public event EventHandler<TextChangingEventArgs> TextChanging;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += OnPreviewTextInput;
CommandManager.AddPreviewExecutedHandler(AssociatedObject, OnPreviewExecutedHandler);
DataObject.AddCopyingHandler(AssociatedObject, OnCopying);
DataObject.AddPastingHandler(AssociatedObject, OnPasting);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
CommandManager.RemovePreviewExecutedHandler(AssociatedObject, OnPreviewExecutedHandler);
DataObject.RemoveCopyingHandler(AssociatedObject, OnCopying);
DataObject.RemovePastingHandler(AssociatedObject, OnPasting);
}
#region Text
private enum CharCategory
{
LetterOrDigit,
Whitespace,
Other
}
private CharCategory GetCharCategory(char c)
{
if (char.IsLetterOrDigit(c))
return CharCategory.LetterOrDigit;
else if (char.IsWhiteSpace(c))
return CharCategory.Whitespace;
else
return CharCategory.Other;
}
private string GetText(string input = null)
{
var box = AssociatedObject;
var text = box.Text ?? string.Empty;
if (input != null)
{
// Delete selection
var deleteCount = box.SelectionLength;
if (deleteCount > 0)
text = text.Remove(box.SelectionStart, deleteCount);
// Insert input
if (input.Length > 0)
text = text.Insert(box.CaretIndex, input);
}
return text;
}
#endregion
private void OnPreviewExecutedHandler(object sender, ExecutedRoutedEventArgs e)
{
var box = AssociatedObject;
var selectionExists = box.SelectionLength > 0;
var caretIndex = box.CaretIndex;
string newText = null;
if (e.Command == ApplicationCommands.Cut)
{
if (selectionExists)
newText = GetText(string.Empty);
}
else if (e.Command == EditingCommands.Backspace)
{
if (selectionExists)
newText = GetText(string.Empty);
else if (caretIndex > 0)
newText = GetText().Remove(caretIndex - 1, 1);
}
else if (e.Command == EditingCommands.Delete)
{
if (selectionExists)
newText = GetText(string.Empty);
else
{
newText = GetText();
if (caretIndex >= newText.Length)
newText = null;
else
newText = newText.Remove(caretIndex, 1);
}
}
else if (e.Command == EditingCommands.DeletePreviousWord)
{
if (selectionExists)
newText = GetText(string.Empty);
else if (caretIndex > 0)
{
newText = GetText();
var startIndex = caretIndex;
// Include whitespaces
do
startIndex--;
while (startIndex > 0 && char.IsWhiteSpace(newText[startIndex]));
// Include the next block
var currentCategory = GetCharCategory(newText[startIndex]);
while (startIndex > 0 && GetCharCategory(newText[startIndex - 1]) == currentCategory)
startIndex--;
newText = newText.Remove(startIndex, caretIndex - startIndex);
}
}
else if (e.Command == EditingCommands.DeleteNextWord)
{
if (selectionExists)
newText = GetText(string.Empty);
else
{
newText = GetText();
if (caretIndex >= newText.Length)
newText = null;
else
{
var endIndex = caretIndex + 1;
// Include the current block
var currentCategory = GetCharCategory(newText[caretIndex]);
while (endIndex < newText.Length && GetCharCategory(newText[endIndex]) == currentCategory)
endIndex++;
// Include whitespaces
while (endIndex < newText.Length && char.IsWhiteSpace(newText[endIndex]))
endIndex++;
newText = newText.Remove(caretIndex, endIndex - caretIndex);
}
}
}
else if (e.Command is RoutedUICommand cmd && new[] { "Space", "ShiftSpace" }.Contains(cmd.Name))
{
newText = GetText(" ");
}
if (newText != null && OnProcessChange(newText))
e.Handled = true;
}
private void OnCopying(object sender, DataObjectCopyingEventArgs e)
{
if (e.IsDragDrop)
{
if (OnProcessChange(GetText(string.Empty)))
e.CancelCommand();
}
}
private void OnPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(string)))
{
if (OnProcessChange(GetText((string)e.DataObject.GetData(typeof(string)))))
e.CancelCommand();
}
}
private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (!string.IsNullOrEmpty(e.Text))
{
if (OnProcessChange(GetText(e.Text)))
e.Handled = true;
}
}
private bool OnProcessChange(string newValue)
{
var oldValue = GetText();
if (string.Equals(oldValue, newValue, StringComparison.Ordinal))
return false;
else
{
var args = new TextChangingEventArgs(oldValue, newValue);
OnTextChanging(args);
return args.Cancel;
}
}
protected virtual void OnTextChanging(TextChangingEventArgs e)
{
TextChanging?.Invoke(this, e);
}
}
public class TextChangingEventArgs : EventArgs
{
public string OldValue { get; }
public string NewValue { get; }
public bool Cancel { get; set; }
public TextChangingEventArgs(string oldValue, string newValue)
{
OldValue = oldValue;
NewValue = newValue;
}
}
回答by WhoIsNinja
private void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
string txt = TextBox1.Text;
if (txt != "")
{
TextBox1.Text = Regex.Replace(TextBox1.Text, "[^0-9]", "");
if (txt != TextBox1.Text)
{
TextBox1.Select(TextBox1.Text.Length, 0);
}
}
}
回答by aggietech
Another possible solution is to use one of the "Masked TextBox" wpf implementation using the "MaskedTextProvider" class that is used by the "MaskedTextBox" from Winforms.
另一种可能的解决方案是使用 Winforms 中的“MaskedTextBox”使用的“MaskedTextProvider”类使用“Masked TextBox”wpf实现之一。
Two potential solutions are found here -> https://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBoxand here -> http://marlongrech.wordpress.com/2007/10/28/masked-textbox/
这里有两个潜在的解决方案 -> https://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox和这里 -> http://marlongrech.wordpress.com/2007/10/28/masked-textbox/