Implement IDataErrorInfo on button click event

979 Views Asked by At

I'm trying to achieve validation on button click for a textbox using binding. Basically when I click Submit my textbox is not turning red and giving me the "Required" error, it is when I add text to it.

I'm new at validation and been looking at this for almost a week on and off in frustration. I think my answer may have something to-do with propertychangedevent? but I'm not sure and resorting to asking the professionals.

All and any help with this will be must appreciated.

Here is my Model class:

 public class sForms : INotifyPropertyChanged, IDataErrorInfo

{
    private string name;
    public string NAME { get { return name; } set { if (name != value) name = value.Trim(); OnPropertyChanged("NAME"); } }


    public string this[string columnName]
    {
        get
        {
            return ValidationError(columnName);
        }

    }

    public string Error { get { return null; } }


    private string ValidationError(string columnName)
    {
        string error = null;
        switch (columnName)
        {
            case "NAME":
                error = IsNameValid();
                break;
        }
        return 
            error;
    }

    static readonly string[] ValidatedProperties = { "NAME" };

    public bool IsValid
    {
        get
        {
            foreach (string property in ValidatedProperties)
            {
                if (ValidationError(property) != null)
                {
                    return
                        false;
                }
            }
            return
                true;
        }
    }

    public string IsNameValid()
    {
        if (string.IsNullOrWhiteSpace(NAME) || string.IsNullOrEmpty(NAME))
            return "Required";
        else
            return
                null;
    }


    #region Property Changed
    private void OnPropertyChanged(string propertyName)
    {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion
}

Here is my XAML for my button + Text Box;

        <TextBox Controls:TextBoxHelper.UseFloatingWatermark="True" 
                     Controls:TextBoxHelper.Watermark="Name *"                          
                     Grid.Column="1" Grid.Row="1"
                     Margin="0 0 2 0"         
                     Text="{Binding Path=NAME, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
                     >
        <Button Content="Submit"                    
                Style="{DynamicResource SquareButtonStyle}"
                VerticalAlignment="Bottom" HorizontalAlignment="Right"
                Margin="0 0 10 0"    
                Click="Submit_Click"
                />

Here is my code behind;

        public v_subsForm()
    {
        InitializeComponent();
        this.DataContext = subs;
    }

    sForms subs = new sForms();
    #region PropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    private void Submit_Click(object sender, RoutedEventArgs e)
    {
        if (subs.IsValid)
            MessageBox.Show("True");
        else
            MessageBox.Show("False");
    }
1

There are 1 best solutions below

0
djomlastic On

First, your code works as it should, assuming you included all MahApps.Metro resources that are required. Also, you don't need to implement INotifyPropertyChanged in your code-behind (that's your MainWindow I guess).

I'm trying to achieve validation on button click for a textbox using binding.

That is not how IDataErrorInfo works. IDataErrorInfo defines an API that the binding can query for errors on the object that it's bound to. So, when your NAME property is changed, the binding will query the indexer on your sForms object: subs["NAME"]. If it gets an error, error template is applied. This is usually paired with a submit button whose Command property is bound to a command whose CanExecute checks for errors and if there are errors the button is disabled (so you can not submit if there are errors, button is disabled).

If you want to do your validation on button click, you don't need to implement IDataErrorInfo. System.Windows.Controls.Validation class has attached properties that drive presentation of errors: HasError, Errors, ErrorTemplate. But you can't just set Validation.HasError to true (there is no accessible setter) like you can set Validation.ErrorTemplate. To set Validation.HasError in code-behind, you can use Validation.MarkInvalid method, but this is not how these things are usually done. Here is a quick example, for this to work you need to set Name property on your TextBox to MyTextBox:

private void Submit_Click(object sender, RoutedEventArgs e)
{
    if (!string.IsNullOrEmpty(MyTextBox.Text)) return;

    BindingExpression bindingExpression = 
        BindingOperations.GetBindingExpression(MyTextBox, TextBox.TextProperty);

    BindingExpressionBase bindingExpressionBase =
        BindingOperations.GetBindingExpressionBase(MyTextBox, TextBox.TextProperty);

    ValidationError validationError =
        new ValidationError(new ExceptionValidationRule(), bindingExpression);

    validationError.ErrorContent = "My error message.";

    Validation.MarkInvalid(bindingExpressionBase, validationError);
}

So if MyTextBox.Text is empty, it will be considered invalid.