Laden...

In Textbox nur Zahlen zulassen

Erstellt von jojo2100 vor 14 Jahren Letzter Beitrag vor 10 Jahren 53.794 Views
J
jojo2100 Themenstarter:in
49 Beiträge seit 2009
vor 14 Jahren
In Textbox nur Zahlen zulassen

Hallo,

wie kann ich in einer WPF Anwendung in einer Textbox nur Zahlen zulassen?

In einer Windows Forms Anwendung habe ich das so gelöst:


            public static void onlynum_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
            {
                if (e.KeyChar != '\b' && e.KeyChar != ',')
                    //Copy & paste zulassen
                    if(Char.IsControl((e.KeyChar)))
                    {
                    }
                    //Nur Nummern zulassen
                    else if (!Char.IsDigit(e.KeyChar))
                    {
                        e.Handled = true;
                    }
            }

3.430 Beiträge seit 2007
vor 14 Jahren

Hallo und willkommen bei myCSharp.de,

du musst auf das PreviewTextInput-Event reagieren und dann checken ob es eine Zahl ist.
Siehe dazu:
http://dotnetus.spaces.live.com/Blog/cns!4E39ECD492E4EEC1!550.entry

Gruss
Michael

J
jojo2100 Themenstarter:in
49 Beiträge seit 2009
vor 14 Jahren

Danke.

Funktioniert so.
Aber die Leerzeichen werden noch eingegeben.
Wie kann ich die Leerzeichen entfernen?

821 Beiträge seit 2009
vor 14 Jahren

ähhhh, lass mich überlergen.....
.....
.....
Nach stundenlanger Überlegung bin ich zu der genialen Idee gekommen, dass du den Code, den dir michlG gepostet hat, wohl noch dahingehend erweitern musst, dass er zusätzlich prüft, ob du wohl ein Leerzeichen eingegeben hast.

J
jojo2100 Themenstarter:in
49 Beiträge seit 2009
vor 14 Jahren

Nach Stundenlangen versuchungen hab ich es nicht auf die Reihe bekommen. 🙂

Selbst wenn ich e.Handled immer auf true setze werden Leerzeichen trotzdem noch akzeptiert!?

U
1.578 Beiträge seit 2009
vor 14 Jahren

ich hatte das vor langer zeit schon selber gebastelt:

IntegerTextBox


using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Ast.CustomControls
{
	public class IntegerTextBox : TextBox
	{
		public IntegerTextBox()
		{
			CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, null, CanPasteCommand));
		}

		public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(
											"MinValue",
											typeof(int?),
											typeof(IntegerTextBox));
		public int? MinValue
		{
			get { return (int?)GetValue(MinValueProperty); }
			set { SetValue(MinValueProperty, value); }
		}

		public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(
											"MaxValue",
											typeof(int?),
											typeof(IntegerTextBox));
		public int? MaxValue
		{
			get { return (int?)GetValue(MaxValueProperty); }
			set { SetValue(MaxValueProperty, value); }
		}

		private bool Parse(TextBox box, string insertText)
		{
			string input = box.Text;
			input = input.Remove(box.SelectionStart, box.SelectionLength);
			input = input.Insert(box.SelectionStart, insertText);
			if ((input.Equals("-", StringComparison.Ordinal)) &&
				(MinValue == null || MinValue < 0))
				return true;
			else
			{
				int value = 0;
				if (int.TryParse(input, System.Globalization.NumberStyles.Integer, CultureInfo.CurrentUICulture, out value) &&
					IsValidRange(value))
					return true;
				else
					return false;
			}
		}

		private void CanPasteCommand(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = false;
			if (Clipboard.ContainsText())
			{
				if (Parse(sender as TextBox, Clipboard.GetText()))
					e.CanExecute = true;
			}
			e.Handled = true;
		}

		protected override void OnPreviewTextInput(TextCompositionEventArgs e)
		{
			if (Parse(this, e.Text))
				base.OnPreviewTextInput(e);
			else
				e.Handled = true;
		}

		private bool IsValidRange(int value)
		{
			if ((MinValue == null || value >= MinValue) &&
				(MaxValue == null || value <= MaxValue))
				return true;
			return false;
		}
	}
}

DoubleTextBox


