locked
Numeric validation in TextBox improvement RRS feed

  • Question

  • Hi all, I am trying to implement a logic to validate the user input and the expectation is

    1. value range is from 0 - 100

    2. 2 decimal places

    3. Do not import extra dll reference (system.windows.interactivity and other).

    4. Do not automatically convert the value to minimum / maximum value when it was out of range.

    The code is working but I would like to seek for improvement or better handling. My current implementation still have flaws such as when user input 44.44 then backspace the dot, the value will be 4444 and it already over range. 

    MainWindow.xaml.cs

    using System;
    using System.Globalization;
    using System.Linq;
    using System.Text.RegularExpressions;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    
    namespace ValidateDecimal
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            private bool isTextHas3Digit;
            private bool isTextHas2Decimal;
            private TextBox textBox;
            private readonly string currentNumberDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    
            public MainWindow()
            {
                InitializeComponent();
                DataContext = new MainWindowViewModel();
            }
    
            private void TestSample_Loaded(object sender, RoutedEventArgs e)
            {
                SetComboBoxMaxLength(sender, "TestSample");
            }
    
            private void SetComboBoxMaxLength(object sender, string name)
            {
                ComboBox comboBox = sender as ComboBox;
    
                if (comboBox == null || !comboBox.Name.Equals(name))
                {
                    return;
                }
    
                comboBox.HorizontalContentAlignment = HorizontalAlignment.Right;
                textBox = comboBox.Template.FindName("PART_EditableTextBox", comboBox) as TextBox;
                DataObject.AddPastingHandler(textBox, OnPaste);
                textBox.HorizontalContentAlignment = HorizontalAlignment.Right;
    
                if (textBox == null)
                {
                    return;
                }
    
                textBox.MaxLength = 6;
                textBox.PreviewTextInput += TextBox_PreviewTextInput;
                textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
                textBox.TextChanged += TestPatternTextBox_TextChanged;
            }
    
            private void OnPaste(object sender, DataObjectPastingEventArgs e)
            {
                if (e.DataObject.GetDataPresent(typeof(string)))
                {
                    string text = e.DataObject.GetData(typeof(string)) as string;
                    decimal enteredTextResult;
    
                    if (string.IsNullOrEmpty(text) || text.Trim().Length < 1 || !decimal.TryParse(text, NumberStyles.Float, CultureInfo.CurrentCulture, out enteredTextResult))
                    {
                        e.CancelCommand();
                    }
                }
            }
    
            private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
            {
                //only allow 1 separator
                if (textBox.Text.Length >= 1 && e.Text.Equals(currentNumberDecimalSeparator))
                {
                    bool isSeparatorExists = textBox.Text.Any(x => x == currentNumberDecimalSeparator[0]);
    
                    if (isSeparatorExists)
                    {
                        e.Handled = true;
                        return;
                    }
                }
    
                if (isTextHas3Digit)
                {
                    e.Handled = true;
                    return;
                }
    
                if (!isTextHas2Decimal)
                {
                    //check is input 0 to 9 and valid separator
                    e.Handled = !IsValidInputCharacter(e.Text);
                }
                else
                {
                    //if keyboard pointer is before separator, then handle front part
                    if (textBox.SelectionStart <= textBox.Text.IndexOf(currentNumberDecimalSeparator, StringComparison.Ordinal))
                    {
                        e.Handled = !IsValidInputCharacter(e.Text);
                        return;
                    }
    
                    //since text has reached max decimal point and if character have highlighted, then handle tail part
                    if (textBox.SelectedText.Length > 0)
                    {
                        e.Handled = !IsValidInputCharacter(e.Text);
                    }
                    else
                    {
                        e.Handled = true;
                    }
                }
            }
    
            private bool IsValidInputCharacter(string input)
            {
                Regex regex = new Regex("^[0-9+]");
                return regex.IsMatch(input) || input.Equals(currentNumberDecimalSeparator);
            }
    
            private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Space)
                {
                    e.Handled = true;
                }
            }
    
            private void TestPatternTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                string[] splitedText = textBox.Text.Split(currentNumberDecimalSeparator[0]);
                string tail = string.Empty;
                string head = string.Empty;
    
                if (splitedText.Length > 1)
                {
                    tail = splitedText[1];
                }
                else
                {
                    head = splitedText[0];
                }
    
                if (tail.Length >= 2)
                {
                    isTextHas2Decimal = true;
                    //e.Handled = textBox.SelectionStart <= textBox.Text.IndexOf(currentNumberDecimalSeparator, StringComparison.Ordinal) != true;
                }
                else
                {
                    isTextHas2Decimal = false;
                }
    
                if (head.Length >= 3)
                {
                    isTextHas3Digit = true;
                    //e.Handled = textBox.SelectionStart <= textBox.Text.IndexOf(currentNumberDecimalSeparator, StringComparison.Ordinal) != true;
                }
                else
                {
                    isTextHas3Digit = false;
                }
    
                e.Handled = true;
            }
        }
    }
    

    MainWindowViewModel.cs

    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Input;
    
    namespace ValidateDecimal
    {
        public class MainWindowViewModel : BaseViewModel
        {
            private string userInput;
            public string UserInput
            {
                get
                {
                    return userInput;
                }
                set
                {
                    userInput = value;
                    OnPropertyChanged("UserInput");
                }
            }
    
            private ICommand submitCommand;
            public ICommand SubmitCommand => submitCommand ?? (submitCommand = new RelayCommand(ExecuteSubmitCommand));
    
            public IReadOnlyCollection<string> Samples
            {
                get
                {
                    return new List<string>
                    {
                        "25", "50", "100"
                    };
                }
            }
    
            private void ExecuteSubmitCommand(object obj)
            {
                var convertedUserInput = Convert.ToDecimal(userInput);
    
                if (convertedUserInput < 0 || convertedUserInput > 100)
                {
                    MessageBox.Show("Enter value out of range.");
                }
            }
        }
    }


    Download Project

    Thursday, September 15, 2016 2:15 PM

Answers

  • WPF has a framework for validation.  You are using winforms type code to do something that WPF already provides.

    https://msdn.microsoft.com/en-us/library/ms753962%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

    The above link shows how to define a validation and use the result in an error template.  While the validation does not do what you want you already have some code to use as validation code.


    Lloyd Sheen

    • Proposed as answer by DotNet Wang Saturday, September 24, 2016 1:35 AM
    • Marked as answer by DotNet Wang Wednesday, September 28, 2016 2:22 AM
    Thursday, September 15, 2016 3:18 PM