Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to suppress validation when nothing is entered

I use WPF data binding with entities that implement IDataErrorInfo interface. In general my code looks like this:

Business entity:

public class Person : IDataErrorInfo 
{
  public string Name { get; set;}

  string IDataErrorInfo.this[string columnName]
  {
    if (columnName=="Name" && string.IsNullOrEmpty(Name))
      return "Name is not entered";
    return string.Empty;
  }  
}

Xaml file:

<TextBox Text="{Binding Path=Name, Mode=TwoWay, ValidatesOnDataErrors=true}" />

When user clicks on "Create new person" following code is executed:

DataContext = new Person();

The problem is that when person is just created its name is empty and WPF immediately draws red frame and shows error message. I want it to show error only when name was already edited and focus is lost. Does anybody know the way to do this?

like image 282
Alex Kofman Avatar asked Oct 01 '09 06:10

Alex Kofman


3 Answers

You can change your person class to fire validation error only if Name property was ever changed:

public class Person : IDataErrorInfo {

    private bool nameChanged = false;
    private string name;
    public string Name {
        get { return name; }
        set { 
            name = value;
            nameChanged = true;
        }
    }

//... skipped some code

    string IDataErrorInfo.this[string columnName] {
        get {
            if(nameChanged && columnName == "Name" && string.IsNullOrEmpty(Name)) 
                return "Name is not entered"; 
            return string.Empty;
        }
    }
}
like image 198
Stanislav Kniazev Avatar answered Nov 11 '22 17:11

Stanislav Kniazev


There is another solution which i found but i don't like it a lot. You have to clear validation on page load.

What i mean is you have to do this :

Validation.ClearInvalid(...) for instance if you have a textbox you dont want to be validated should call

Validation.ClearInvalid(txtSomething.GetBindingExpression(TextBox.TextProperty)) or something like that.

You should do this for every control you want to be cleared of validation.

I didn't like the solution but that was the best i found. I hoped wpf had something "out of the box" that worked but didn't find it.

like image 5
6lix Avatar answered Nov 11 '22 18:11

6lix


I think @Stanislav Kniazev approach is the correct one. Your comment about not adding logic to the business object is also valid. To have a clean separation of concern, how about keeping the Person in the business layer (or the data model layer), and introduce a new class PersonVm with view logic. For the VM layer I like the containment pattern more than inheritence, and at this layer I also implement INotifyPropertyChanged, which is also a property of the VM and not the data model.

public class PersonVm : IDataErrorInfo, INotifyPropertyChanged
{
    private Person _person;

    public PersonVm( ) {
        // default constructor
        _person = new Person( );
        _dirty = false;
    }

    public PersonVm( Person p ) {
        // User this constructor when you get a Person from database or network
        _person = p;
        _dirty = false;
    }

    void fire( string prop ) {
        PropertyChanged( this, new PropertyChangedEventArgs( prop ) );
    }

    public string name {
        get { return _person.name; }
        set { _person.name = value; fire( "name" ); dirty = true; }
    }

    ...

    string IDataErrorInfo.this[string columnName] { 
        get {
            if( dirty ) return _person[columnName];
        }
    }

}

The idea is to put the logic of each layer at the appropriate class. At the data model layer, you do validation that only concern the pure data. The the View Model layer, you add logic that concerns the View Model (as well as notificaton and other View Model logic).

like image 4
Uri Avatar answered Nov 11 '22 17:11

Uri