wpf ViewModel 为特定属性订阅模型的 PropertyChanged 事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24658771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:06:55  来源:igfitidea点击:

ViewModel subscribing to Model's PropertyChanged event for a specific property

c#wpf

提问by William Thomas Waller

I'd like to have methodToBeCalledWhenPropertyIsSet() execute when Property in the Model is changed.

我希望在更改模型中的属性时执行 methodToBeCalledWhenPropertyIsSet()。

How could I do this?

我怎么能这样做?

If I understand correctly, I could add MyModel.PropertyChanged += methodToBeCalledWhenPropertyIsSetsomewhere in my ViewModel to subscribe to the PropertyChanged event in general but I only care when Property is set

如果我理解正确,我可以MyModel.PropertyChanged += methodToBeCalledWhenPropertyIsSet在我的 ViewModel 中添加某个地方来订阅 PropertyChanged 事件,但我只关心何时设置 Property

public class ViewModel : INotifyPropertyChanged
{
    ...

    public Model MyModel { get; set; }

    public void methodToBeCalledWhenPropertyIsSet() { }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Model : INotifyPropertyChanged
{
    object _propertyField;
    public object Property
    {
        get
        {
            return _propertyField;
        }
        set
        {
            _propertyField = value;
             OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

回答by Daniel Johnson

The INotifyPropertyChanged interface solves this problem. Subscribe your View Model to the Models PropertyChangedEventHandler and filter your results.

INotifyPropertyChanged 接口解决了这个问题。将您的视图模型订阅到模型 PropertyChangedEventHandler 并过滤您的结果。

public class ViewModel : INotifyPropertyChanged
{
    ...

    public Model MyModel { get; set; }

    public void methodToBeCalledWhenPropertyIsSet() { }

    public event PropertyChangedEventHandler PropertyChanged;

    public ViewModel()
    {
        // MyModel would need to be set in this example.
        MyModel.PropertyChanged += Model_PropertyChanged;
    }

    private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if(e.PropertyName == "Property")
        {
             methodToBeCalledWhenPropertyIsSet();
        }
    }
}

In the MVVM pattern the View Model is intended to deal with messy situations like this. This still maintains your abstraction from the model.

在 MVVM 模式中,视图模型旨在处理这样的混乱情况。这仍然保持您对模型的抽象。

EditAs HighCore pointed out, this code doesn't work copy paste. MyModel would need to be instantiated beforehand. I use MEF(http://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx) for this purpose. You can either grab a model class directly or use some kind of factory/manager to get a reference.

编辑正如 HighCore 指出的那样,此代码无法复制粘贴。MyModel 需要事先实例化。为此,我使用 MEF(http://msdn.microsoft.com/en-us/library/dd460648( v=vs.110) .aspx)。您可以直接获取模型类或使用某种工厂/管理器来获取参考。

回答by Federico Berasategui

One possible, commonly used solution is to wrap the Model's Propertywith an equivalent property in the ViewModel:

一种可能的、常用的解决方案是Property在 ViewModel 中使用等效的属性包装模型:

public class ViewModel
{
    public object Property
    {
        get
        {
            return Model.Property;
        }
        set
        {
             Model.Property = value;
             methodToBeCalledWhenPropertyIsSet(); // Here you call your method directly
        }
    }
}

And bind your UI to this property rather than the Model's.

并将您的 UI 绑定到此属性而不是模型的。

Edit:If the model changes due to UI interaction, then that interaction will occur "thru" the ViewModel. If the model changes due to internal business logic in the model itself, then you'll have no other option but to subscribe to it's PropertyChangedevent, but make sure you properly unsubscribe at a later moment. I usually put such subscription / unsubscription code In the setter of the Modelproperty in the VM:

编辑:如果模型因 UI 交互而发生变化,则该交互将“通过”ViewModel 发生。如果模型由于模型本身的内部业务逻辑而发生更改,那么您将别无选择,只能订阅它的PropertyChanged事件,但请确保稍后正确取消订阅。我通常会把这样的订阅/取消订阅代码Model放在VM中的属性的setter中:

public MyModel Model
{
    get { return _model; }
    set
    {
        if (_model != null)
            _model.PropertyChanged -= OnModelPropertyChanged;

        _model = value;

        if (_model != null
            _model.PropertyChanged += OnModelPropertyChanged;

        NotifyPropertyChange(() => Model);
     } 
}