【问题标题】:OnPropertyChanged 2nd level updateOnPropertyChanged 2 级更新
【发布时间】:2018-12-13 22:50:25
【问题描述】:

搜索没有给我带来任何线索,我有点不知所措。 到目前为止,WPF 是自学的,所以我可能会忽略一些简单的东西。

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

<TextBlock Text={Binding BoundTextProperty}"/>

这是简化的xml

public class MainViewModel
{
    private Model Data;
    public MainViewModel()
    {...}
    public string BoundTextProperty => Data.BoundTextProperty;
    ...
}

绑定的属性引用了模型中保存数据的属性

public class Model : INotifyPropertyChanged
{
    private long number;
    public long Number
    {
        get { return number; }
        set 
        {
            number = value;
            OnPropertyChanged(nameof(BoundTextProperty));
        }
    }

    public string BoundTextProperty => $"Some text {Number} some text again";

    public virtual event PropertyChangedEventHandler PropertyChanged;

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

我发誓它在某些时候有效。 该字符串还有几个其他变量,但这是它如何工作的基本原理,或者说没有。

我的问题是绑定是否真的可以冒泡,如果可以,为什么不呢?

【问题讨论】:

  • 不,它不会自动冒泡,您还必须在 MainViewModel 中调用 OnPropertyChanged,例如通过处理DataPropertyChanged 事件,例如喜欢Data.PropertyChanged += ... handler that calls OnPropertyChanged ...
  • 默认情况下绝对不会冒泡。我还强烈建议不要用INotifyPropertyChanged 接口混淆你的Model 类。您的模型应该与 MVVM / 绑定架构无关。属性更改事件应始终来自您的 ViewModel。
  • 要添加另一个意见,在模型类中实现INotifyPropertyChanged 是完全有效的。这样做不会违反 MVVM 模式,尤其不会“弄脏”任何东西。
  • 所以模型中根本没有 OnPropertyChanged 调用?编辑:这正是 wpf 令人困惑的原因,因为每个人都以不同的方式这样做
  • 我在 Model 和 ViewModel 上都有 INotifyPropertyChangedImplemented,因为某些数据在 viewModel 中比在模型中更有意义

标签: c# wpf binding inotifypropertychanged


【解决方案1】:

您必须添加代码以将模型的 PropertyChanged 事件从 ViewModel 冒泡到 View。 这是一个示例(基于您的代码):

public class MainViewModel : ViewModelBase
{
    private readonly Model Data;

    public MainViewModel()
    {
        Data = new Model();
        Data.PropertyChanged += ModelOnPropertyChanged;
    }

    private void ModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(Model.BoundTextProperty):
                OnPropertyChanged(nameof(MainViewModel.BoundTextProperty));
                break;
            // add cases for other properties here:
        }
    }

    public string BoundTextProperty => Data.BoundTextProperty;
}

public class Model : ModelBase
{
    private long number;
    public long Number
    {
        get { return number; }
        set
        {
            number = value;
            OnPropertyChanged(nameof(BoundTextProperty));
        }
    }

    public string BoundTextProperty => $"Some text {Number} some text again";

}

public abstract class ViewModelBase : Base
{
    // add other ViewModel related stuff here
}


public abstract class ModelBase : Base
{
    // add other Model related stuff here
}

public abstract class Base : INotifyPropertyChanged
{
    public virtual event PropertyChangedEventHandler PropertyChanged;

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

【讨论】:

  • 非常感谢,我特别喜欢Base,没想到。我会要求跟进,因为我现在有点怀疑我的结构设置..
【解决方案2】:

您需要为您在 XAML 中绑定到的源属性引发 PropertyChanged 事件。在这种情况下,您绑定到MainViewModelBoundTextProperty,这意味着MainViewModel 类应该引发PropertyChanged 事件。

源属性是否包装了会引发PropertyChanged 事件的类的另一个属性并不重要。通知视图的是绑定的源对象。

您也可以直接绑定到“模型”属性,前提是您将Data 转换为视图模型中的公共属性:

public Model Data { get; private set; }
...
<TextBlock Text="{Binding Data.BoundTextProperty}"/>

如果您选择坚持使用包装器属性,MainViewModel 必须实现 INotifyPropertyChanged 事件并在模型更新时引发 PropertyChanged 事件:

public class MainViewModel : INotifyPropertyChanged
{
    private readonly Model Data;

    public MainViewModel()
    {
        Data = new Model();
        Data.PropertyChanged += Data_PropertyChanged;
    }

    private void Data_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        OnPropertyChanged("BoundTextProperty");
    }

    public string BoundTextProperty => Data.BoundTextProperty;

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

【讨论】:

  • 为此,我必须在 mainViewModel 中设置属性,而我没有。这意味着 OnProperty 不会知道 Data 属性已更改
  • “那个”是什么意思?如果您在 XAML 中绑定到 Data.BoundTextProperty,则在您设置模型的属性时,TextBlock 将被更新。如果您绑定到视图模型属性,则不需要设置此属性,但您需要为其引发 PropertyChanged 事件,正如我在回答中所解释的那样。
  • 我指的是你答案中的第一部分,其余部分是正确的,但这对我没有帮助,因为我想要验证mainViewModel中的数据,这意味着我还需要绑定在那里。
  • “其余的都正确”?第一部分当然也是正确的。如果您绑定到视图模型属性并更新模型属性,则视图模型应该订阅模型的PropertyChanged 事件并在模型属性更改时引发自己的PropertyChanged 事件。否则视图应该如何知道模型属性已更改?
  • 我试过了,没用,所以我问了这个问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 2018-08-06
  • 2021-02-13
  • 1970-01-01
相关资源
最近更新 更多