OnPropertyChanged 二级更新

OnPropertyChanged 2nd level update

搜索没有给我带来任何线索,我有点不知所措。 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));
    }
}

我发誓它在某些时候起作用了。 该字符串还有一些其他变量,但这是它工作原理的基本原理,或者更确切地说不是。

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

您必须添加代码以将 Model 的 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));
    }
}

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

来源 属性 是否包含引发 PropertyChanged 事件的 class 的另一个 属性 并不重要。它是通知视图的绑定的源对象。

您也可以直接绑定到 "model" 属性,前提是您在视图模型中将 Data 转换为 public 属性:

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