Come definire restrizioni di input TextBox?

Come posso limitare TextBox ad accettare solo lettere maiuscole o, ad esempio, cifre o vietare di inserire caratteri speciali?

Certo è un pezzo di torta per catturare l’evento TextInput e gestire il testo qui, ma è il modo corretto per farlo?

L’ho fatto in passato con un comportamento allegato, che può essere utilizzato in questo modo:

 

Il codice di comportamento allegato si presenta così:

 ///  /// Provides masking behavior for any . ///  public static class Masking { private static readonly DependencyPropertyKey _maskExpressionPropertyKey = DependencyProperty.RegisterAttachedReadOnly("MaskExpression", typeof(Regex), typeof(Masking), new FrameworkPropertyMetadata()); ///  /// Identifies the  dependency property. ///  public static readonly DependencyProperty MaskProperty = DependencyProperty.RegisterAttached("Mask", typeof(string), typeof(Masking), new FrameworkPropertyMetadata(OnMaskChanged)); ///  /// Identifies the  dependency property. ///  public static readonly DependencyProperty MaskExpressionProperty = _maskExpressionPropertyKey.DependencyProperty; ///  /// Gets the mask for a given . ///  ///  /// The  whose mask is to be retrieved. ///  ///  /// The mask, or  if no mask has been set. ///  public static string GetMask(TextBox textBox) { if (textBox == null) { throw new ArgumentNullException("textBox"); } return textBox.GetValue(MaskProperty) as string; } ///  /// Sets the mask for a given . ///  ///  /// The  whose mask is to be set. ///  ///  /// The mask to set, or  to remove any existing mask from . ///  public static void SetMask(TextBox textBox, string mask) { if (textBox == null) { throw new ArgumentNullException("textBox"); } textBox.SetValue(MaskProperty, mask); } ///  /// Gets the mask expression for the . ///  ///  /// This method can be used to retrieve the actual  instance created as a result of setting the mask on a . ///  ///  /// The  whose mask expression is to be retrieved. ///  ///  /// The mask expression as an instance of , or  if no mask has been applied to . ///  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; } } 

Ho migliorato la risposta di Kent Boogaart gestendo anche le seguenti azioni che in precedenza potevano causare la violazione del pattern:

  • Backspace
  • Selezionare e trascinare il testo in un modo che può violare il modello
  • Comando di taglio

Ad esempio, la risposta di Kent Boogaart ha permesso all’utente di inserire “ac” inserendo prima “abc” e successivamente eliminare la “b” con il backspace che viola la regex seguente

 ^(a|ab|abc)$ 

Utilizzo (invariato):

Classe maschera:

 public static class Masking { private static readonly DependencyPropertyKey _maskExpressionPropertyKey = DependencyProperty.RegisterAttachedReadOnly("MaskExpression", typeof(Regex), typeof(Masking), new FrameworkPropertyMetadata()); ///  /// Identifies the  dependency property. ///  public static readonly DependencyProperty MaskProperty = DependencyProperty.RegisterAttached("Mask", typeof(string), typeof(Masking), new FrameworkPropertyMetadata(OnMaskChanged)); ///  /// Identifies the  dependency property. ///  public static readonly DependencyProperty MaskExpressionProperty = _maskExpressionPropertyKey.DependencyProperty; ///  /// Gets the mask for a given . ///  ///  /// The  whose mask is to be retrieved. ///  ///  /// The mask, or  if no mask has been set. ///  public static string GetMask(TextBox textBox) { if (textBox == null) { throw new ArgumentNullException("textBox"); } return textBox.GetValue(MaskProperty) as string; } ///  /// Sets the mask for a given . ///  ///  /// The  whose mask is to be set. ///  ///  /// The mask to set, or  to remove any existing mask from . ///  public static void SetMask(TextBox textBox, string mask) { if (textBox == null) { throw new ArgumentNullException("textBox"); } textBox.SetValue(MaskProperty, mask); } ///  /// Gets the mask expression for the . ///  ///  /// This method can be used to retrieve the actual  instance created as a result of setting the mask on a . ///  ///  /// The  whose mask expression is to be retrieved. ///  ///  /// The mask expression as an instance of , or  if no mask has been applied to . ///  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; } } 

Ho modificato il codice di VitalyB per supportare i temi del colore. Invece di bloccare l’input dell’utente se non soddisfa lo script RegEx, evidenzia semplicemente la casella di testo. La casella di testo sarà l’impostazione predefinita del tema senza interazione, quindi verrà automaticamente impostata su una luce verde o rossa a seconda del valore dopo l’impostazione dell’ingresso. È inoltre ansible impostare il fail e passare i colors in modo programmatico con:

 b:ColorMasking.PassColor = "Hexadecimal Value" b:ColorMasking.FailColor = "Hexadecimal Value" 

La class è qui sotto:

 public class ColorMasking : DependencyObject { private static readonly DependencyPropertyKey _maskExpressionPropertyKey = DependencyProperty.RegisterAttachedReadOnly("MaskExpression", typeof(Regex), typeof(ColorMasking), new FrameworkPropertyMetadata()); ///  /// Identifies the  dependency property. ///  /// 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); } ///  /// Identifies the  dependency property. ///  public static readonly DependencyProperty MaskExpressionProperty = _maskExpressionPropertyKey.DependencyProperty; ///  /// Gets the mask for a given . ///  ///  /// The  whose mask is to be retrieved. ///  ///  /// The mask, or  if no mask has been set. ///  public static string GetMask(TextBox textBox) { if (textBox == null) { throw new ArgumentNullException("textBox"); } return textBox.GetValue(MaskProperty) as string; } ///  /// Sets the mask for a given . ///  ///  /// The  whose mask is to be set. ///  ///  /// The mask to set, or  to remove any existing mask from . ///  public static void SetMask(TextBox textBox, string mask) { if (textBox == null) { throw new ArgumentNullException("textBox"); } textBox.SetValue(MaskProperty, mask); } ///  /// Gets the mask expression for the . ///  ///  /// This method can be used to retrieve the actual  instance created as a result of setting the mask on a . ///  ///  /// The  whose mask expression is to be retrieved. ///  ///  /// The mask expression as an instance of , or  if no mask has been applied to . ///  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; } } 

Per eseguire, lo script richiede una class scritta da Aaron C, spiegata qui: Silverlight / WPF imposta l’ellisse con il colore esadecimale mostrato qui: http://www.wiredprairie.us/blog/index.php/archives/659

Il codice è sotto nel caso in cui il sito web sia mai stato spostato:

 public static class Extensions { public static void SetFromHex(this Color c, string hex) { Color c1 = ToColorFromHex(hex); cA = c1.A; cR = c1.R; cG = c1.G; cB = 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]); } } 
 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); } } } 

Un’altra ansible soluzione consiste nell’utilizzare una delle implementazioni wpf di “Masked TextBox” utilizzando la class “MaskedTextProvider” utilizzata da “MaskedTextBox” da Winforms.

Qui sono disponibili due soluzioni potenziali -> https://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox e qui -> http://marlongrech.wordpress.com/2007/10/28/masked-textbox/