在模型中更改属性时收到通知

时间:2012-03-29 18:31:48

标签: c# wpf mvvm

关于是否应该在模型中实现INotifyPropertyChanged似乎存在矛盾的想法。我认为它应该在ViewModel中实现,但我无法弄清楚它是如何实现的。在stackoverlow.com(In MVVM model should the model implement INotifyPropertyChanged interface?In MVVM should the ViewModel or Model implement INotifyPropertyChanged?)上有很多提及同样的想法,但我找不到任何示例来说明如何做到这一点。

比方说,我有一个模型Person:

Public Person {
  public int Age { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public void NameChange( string newName );
}

我如何实施ViewModel,以便AgeFirstNameLastName中的更改都被识别?

Public PersonViewModel : INotifyPropertyChanged {
  Person _person;
  public event PropertyChangedEventHandler PropertyChanged;
  void OnPropertyChanged(string propertyName) {
    if(this.PropertyChanged != null)
      this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }
  //ctor, Properties, etc...
}

编辑 - 澄清:

所以而不更改Person模型如何修改ViewModel以获得更新通知?

这甚至可能吗?如果没有,订阅“模型中的INPC是 baaaad ”的人如何得到模型变化的通知?

4 个答案:

答案 0 :(得分:2)

ViewModel绝对应该实现INotifyPropertyChanged。我对是否应该在模型中实施它没有强烈的意见。当模型属性在绑定到View时不会独立于ViewModel进行更改时,我认为您不需要它。

无论如何,这是我在ViewModel中实现INotifyPropertyChanged时尚未在模型中实现的方式:

public class PersonViewModel : INotifyPropertyChanged 
{
    private Person person;

    public event PropertyChangedEventHandler PropertyChanged;

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

    public PersonViewModel(Person person)
    {
        this.person = person;
    }

    public int Age
    {
        get { return person.Age; }
        set
        {
            if (value != person.Age)
            {
                person.Age = value;
                OnPropertyChanged("Age");
            }
        }
    }

    public string FirstName
    {
        get { return person.FirstName; }
        set
        {
            if (value != person.FirstName)
            {
                person.FirstName = value;
                OnPropertyChanged("FirstName");
            }
        }
    }

    public string LastName
    {
        get { return person.LastName; }
        set
        {
            if (value != person.LastName)
            {
                person.LastName = value;
                OnPropertyChanged("LastName");
            }
        }
    }
}

看到你如何更新你的问题,我需要在没有模型中实现的INotifyPropertyChanged(或类似的自定义通知事件)的情况下添加它,你无法获得有关模型中发生的变化的通知,而不是视图模型。我猜你应该能够避免这种情况。否则只需在其中实现INotifyPropertyChanged。如果你需要,那没有什么不妥。

答案 1 :(得分:1)

有趣的问题。我已经阅读了一年多的MVVM,我仍然不确定。

如果您的应用程序表示某个进程的状态,并且该状态在内部进行了修改而没有用户的任何交互,那么您的模型需要能够通知您的viewmodel它已更改。 因此,如果您的模型实现了INotifyPropertyChanged,并且您的viewmodel只将相同的信息传递给视图,那么......您的viewmodel是否确实需要存在...?

在我们公司,我们考虑两个主要案例:

  • 我们在开发之前使用非常严格的UML分析来构建我们的软件(不那么敏捷)。当我们想要在屏幕上显示我们的对象时,它们会返回不同的视图,这些视图在需要时使用Bindings(使用ContentControl等)。我们软件所需的大多数视图都显示了这些实现INotifyPropertyChanged的对象,因此也是ViewModel的一种。

  • 要构建软件主视图(视图结构),我们为它们创建全局视图和ViewModel。那是我们真正遵循MVVM实践的时候。

也许我错过了一个关于MVVM的观点,但根据我的经验,这并不是你必须始终遵循的模式。这是开发WPF应用程序的一种非常好的思维方式,但是对于每个视图创建ViewModel在我看来都是一个很大的开销。

你们怎么看待这种做法?

致以最诚挚的问候,

安托

编辑31.03.2012

我找到了一篇非常有趣的文章,解释了如何在viewmodel中处理模型属性,而不必在viewModel中为每个属性实现代理属性。 作者还说了一些关于在模型中实现INPC以及视图模型监听它的说法。 我认为这是迄今为止我读过的关于MVVM的最实用的文章。 看看这个 : http://msdn.microsoft.com/en-us/magazine/ff798279.aspx

答案 2 :(得分:0)

根据我的经验,Model个对象不必(也可能不应该)知道它们是在View中构建的。通常,Model个对象是永远不会被允许处于无效状态的实体ViewModel个对象是构造Model对象的东西。

所以,既然你永远不想创造一个年纪很大或很年轻的人,并且每个人都需要一个名字,那么你的Person课程可能会是这样的:

public class Person {
    public int Age { get; private set; }
    public string Name { get; private set; }
    public Person(int age, string name) {
        if (age < 0 || age > 150) throw new ArgumentOutOfRangeException();
        if (string.IsNullOrEmpty(name)) throw new ArgumentNullException();
        Age = age;
        Name = name;
    }
}

您的PersonViewModel可能看起来像这样::

class PersonViewModel : INotifyPropertyChanged {
    private int _Age;
    private int _Name;
    public int Age {
        get { return _Age; }
        set {
            if (_Age.Equals(value)) return;
            _Age = value;
            RaisePropertyChanged("Age");
        }
    }
    public string Name {
        get { return _Name; }
        set {
            if (_Name.Equals(value)) return;
            _Name = value;
            RaisePropertyChanged("Name");
        }
    }
    public Person CreatePerson() {
        return new Person(_Age, _Name);
    }
}

然后,您可以在PersonViewModel中添加所需的任何值,而无需担心创建无效的Person对象。您还可以在PersonViewModel中实施可能比Person类中的验证更严格的验证(例如,将年龄范围限制为18岁以上的成年人(请参阅IDataErrorInfo) )。

答案 3 :(得分:0)

除了错字之外,你几乎拥有它;)

您需要添加的是构造函数和属性定义:

public class PersonViewModel : INotifyPropertyChanged
{
    Person _person;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }

    public PersonViewModel(Person person)
    {
        _person = person;
    }

    public int Age
    {
        get
        {
            return _person.Age;
        }
        set
        {
            _person.Age = value;
            OnPropertyChanged("Age");
        }
    }
}

如果您有选择,我肯定会建议在模型中实现INotifyPropertyChanged,因为您不必担心将模型转换为ViewModel并返回。

但如果你不能,请参见上文:)