C# 维护WPF MVVM视图模型&;模型关系

C# 维护WPF MVVM视图模型&;模型关系,c#,wpf,mvvm,C#,Wpf,Mvvm,这似乎是一个基本问题,但我无法找出最佳实现。如何管理两个视图模型及其对应模型之间的关系 例如,如果您在PersonViewModel上更改了Occupation属性,那么该更改将如何深入到PersonModel中的Occupation属性 我现在看到它的唯一方式是在视图模型中公开模型,但我认为这挫败了MVVM的目的——将模型与视图分离 internal class PersonViewModel : INotifyPropertyChanged { private readonly Pe

这似乎是一个基本问题,但我无法找出最佳实现。如何管理两个视图模型及其对应模型之间的关系

例如,如果您在PersonViewModel上更改了Occupation属性,那么该更改将如何深入到PersonModel中的Occupation属性

我现在看到它的唯一方式是在视图模型中公开模型,但我认为这挫败了MVVM的目的——将模型与视图分离

internal class PersonViewModel : INotifyPropertyChanged
{
    private readonly PersonModel person;

    private OccupationViewModel occupation;

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

    public OccupationViewModel Occupation
    {
        get { return this.occupation; }
        set 
        { 
            if (!this.occupation.Equals(value))
            {
                this.occupation = value;
                this.person.Occupation = this.occupation.Occupation; // Doesn't seem right

                this.OnPropertyChanged(new PropertyChangedEventArgs("Occupation"));
            }
        }
    }
}

internal class OccupationViewModel : INotifyPropertyChanged
{
    public OccupationViewModel(OccupationModel occupation)
    {
        this.Occupation = occupation;
    }

    public OccupationModel Occupation { get; set; } // Is this right?
} 

internal class PersonModel
{
    public OccupationModel Occupation { get; set; }
}
视图模型正在将模型与视图分离。看起来您可能混淆了视图和视图模型的概念

视图模型既是两者之间的网关又是守门人——它决定了是什么使它从模型到视图,从视图回到模型,以及以什么形式

您可以在VM setter中设置模型属性,也可以不设置。您可以从视图中保存状态,并且仅当用户单击“保存”时才将这些更改传播到模型。您可以在其他位置保留该状态,以便有人可以在将其保留到视图之前返回并进一步处理该状态

internal class PersonViewModel : INotifyPropertyChanged
{
    private readonly PersonModel person;

    private OccupationViewModel occupation;

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

    public OccupationViewModel Occupation
    {
        get { return this.occupation; }
        set 
        { 
            if (!this.occupation.Equals(value))
            {
                this.occupation = value;
                this.person.Occupation = this.occupation.Occupation; // Doesn't seem right

                this.OnPropertyChanged(new PropertyChangedEventArgs("Occupation"));
            }
        }
    }
}

internal class OccupationViewModel : INotifyPropertyChanged
{
    public OccupationViewModel(OccupationModel occupation)
    {
        this.Occupation = occupation;
    }

    public OccupationModel Occupation { get; set; } // Is this right?
} 

internal class PersonModel
{
    public OccupationModel Occupation { get; set; }
}
视图模型可以非常了解模型,因此视图根本不必知道它


我不确定我是否理解您关于在视图模型中公开模型的担忧。在示例代码中没有这样做。您已经公开了一个与您在模型中使用的类型相同的对象,但这就像在模型和视图模型中使用
int
表示年龄一样——您没有公开实际的模型对象;您仍然可以控制是否以及何时在模型上设置视图模型中的值。

看起来您应该在PersonViewModel上公开“Person”属性,而不是公开Occupation属性。占用财产似乎是不必要的一层

person属性如下所示,Occupation属性可以由类似“viewModel.person.Occupation”的内容引用

        public Person Person
    {
        get
        {
            return this.person;
        }
        set
        {
            if (!this.person.Equals(value))
            {
                this.person = value;
                this.OnPropertyChanged(new PropertyChangedEventArgs("Person"));
            }
        }
    }

为了显示Model和ViewModel之间可能的关系,我首先简化了您的示例,将
职业
的类型更改为
字符串
。然后,
PersonModel
PersonViewModel
可能如下所示:

public class PersonModel : INotifyPropertyChanged
{
  private string occupation;
  public string Occupation
  {
    get
    {
      return this.occupation;
    }
    set
    {
      if (this.occupation != value)
      {
        this.occupation = value;
        this.OnPropertyChanged("Occupation");
      }
    }
  }
}

public class PersonViewModel: INotifyPropertyChanged
{
  private PersonModel model;

  public string Occupation
  {
    get
    {
      return this.model.Occupation;
    }
    set
    {
      this.model.Occupation = value;
    }
  }

