none
Textboxeingabe mit StringFormat formatieren RRS feed

  • Frage

  • Hallo alle,

    ich habe mir eine WPF- Form aufgebaut mit verschiedenen Labeln und Textboxen. In den Textboxen soll der User eine Zahl (double) eingeben können ohne Komma, die dann entsprechend formatiert wird. Bsp: User gibt ein: "5759", Textboxt macht daraus "5,759" alternativ auch "5.759".

    Ich habe gefühlt 5 Millionen Webseiten durch, die alle das sagen, was ich auch implementiert habe:

    <TextBox Margin="180,52,0,0" Name="txbMP001" HorizontalAlignment="Left" Width="80" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"  Height="23.04" VerticalAlignment="Top"
                        Text="{Binding Path=Value, Mode=TwoWay, StringFormat={}{0:0.000}}" />

    Das Dumme ist nur: es funzt nicht. "5759" bleibt "5759". ("Binding Path=Double", hatte ich auch schon)

    Hat noch jemand einen entscheidenden Hinweis/ alternative Lösung?

    Grüße

      Heiko

    Freitag, 8. August 2014 11:43

Antworten

  • Hi Heiko,

    wenn es nur um die Darstellung geht, ist das mit dem Format schon richtig. Du musst halt nur wissen ob du Komma oder den Punkt möchtest und dann die passende Sprache wählen.

    Wenn du die zahl geteilt durch 1000 machen solltest du es im VM oder im Modell  machen.

    MFG

    Björn

    • Als Antwort markiert Heiko Sturm Montag, 11. August 2014 11:18
    Freitag, 8. August 2014 14:11
  • Hallo Heiko,

    schon geschrieben, müsstest Du es in der Eigenschaft erledigen.

    Ein möglicher Ansatz (bezogen auf mein XAML Stück):

            private double _doubleValue;
            public double DoubleValue
            {
                get { return _doubleValue; }
                set
                {
                    if (value != _doubleValue)
                    {
                        if (Math.Truncate(value) == value)
                            value /= 1000.0;
    
                        this._doubleValue = value;
                        OnPropertyChanged("DoubleValue");
                    }
                }
            }
    

    dabei würde der Wert durch 1000 geteilt, wenn es keinen Nachkomma-Anteil gibt; ansonsten bleibt der Wert so wie er eingegeben wurde.

    Gruß Elmar

    • Als Antwort markiert Heiko Sturm Montag, 11. August 2014 11:18
    Freitag, 8. August 2014 15:44

