Forum Xamarin.Forms

Xamarin Forms Validation. Aggregate the result from multiple control behaviors

Hi,
I have a page which has 3-4 entry controls. I've created a behavior to validate these entries. I've also extended the entry class to have borders, border colour and border radius. So the Xaml is somethin like this

<controls:ExtendedEntry x:Name="emailaddressEntry" Grid.Row="0" Grid.Column="1" BorderColor="Black" HasBorder="True" IsEnabled="True" Placeholder="Email Address" Text="{Binding EmailAddress}"> <controls:ExtendedEntry.Behaviors> <v:EntryValidatorBehavior x:Name="EmailAddressValidator" IsCheckEmail="True" IsCheckEmpty="True" /> </controls:ExtendedEntry.Behaviors> </controls:ExtendedEntry>

Now the EntryValidator Behavior has an IsValid property which tells us if the control is in a valid state or not. What I want to do is enable or disable my submit button based on the IsValid of all the entries. Something like this

<Button Command="{Binding OnCreateCommand}" Text="Submit Account Application" BindingContext="{x:Reference EmailAddressValidator,PasswordValidator,AddressValidator}" IsEnabled="{Binding Path=EmailAddressValidator.IsValid && PasswordValidator.IsValid && AddressValidator.IsValid}"/>

Best Answer

