【问题标题】:Get notified when properties change in the Model当模型中的属性发生变化时得到通知
【发布时间】:2012-04-13 10:49:15
【问题描述】:

对于是否应该在模型中实现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”的人如何获得模型变化的通知?

【问题讨论】:

  • 作为一个实际问题,在模型内启动的各个字段的更改多久会发生一次?
  • 我不熟悉C#,但是你能用观察者设计模式来处理这个吗?
  • @Rich - 我没有具体的型号。我编写了实现 INotifyPropertyChanged 的​​模型,但总感觉不对。我读过很多人也觉得它是错误的,所以我的问题主要是弄清楚那些人是如何管理他们的模型的。
  • @Bwalks 我曾考虑过一个 ObservableCollection,但又一次由模型来实现 ObservableCollection。在我看来,这与 INotifyPropertyChanged 几乎相同,因为模型必须实现一些让消费者知道变化的东西。

标签: c# wpf mvvm


【解决方案1】:

ViewModel 绝对应该实现 INotifyPropertyChanged。我对是否也应该在模型中实现它没有强烈的意见。当模型属性在绑定到视图时不会独立于 ViewModel 发生变化时,我认为您不需要它。

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

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 。如果你需要,那也没什么问题。

【讨论】:

  • 所以这种方法只有在模型属性仅依赖于 ViewModel 的情况下才有效?
  • @SaulBack 是的,在这种情况下,模型无法让视图模型或视图知道它已更改。当然,这仅适用于视图打开或模型绑定它的时间。当您将模型重新绑定到视图时,后者当然会显示它的最新状态。您必须问问自己,您的模型是否真的会自行改变(或需要改变)。
【解决方案2】:

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

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

在我们公司,我们主要考虑两种情况:

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

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

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

大家觉得这种做法怎么样?

最好的问候,

安东尼

编辑 31.03.2012

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

【讨论】:

    【解决方案3】:

    根据我的经验,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))。

    【讨论】:

      【解决方案4】:

      保留你几乎有的错别字;)

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

      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,因为您不必担心将模型转换为视图模型并返回。

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

      【讨论】:

        猜你喜欢
        • 2022-07-22
        • 2017-10-18
        • 1970-01-01
        • 2010-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-18
        • 2015-02-04
        相关资源
        最近更新 更多