Alle Antworten

  • Hi Heiko,

    sieht schon richtig aus nur das der Punkt im allgemeiner für die Dezimalzahlen und nicht für die Tausender stelle steht. Schau dir hier mal die Formatierung für die Tausender stelle an und stell die Sprache des auf Deutsch. Das sollte eigentlich schon Reichen.

    MFG

    Björn

    Freitag, 8. August 2014 12:27
  • Hallo Heiko,

    zunächst handelt sich dabei um eine reine Formatierung nach den Regeln von .NET, siehe numerische Zahlenformatierung.

    Dabei kann eine Gruppierung (Tausender) vorgenommen werden, in dem man ein Komma (",") als Symbol einfügt. Nicht leisten kann sie eine automatische Teilung durch 1000, um daraus Nachkommastellen anzuzeigen. Wenn Du eine Zahl wie 1234 (oder 5759) eingibst, handelt sich dabei um eine ganze Zahl ohne Nachkommastellen. Willst Du so etwas haben, so müsste dies durch die Eigenschaft implementiert werden.

    Zudem gilt: Damit die deutschen Dezimalzeichen bei der Eingabe berücksichtigt werden, muss die Sprache entweder über das xml:lang Attribut auf deutsch "de-DE" gesetzt werden - denn voreingestellt ist zunächst "en-US", z. B.:

            <TextBox xml:lang="de-DE" Name="DoubleValueTextBox" 
                     HorizontalAlignment="Right" VerticalAlignment="Bottom"
                     HorizontalContentAlignment="Right"  VerticalContentAlignment="Center"  
                     Width="80" Height="23" Margin="4,4"
                     Text="{Binding Path=DoubleValue, Mode=TwoWay, StringFormat={}{0:#\,0.000}}" />
    

    wobei man die Einstellung üblicherweise eher auf Formularebene festlegen würde.

    Alternativ kann man die Framework.LanguageProperty Eigenschaft global festlegen, siehe

    WPF Bindings and CurrentCulture Formatting.

    Gruß Elmar

    Freitag, 8. August 2014 12:36
  • Hallo Björn, hallo Elmar,

    die Teilung durch Tausend wird wohl das Problem sein. Gemeint hatte ich aus 1234 1(Komma)234 zu machen, nicht 1(Tausend)234. Sorry, schlecht erklärt.

    Also müsste ich doch auf eine Stück Code Behind zurückgreifen?

    Grüße

      Heiko

    Freitag, 8. August 2014 12:53
  • Hi Heiko,

    wenn es nur um die Darstellung geht, ist das mit dem Format schon richtig. Du musst halt nur wissen ob du Komma oder den Punkt möchtest und dann die passende Sprache wählen.

    Wenn du die zahl geteilt durch 1000 machen solltest du es im VM oder im Modell  machen.

    MFG

    Björn

    • Als Antwort markiert Heiko Sturm Montag, 11. August 2014 11:18
    Freitag, 8. August 2014 14:11
  • Hallo Heiko,

    schon geschrieben, müsstest Du es in der Eigenschaft erledigen.

    Ein möglicher Ansatz (bezogen auf mein XAML Stück):

            private double _doubleValue;
            public double DoubleValue
            {
                get { return _doubleValue; }
                set
                {
                    if (value != _doubleValue)
                    {
                        if (Math.Truncate(value) == value)
                            value /= 1000.0;
    
                        this._doubleValue = value;
                        OnPropertyChanged("DoubleValue");
                    }
                }
            }
    

    dabei würde der Wert durch 1000 geteilt, wenn es keinen Nachkomma-Anteil gibt; ansonsten bleibt der Wert so wie er eingegeben wurde.

    Gruß Elmar

    • Als Antwort markiert Heiko Sturm Montag, 11. August 2014 11:18
    Freitag, 8. August 2014 15:44
  • Hallo Björn, hallo Elmar,

    danke, hab's hingekriegt.

    Für diejenigen, die mal ein ähnliches Problem haben: Siehe auch:

    http://msdn.microsoft.com/en-us/library/aa970451%28v=vs.110%29.aspx

    und

    http://msdn.microsoft.com/en-us/library/ms743695.aspx

    Grüße

      Heiko

    Montag, 11. August 2014 11:21
  • Hallo alle,

    tja, zu früh gefreut.

    Ich habe mir jetzt eine Klasse erstellt, die einen PropertyChangedEventHandler enthält, und meine 1000er Teilungsmethode dort implementiert. Meine XAML habe ich so angepasst:

    <Window.Resources>
            <local:ErrorsToMessageConverter x:Key="eToMConverter" />
    	<local:clsProperty x:Key="Props"/>		
        </Window.Resources>

    (clsProperty ist die Klasse, die die Tausenderteilung enthält.) Und meine Textbox habe ich wie folgt angebunden:

    <TextBox Margin="180,52,0,0" Name="txbMP001" HorizontalAlignment="Left" Width="80" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"  Height="23.04" VerticalAlignment="Top"
    	Text="{Binding Source={StaticResource Props}, Path=SetzeDreiNachkommastellen}">
    </TextBox>

    ("SetzeDreiNachkommastellen" ist die Methode zum Teilen durch 1.000 wie von Elmar beschrieben)

    Mit obigem Textbox- Statement habe ich alle Textboxen beschreiben, und siehe da: alle Textboxen enthalten den gleichen Wert, was natürlich nicht Sinn der Sache ist.

    Ich vermute den Fehler in der "Path"- Angabe. Nur, wie richtig machen?

    Grüße

      Heiko

    Montag, 11. August 2014 14:31
  • Hallo Heiko,

    Du brauchst für jedes Steuerelement eine eigene Eigenschaft, denn die speichert dabei den Wert, der angezeigt wird. Also erweitere Deine Klasse um zwei weitere Eigenschaften und verweise mit Path darauf.

    Im übrigen würde ich die Bezeichnungen von Eigenschaften und Steuerelementen in etwa deckungsgleich halten, damit man weiß, was woher kommt und wohin geht.

    Ich hatte es mir nebenbei einfacher gemacht und für das Fenster INotifyPropertyChanged implementiert und es als DataContext verwendet.

    Gruß Elmar


    • Bearbeitet Elmar Boye Montag, 11. August 2014 15:27
    Montag, 11. August 2014 15:26
  • Hallo Elmar,

    ich hab DataContext eingebaut:

    <GroupBox Header="ZSB Betätigung" Margin="18,137,0,58" Name="grpBetätigung" HorizontalAlignment="Left" Width="300" GotFocus="grpBetätigung_GotFocus">
      <GroupBox.Resources>
        <local:clsBetätigung x:Key="Betätigung"/>				
      </GroupBox.Resources>
      <GroupBox.DataContext>
        <Binding Source="{StaticResource Betätigung}"/>
      </GroupBox.DataContext>

    Ein Textbox habe ich testweise angebunden:

    <TextBox Margin="180,52,0,0" Name="txbMP001" HorizontalAlignment="Left" Width="80"
    HorizontalContentAlignment="Right" VerticalContentAlignment="Center"  Height="23.04" VerticalAlignment="Top"	
    Text="{Binding FormatTxbMp001}">

    Hinter "FormatTxbMp001" steht eine get/ set Routine, die ein evtl. fehlendes Komma einfügt (siehe oben).

    Da es hierbei nun um die Erfassung von Messwerten geht, brauche ich natürlich eine Validierung. Aber: Bei der o.g. Definition von Textbox kann ich keine ValidationRule einfügen, und wenn ich eine ValidationRule einfüge:

    <TextBox Margin="180,52,0,0" Name="txbMP001" HorizontalAlignment="Left" Width="80" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"  Height="23.04" VerticalAlignment="Top">
      <TextBox.Text>
        <Binding Path="FormatTxbMp001" Mode="TwoWay" Source="{StaticResource Betätigung}" UpdateSourceTrigger="LostFocus">
          <Binding.ValidationRules>
    	<local:MesswertValidationRule MP="001"/>
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>

    dann funktioniert zwar die Validierung, aber die Formatierung nicht mehr.

    Grüße

      Heiko

    Dienstag, 12. August 2014 14:00
  • Hallo Heiko,

    zum eienen fehlt oben die StringFormat Angabe.

    Ansonsten: Wie sieht Deine Validierung aus?

    Da sich der Wert der Eingabe verändern kann - anfangs ganzahlig (> 1000) und später mit Nachkommastellen -, muss dies von der Validierung berücksichtigt werden.

    Gru8 Elmar

    Dienstag, 12. August 2014 16:27
  • Guten Morgen Elmar,

    die StringFormat- Angabe ist dem Umstand, dass keine Teilung durch 10/ 100/ 1000 möglich ist, zum Opfer gefallen. (Es geht um die Eingabe von Messwerten die 5,3 oder 5,36 oder 5,364 sein können)

    Die Kommasetzung/ "Teilung" mache ich jetzt so:

    namespace EolDL800
    {
        public class clsBetätigung : IDisposable
        {
    	DataSet _SollwerteBetätigung = new DataSet();
    	DataTable _SollwerteBetätigungTabelle = new DataTable();
    	string _mp = "";
    	private string _formatTxbMp001;
    	private string _formatTxbMp002;
    	public event PropertyChangedEventHandler PropertyChanged;
        //
        //Sonstiger Code
        //
        public string FormatTxbMp001
        {
            get { return _formatTxbMp001; }
    	set
    	{
    	   value = value.Replace(",", "");
    	   _formatTxbMp001 = value.Substring(0, 1) + "," + value.Substring(1);
    	  //Falls ein Wert mit Punkt statt Komma eingebenen wurde, den Punkt zum Komma machen
    	  _formatTxbMp001.Replace(".", ",");
    	  OnPropertyChanged("FormatTxbMp001");
    	}
        }
        public string FormatTxbMp002
        {
            get { return _formatTxbMp002; }
            set
            {
               value = value.Replace(",", "");
    	   _formatTxbMp002 = value.Substring(0, 1) + "," + value.Substring(1);
    	   //Falls ein Wert mit Punkt statt Komma eingebenen wurde, den Punkt zum Komma machen
    	   _formatTxbMp002.Replace(".", ",");
    	   OnPropertyChanged("FormatTxbMp002");
    	}
        }
    
        //Konstruktor
        public clsBetätigung()
        {
        }
    
        //
        //Sonstiger Code
        //
        protected void OnPropertyChanged(string name)
        {
    	PropertyChangedEventHandler handler = PropertyChanged;
    	if (handler != null)
    	{
                handler(this, new PropertyChangedEventArgs(name));
    	}
        }
    }

    Die Validierung:

    namespace EolDL800
    {
        //http://pholpar.wordpress.com/2010/11/05/wpf-validation-without-real-data-binding/
        public class InputValidationRule:ValidationRule
        {
            public InputValidationRule()
    	{
    	}
            //ValidationRule für die Messwerte
    	public class MesswertValidationRule : ValidationRule
    	{
    		private double _istwert;
    		private string _mp;
    
    		public double Istwert
    		{
    			get { return _istwert; }
    			set { _istwert = Convert.ToSingle(value); }
    		}
    		public string MP
    		{
    			get { return _mp;}
    			set { _mp = value; }
    		}
    
    		public MesswertValidationRule() { }
    
    		public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    		{
    			//Die entsprechenden Properties aus der "ZSB" zugewiesenen Klasse holen
    //@Elmar: Ich arbeite mit zwei unterschiedlichen Produkten in einer Form, für die Messwerte erfasst werden sollen. 
    //Um nicht doppelte Programmierarbeit zu haben, weise ich bei Groupbox_GotFocus, dem object "ZSB" die entsprechende Klasse zu.
    			PropertyInfo _MP = Window1.ZSB.GetType().GetProperty("MP");
    			PropertyInfo _MpWerte = Window1.ZSB.GetType().GetProperty("MpWerte");
    			DataRow mpWerte;
    
    			//value in ein double umwandeln
    			try
    			{
    				_istwert=Convert.ToSingle(value);
    			}
    			catch (InvalidCastException e)
    			{
    				return new ValidationResult(false,null);
    			}
                            //Sollwerte aus DataSet (clsBetätigung) holen
    			_MP.SetValue(Window1.ZSB, _mp, null);
    			mpWerte = (DataRow)_MpWerte.GetValue(Window1.ZSB, null);
    
    			//Werte prüfen
    			if ((_istwert > (double)mpWerte["soll"] + (double)mpWerte["otg"]) ||
    					(_istwert < (double)mpWerte["soll"] + (double)mpWerte["utg"]))
    				return new ValidationResult(false,null);
    			else return new ValidationResult(true,null);
    		}
    	}

    Aus der XAML:

    <Window x:Class="EolDL800.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:EolDL800"   
        Title="EOL DL800" Height="495" Width="683" Name="wndEOL">
        <Window.Resources>
            <local:ErrorsToMessageConverter x:Key="eToMConverter" />
    		<local:clsProperty x:Key="Props"/>
    		<local:clsBetätigung x:Key="Betätigung"/>	
        </Window.Resources>
        <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="450">
            <GroupBox Header="ZSB Betätigung" Margin="18,137,0,58" Name="grpBetätigung" HorizontalAlignment="Left" Width="300" GotFocus="grpBetätigung_GotFocus">
    			<!--<GroupBox.Resources>
    				<local:clsBetätigung x:Key="Betätigung"/>				
    			</GroupBox.Resources>-->
    			<GroupBox.DataContext>
    				<Binding Source="{StaticResource Betätigung}"/>
    			</GroupBox.DataContext>
                <Grid Height="248">                
    				<TextBox Margin="180,52,0,0" Name="txbMP001" HorizontalAlignment="Left" Width="80" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"  Height="23.04" VerticalAlignment="Top"
    					Text="{Binding FormatTxbMp001}">
    					<!--<TextBox.Text>
    						<Binding Path="FormatTxbMp001" Mode="TwoWay" Source="{StaticResource Betätigung}" UpdateSourceTrigger="LostFocus">
    							<Binding.ValidationRules>
    								<local:MesswertValidationRule MP="001"/>
    							</Binding.ValidationRules>
    						</Binding>
    					</TextBox.Text>-->
    				</TextBox>
    
    <!-- Rest weggelassen -->

    So, wie die Textbox oben beschreiben ist, wird "set" in "clsBetätigung.FormatTxbMp001" auch aufgerufen, und die Eingabe formatiert.

    Würde ich den jetzt auskommentierten Teil aktivieren, wird zwar beim Initialisieren der Form "get" aufgerufen, aber nach Eingabe eines Wertes "set" nicht mehr. Die Validierung funktioniert jedoch einwandfrei.

    Damit die Validierung für mich gültige Ergebnisse liefert brauche ich das Komma (sprich aus "1234" muss "1,234" werden).

    Das ist halt im Moment mein Problem. Jedes für sich (Formatierung/ Validierung) funzt, im Zusammenspiel nicht mehr

    Grüße

      Heiko

    Mittwoch, 13. August 2014 06:19