简体   繁体   中英

WPF INotifyPropertyChanged two way binding strange action

I implemented INotifyPropertyChanged as recommended by many threads.
Implementation 1

public class Notifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string pName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pName));
    }
}

public class Model : Notifier, IDataErrorInfo
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; OnPropertyChanged("name_changed"); }
    }
}

And the viewmodel consists of the model and command to make changes to model properties.

public class ViewModel : Notifier
{
    private Model _model;

    public Model Model
    {
        get { return _model; }
        set { _model = value; OnPropertyChanged("model_changed"); }
    }

    private ICommand _cmd;

    public ICommand Command
    {
        get { return _cmd; }
        set { _cmd = value; }
    }

    public void ExecuteCommand(object para)
    {
        Console.WriteLine("Command executed");
        Model.Name = "new name";
    }
}

VM is then binded to the view.

<TextBox HorizontalAlignment="Center" VerticalAlignment="Center"  Width="100">
    <TextBox.Text>
        <Binding Path="Model.Name" Mode="TwoWay" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
            <Binding.ValidationRules>
                <ExceptionValidationRule></ExceptionValidationRule>                
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

When the command is executed, the TextBox does not get updated to new value.
However, if I implement the INotifyPropertyChanged like this instruction, the binding works.
Implementation 2

public class Notifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
        return false;
    }
}

public class Model : Notifier, IDataErrorInfo
{
    private string name;

    public string Name
    {
        get { return name; }
        set { SetProperty(ref name, value); }
    }
}

What is missed in the first method?

The main problem with Implementation 1 is that the string parameter of your OnPropertyChanged method needs to be the exact property name that is being changed. For your two examples, "model_changed" should be changed to "Model" and "name_changed" should read "Name" . Here are two great techniques to mitigate potential human error with the typing of literal string names:

1. Use the CallerMemberName Attribute

If you are allowed and have access to the System.Runtime.CompilerServices namespace, you can write your base class as such to have the property name automatically passed as the string parameter of the OnPropertyChanged method:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Notifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string pName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pName));
    }
}

Then you can simply call OnPropertyChanged() in your property's getter.

2. Use the nameof keyword

Alternatively, you may simply replace the literal typed property name with nameof(<InsertPropertyNameHere>) which will return the name without any risk of mistyping, like this example:

public class Model : Notifier, IDataErrorInfo
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; OnPropertyChanged(nameof(Name)); }
    }
} 

Please add property name like this.

public class Model : Notifier, IDataErrorInfo
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; OnPropertyChanged("Name"); }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM