C# MVVM:修改模型,如何正确更新ViewModel和View?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10324009/
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-08-09 13:24:10  来源:igfitidea点击:

MVVM: Modified model, how to correctly update ViewModel and View?

c#silverlightmvvmdomain-driven-designprism

提问by ndsc

Case

案件

Say I have a Personclass, a PersonViewModeland a PersonView.

假设我有一个Person类, aPersonViewModel和 a PersonView

Updating properties from PersonViewto the Personmodel is simple enough. PersonViewModelcontains a Personobject and has public properties the PersonViewbinds to in order to update the Person model.

从更新性质PersonViewPerson模型是很简单的。PersonViewModel包含一个Person对象并具有PersonView绑定到的公共属性以更新 Person 模型。

However.

然而。

Imagine the Personmodel can get updated by Service. Now the property change needs to be communicated to the PersonViewModeland then to the PersonView.

想象一下,Person模型可以通过Service. 现在需要将属性更改传达给PersonViewModel,然后传达给PersonView

This is how I would fix it:

这是我将如何解决它:

For each property on the Personmodel I would raise the PropertyChanged event. PersonViewModelsubscribes to the PropertyChanged event of Person. PersonViewModelwould then raise another PropertyChanged in order to update the PersonView.

对于Person模型上的每个属性,我都会引发 PropertyChanged 事件。PersonViewModel订阅 的 PropertyChanged 事件PersonPersonViewModel然后会引发另一个 PropertyChanged 以更新PersonView.

This to me seems the most obvious way but I kind of want to throw this question out there in the hope of someone showing me a nicer way. Is it really this simple or are there better ways to mark the model as modified and update the respective properties on the ViewModel?

这对我来说似乎是最明显的方式,但我有点想提出这个问题,希望有人向我展示更好的方式。真的这么简单还是有更好的方法来将模型标记为已修改并更新 ViewModel 上的相应属性?

Additions

添加

The PersonView's DataContext is PersonViewModel. Persongets populated from JSON and gets updated many times during its lifetime.

PersonView的DataContext的是PersonViewModelPerson从 JSON 填充并在其生命周期中多次更新。

Feel free to suggest architectual changes for my particular case.

请随时为我的特定情况建议架构更改。

Answer

回答

I marked aqwert as the answer of my question since it provided me with an alternative to the solution I already proposed.

我将 aqwert 标记为我的问题的答案,因为它为我提供了我已经提出的解决方案的替代方案。

采纳答案by aqwert

If the view is binding to the Model directly then as long as the service is using the same instance any changes to the model properties will be propogated to the view.

如果视图直接绑定到模型,那么只要服务使用相同的实例,对模型属性的任何更改都将传播到视图。

However if you are recreating a new model in the service then you will give the viewmodel the new model. I expect to see the model as a property on the view model so when you set that property all binding should be alerted to the change.

但是,如果您在服务中重新创建新模型,那么您将为视图模型提供新模型。我希望将模型视为视图模型上的一个属性,因此当您设置该属性时,所有绑定都应该收到更改的警报。

//in the ViewModel
public Person Model
{
   get { return _person; }
   set { _person = value; 
         RaisePropertyChanged("Model");  //<- this should tell the view to update
        }
}

EDIT:

编辑:

Since you state there are specific ViewModellogic then you can tailor those properties in the ViewModel

由于您声明有特定的ViewModel逻辑,因此您可以在ViewModel

 private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
 {
      if(e.PropertyName == "Prop1") RaisePropertyChanged("SpecicalProperty");
      ...
 }

  public string SpecicalProperty
  {
     get
     {
         reutrn Model.Prop1 + " some additional logic for the view"; 
     }
   }

IN XAML

在 XAML 中

  <TextBlock Text="{Binding Model.PropertyDirect}" />  
  <TextBlock Text="{Binding SpecicalProperty}" />

This way only both the Modeland ViewModelpropertys are bound to the view without duplicating the data.

这样,只有ModelViewModel属性都绑定到视图而不复制数据。

You can get fancier creating a helper to link the property changes from the model to the view model or use a mapping dictionary

您可以创建一个助手以将属性更改从模型链接到视图模型或使用映射字典

 _mapping.Add("Prop1", new string[] { "SpecicalProperty", "SpecicalProperty2" });

and then find the properties to update by getting the list of properties

然后通过获取属性列表找到要更新的属性

 private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
 {

      string[] props;
      if(_mapping.TryGetValue(e.PropertyName, out props))
      {
          foreach(var prop in props)
              RaisePropertyChanged(prop);
      } 
 }

回答by PVitt

When the view binds directly to the model (which is also the case when the ViewModel exposes the Model) you are mixing UI code and data code. The goal of MVVM is to separate these two code domains. That's what the ViewModel is for.

当视图直接绑定到模型时(当 ViewModel 公开模型时也是这种情况),您正在混合 UI 代码和数据代码。MVVM 的目标是将这两个代码域分开。这就是 ViewModel 的用途。

The view model has to have it's own properties the view can bind to. An example:

视图模型必须拥有自己的视图可以绑定到的属性。一个例子:

class PersonViewModel
{
    private Person OriginalModel { get; set; }

    public ValueViewModel<string> Name { get; set; }
    public ValueViewModel<int> Postcode { get; set; }

    protected void ReadFromModel(Person person)
    {
        OriginalModel = person;
        Name.Value = OriginalModel.Name;
        Postcode.Value = OriginalModel.Postcode;
    }

    protected Person WriteToModel()
    {
        OriginalModel.Name = Name.Value; //...
        return OriginalModel;
    }
}

Using such a ViewModel-design really separates your data objects from your user interface code. When the structure of the class Person is changed, the UI doesn't need to be fit accordingly, because the ViewModel separates them from each other.

使用这样的 ViewModel 设计确实将您的数据对象与您的用户界面代码分开。当类 Person 的结构发生变化时,UI 不需要相应地适应,因为 ViewModel 将它们彼此分开。

Now to your question. As you can see in the example above, I used a generic ValueViewModel<T>. This class implements INotifyPropertyChanged(and some other stuff). When you receive a new Personinstance, you only have to call ReadFromModel(newPerson)on your ViewModel to have the UI updated, because the ValueViewModels the View binds to will inform the UI when their value changes.

现在回答你的问题。正如你在上面的例子中看到的,我使用了一个通用的ValueViewModel<T>. 这个类实现INotifyPropertyChanged(和其他一些东西)。当你收到一个新的Person实例时,你只需要调用ReadFromModel(newPerson)你的 ViewModel 来更新 UI,因为 View 绑定到的 ValueViewModels 会在它们的值发生变化时通知 UI。

Here an extremely simplified example of the internal structure of the ValueViewModel:

这是一个极其简化的内部结构示例ValueViewModel

class ValueViewModel<T> : INotifyPropertyChanged
{
    private T _value;
    public T Value 
    {
        get { return _value;}
        set
        {
            _value = value;
            RaisePropertyChanged("Value");
        }
    }
}

This is an approach we used in our MVVM library. It has the advantage that it forces the developer to clearly separate code from the designers concerns. And, as a side effect, it generates a standardized code layout in all your Views and ViewModels and thus improves code quality.

这是我们在 MVVM 库中使用的一种方法。它的优点是它迫使开发人员将代码与设计人员的关注点清楚地分开。而且,作为副作用,它会在您的所有视图和视图模型中生成标准化的代码布局,从而提高代码质量。