How to restrict numeric values entered into an Entry field.

DaveBoggustDaveBoggust USMember, University ✭✭

I need to restrict the value being typed into an Entry field to values between 0 and 20. However, I don't want highlight it in red if it's outside that range or anything, I want to actually prevent the value from appearing in the field if the user types 30, for example (the 3 would appear since it's valid but if the user then types a 0 it should not be accepted). I have successfully used behaviors to validate things like email addresses and date formats and was wondering if I can create a behavior to do this numeric validation and if so how I would go about it? Should I interrogate the entry using the TextChanged event or something?

Best Answer

Answers

  • GeraldVersluisGeraldVersluis NLUniversity ✭✭✭✭
    edited January 2016

    For this I have created a Behavior once.

    It looks like this;

    #region members
    
            public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create("MaxLength", typeof(int), typeof(MaxLengthValidator), 0);
    
            #endregion
    
            #region properties
    
            public int MaxLength
            {
                get { return (int)GetValue(MaxLengthProperty); }
                set { SetValue(MaxLengthProperty, value); }
            }
    
            #endregion
    
            #region methods
    
            protected override void OnAttachedTo(Entry bindable)
            {
                bindable.TextChanged += bindable_TextChanged;
            }
    
            private void bindable_TextChanged(object sender, TextChangedEventArgs e)
            {
                if (e.NewTextValue.Length > 0 && e.NewTextValue.Length > MaxLength)
                    ((Entry)sender).Text = e.NewTextValue.Substring(0, MaxLength);
            }
    
            protected override void OnDetachingFrom(Entry bindable)
            {
                bindable.TextChanged -= bindable_TextChanged;
    
            }
    
            #endregion
    

    Another way is to use a trigger.
    I've once made a trigger which checks if the input is numeric and the length has to be a maximum of 9.

    public class BsnValidationTriggerAction : TriggerAction<Entry>
        {
            private string _prevValue = string.Empty;
    
            protected override void Invoke(Entry entry)
            {
                int n;
                var isNumeric = int.TryParse(entry.Text, out n);
    
                if (!string.IsNullOrWhiteSpace(entry.Text) && (entry.Text.Length > 9 || !isNumeric))
                {
                    entry.Text = _prevValue;
                    return;
                }
    
                _prevValue = entry.Text;
            }
        }
    
  • DaveBoggustDaveBoggust USMember, University ✭✭

    Thanks for the reply @GeraldVersluis . I may not have worded my question very well but it's not the length of the input value that is the issue but I need to restrict the numeric value of the input to the range 0 to 20 (inclusive). So 19 is valid, 20 is valid, but 21 is not valid and yet they are all the same length. So although your behavior isn't quite what I need, I may be able to amend it to check the value entered and if it's > 20, rather than substring the value in the sender field, I could probably set it to 20 so as to "clamp it" within the required range (whatever value is entered that is > 20). Does that make sense?

  • DaveBoggustDaveBoggust USMember, University ✭✭

    Ahh, I missed the edit! I think the trigger could be the answer. I haven't used them before as my Xamarin (Forms) experienced is limited at the moment but I will give it a go. Thanks again.

  • EBatikEBatik NLMember ✭✭✭

    @GeraldVersluis said:
    Another way is to use a trigger.
    I've once made a trigger which checks if the input is numeric and the length has to be a maximum of 9.

    public class BsnValidationTriggerAction : TriggerAction<Entry>
        {
            private string _prevValue = string.Empty;
    
            protected override void Invoke(Entry entry)
            {
                int n;
                var isNumeric = int.TryParse(entry.Text, out n);
    
                if (!string.IsNullOrWhiteSpace(entry.Text) && (entry.Text.Length > 9 || !isNumeric))
                {
                    entry.Text = _prevValue;
                    return;
                }
    
                _prevValue = entry.Text;
            }
        }
    

    @GeraldVersluis Could you please share xaml for your trigger ? I cant manage to make it work

  • MelbourneDeveloperMelbourneDeveloper AUMember ✭✭✭

    The problem with all the answers floating around is that they do not use standard native underlying functioanlity. Each platform should have their own way of handling numeric input, and we should be using those, not a hacked Entry. But, from doing a quick search, neither Android, nor UWP have standard controls for doing this. the more fundamental question is: if you were building a native Android, iOS, or UWP app, what control would you use to force validated numeric entry?

  • EduardCampoEduardCampo Member

    @GeraldVersluis Could you please share xaml for your trigger ? I cant manage to make it work

    Declare at the top of your XAML the namespace where the trigger.cs is. In my case:

    xmlns:helpers="clr-namespace:TestProject.Main.Helpers"
    

    And then set the trigger on your entry like this:

    <Entry Keyboard="Numeric">
        <Entry.Triggers>
            <EventTrigger Event="TextChanged">
                <helpers:BsnValidationTriggerAction />
            </EventTrigger>
        </Entry.Triggers>
    </Entry>
    
    

    I know I'm late but this might help someone else down the road.

  • Damien_DoumerDamien_Doumer Member ✭✭
    edited August 2018

    @DaveBoggust I was facing this too, but the best way to implement this is through behaviors, Here is an article which shows you in detail how to implement this and even more, like limiting the value of the number entered to a given value you set in your XAML.

  • eliblack1eliblack1 Member ✭✭

    I took a stab at implementing an integer-only Entry and found two approaches that work okay:

    Via TextChanged
    Bind to the Entry's TextChanged event and then reset the .Text value if the new value is invalid.

    i.e. (In the view):

        private void IntegerEntry_TextChanged(object sender, TextChangedEventArgs e) {
            var entry = (Entry)sender;
    
            if (!String.IsNullOrEmpty(e.NewTextValue) && !Int32.TryParse(e.NewTextValue, out int parsedInt)) {
                entry.Text = e.OldTextValue;
            }
        }
    

    Via ViewModel

    i.e. (In the view model):

    private int? _integerValue;
    
    public string IntegerValue {
        get => _integerValue?.ToString();
        set {
            if (int.TryParse(value, out int parsedInt)) {
                _integerValue = parsedInt;
            }
    
            NotifyPropertyChanged(nameof(IntegerValue));
        }
    }
    

    Both of these approaches work, but they're not perfect: If you starting typing a word in the integer field, you can see each letter flash on the screen before it's removed from the Entry. For example, if you start typing "apple" in the integer Entry field, you'll see the letters "a", "p", "p", "l", and "e" briefly flash in the Entry before they're removed :\

    I think a better approach might be to override OnKeyPress and OnPaste events for the Entry, but I'm not sure if Xamarin supports those events or not.

    As @MelbourneDeveloper said, it looks like there aren't even native controls for numerical input, so trying to come up with a good cross-platform solution for this would probably be difficult.

Sign In or Register to comment.