Search code examples
c#wpfinotifypropertychangedtwo-way-binding

Don't Understand How INotifyPropertyChanged function


INotifyPropertyChanged interface implementation (appropriate event) and helper method works fine in my code but I really don't understand how it works. My book does a poor job of explaining it to me in my opinion or I am not very smart. We have a separate class Car.cs added to my solution with the following code (example from the book) and it should work in TwoWay Binding regards to TextBox control in my WPF application being changed when object's instance is being changed too:

public class Car: INotifyPropertyChanged
{
private string _make;
private string _model;

public event PropertyChangedEventHandler PropertyChanged;

public Car()
{

}
    
public string Make
{
    get { return _make; }
    set
    {
        if (_make != value)
        {
            _make = value;
            OnPropertyChanged("Make");
        }
    }
}
public string Model
{
    get { return _model; }
    set
    {
        if(_model != value)
        {
            _model = value;
            OnPropertyChanged("Model");
        }
    }
}

private void OnPropertyChanged (string propertyName)
{
    if (PropertyChanged != null)
    {
        
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}}

And here is the code which I made myself after learning Microsoft learning materials and it looks and works better imo:

public class Car: INotifyPropertyChanged
{
private string _make; 
private string _model;

public event PropertyChangedEventHandler PropertyChanged;

public Car()
{

}
    
public string Make
{
    get { return _make; }
    set
    { 
        _make = value;
        OnPropertyChanged(); 
    }
}
public string Model
{
    get { return _model; }
    set
    {
        _model = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged ()
{
    if (PropertyChanged != null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
    }
} }

So I have the foolowing question regards to example from the book (the first one):

  1. What this part means? What's the point to check if property not equals to value (never saw anything like it before when during the Set part of the property you check that field of the class checked for being not equalled to value)? Code works even without it. No explanation for it in the book. Here is the code under question:

    set { if (_make != value) { _make = value; OnPropertyChanged("Make"); }

  2. Why if you'll write the name of the property without brackets in the above-mentioned small piece of code (in subparagraph one) then it will not work? I meant if you'll write Make then it does not work and you should write "Make".

Thanks in advance!


Solution

  • What's the point to check if property not equals to value

    it is just to avoid unnecessary updates. It is not strictly required. Whenever you have events to signal that something is updated it is often a good idea to check if the thing actually changed, since you do not know what is listening to the event, it could potentially trigger some slow computations.

    Why if you'll write the name of the property without brackets in the above-mentioned small piece of code (in subparagraph one) then it will not work?

    Because the event needs a string. You can let the compiler insert this string for you by writing OnPropertyChanged(nameof(Make)); You can also use CallerMemberName attribute to let the compiler insert this automatically, just call OnPropertyChanged() within your property.

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    Going one step further you you can create a method that does both the comparison, field update, and event, letting you write a one line setter => Set(ref _make, value);

    private void Set<T>(ref T field, T value, [CallerMemberName] string caller = "")
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(caller);
        }
    }
    

    However, my preference tend to be to wrap all of this inside a separate class:

        public class Changeable<T> : INotifyPropertyChanged
        {
            private T currentValue;
            public Changeable(T initialValue) => currentValue = initialValue;
            public T Value
            {
                get => currentValue;
                set
                {
                    if (!EqualityComparer<T>.Default.Equals(currentValue, value))
                    {
                        this.currentValue = value;
                        OnPropertyChanged();
                    }
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    

    That way you can declare a property like public Changeable<string> Make {get;} = new (""); and bind to it like {Binding Make.Value}