1
votes

How can I disable button until good textbox validation? I have done some stuff but I don't know how to hide button until good validation in the right way with using MVVM pattern. I am still learning this MVVM pattern. Here are my fragments of code:

ValidationRule Class:

public class NotEmptyValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        return string.IsNullOrWhiteSpace((value ?? "").ToString())
            ? new ValidationResult(false, "Field is required")
            : ValidationResult.ValidResult;
    }
}

xaml:

<TextBox
Style="{StaticResource MaterialDesignFilledTextFieldTextBox}"
AcceptsReturn="True"
TextWrapping="Wrap"
materialDesign:HintAssist.Hint="Content"
IsEnabled="{Binding Path=IsChecked, 
ElementName=MaterialDesignOutlinedTextFieldTextBoxEnabledComboBox}" 
MaxLength="1000" materialDesign:ValidationAssist.UsePopup="True">
        <TextBox.Text>
            <Binding Path="Task.Content" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <domain:NotEmptyValidationRule
                     ValidatesOnTargetUpdated="True" />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

ViewModel:

private ICommand _submitCommand;
public ICommand SubmitCommand
{
   get
   {
       if (_submitCommand == null)
       {
          _submitCommand = new RelayCommand(SubmitExecute, anSubmitExecute, false);
       }
       return _submitCommand;
    }
}

private void SubmitExecute(object parameter)
{
     Task.Id++;
     Tasks.Add(Task);
}

private bool CanSubmitExecute(object parameter)
{
     // What can I do to prevent to show this button when validation 
     // failed
     return true;
}
1
You should implement the INotifyDataErrorInfo interface in your Task class.mm8

1 Answers

2
votes

You should either implement the INotifyDataErrorInfo interface in your Task class, or implement it in your view model class and wrap the databound Task.Content property, e.g.:

private RelayCommand _submitCommand;
public ICommand SubmitCommand
{
    get
    {
        if (_submitCommand == null)
        {
            _submitCommand = new RelayCommand(SubmitExecute, CanSubmitExecute, false);
        }
        return _submitCommand;
    }
}

private void SubmitExecute(object parameter)
{
    Task.Id++;
    Tasks.Add(Task);
}

public string Content
{
    get { return Task.Content; }
    set
    {
        Task.Content = value;
        if (string.IsNullOrEmpty(Task.Content))
            _validationErrors[nameof(Content)] = "Field is required";
        else
            _validationErrors.Remove(nameof(Content));
        ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Content)));
        _submitCommand.RaiseCanExecuteChanged();
    }
}

private bool CanSubmitExecute(object parameter)
{
    return HasErrors;
}

#region INotifyDataErrorInfo
private readonly Dictionary<string, string> _validationErrors = new Dictionary<string, string>();

public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

public System.Collections.IEnumerable GetErrors(string propertyName)
{
    if (string.IsNullOrEmpty(propertyName)
        || !_validationErrors.ContainsKey(propertyName))
        return null;

    return new string[1] { _validationErrors[propertyName] };
}

public bool HasErrors
{
    get { return _validationErrors.Count > 0; }
}
#endregion

XAML:

<TextBox Text="{Binding Content, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />

You should not use a validation rule. They are not MVVM friendly. Please refer to this blog post for more information.

You also need to raise the CanExecuteChanged event of the ICommand. How to do this depends on the implementation. Most implementations have a method called something like RaiseCanExecuteChanged() or similar.

WPF 4.5: Validating Data in Using the INotifyDataErrorInfo Interface