using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Ast.CustomControls
{
	public class DoubleTextBox : TextBox
	{
		public DoubleTextBox()
		{
			CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, null, CanPasteCommand));
		}

		public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(
											"MinValue",
											typeof(double?),
											typeof(IntegerTextBox));
		public double? MinValue
		{
			get { return (double?)GetValue(MinValueProperty); }
			set { SetValue(MinValueProperty, value); }
		}

		public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(
											"MaxValue",
											typeof(double?),
											typeof(IntegerTextBox));
		public double? MaxValue
		{
			get { return (double?)GetValue(MaxValueProperty); }
			set { SetValue(MaxValueProperty, value); }
		}

		private bool Parse(TextBox box, string insertText)
		{
			string input = box.Text;
			input = input.Remove(box.SelectionStart, box.SelectionLength);
			input = input.Insert(box.SelectionStart, insertText);
			if ((input.Equals("-", StringComparison.Ordinal)) &&
				(MinValue == null || MinValue < 0))
				return true;
			else
			{
				double value = 0;
				if (double.TryParse(input, System.Globalization.NumberStyles.Float, CultureInfo.CurrentUICulture, out value) &&
					IsValidRange(value))
					return true;
				else
					return false;
			}
		}

		private void CanPasteCommand(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = false;
			if (Clipboard.ContainsText())
			{
				if (Parse(sender as TextBox, Clipboard.GetText()))
					e.CanExecute = true;
			}
			e.Handled = true;
		}

		protected override void OnPreviewTextInput(TextCompositionEventArgs e)
		{
			if (Parse(this, e.Text))
				base.OnPreviewTextInput(e);
			else
				e.Handled = true;
		}

		private bool IsValidRange(double value)
		{
			if ((MinValue == null || value >= MinValue) &&
				(MaxValue == null || value <= MaxValue))
				return true;
			return false;
		}
	}
}

es gibt derzeit nur unstimmigkeiten wenn man text in der xaml direkt eingibt - es werden nur user eingaben abgefangen
auch wenn man eine minimum eingibt und eine zahl durch markieren loescht, dann kann man ausser paste die zahl nicht mehr eintippen - aber die probleme sind recht klein - ich persoenlich setz das minimum meist auf 0 usw - dann passiert nix
meine erste implementation hatte auch ein "Value" property - da war aber staendig das problem es mit "Text" in sync zu halten
wie dem auch sei
seit ich diese box habe, kann ich problemlos double.Parse und int.Parse ohne Try verwenden da ich weiss das nichts falsches rein kommen kann

16.834 Beiträge seit 2008
vor 14 Jahren

Das Problem beim PreviewTextInput ist, dass die Leertaste keinen Event abfeuert.
Daher kannst Du das so einfach nicht über den Event abfangen. Hab' derzeit die gleiche Hürde.

Ich seh jetzt nicht wie Mr. Evil das Problem löst.

U
1.578 Beiträge seit 2009
vor 14 Jahren

das leertaste problem besteht auch - grad probiert
ist mir persoenlich noch nie aufgefallen #gg

U
1.578 Beiträge seit 2009
vor 14 Jahren

TextBox's PreviewTextInput event does not trigger on spaces input

eine moeglichkeit waere beim parsen eines normalen textes die leerzeichen zu entfernen - bei integer oder double textbox sind leerzeichen eh humbug

(string.Trim)

16.834 Beiträge seit 2008
vor 14 Jahren

Ich hab das Problem auf diesem Wege gelöst:


private void OnTextBox_Name_TextChanged( object sender, TextChangedEventArgs a_textArgs)
{
	TextBox a_textBox = ( TextBox )sender;

	if ( !Regex.IsMatch( a_textBox.Text, "^[a-zA-Z0-9-]+$" ) )
	{
		int a_charToRemove =  ( (TextBox) ( ( ( RoutedEventArgs )( a_textArgs ) ).OriginalSource ) ).CaretIndex;
		a_textBox.Text = a_textBox.Text.Remove( a_charToRemove -1 , 1 );
		a_textBox.SelectionStart = a_textBox.Text.Length;
		a_textArgs.Handled = true;
	}
}

TextChanged wird IMMER aufgerufen - auch bei Sonder- und Leerzeichen.
Ich überprüfe zwar nicht auf Zahlen, sondern auf bestimmte Zeichen; das Prinzip ist aber das selbe.

Sobald der Text meine Vorgabe nicht entspricht wird durch den "CaretIndex" das Zeichen ermittelt, das sich geändert hat. Es muss also das Zeichen sein, das ungültig ist - schließlich war die Eingabe davor noch in Ordnung.
Dieses Zeichen entferne ich mit Remove() und setze das SelectionSymbol an das Ende der Eingabe - theoretisch kann man es auch an den CaretIndex setzen.

Was im Moment nicht abgefangen wird ist das Einfügen von Text!

😃

16.834 Beiträge seit 2008
vor 14 Jahren

Nun fängt es auch Mehrfangeingaben durch nen Paste ab:

        private void OnTextBox_Name_TextChanged( object sender, TextChangedEventArgs a_textArgs )
        {
            TextBox a_textBox = ( TextBox )sender;
            string a_newText = string.Empty;

            for ( int i = 0; i < a_textBox.Text.Length; i++ )
            {
                if ( Regex.IsMatch( a_textBox.Text[ i ].ToString( ), "^[a-zA-Z0-9-]+$" ) )
                {
                    a_newText += a_textBox.Text[ i ];
                }
            }

            a_textBox.Text = a_newText;
            a_textBox.SelectionStart = a_textBox.Text.Length;
        }

