Thursday, November 13, 2014

Numeric text box

 Note: This is not my original work. Have blogged it here just for my future reference and of course if someone wants to use it.

This post shows how we can create a custom control for our application to allow a common behavior for all textboxes which allow only numeric values. heres the code and the comments make it pretty self explanatory.

public class NumericTextBox :TextBox
   {
       #region Properties
       /// <summary>
       /// Gets or sets the character to be used as decimal separator
       /// </summary>
       public string DecimalSeparator { get; set; }

       /// <summary>
       /// Gets or sets the mask to apply to the textbox
       /// </summary>
       public Boolean IsDecimalAllowed
       {
           get { return (Boolean)GetValue(IsDecimalAllowedProperty); }
           set { SetValue(IsDecimalAllowedProperty, value); }
       }

       /// <summary>
       /// Dependency property to store the decimal is allowed to be entered in the textbox
       /// </summary>
       public static readonly DependencyProperty IsDecimalAllowedProperty =
           DependencyProperty.Register("IsDecimalAllowed", typeof(Boolean), typeof(NumericTextBox), new PropertyMetadata(false));


       /// <summary>
       /// Gets or sets the mask to apply to the textbox
       /// </summary>
       public int Scale
       {
           get { return (int)GetValue(ScaleProperty); }
           set { SetValue(ScaleProperty, value); }
       }

       /// <summary>
       /// Dependency property to store the decimal is allowed to be entered in the textbox
       /// </summary>
       public static readonly DependencyProperty ScaleProperty =
           DependencyProperty.Register("Scale", typeof(int), typeof(NumericTextBox), new PropertyMetadata(0));

       #endregion

       /// <summary>
        /// Static Constructor
        /// </summary>
       static NumericTextBox()
        {   
        }

       /// <summary>
        /// To check the character enetered
        /// </summary>
       protected override void OnPreviewTextInput(TextCompositionEventArgs e)
       {
           e.Handled = !AreAllValidNumericChars(e.Text);
           if (!e.Handled)
           {
               e.Handled = !MaxLengthReached(e);
           }
           base.OnPreviewTextInput(e);
       }

        /// <summary>
        ///To check if numbers entered are all valid numeric numbers
        /// </summary>
        bool AreAllValidNumericChars(string str)
       {
           if (string.IsNullOrEmpty(DecimalSeparator))
               DecimalSeparator = ".";

           bool ret = true;
           if (str == System.Globalization.NumberFormatInfo.CurrentInfo.NegativeSign |
               str == System.Globalization.NumberFormatInfo.CurrentInfo.PositiveSign)
               return ret;
           if (IsDecimalAllowed && str == DecimalSeparator)
               return ret;

           int l = str.Length;
           for (int i = 0; i < l; i++)
           {
               char ch = str[i];
               ret &= Char.IsDigit(ch);
           }
          
           return ret;
       }

       /// <summary>
        /// This method was added to prevent arithmetic overflows while saving in db on decimal part.
        /// </summary>
       bool MaxLengthReached(TextCompositionEventArgs e)
       {
           TextBox textBox = (TextBox)e.OriginalSource;
           int precision = textBox.MaxLength - Scale - 2;

           string textToValidate = textBox.Text.Insert(textBox.CaretIndex, e.Text).Replace("-","");
           string[] numericValues = textToValidate.Split(Convert.ToChar(DecimalSeparator));

           if ((numericValues.Length <= 2) && (numericValues[0].Length <= precision) && ((numericValues.Length == 1) || (numericValues[1].Length <= Scale)))
           {
               return true;
           }
           else
           {
               return false;
           }
       }
    }

To use it in XAML is simple like any other default WPF control
 <local:NumericTextBox Width="300" Text="{Binding MyBinding, Mode=TwoWay, UpdateSourceTrigger=LostFocus,   NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, ConverterCulture='en-US', StringFormat='F2'}" IsDecimalAllowed="True"
                       MaxLength="20" Scale="2" IsEnabled="false"  />

No comments:

Post a Comment