【问题标题】:MVVM use of INotifyPropertyChanged Model not notifying ViewModelMVVM 使用 INotifyPropertyChanged 模型未通知 ViewModel
【发布时间】:2011-11-17 07:23:18
【问题描述】:

我是第一次尝试使用 MVVM。我有一个 Windows Phone 应用程序 (Mango),它有一个模型类、一个视图模型类和一个视图 xaml 页面。我有绑定到 VM 的控件(文本框),并且 VM 绑定到模型。

模型和视图模型都实现了INotifyPropertyChanged。我正在使用的实现被复制,这样我就可以用它来试图弄清楚我在用 INPC 做什么。这是两个类中列出的代码:

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(String info)
{
   if (PropertyChanged != null)
   {
      PropertyChanged(this, new PropertyChangedEventArgs(info));
   }
}

我在 Model 类中有一个属性,可以手动设置(从文本框)或计算(通过更改其他属性之一)。让我们称之为一个结果。

如果我更改其他属性之一并逐步执行,则在 Model 类中更改的属性和重新计算的属性中都会调用 INPC,尽管 PropertyChangednull,因此这部分代码被跳过.然后在 VM 中,被更改的属性会逐步通过该类的 INPC(作为 set 访问器的一部分),而这次 PropertyChanged 不是 null,因此会调用 PropertyChanged 方法。但是,对于 Result 属性,不会引发 INPC(该属性没有由其他属性的 set 访问器调用的 INPC)。

这里是Model中的属性之一,不是计算出来的属性:

public int AgeSetting
{
   get
   {
      return (int)GetValueOrDefault(AgeSettingKeyName, AgeSettingDefault);
   }
   set
   {
      AddOrUpdateValue(AgeSettingKeyName, value);
      Calculate();
   }
}

这是模型中计算值的属性。

public int PointsSetting
{
   get
   {
      return (int)GetValueOrDefault(PointsSettingKeyName, PointsSettingDefault);
   }
   set
   {
      AddOrUpdateValue(PointsSettingKeyName, value);
   }
}

在 ViewModel 中,这两个属性都有:

public int Age
{
   get
   {
      return person.AgeSetting;
   }
   set
   {
      person.AgeSetting = value;
      NotifyPropertyChanged("Age");
   }
} 

public int PointsAllowed
{
   get
   {
      return person.PointsSetting;
   }
   set
   {
      person.PointsSetting = value;
      NotifyPropertyChanged("PointsAllowed");
   }
}

以前从未这样做过,我期待 INPC 应该从模型类冒泡到 VM 类,再到 UI。它似乎不是那样工作的。

我知道 Result (calculated) 属性正在发生变化,因为我可以离开页面并返回,并且新显示的值是正确的。我只是不知道如何从模型中的计算值到视图模型,再到视图。

感谢您的任何建议。

【问题讨论】:

  • 一个有用的工具是 VS 中的输出窗口。这将告诉您在启动应用程序/初始化 UI 时是否有任何绑定失败。可能是您的绑定不正确。
  • 我没有意识到这一点。谢谢你。但是,绑定正在工作,因为它确实将存储在 isolatedstoragesettings 中的值拉入文本框
  • 我没有时间弄清楚你真正要问的是什么,所以请你给我一个最爱和edit your title,这样这是一个与你的问题有关的实际问题'再问?仅有机地使用标签的奖励积分。谢谢!
  • 有机地使用标签。这是什么意思?

标签: c# silverlight windows-phone-7 mvvm windows-phone-7.1


【解决方案1】:

如果计算属性的值发生更改,则还引发该属性的 PropertyChanged 事件。这意味着当单个属性更改时,可能会为多个属性触发事件。

此外,如果您的设计可能/智能,您可以直接绑定到模型属性并使模型成为您的 ViewModel 的属性。这大大减少了代码维护。

【讨论】:

  • +1。我看不出在已经实现 INotifyPropertyChanged 的​​类上使用 ViewModel 有什么意义,除非需要其他 MVVM 功能(即,调整您已经可以很好地绑定的东西是没有意义的)。
  • 谢谢你们。是的,巴斯,我可以看到我可能需要为多个属性开火的地方。我认为当 M 发生变化时它是自动的,VM 会看到它。另外,是的,我可以直接从 V 转到 M,但是这个练习的重点是学习 MVVM。我以前没做过,所以想试试。不过,我认为关键是 Maxim 在下面所说的关于事件处理程序和订阅(我不知道的事情,所以会搜索),但是我的 VM 没有“订阅”到 M,它只是获取/设置属性M 的一个实例。我想这就是我需要看的地方。
  • 通常,在 MVVM 中,您根本不会让模型类通知。模型只是逻辑的,并且由视图模型访问/修改。基本上,模型永远不会改变,除非视图模型指示这样做(类似于 MVC 中的模型/控制器交互)。例如,ViewModel 可能会告诉 Model 从数据库中加载数据,然后 ViewModel 会在数据加载完成时通知。
  • 是的,这就是为什么“如果可能/聪明”,它不是领域驱动开发中最干净的方式,但它会节省很多时间。
  • 在这种情况下,我最终消除了 MV。没有真正需要它,因为它只是将模型传递给视图。我将在应用程序的另一部分使用模型视图,但对于这一部分,它确实没有必要。我试图使用它只是因为我以前从未使用过 mvvm(也没有使用过 MVC),但这里确实不需要它。谢谢。
