0

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?

Lord Relix
  • 922
  • 5
  • 23
  • 50
  • 3
    When using the second parameter of the RelayCommand constructor, the button is only enabled, if the function, the second parameter is pointing to returns true. So instead of using "null" as the second parameter you could point to your IsValid function. – Basti Nov 24 '15 at 14:55
  • 1
    I can't see your Command implementation, but there should be a `CanExecute` parameter that you can set that will automatically be used for determining if the Button is enabled or not. If it is based on a method instead of a property like you have in your `Save_CanExecute` method, you may need to add a PropertyChange notification to your object that calls something like `SaveCommand.RaiseCanExecuteChanged()` whenever a property changes that could invalidate the object. – Rachel Nov 24 '15 at 15:29

2 Answers2

1

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.

Community
  • 1
  • 1
Rachel
  • 81
  • 1
  • 9
0

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>
Basti
  • 994
  • 6
  • 11
  • Tried to bind the IsEnabled to my "IsValid" function but it didn't seem to work. Will keep checking around and see how can I get it working. – Lord Relix Nov 24 '15 at 16:16
  • It has to be a Property with the return type boolean. Furthermore it must not have an parameter as your IsValid Function has. So in your case it would be sufficient to add an IsValid Property which returns the value of your function with according parameters. – Basti Nov 24 '15 at 16:32
  • Something akin to this? *UPDATEDOP* – Lord Relix Nov 24 '15 at 16:39
  • See Rachael's comment for the answer. Hence, the Command object already supports this behavior for enabling and disabling. As a result, adding another property (i.e. IsEnabled) is verbose. – Scott Nimrod Nov 24 '15 at 18:20