Lord Relix
Lord Relix

Reputation: 942

How to disable button on validation error when using MVVM?

I am learning MVVM and so far so good, except when disabling a button upon validation. My validation works flawlessly not so the disabling the button part. Here's my code:

ViewModelBase:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

My validation Class:

public class NumberValidation : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        if (String.IsNullOrEmpty(value.ToString()))
        {
            return new ValidationResult(false, "No debe estar vacio");
        }
        double result = 0;
        bool canConvert = double.TryParse(value as string, out result);
        return new ValidationResult(canConvert, "No es numero valido");
    }


}

And my ViewModel:

public class CommercePayment : ViewModelBase
{
    private ICommand _SubmitCommand;
    private PayCommerce _payCommerce;

    public PayCommerce PayCommerces
    {
        get
        {
            return _payCommerce;
        }
        set
        {
            _payCommerce = value;
            NotifyPropertyChanged("PayCommerces");
        }
    }


    public CommercePayment()
    {
        PayCommerces = new PayCommerce();
    }

    void PayCommerce_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        NotifyPropertyChanged("PayCommerces");
    }
    public void Submit()
    {
        var test = PayCommerces.AccountNumber;
        var test2 = PayCommerces.PaymentAmount;
 //          var test2 = PayCommerces.Commerce.MerchantFullName;
    }





    #region Helpers
    public ICommand SubmitCommand
    {
        get
        {
            if (_SubmitCommand == null)
            {
                _SubmitCommand = new RelayCommand(param => this.Submit(),
                    null);
            }
            return _SubmitCommand;
        }
    }
    #endregion
}

}

My XML is binded this way:

<Controls:Tile x:Name="tlProcesar" Title="" 
                TiltFactor="2"
                Width="Auto" Height="Auto" 
                Count="Procesar"
                Command="{Binding SubmitCommand}" Margin="185,189,200,-59"
                           >
            </Controls:Tile>

In my codebehind I have this (was testing and learning):

    private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = IsValid(sender as DependencyObject);

    }
    private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        ViewModel.CommercePayment payments = new ViewModel.CommercePayment();
        payments.Submit();
    }


    private bool IsValid(DependencyObject obj)
    {
        return !Validation.GetHasError(obj) && LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>().All(IsValid);
    }

If I changed my control binding to:

 Command="ApplicationCommands.Save"

Then the button is disabled, but of course I get no data back in my Submit() method in the ViewModel because its not binding. If I leave it as it is, even with failing validation the button will still work. What can I do?

Update 1:

Created this:

public bool IsValid
    {
        get { return _isValid; }
        set
        {
            _isValid = value;
            NotifyPropertyChanged("IsValid");
        }
    }

Now I guess I need to tie it to the validation?

Upvotes: 0

Views: 3728

Answers (2)

Rachel
Rachel

Reputation: 91

I can see that you mix two kind of validation solution in your setup, an half that happens in the view, and the other in the view model.

You are implementing the NumberValidation class, but I do not see any xaml code that use it. You need to use it in your xaml code, Something like this :

<TextBox.Text>
  <Binding Path="PayCommerces">
    <Binding.ValidationRules>
      <val:NumberValidation/>
    </Binding.ValidationRules>
  </Binding>
</TextBox.Text>

But if you are using this method, the validation is done in the View layer. So it means that your ViewModel doesn't know that it has an error and your IsValid doesn't detect that there is an error.

If you want to put all your validation in the view, use the multibinding to add binding between the controls in errors and your button. You can see an example here. Disabling button on Validation error

But instead if you want your validations to happens in the ViewModel instead, you need your view model to implements the IDataErrorInfo interface. Then your NumberValidation class will be useless.

Upvotes: 1

Basti
Basti

Reputation: 1004

In general you can bind the IsEnabled property of the Button to an Property in the xaml.cs returning the "IsValid" status.

<Controls:Tile x:Name="tlProcesar" Title="" 
  TiltFactor="2"
  Width="Auto" Height="Auto" 
  Count="Procesar"
  Command="{Binding SubmitCommand}" Margin="185,189,200,-59"
  IsEnabled="{Binding IsValidProperty}">
</Controls:Tile>

Upvotes: 0

Related Questions