如果 viewmodel1 属性发生变化,通知 viewmodel2

Posted

技术标签:

【中文标题】如果 viewmodel1 属性发生变化,通知 viewmodel2【英文标题】:notify viewmodel2 if viewmodel1 property changes 【发布时间】:2016-11-18 07:18:30 【问题描述】:

我在 wpf +mvvm 中有一个场景,即如果我的特定属性在 viewmodel1 中发生变化,那么我想通知 viewmodel2 具有可观察的集合,属性“A”已被更改 1)我想为特定财产而不是全部解雇它。

我尝试了下面的代码但没有工作。请让我知道我是如何做到这一点的。

public class Model1 : INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    // Create custom event
    public event EventHandler NotifyChange;

    private string testProperty;
    public string TestProperty
    
        get
        
            return testProperty;
        
        set
        
            testProperty = value;
            // If changing properties, fire your OnPropertyChanged to update UI
            OnPropertyChanged("TestProperty");
        
    

    private void OnPropertyChanged(string propName)
    
        if (PropertyChanged != null)
        
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
            // Fire your custom event if a property changed
            NotifyChange(this, null);
        
    



public class Model2 : INotifyCollectionChanged

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public Model2()
    
        // Assuming there is an accessible instance of model1
        Model1 m1Instance = new Model1();
        // Hook up your NotifyChange event from model1
        m1Instance.NotifyChange += Model1Changed;
    

    private void Model1Changed(object sender, EventArgs e)
    
        // this will be triggered on change in model1
    

    private void OnCollectionChanged(object singleObject)
    
        if (CollectionChanged != null)
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, singleObject));
    

【问题讨论】:

那么,您是说当您在 Model1 上设置属性 TestProperty 时,不会调用 Model2 中的事件处理程序? @TimothyGhanem 我到底想做的是,当我在 viwmodel1 中的属性发生变化时,我想在 viewmodel2 中重新绑定我的可观察集合..... 为什么不让Model2继承自ObservableCollection,然后从Model1绑定到UI呢? @stylishCoder :确保两个 VM 都实现 INotifyPropertyChanged。订阅其他视图模型实例的 PropertyChanged 事件。这就是您识别属性更改所需要做的一切。 我可以建议您研究一下 PubSub 事件的某种事件聚合器吗?如果您要遵循正确的 MVVM 实践,这将是您的最佳选择。如果可以的话,我建议使用 Prism。以下是更多信息:c-sharpcorner.com/UploadFile/5ffb84/… 【参考方案1】:

使用 PubSub 事件

我的建议是调查PubSub 事件。

我的建议是使用 Prism。以下是更多信息:http://www.c-sharpcorner.com/UploadFile/5ffb84/prism-event-aggregator-in-wpf-with-mvvm/

在这种情况下,您将坚持正确的 MVVM 做法。

这是 MSDN 非常有用的指南:https://msdn.microsoft.com/en-us/library/ff649664.aspx

对它的工作原理以及如何使用/实现它有一个很好的阅读。


或者

这可行,但如果可能,我仍会推迟使用 PubSub 事件。

你可以试试这个:

public class Model1 : INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    private string _property;
    public string Property
    
        get  return _property; 
        set
        
            _property = value;
            OnPropertyChanged("Property");
        
    

    private void OnPropertyChanged(string propertyName)
    
        var handler = PropertyChanged;
        if (handler != null)
        
            handler(this, new PropertyChangedEventArgs(propertyName));
        
    


public class Model2

    public Model2()
    
        // You might be storing your Model1 as a property in the Model2?
        // I don't know, but I've put it in the constructor just for example.
        var model1 = new Model1();
        model1.PropertyChanged += OnModel1PropertyChanged;
    

    private void OnModel1PropertyChanged(object sender, PropertyChangedEventArgs e)
    
        if (e.PropertyName == "Property")
        
            // Do stuff here when the right property has changed in model 1
        
    

例如,我在 Model2 的构造函数中只有 newModel1 - 您可能正在分配它并将其作为字段或属性存储在 Model2 ViewModel 的其他地方。

如果您在 ViewModel 中有 ViewModel(父 VM > 子 VM),这可能特别有用。