Answers

  • EminSevincEminSevinc TRMember

    Hi,

    I'm having the same problem too.

  • madhav.shenoy83madhav.shenoy83 AUMember ✭✭

    @EminSevinc Why flag it as abuse? I asked this question in September and found the solution on my own a few days ago?

  • Umar3xUmar3x FRMember ✭✭✭

    Well,

    I guess that you did not find a easier solution ?
    I want to do the exact same thing but without custom colors and so for entries.
    I've got a form, with a phoneNumber entry, and password/passwordConfirmation entries.

    I created three behaviors, one for the phoneNumber, one for password format and another one for password confirmation.

    What I just want is to bind isEnabledProperty of the submit button on a global isValid of all my behaviors.

    An answer was given to you on SOF : http://stackoverflow.com/questions/39801629/xamarin-forms-validation-aggregate-the-result-from-multiple-control-behaviors

    But I don't understand exactly this part of the given answer :

    "Bind your ExtendedEntry IsValid properties to booleans in your PageModel, i.e. IsEmailValid. And then in your PageModel a boolean IsFormValid to which your bind your button like this: ."

    What I don't see, is how the IsFormValid is binded to all the valid properties of all behaviors.

    Thank you.

  • madhav.shenoy83madhav.shenoy83 AUMember ✭✭

    What I wanted to create was a generic architecture all my viewmodels could use. ValidateForm is written in the base viewmodel class and AddValidations is a virtual method which gets overridden in each ViewModel if needed.

    Coming back to your problem, I dont know how to bind multiple properties via Xaml. But I do know how to do it via C#
    You should bind the IsValid property of your behavior with a property in the Viewmodel which has implemented INotifyPropertyChanged, so foreach entry you will have an isvalid property, like IsPhoneNumberValid IsPasswordValid and so on.

    Now create a property called IsFormValid. And in the setter of IsFormValid return
    IsPhonumberValid & IsPasswordValid & IsEmailValid etc. This way IsFormValid will be true when all of your properties are true. Hope that helps.

  • Umar3xUmar3x FRMember ✭✭✭
    edited March 2017

    Hello mshenoyOcean.

    So what you mean is to create as many properties in my view model as my number of behavior with valid property.

    And finally I have to create a IsFormValid property on my viewModel which will return its value which is an aggregation of other properties of the viewModel ? But should I create a IsFormValid property in my viewModel only ? I can't get my _isFormValid in my vm being updated when others properties become true. It is sucessfully set to false when page is pushed, and when I dump my binded properties in the view model (which are binded to the IsValidProperty of each behavior) it changes well. But how to make isFormValid setter called when one of the other properties has changed

    Thanks :)

  • Umar3xUmar3x FRMember ✭✭✭
    edited March 2017

    This is my viewModel :

    public class RegisterPublicPageViewModel : BaseViewModel
        {
    
            private bool _isPhoneNumberValid;
            private bool _isPasswordValid;
            private bool _isFormValid;
    
            /// <summary>
            ///
            /// </summary>
            public bool IsPhoneNumberValid
            {
                get { return _isPhoneNumberValid; }
                set
                {
                    if (_isPhoneNumberValid == value) return;
                    _isPhoneNumberValid = value;
                    OnPropertyChanged("IsPhoneNumberValid");
                }
            }
    
            public bool IsPasswordValid
            {
                get { return _isPasswordValid; }
                set
                {
                    if (_isPasswordValid == value) return;
                    _isPasswordValid = value;
                    OnPropertyChanged("IsPasswordValid");
                }
            }
    
            public bool IsFormValid
            {
                get { return _isFormValid; }
                set
                {
                    if(_isFormValid == value) return;
                    _isFormValid = IsPasswordValid && IsPhoneNumberValid;
                    OnPropertyChanged("IsFormValid");
                }
            }
    

    And I got this on my contentPage :

     _okButton.SetBinding(IsEnabledProperty, "IsFormValid");
                phoneBehavior.SetBinding(PhoneNumberValidatorBehavior.IsValidProperty, "IsPhoneNumberValid");
                passwordBehavior.SetBinding(PasswordValidatorBehavior.IsValidProperty, "IsPasswordValid");
                var vm = new RegisterPublicPageViewModel();
    
                _okButton.BindingContext = vm;
                phoneBehavior.BindingContext = vm;
                passwordBehavior.BindingContext = vm;
    
  • madhav.shenoy83madhav.shenoy83 AUMember ✭✭

    @Umar3x Since you're doing everything in C#, I'm assuming you don't have a Xaml page. I do most of my binding in Xaml.
    So frankly my knowledge of binding properties using C# is a bit limited. But, If you look at @JamesRusso answer here it should shed some light on how to set binding via C#. Apparently, you need to bind properties this way
    _okButton.SetBinding(IsEnabledProperty, new Binding("IsFormValid"));

    But that apart you seem to be doing it right. I'm not sure what your behaviors do. I can only assume that the behavior checks some rule you've set which gets invoked during the textchanged event of the entry control. If that is true, then you should be good.

  • Umar3xUmar3x FRMember ✭✭✭
    edited March 2017

    @mshenoyOcean Hello mate, thanks for your answer.

    You're right, I don't have any xaml in my project, everything is in c#. I tried that too before, seems not to work.

    It does not seem to be an issue on the page but more about how I'm listening to the OnPropertyChanged of the isFormValid property of the viewModel. When my page appears my button is not Enabled, which means that the get method of the isFormValid property is well called.

    But, it seems that I'm forgetting something that will call the setter when one of the other property changes to set the isFormValid with the new value, dont you think so ? But I dont know what I'm missing ...

    Thanks :)

  • madhav.shenoy83madhav.shenoy83 AUMember ✭✭

    The easiest way to test this is, create a common TextChanged handler for all your Entry controls and conduct your validation within them.

    I've created a couple of gists have a look https://gist.github.com/mshenoy83 LoginViewModel has a sample where I use a single function EntryTextChanged to check if my Username and Password have any text in them. If they do I enable my login button or else I keep it disabled

  • Umar3xUmar3x FRMember ✭✭✭

    @mshenoyOcean Thanks for you're exemples but I did some debug and all properties setters are well called except the setter of isFormValid, only the getter is called when page is being initialized.

    Anyway I'll find the solution or post somewhere else as it is not the subject of this topic. Thanks !

  • @Umar3x I think I understand what you are looking for. I adjusted the view model below. I didnt test it but it should work. Basically the IsFormValid property has a get only since its value is decided by the other 2 properties. I also invoked OnPropertyChanged under the 2 fields but passed in the IsFormValid property name. This way every time one of the 2 fields are changed it re-evaluates and updates the IsFormValid property.

    public class RegisterPublicPageViewModel : BaseViewModel
    {

        private bool _isPhoneNumberValid;
        private bool _isPasswordValid;
        //private bool _isFormValid; //shouldn't need a local property
    
        /// <summary>
        ///
        /// </summary>
        public bool IsPhoneNumberValid
        {
            get { return _isPhoneNumberValid; }
            set
            {
                if (_isPhoneNumberValid == value) return;
                _isPhoneNumberValid = value;
                OnPropertyChanged("IsPhoneNumberValid");
        OnPropertyChanged("IsFormValid");
            }
        }
    
        public bool IsPasswordValid
        {
            get { return _isPasswordValid; }
            set
            {
                if (_isPasswordValid == value) return;
                _isPasswordValid = value;
                OnPropertyChanged("IsPasswordValid");
        OnPropertyChanged("IsFormValid");
            }
        }
    
        public bool IsFormValid
        {
            get { return IsPasswordValid && IsPhoneNumberValid; }
        }
    

    }

    HTH
    Brian

  • Umar3xUmar3x FRMember ✭✭✭

    @BrianNordstrom.1345 Eh eh, I tried that before but I was keeping the local property and was calling OnPropertyChanged in the IsFormValid. Now it makes sense with your answer as it should be updated when one or the other prop changes :)

    Awesome. It works like a charm :). Thank you very much

Sign In or Register to comment.