维护WPF MVVM查看模型和模型关系

Pat*_*ick 5 c# wpf mvvm

这似乎是一个基本问题,但我无法弄清楚最佳实施.如何管理两个视图模型及其相应模型之间的关系.

例如,如果您在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; }
}
Run Code Online (Sandbox Code Playgroud)

Jay*_*Jay 6

您的视图模型解耦从视图模型.看起来您可能会混淆视图和视图模型的概念.

视图模型既是网关又是两者之间的守门人 - 它决定了从模型到视图以及从视图返回到模型的方式,以及以何种形式.

您可以在VM setter中设置model属性.您可以从视图中保存状态,并仅在用户单击"保存"时将这些更改传播到模型.你可以在其他地方坚持这种状态,这样有人可以在坚持到视图之前回来再努力.

视图模型可以非常熟悉模型,因此视图根本不需要知道它.

我不确定我是否关注在视图模型中公开展示模型.您还没有在示例代码中这样做.您已经公开了一个与您在模型中使用的类型相同的对象,但这就像int在模型和视图模型中使用age 一样- 您没有公开实际的模型对象; 您仍然可以控制是否以及何时在模型中设置视图模型中的值.


Tom*_*Bot 5

为了显示Model和ViewModel之间的可能关系,我首先通过更改Occupationinto 的类型来简化您的示例string.然后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);
  }
}
Run Code Online (Sandbox Code Playgroud)

与您的版本的重要区别在于PersonModel并且PersonViewModel 实现了INotifyPropertyChanged.这很重要,因为否则PersonModel直接更改属性(即不经过PersonViewModel)将对View无效.还要注意PropertyChangedEvent模型如何通过管道传输到View.

现在假设Occupation不是一个string具有自己属性的类,例如:

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");
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在View和Model之间使用ViewModel可以灵活地将数据呈现给View.以下是两种选择:

选项1Occupation直接公开属性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);
  }
}
Run Code Online (Sandbox Code Playgroud)

OccupationSalary物业可直接进入Salary酒店Occupation.请注意现在需要处理的PropertyChanged事件Occupation,以及我们必须重命名该属性occupation_PropertyChanged.

选项2(推荐)Occupation通过a 公开属性OccupationViewModel.如果您需要实现特定于的任何业务逻辑,则应该执行此操作Occupation.举个例子,这可能是你打算做的:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的OccupationViewModel那样,结构与PersonViewModel我在开头所展示的简化结构完全相同.你的版本最重要的区别OccupationViewModel是,它暴露了性质OccupationModel,而不是OccupationModel自己.