【问题标题】:WPF INotifyPropertyChanged two way binding strange actionWPF INotifyPropertyChanged 两种方式绑定奇怪的动作
【发布时间】:2019-03-28 04:17:48
【问题描述】:

我按照许多线程的建议实现了INotifyPropertyChanged
实现 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"); }
    }
}

视图模型由模型和用于更改模型属性的命令组成。

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 然后绑定到视图。

<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>

执行命令时,TextBox 不会更新为新值。
但是,如果我像 this 指令那样实现 INotifyPropertyChanged,则绑定有效。
实施 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); }
    }
}

第一种方法遗漏了什么?

【问题讨论】:

    标签: c# wpf mvvm data-binding inotifypropertychanged


    【解决方案1】:

    实现 1 的主要问题是您的 OnPropertyChanged 方法的字符串参数需要是正在更改的确切属性名称。对于您的两个示例,"model_changed" 应更改为 "Model""name_changed" 应为"Name"。这里有两种很好的技术可以减少输入文字字符串名称时可能出现的人为错误:

    1.使用CallerMemberName 属性

    如果您被允许并有权访问System.Runtime.CompilerServices 命名空间,您可以编写您的基类,以使属性名称自动作为OnPropertyChanged 方法的字符串参数传递:

    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));
        }
    }
    

    然后你可以简单地在你的属性的 getter 中调用OnPropertyChanged()

    2。使用nameof 关键字

    或者,您可以简单地用nameof(&lt;InsertPropertyNameHere&gt;) 替换文字类型的属性名称,这将返回名称而不会有任何输入错误的风险,如下例所示:

    public class Model : Notifier, IDataErrorInfo
    {
        private string name;
    
        public string Name
        {
            get { return name; }
            set { name = value; OnPropertyChanged(nameof(Name)); }
        }
    } 
    

    【讨论】:

    • 是的。才注意到这一点。大多数教程都没有提到这一点。我用的是第一种方法。
    【解决方案2】:

    请像这样添加属性名称。

    public class Model : Notifier, IDataErrorInfo
    {
        private string name;
    
        public string Name
        {
            get { return name; }
            set { name = value; OnPropertyChanged("Name"); }
        }
    }
    

    【讨论】:

    • 成功了。因此,规则是“传递给事件的参数必须与属性名称相同”。一直以为只要是唯一的字符串就有效。
    • @M.Ko 当您调用 OnPropertyChanged("Name") 时,您基本上是在告诉 WPF Model 上的属性 Name 已更改。因此,它不能是任意字符串。附带说明一下,如果需要,您还可以执行多个 OnPropertyChanges。例如,如果您有 GivenNames 和 LastName 属性以及从 GivenNames + LastName 派生的 FullName 属性,您可以拥有:public LastName { set { name = value; OnPropertyChange("LastName"); OnPropertyChange("FullName") } }
    猜你喜欢
    • 2017-11-18
    • 2020-07-07
    • 2012-06-12
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 2021-03-01
    • 2016-10-24
    • 2011-04-09
    相关资源
    最近更新 更多