Ich suche nach Ziffern und Dezimalzeichen, aber ohne Vorzeichen.
Ich habe mir Beispiele mit dem NumericUpDown-Steuerelement für Windows Forms angesehen und dieses Beispiel eines benutzerdefinierten NumericUpDown-Steuerelements von Microsoft . Bisher scheint es jedoch so, als ob NumericUpDown (von WPF unterstützt oder nicht) nicht die Funktionalität bietet, die ich möchte. So wie meine Anwendung gestaltet ist, wird sich niemand im klaren Verstand mit den Pfeilen herumschlagen wollen. Sie ergeben im Zusammenhang mit meiner Bewerbung keinen praktischen Sinn.
Ich suche also nach einer einfachen Möglichkeit, eine Standard-WPF-TextBox nur die Zeichen zu akzeptieren, die ich möchte. Ist das möglich? Ist das praktisch?
Fügen Sie ein Vorschau-Texteingabeereignis hinzu. Wie so: <TextBox PreviewTextInput="PreviewTextInput" />
.
Dann drin den e.Handled
setzen, wenn der Text nicht erlaubt ist. e.Handled = !IsTextAllowed(e.Text);
Ich verwende einen einfachen regulären Ausdruck in der IsTextAllowed
-Methode, um zu sehen, ob ich zulassen soll, was sie eingegeben haben. In meinem Fall möchte ich nur Zahlen, Punkte und Bindestriche zulassen.
private static readonly Regex _regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
private static bool IsTextAllowed(string text)
{
return !_regex.IsMatch(text);
}
Wenn Sie das Einfügen falscher Daten verhindern möchten, schließen Sie das DataObject.Pasting
-Ereignis DataObject.Pasting="TextBoxPasting"
wie gezeigt an hier (Code-Auszug):
// 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();
}
}
Der Ereignishandler zeigt eine Vorschau der Texteingabe an. Hier stimmt ein regulärer Ausdruck nur dann mit der Texteingabe überein, wenn es sich nicht um eine Zahl handelt, und wird dann nicht in das Eingabefeld eingefügt.
Wenn Sie nur Buchstaben wünschen, ersetzen Sie den regulären Ausdruck als [^a-zA-Z]
.
<TextBox Name="NumberTextBox" PreviewTextInput="NumberValidationTextBox"/>
using System.Text.RegularExpressions;
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
Ich habe einiges von dem verwendet, was bereits hier war, und habe meine eigene Wendung mit einem Verhalten verwendet, damit ich diesen Code nicht durch eine Menge von Views verbreiten muss ...
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;
}
}
}
Hier ist der entsprechende Ansichtscode:
<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>
Dies ist eine verbesserte Lösung der Antwort von WilP s. Meine Verbesserungen sind:
EmptyValue
-Eigenschaft hinzugefügt, wenn eine leere Zeichenfolge nicht geeignet ist/// <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] --------------------------------
}
Die Verwendung ist ziemlich unkompliziert:
<i:Interaction.Behaviors>
<behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>
Fügen Sie eine Validierungsregel hinzu, damit bei Änderungen des Textes überprüft werden kann, ob die Daten numerisch sind. Wenn dies der Fall ist, kann die Verarbeitung fortgesetzt werden. Ist dies nicht der Fall, wird der Benutzer aufgefordert, nur numerische Daten in diesem Feld zu akzeptieren.
Weitere Informationen finden Sie in Validierung in Windows Presentation Foundation
Hier ist eine sehr einfache und einfache Möglichkeit, dies mit MVVM zu tun.
Binden Sie Ihre textBox mit einer Ganzzahl-Eigenschaft im Ansichtsmodell. Dies funktioniert wie ein Edelstein. Die Validierung wird sogar angezeigt, wenn eine Nicht-Ganzzahl in das Textfeld eingegeben wird.
XAML-Code:
<TextBox x:Name="contactNoTxtBox" Text="{Binding contactNo}" />
Modellcode anzeigen:
private long _contactNo;
public long contactNo
{
get { return _contactNo; }
set
{
if (value == _contactNo)
return;
_contactNo = value;
OnPropertyChanged();
}
}
Könnte auch einfach eine Validierungsregel implementieren und auf die TextBox anwenden:
<TextBox>
<TextBox.Text>
<Binding Path="OnyDigitInput" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<conv:OnlyDigitsValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
Mit der Implementierung der Regel wie folgt (unter Verwendung derselben Regex wie in anderen Antworten vorgeschlagen):
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;
}
}
Hier habe ich eine einfache Lösung, die von Rays Antwort inspiriert ist. Dies sollte ausreichen, um irgendeine Form von Nummer zu identifizieren.
Diese Lösung kann auch leicht geändert werden, wenn Sie nur positive Zahlen, ganzzahlige Werte oder Werte mit einer maximalen Anzahl von Dezimalstellen usw. wünschen.
Wie in Rays Antwort vorgeschlagen, müssen Sie zuerst ein PreviewTextInput
-Ereignis hinzufügen:
<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>
Dann füge folgendes in den Code hinter:
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);
}
Ich habe numerische Tastaturnummern und Rücktaste erlaubt:
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);
}
Ich gehe davon aus, dass:
Für Ihre TextBox, für die Sie nur numerische Eingaben zulassen möchten, ist die Text-Eigenschaft anfangs auf einen gültigen Zahlenwert (z. B. 2.7172) gesetzt.
Ihre Textbox ist ein Kind Ihres Hauptfensters
Ihr Hauptfenster hat die Klasse Window1
Ihr TextBox-Name lautet numericTB
Die Grundidee:
Fügen Sie: private string previousText;
zu Ihrer Hauptfensterklasse hinzu (Window1)
Fügen Sie dem Hauptfenster-Konstruktor: previousText = numericTB.Text;
hinzu
Erstellen Sie einen Handler für das numericTB.TextChanged -Ereignis, das ungefähr so aussieht:
private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
{
double num = 0;
bool success = double.TryParse(((TextBox)sender).Text, out num);
if (success & num >= 0)
previousText = ((TextBox)sender).Text;
else
((TextBox)sender).Text = previousText;
}
Damit setzen Sie previousText auf numericTB.Text, solange es gültig ist, und setzen Sie numericTB.Text auf den letzten gültigen Wert, wenn der Benutzer etwas schreibt, das Ihnen nicht gefällt. Natürlich ist dies nur eine Grundidee und es ist nur "idiotensicher", nicht "idiotensicher". Es behandelt nicht den Fall, in dem der Benutzer beispielsweise mit Leerzeichen durcheinander kommt. Hier ist also eine Komplettlösung, die ich für "idiotensicher" halte, und wenn ich falsch liege, dann sag mir bitte
Inhalt Ihrer Window1.xaml-Datei:
<Window x:Class="IdiotProofNumericTextBox.Window1"
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="30" Width="100" Name="numericTB" TextChanged="numericTB_TextChanged"/>
</Grid>
</Window>
Inhalt Ihrer Window.xaml.cs-Datei:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace IdiotProofNumericTextBox
{
public partial class Window1 : Window
{
private string previousText;
public Window1()
{
InitializeComponent();
previousText = numericTB.Text;
}
private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrEmpty(((TextBox)sender).Text))
previousText = "";
else
{
double num = 0;
bool success = double.TryParse(((TextBox)sender).Text, out num);
if (success & num >= 0)
{
((TextBox)sender).Text.Trim();
previousText = ((TextBox)sender).Text;
}
else
{
((TextBox)sender).Text = previousText;
((TextBox)sender).SelectionStart = ((TextBox)sender).Text.Length;
}
}
}
}
}
Und das ist es. Wenn Sie über viele TextBoxen verfügen, empfehle ich, ein CustomControl zu erstellen, das von TextBox erbt, sodass Sie previousText und numericTB_TextChanged in einer separaten Datei einpacken können.
Wenn Sie nicht viel Code schreiben möchten, um eine grundlegende Funktion auszuführen (ich weiß nicht, warum Leute lange Methoden machen), können Sie dies einfach tun:
Namespace hinzufügen:
using System.Text.RegularExpressions;
Legen Sie in XAML eine TextChanged-Eigenschaft fest:
<TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
Fügen Sie in WPF unter der Methode txt1_TextChanged den Code Regex.Replace
hinzu:
private void txt1_TextChanged(object sender, TextChangedEventArgs e)
{
txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", "");
}
Dies ist der einzige Code, der benötigt wird:
void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = new Regex("[^0-9]+").IsMatch(e.Text);
}
Dadurch können nur Zahlen in das Textfeld eingegeben werden.
Um einen Dezimalpunkt oder ein Minuszeichen zuzulassen, können Sie den regulären Ausdruck in [^0-9.-]+
ändern.
PreviewTextInput += (s, e) =>
{
e.Handled = !e.Text.All(char.IsDigit);
};
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;
im Preview-Keydown-Ereignis des Textfelds.
Wir können eine Validierung für ein geändertes Ereignis in einem Textfeld durchführen. Die folgende Implementierung verhindert eine andere Tastendruckeingabe als eine numerische und eine Dezimalstelle.
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;
}
Ein anderer Ansatz ist die Verwendung eines angefügten Verhaltens. Ich habe meine benutzerdefinierte TextBoxHelper -Klasse implementiert, die in Textboxen in meinem gesamten Projekt verwendet werden kann. Ich habe mir gedacht, dass das Abonnieren der Ereignisse für jedes Textfeld und für jede einzelne XAML-Datei zu diesem Zweck sehr zeitaufwändig sein kann.
Die von mir implementierte TextBoxHelper-Klasse verfügt über folgende Funktionen:
Hier ist die Implementierung der TextBoxHelper-Klasse:
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;
}
}
}
}
Und hier ist ein Beispiel für die einfache Verwendung:
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double"
viewHelpers:TextBoxHelper.DefaultValue="1"/>
Oder
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural"
viewHelpers:TextBoxHelper.DefaultValue="3"
viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>
Hinweis dass sich mein TextBoxHelper im Alias viewHelpers xmlns befindet.
Ich hoffe, dass diese Implementierung die Arbeit eines anderen erleichtert :)
In Windows Forms war das einfach; Sie können ein Ereignis für KeyPress hinzufügen und alles funktioniert problemlos. In WPF ist dieses Ereignis jedoch nicht vorhanden. Es gibt jedoch einen viel einfacheren Weg dafür.
Die WPF-TextBox hat das TextChanged-Ereignis, das für alles allgemein gilt. Dazu gehört das Einfügen, Tippen und alles, was Ihnen in den Sinn kommen kann.
So kannst du so etwas machen:
XAML:
<TextBox name="txtBox1" ... TextChanged="TextBox_TextChanged"/>
CODE HINTER:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
string s = Regex.Replace(((TextBox)sender).Text, @"[^\d.]", "");
((TextBox)sender).Text = s;
}
Dies akzeptiert auch .
. Wenn Sie dies nicht möchten, entfernen Sie es einfach aus der regex
-Anweisung, um @[^\d]
zu sein.
Hinweis : Dieses Ereignis kann für viele TextBoxen verwendet werden, da es den Text des sender
-Objekts verwendet. Sie schreiben das Ereignis nur einmal und können es für mehrere TextBoxen verwenden.
Wenn Sie nach einer schnellen und sehr einfachen Implementierung für diese Art von Problem suchen, die nur Ganzzahlen und Dezimalzahlen verwendet, fügen Sie in Ihrer XAML-Datei eine PreviewTextInput
-Eigenschaft zu Ihrer TextBox
hinzu und verwenden Sie dann in Ihrer xaml.cs-Datei Folgendes:
private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !char.IsDigit(e.Text.Last()) && !e.Text.Last() == '.';
}
Es ist gewissermaßen überflüssig, die gesamte Zeichenfolge jedes Mal zu überprüfen, es sei denn, Sie haben, wie bereits erwähnt, etwas mit wissenschaftlicher Notation gemacht (wenn Sie jedoch bestimmte Zeichen wie 'e' hinzufügen, ist dies eine einfache Regex, die Symbole/Zeichen hinzufügt wirklich einfach und in anderen Antworten dargestellt). Für einfache Gleitkommawerte reicht diese Lösung aus.
Als One-Liner mit Lambda-Ausdruck geschrieben:
private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) => e.Handled = !char.IsDigit(e.Text.Last() && !e.Text.Last() == '.');
Jetzt weiß ich, dass diese Frage eine akzeptierte Antwort hat, aber ich persönlich finde sie etwas verwirrend und glaube, dass es einfacher sein sollte. Also werde ich versuchen zu demonstrieren, wie ich es so gut wie möglich zum Laufen gebracht habe:
In Windows Forms gibt es ein Ereignis namens KeyPress
, das für diese Art von Aufgabe perfekt geeignet ist. Da dies in [~ # ~] wpf [~ # ~] nicht vorhanden ist, verwenden wir stattdessen PreviewTextInput
Veranstaltung. Ich glaube auch, dass man zur Validierung ein foreach
verwenden kann, um durch das textbox.Text
und überprüfe, ob es mit der Bedingung übereinstimmt ;), aber ehrlich gesagt ist dies, wofür reguläre Ausdrücke sind.
Noch etwas, bevor wir uns mit dem heiligen Code befassen. Damit das Ereignis ausgelöst wird, kann man zwei Dinge tun:
<PreviewTextInput="textBox_PreviewTextInput/>
Loaded
des Formulars (in dem sich die Textbox befindet): textBox.PreviewTextInput += onlyNumeric;
Ich denke, die zweite Methode ist besser, weil Sie in solchen Situationen meistens dieselbe Bedingung (regex
) auf mehr als eine TextBox
und Sie tun es nicht) anwenden müssen. Ich will mich nicht wiederholen! .
Zum Schluss sehen Sie, wie Sie es machen:
private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$";
Regex regex = new Regex(onlyNumeric);
e.Handled = !regex.IsMatch(e.Text);
}
Wenn Sie einen Zahlenwert überprüfen, können Sie die Funktion VisualBasic.IsNumeric verwenden.
Hier ist eine Bibliothek für die numerische Eingabe in WPF
Es hat Eigenschaften wie NumberStyles
und RegexPattern
für die Validierung.
Unterklassen WPF TextBox
Ich arbeitete mit einer ungebundenen Box für ein einfaches Projekt, an dem ich arbeitete, und konnte daher den Standardbindungsansatz nicht verwenden. Daher habe ich einen einfachen Hack erstellt, der für andere sehr praktisch sein könnte, wenn das vorhandene TextBox-Steuerelement erweitert wird:
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;
}
}
}
}
Für einen Floating-Typ möchten Sie ihn natürlich als Float und so weiter analysieren. Es gelten die gleichen Prinzipien.
Dann müssen Sie in der XAML-Datei den entsprechenden Namespace angeben:
<UserControl x:Class="MyApplication.UserControls.UnParameterisedControl"
[ Snip ]
xmlns:interfaceSupport="clr-namespace:MyApplication.InterfaceSupport"
>
Danach können Sie es als reguläre Kontrolle verwenden:
<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >
Nachdem ich einige der Lösungen einige Zeit hier verwendet hatte, entwickelte ich meine eigene, die für mein MVVM-Setup gut geeignet ist. Beachten Sie, dass es nicht so dynamisch ist wie einige der anderen, in dem Sinne, dass Benutzer immer noch fehlerhafte Zeichen eingeben können. Sie werden jedoch daran gehindert, die Taste zu drücken und somit nichts zu tun. Dies passt gut zu meinem Thema, dass Schaltflächen ausgegraut sind, wenn keine Aktionen ausgeführt werden können.
Ich habe eine TextBox
, dass ein Benutzer eine Anzahl von Dokumentseiten eingeben muss, die gedruckt werden sollen:
<TextBox Text="{Binding NumberPagesToPrint, UpdateSourceTrigger=PropertyChanged}"/>
... mit dieser Bindungseigenschaft:
private string _numberPagesToPrint;
public string NumberPagesToPrint
{
get { return _numberPagesToPrint; }
set
{
if (_numberPagesToPrint == value)
{
return;
}
_numberPagesToPrint = value;
OnPropertyChanged("NumberPagesToPrint");
}
}
Ich habe auch einen Button:
<Button Template="{DynamicResource CustomButton_Flat}" Content="Set"
Command="{Binding SetNumberPagesCommand}"/>
... mit dieser Befehlsbindung:
private RelayCommand _setNumberPagesCommand;
public ICommand SetNumberPagesCommand
{
get
{
if (_setNumberPagesCommand == null)
{
int num;
_setNumberPagesCommand = new RelayCommand(param => SetNumberOfPages(),
() => Int32.TryParse(NumberPagesToPrint, out num));
}
return _setNumberPagesCommand;
}
}
Und dann gibt es noch die Methode von SetNumberOfPages()
, die für dieses Thema jedoch unwichtig ist. In meinem Fall funktioniert es gut, da ich der Code-Behind-Datei der View keinen Code hinzufügen muss und ich das Verhalten mithilfe der Command
-Eigenschaft steuern kann.
In der WPF-Anwendung können Sie dies behandeln, indem Sie das Ereignis TextChanged
behandeln:
void arsDigitTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
bool handle = regex.IsMatch(this.Text);
if (handle)
{
StringBuilder dd = new StringBuilder();
int i = -1;
int cursor = -1;
foreach (char item in this.Text)
{
i++;
if (char.IsDigit(item))
dd.Append(item);
else if(cursor == -1)
cursor = i;
}
this.Text = dd.ToString();
if (i == -1)
this.SelectionStart = this.Text.Length;
else
this.SelectionStart = cursor;
}
}
Hier ist meine Version davon. Sie basiert auf einer ValidatingTextBox
-Basisklasse, die nur das macht, was getan wurde, wenn es nicht "gültig" ist. Es unterstützt Einfügen, Ausschneiden, Löschen, Rücktaste, +, - usw.
Für eine 32-Bit-Ganzzahl gibt es eine Int32TextBox-Klasse, die nur mit einem Int verglichen wird. Ich habe auch Gleitkomma-Validierungsklassen hinzugefügt.
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);
}
Hinweis 1: Wenn Sie die WPF-Bindung verwenden, müssen Sie sicherstellen, dass Sie die Klasse verwenden, die zum gebundenen Eigenschaftstyp passt. Andernfalls kann dies zu merkwürdigen Ergebnissen führen.
Hinweis 2: Wenn Sie Gleitkommaklassen mit WPF-Bindung verwenden, stellen Sie sicher, dass die Bindung die aktuelle Kultur verwendet, um der TryParse-Methode zu entsprechen, die ich verwendet habe.
Wie wäre es damit? Funktioniert gut für mich. Hoffe, ich habe keine Edge-Fälle verpasst ...
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();
}
});
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);
}
Benutzen:
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
Folgendes würde ich verwenden, um ein WPF-Textfeld zu erhalten, das Ziffern und den Dezimalpunkt akzeptiert:
class numericTextBox : TextBox
{
protected override void OnKeyDown(KeyEventArgs e)
{
bool b = false;
switch (e.Key)
{
case Key.Back: b = true; break;
case Key.D0: b = true; break;
case Key.D1: b = true; break;
case Key.D2: b = true; break;
case Key.D3: b = true; break;
case Key.D4: b = true; break;
case Key.D5: b = true; break;
case Key.D6: b = true; break;
case Key.D7: b = true; break;
case Key.D8: b = true; break;
case Key.D9: b = true; break;
case Key.OemPeriod: b = true; break;
}
if (b == false)
{
e.Handled = true;
}
base.OnKeyDown(e);
}
}
Fügen Sie den Code in eine neue Klassendatei ein
using System.Windows.Controls;
using System.Windows.Input;
am Anfang der Datei und erstellen Sie die Lösung. Das numericTextBox-Steuerelement wird dann oben in der Toolbox angezeigt.