  public PersonViewModel(PersonModel model)
  {
    this.model = model;
    this.model.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);
  }

  private void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
  {
    this.OnPropertyChanged(e.PropertyName);
  }
}
与您的版本的重要区别在于
PersonModel
PersonViewModel
都实现了
INotifyPropertyChanged
。这一点很重要,因为直接更改
PersonModel
的属性(即不经过
PersonViewModel
)在视图中不会产生任何效果。还要注意模型中的
属性changedevent
是如何通过管道传输到视图的

现在假设
职业
不是一个
字符串
,而是一个具有自己属性的类,例如:

public class OccupationModel : INotifyPropertyChanged
{
  private double salary;
  public double Salary
  {
    get
    {
      return this.salary;
    }
    set
    {
      if (this.salary != value)
      {
        this.salary = value;
        this.OnPropertyChanged("Salary");
      }
    }
  }
}
在视图和模型之间使用ViewModel可以在如何向视图显示数据方面提供一定的灵活性。以下是两种方法:

选项1直接在
PersonViewModel
中公开
职业的属性。这是一个简单的解决方案,因为您不需要实现另一个ViewModel

public class PersonViewModel: INotifyPropertyChanged
{
  private PersonModel model;

  public double OccupationSalary
  {
    get
    {
      return this.model.Occupation.Salary;
    }
    set
    {
      this.model.Occupation.Salary = value;
    }
  }

  public PersonViewModel(PersonModel model)
  {
    this.model = model;
    this.model.Occupation.PropertyChanged += new PropertyChangedEventHandler(occupation_PropertyChanged);
  }

  private void occupation_PropertyChanged(object sender, PropertyChangedEventArgs e)
  {
    this.OnPropertyChanged("Occupation" + e.PropertyName);
  }
}
occulationsalary
属性可直接访问
occulation
中的
Salary
属性。请注意,现在需要如何处理
occulation
PropertyChanged
事件,并且我们必须在
occulation\u PropertyChanged
中重命名属性

选项2(推荐)通过
职业视图模型
公开
职业的属性。如果您需要实现特定于
职业的任何业务逻辑,您应该这样做。举个例子,这可能就是您打算做的:

public class PersonViewModel: INotifyPropertyChanged
{
  private PersonModel model;
  private OccupationViewModel occupationViewModel;
  public OccupationViewModel OccupationViewModel
  {
    get
    {
      return this.occupationViewModel;
    }
  }

  public PersonViewModel(PersonModel model)
  {
    this.model = model;
    this.occupationViewModel = new OccupationViewModel(this.model.occupation);
  }
}

public class OccupationViewModel : INotifyPropertyChanged
{
  private OccupationModel model;

  public double Salary
  {
    get
    {
      return this.model.Salary;
    }
    set
    {
      this.model.Salary = value;
    }
  }

  public OccupationViewModel(OccupationModel model)
  {
    this.model = model;
    this.model.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);
  }

  private void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
  {
    this.OnPropertyChanged(e.PropertyName);
  }
}

正如您所看到的,
OccupationViewModel
的结构与我在开始时展示的简化的
PersonViewModel
完全相同。与您的
职业视图模型
版本的重要区别在于,它公开了
职业模型
的属性,而不是
职业模型
本身。

为什么PersonViewModel.Occupation是可写属性?通常它是只读的——毕竟,谁来设置它呢?我正在考虑公开它,以便其他视图模型可以更改它。或者这是某种方法或命令的用武之地?顺便说一句,我是一个WPF、MVVM新手。Patrick我想Joe说的是,视图模型可以在外部修改,而无需设置它(通过访问其属性):
personViewModel.Occupation.Occupation=new OccupationModel(“软件工程师”)…此外,用户不会收到您的评论通知,除非(a)您对他们的答案发表评论,(b)他们对问题加了星号/偏好,或者(c)您在开始提问时,至少在他们的部分姓名前加上“@”--比如“@Joe I wasing…”@Jay,在某些情况下您不想直接设置视图模型吗?我认为视图模型有很多属性。在我看来,设置属性(“PersonViewModel.Occupation”)比遍历所有属性并更改它们更容易。你觉得怎么样?谢谢你的解释。我担心在职业视图模型中暴露职业模型。在视图模型中公开模型是错误的做法吗?我的假设是,模型应该由视图模型封装并对其他人隐藏。@在简单、直接的应用程序中,有时直接公开模型是有利的。如果您处理的是复杂性度量,那么是的,最好通过视图模型上的属性传递您想要公开的任何内容。当您处理组合时,作为一个面向对象的原则,更一般地说,这是正确的。