【解决方案2】:

对于依赖于其他属性值的属性,程序员应通知多个属性已更改。

给定一个具有三个属性的类 - Score、Multiplier 和 Total - 其中 Total 取决于 Score 和 Multiplier 的值,我可以这样编写类:

public class ViewModel
{
    private int score;
    public int Score
    {
        get
        {
            return this.score;
        }
        set
        {
            if (this.score != value)
            {
                this.score = value;
                // Notify that this property has changed
                NotifyOfPropertyChange(() => this.Score);
                // Notify that a dependant property has changed
                NotifyOfPropertyChange(() => this.Total);
            }
        }
    }

    private int multiplier;
    public int Multiplier
    {
        get
        {
            return this.multiplier;
        }
        set
        {
            if (this.multiplier != value)
            {
                this.multiplier = value;
                // Notify that this property has changed
                NotifyOfPropertyChange(() => this.Multiplier);
                // Notify that a dependant property has changed
                NotifyOfPropertyChange(() => this.Total);
            }
        }
    }

    public int Total
    {
        get
        {
            return this.Score * this.Multiplier;
        }
    }
}

您会注意到,例如,当我设置 Score 的值时,我通知 Score 和 Total 都发生了变化。您对 NotifyOfPropertyChange 的具体实施看起来与我的不同,但中心思想是相同的。因为 Total 是一个依赖于其他属性值的属性,所以它从不通知。相反,Total 所依赖的属性负责通知。

【讨论】:

    【解决方案3】:

    在模型中,在计算属性的setter中调用NotifyPropertyChanged方法。您的应用程序中的一些逻辑应该是更新模型。确保 ViewModel 订阅了此事件。这非常重要,因为我认为这正是您在这里所缺少的。

    然后,在 ViewModel 中的 EventHandler 中,即订阅了 Models on PropertyChanged 事件,更改 ViewModel 的相应属性值(此处视图模型中的方法更新视图模型中的属性),即绑定 UI到。

    【讨论】:

    • 在我的模型中,每当属性发生变化时,都会调用 NPC 方法,尽管它不在 setter 中,而是在从 setter 调用的另一个方法中(保存值的方法到isostore)。我已经完成了,并且调用了该方法。但是,我可能缺少的是您所说的事件处理程序。我不确定您所说的“订阅模型的 PropertyChanged”事件是什么意思,所以我会调查一下。事实上,我的虚拟机属性只是从模型的一个实例中提取。这可能是关键。我会调查的。谢谢!
    • 例如,如果您的模型有一个公共事件 PropertyChanged,那么您的视图模型应该 A) 具有对模型的引用 B) 在某些时候订阅此事件。像 model.PropertyChanged += OnModelChanged;在 OnModelChanged 中设置 UI 绑定到的公共属性的值。这样通知链接就完成了。
    【解决方案4】:

    听起来您还没有在模型类上实现 INotifyPropertyChanged 接口。如果没有这个,您的事件处理程序在被调用时将为空。

    public class ViewModel : INotifyPropertyChanged
    {
       // Stuff
    }
    
    public class Model : INotifyPropertyChanged
    {
       // Stuff
    }
    

    还可以在这里查看我的博客,了解一些 MVVM 内容。我有一些教程以及一些在不使用字符串值的情况下触发 Inotify 的方法(改用反射)。

    【讨论】:

    • 我看了你的博文。我还没有尝试实现它,但我相信我会的。我喜欢您使用反射来获取已更改属性的名称的方式。但是,在这种情况下,我认为最好的方法是消除此视图的模型视图。我看了看,在这种情况下,模型视图除了将模型传递给视图之外什么也没做。感谢您的提示。
    猜你喜欢
    • 1970-01-01
    • 2011-08-14
    • 1970-01-01
    • 2012-08-21
    • 2013-01-27
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    相关资源
    最近更新 更多