我经常使用父 > 子 VM,我不认为这违反了 MVVM 最佳实践,但我仍然使用EventAggregator,而不是events


附带说明,如果您使用的是 C#6:

使用nameof(Property) 代替“魔术字符串”(例如"Property")。这使得重构更容易,编译器可以告诉你错误——但本质上是做同样的工作。在您的setter 中的OnPropertyChanged() 调用中使用它 查看属性名时也可以使用nameof关键字,原理同上。像这样:if (e.PropertyName == nameof(Model1.Property)) ... 使用空传播:PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));。将您的 OnPropertyChanged 的方法主体更改为一个不错的单行,同时仍然做同样的工作

我离题了

我会始终尽可能坚持正确的 MVVM 实践。

我每天都使用 Prism 的 EventAggregator,我会发誓。

仔细阅读 PubSub Events(您可以使用任何类型的事件聚合器,但我会说最好使用 Prism 的)

希望这会有所帮助! :)

【讨论】:

当我点击 tab1 中的任何按钮时尝试通知 tab 2 时,我遇到了一个问题。您能提供任何解决方案吗? 任何按钮?您是使用ICommands(或DelegateCommands,如果您使用Prism's)来处理您的按钮点击,还是使用btn1.Click += ... 委托处理程序? 我正在使用delegatecommand,当它被触发时,我想调用tab2 viewmodel.....我无法调用......请让我知道如何在这里调用tab2方法??? 与 PubSub 事件相同的主体。在命令的处理程序中,您需要使用事件聚合器发布事件。在 tab2 VM 中,您需要确保在 ViewModel 加载/初始化/其他时订阅该事件,并在它从事件聚合器接收事件时创建一个处理程序。【参考方案2】:

如果这只是为了通知 Model2,我认为您不需要所有这些实现。你可以做类似的事情

public string TestProperty

    get
    
        return testProperty;
    
    set
    
        testProperty = value;
        // If changing properties, fire your OnPropertyChanged to update UI
        OnPropertyChanged("TestProperty");
        //Here you can call a method of Model2 sating that its changed
          Model2 m2Instance = new Model2();
          m2Instance.ValueChanged();

    

在模型 2 中添加方法 ValueChanged。

【讨论】:

但问题来了,我们只对一个属性进行硬编码,不是吗? 您在问题中提到,例如您更改特定属性时。所以我想你的要求只是这样。 ok np @Joseph 我到底想做的是,当我在 viwmodel1 中的属性发生变化时,我想在 viewmodel2 中重新绑定我的可观察集合【参考方案3】:

那是因为您没有注册到PropertyChanged。您正在NotifyChange 上注册您的事件处理程序,因此Model1 中的PropertyChangedNULL,因此不会触发NotifyChange

因此,您需要按照以下方式实现您的OnPropertyChanged

private void OnPropertyChanged(string propName)

    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propName));

    if (NotifyChange != null)
        NotifyChange(this, null);

最后,如果您希望针对特定属性触发 NotifyChange,则在触发事件之前调整上面的代码以检查 propName

【讨论】:

你能编辑上面的代码以便我更好地理解吗? @Timothy,我能否建议您更好地调用事件。当您进入null 时,总是有可能检查PropertyChanged 实际上可能变成null。像这样:var handler = PropertyChanged; if (handler != null) handler(this, new ...); 。更好的是,在 C#6 中更容易使用 null 传播:PropertyChanged?.Invoke(this, new ...);

以上是关于如果 viewmodel1 属性发生变化,通知 viewmodel2的主要内容,如果未能解决你的问题,请参考以下文章

vue的computed如果没有出现在模板里面,当它依赖的响应式属性发生变化,getter会触发吗?

vue的computed如果没有出现在模板里面,当它依赖的响应式属性发生变化,getter会触发吗?

Knockout JS 订阅 2 个 observables 的方式是,如果 2 个 observables 中的任何一个发生变化,我只会收到一次通知

使用多任务处理时视图大小发生变化时如何设置通知 |迅速

如果属性似乎没有名称,我如何告诉 NotifyPropertyChanged 方法哪个属性发生了变化?

vue的数据双向绑定