Es geht bei jeder Eingabe jedes Zeichen durch.
Wenn ich das Zeichen einzeln herausziehe ist die Laufzeit höher, obwohl nur ein Zeichen überprüft wird.

Liegt wohl am Herausziehen durch den CaretIndex - wieso auch immer.

446 Beiträge seit 2004
vor 14 Jahren

Hallo,

normalerweise bindet man eine textbox auf eine Eigenschaft. Dies würde ich mir an dieser Stelle zu nutzen machen.

Ich hätte das so gemacht

<TextBox Text="{Binding Path=Prop,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>

Und im Propertie prüfen, welchen value man erhält:


private string _Prop = String.Empty;
        public string Prop
        {
            get
            {
                return _Prop;
            }
            set
            {
                //Mittels Regex  / oder sonst einer prüfungsroutine prüfen ob wir nur zahlen haben
                string inputString = value as string;

                int inputNumber;
                if (false == int.TryParse(value, out inputNumber))
                {
                    _Prop = string.Empty;
                }
                else
                {
                    _Prop = value;
                }
            }

Mittels Regex wär das schöner, angenommen der Benutzer gibt adg456asdf45.

Dann könnte man die vorhandenen Zahlen in die Propertie schreiben und die Buchstaben lässt man weg.

Mit dieser Variante benötigt man kein Event.
Vlt. sollte man an dieser Stelle auch noch IDataErrorInfo und ValidationsRules erwähnen, auch wenn sie das Thema nur bedingt streifen.

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

U
1.578 Beiträge seit 2009
vor 14 Jahren

deine idee ist im prinzip kein eigenes control zu schreiben sondern das im property der viewmodel zu machen

nur wenn man so eine box sehr oft braucht muss man kopieren oder die methode sharen - da ist ein custom control besser

ich denke da an das beispiel von abt
man koennte mit seiner implementation die leerzeichen killen und dies dann in meine Integer oder DoubleTextBox packen - dann duerfte es vollstaendig sein #gg

R
1 Beiträge seit 2013
vor 10 Jahren

Ich hab mir eine sehr einfache Lösung ausgedacht :


        private void Textbox1_TextChanged(object sender, EventArgs e)
        {
            try
            {
                UInt16 Variable = Convert.ToUInt16(Textbox1.Text);
            }
            catch (FormatException)
            {
                Textbox1.Clear();
            }
        }

Beschreibung:

Das Programm versucht bei änderung der Eingabe den Text von String in UInt16 (kann auch Double usw. sein) zu konvertieren. Da Buchstaben (auch Leerzeichen) bei der Konvertierung eine FormatException auslösen, wird diese hier abgefangen und der Text in der Box wird gelöscht. In dem Catch Codeblock könnte auch eine Messagebox geöffnet werden oder sonst was.

Viel Spass damit 🙂

J
jojo2100 Themenstarter:in
49 Beiträge seit 2009
vor 10 Jahren

@robinator: Erstmal Danke. Die Exceptionlösung finde ich jedoch nicht so toll.

5.658 Beiträge seit 2006
vor 10 Jahren

Die Exceptionlösung finde ich jedoch nicht so toll.

Da stimme ich zu. Exceptions sollten nicht genutzt werden, um den Programmfluß zu steuern. Besser wäre hier die Verwendung der UInt16.TryParse-Methode.

Christian

Weeks of programming can save you hours of planning

T
314 Beiträge seit 2013
vor 10 Jahren

Man kann dies auch über ein Behavior lösen.

Basierend auf Stackoverflow einfach wie bereits hier im Thread erwähnt das PreviewEvent durch TextChanged austauschen und entsprechend behandeln.

Aussehen kann das TextChanged dann z.B. so:


void AssociatedObject_TextChanged(object sender, TextChangedEventArgs e)
{
    bool exceedsMaxLength = false;

    var change = e.Changes.FirstOrDefault() as TextChange;

    if (MaxLength > 0)
    {
        exceedsMaxLength = this.AssociatedObject.Text.Length > MaxLength;
    }

    if (!System.Text.RegularExpressions.Regex.IsMatch(this.AssociatedObject.Text, RegularExpression) || exceedsMaxLength)
    {
        var c = this.AssociatedObject.CaretIndex;
        this.AssociatedObject.Text = this.AssociatedObject.Text.Remove(change.Offset, change.AddedLength);
        this.AssociatedObject.CaretIndex =  c -1;
    }
}

Gruß
t0ms3n