Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF TextBox to enter decimal values

Tags:

Is there any decent way to get a WPF control which is bound to a decimal value?

When I just bind the TextBox or DataGridTextColumn to a decimal, data entry is a problem.

<TextBox Text="{Binding MyDecimal, UpdateSourceTrigger=PropertyChanged,      ValidatesOnDataErrors=True}"/> 

When I try to enter "0,5" in this TextBox I'll get "5" as a result. It is nearly impossible to enter "0,5" at all (apart from entering 1,5 and replacing the "1" with a "0").

When I use StringFormat, data entry is only slightly improved:

<TextBox Text="{Binding MyDecimal, StringFormat=F1, UpdateSourceTrigger=PropertyChanged,     ValidatesOnDataErrors=True}"/> 

Now, when I try to enter "0,5" I'll end up with "0,5,0", which still is wrong but at least I can remove the trailing ",0" without much difficulty.

Still, entering decimal types using WPF is very awkward, because these TextBoxes are very prone to data entry errors, which is a real pain especially for values!

So what am I supposed to use for decimal data entry in WPF? Or does Microsoft not support decimal data??

like image 560
Sam Avatar asked Jun 04 '13 09:06

Sam


2 Answers

I currently use this behavior for digital and decimal input:

public class TextBoxInputBehavior : Behavior<TextBox> {     const NumberStyles validNumberStyles = NumberStyles.AllowDecimalPoint |                                                NumberStyles.AllowThousands |                                                NumberStyles.AllowLeadingSign;     public TextBoxInputBehavior()     {         this.InputMode = TextBoxInputMode.None;         this.JustPositivDecimalInput = false;     }      public TextBoxInputMode InputMode { get; set; }       public static readonly DependencyProperty JustPositivDecimalInputProperty =      DependencyProperty.Register("JustPositivDecimalInput", typeof(bool),      typeof(TextBoxInputBehavior), new FrameworkPropertyMetadata(false));      public bool JustPositivDecimalInput     {         get { return (bool)GetValue(JustPositivDecimalInputProperty); }         set { SetValue(JustPositivDecimalInputProperty, value); }     }      protected override void OnAttached()     {         base.OnAttached();         AssociatedObject.PreviewTextInput += AssociatedObjectPreviewTextInput;         AssociatedObject.PreviewKeyDown += AssociatedObjectPreviewKeyDown;          DataObject.AddPastingHandler(AssociatedObject, Pasting);      }      protected override void OnDetaching()     {         base.OnDetaching();         AssociatedObject.PreviewTextInput -= AssociatedObjectPreviewTextInput;         AssociatedObject.PreviewKeyDown -= AssociatedObjectPreviewKeyDown;          DataObject.RemovePastingHandler(AssociatedObject, Pasting);     }      private void Pasting(object sender, DataObjectPastingEventArgs e)     {         if (e.DataObject.GetDataPresent(typeof(string)))         {             var pastedText = (string)e.DataObject.GetData(typeof(string));              if (!this.IsValidInput(this.GetText(pastedText)))             {                 System.Media.SystemSounds.Beep.Play();                 e.CancelCommand();             }         }         else         {             System.Media.SystemSounds.Beep.Play();             e.CancelCommand();         }      }       private void AssociatedObjectPreviewKeyDown(object sender, KeyEventArgs e)      {         if (e.Key == Key.Space)         {             if (!this.IsValidInput(this.GetText(" ")))             {                 System.Media.SystemSounds.Beep.Play();                 e.Handled = true;             }         }      }       private void AssociatedObjectPreviewTextInput(object sender, TextCompositionEventArgs e)      {         if (!this.IsValidInput(this.GetText(e.Text)))         {             System.Media.SystemSounds.Beep.Play();             e.Handled = true;         }      }       private string GetText(string input)      {         var txt = this.AssociatedObject;          int selectionStart = txt.SelectionStart;         if (txt.Text.Length < selectionStart)              selectionStart = txt.Text.Length;          int selectionLength = txt.SelectionLength;         if (txt.Text.Length < selectionStart + selectionLength)              selectionLength = txt.Text.Length - selectionStart;          var realtext = txt.Text.Remove(selectionStart, selectionLength);          int caretIndex = txt.CaretIndex;         if (realtext.Length < caretIndex)              caretIndex = realtext.Length;          var newtext = realtext.Insert(caretIndex, input);          return newtext;      }       private bool IsValidInput(string input)      {         switch (InputMode)         {             case TextBoxInputMode.None:                 return true;             case TextBoxInputMode.DigitInput:                 return CheckIsDigit(input);              case TextBoxInputMode.DecimalInput:                 decimal d;                 //wen mehr als ein Komma                 if (input.ToCharArray().Where(x => x == ',').Count() > 1)                     return false;                   if (input.Contains("-"))                 {                      if (this.JustPositivDecimalInput)                          return false;                        if (input.IndexOf("-",StringComparison.Ordinal) > 0)                            return false;                        if(input.ToCharArray().Count(x=>x=='-') > 1)                           return false;                          //minus einmal am anfang zulässig                        if (input.Length == 1)                             return true;                     }                      var result = decimal.TryParse(input, validNumberStyles, CultureInfo.CurrentCulture, out d);                     return result;                default: throw new ArgumentException("Unknown TextBoxInputMode");          }         return true;      }       private bool CheckIsDigit(string wert)      {         return wert.ToCharArray().All(Char.IsDigit);      } }   public enum TextBoxInputMode  {   None,   DecimalInput,   DigitInput   } 

The XAML usage looks like this:

<TextBox Text="{Binding Sum}">     <i:Interaction.Behaviors>         <Behaviors:TextBoxInputBehavior InputMode="DecimalInput"/>     </i:Interaction.Behaviors> </TextBox> 
like image 77
blindmeis Avatar answered Oct 13 '22 02:10

blindmeis


    private void DecimalTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)     {         bool approvedDecimalPoint = false;          if (e.Text == ".")         {             if (!((TextBox)sender).Text.Contains("."))                 approvedDecimalPoint = true;         }          if (!(char.IsDigit(e.Text, e.Text.Length - 1) || approvedDecimalPoint))             e.Handled = true;     } 
like image 24
CMarsden Avatar answered Oct 13 '22 02:10

CMarsden