MVVM正确的方式

Kal*_*ale 2 c# wpf binding mvvm

我在其他关于如何实现MVVM的问题中得到了提示.在Student类本身进行更改时(我在项目中发生了很多变化),我在将绑定更新传递给GUI时遇到了问题.有没有办法简化这些事情并以比实现更紧凑的方式使用它?或者这是实现MVVM的最先进技术?

class MainWindowViewModel : INotifyPropertyChanged
{
   ObservableCollection<StudentViewModel> studentViewModels = new ObservableCollection<StudentViewModel>();

   public ObservableCollection<StudentViewModel> StudentViewModels
   {
      get { return studentViewModels; }
   }

   public MainWindowViewModel()
   {
      studentViewModels.Add(new StudentViewModel());
      studentViewModels.Add(new StudentViewModel());
      studentViewModels.Add(new StudentViewModel());
   }

   public event PropertyChangedEventHandler PropertyChanged;
   internal void OnPropertyChanged(String propertyName)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}


class StudentViewModel : INotifyPropertyChanged
{
   Student model;
   public String StudentFirstName
   {
      get { return model.StudentFirstName; }
      set { model.StudentFirstName = value; }
   }
   public String StudentLastName
   {
      get { return model.StudentLastName; }
      set { model.StudentLastName = value; }
   }

   public StudentViewModel()
   {
      model = new Student();
      this.model.PropertyChanged += (sender, e) => 
      {
         switch (e.PropertyName)
         {
            case "StudentFirstName": OnPropertyChanged("StudentFirstName"); break;
            case "StudentLastName": OnPropertyChanged("StudentLastName"); break;
            default: break;
         }
      };
   }

   public StudentViewModel(Student model)
   {
      this.model = model;

      this.model.PropertyChanged += (sender, e) =>
      {
         switch (e.PropertyName)
         {
            case "StudentFirstName": OnPropertyChanged("StudentFirstName"); break;
            case "StudentLastName": OnPropertyChanged("StudentLastName"); break;
            default: break;
         }
      ;
   }

   public void changeStudent()
   {
      model.changeStudent();
   }

   public event PropertyChangedEventHandler PropertyChanged;
   internal void OnPropertyChanged(String propertyName)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}



class Student : INotifyPropertyChanged
{
   public String studentFirstName;
   public String StudentFirstName
   {
      get { return studentFirstName; }
      set 
      {
         if (studentFirstName != value)
         {
            studentFirstName = value;
            OnPropertyChanged("StudentFirstName");
         }
      }
   }
   public String studentLastName;
   public String StudentLastName
   {
      get { return this.studentLastName; }
      set
      {
         if (studentLastName != value)
         {
            studentLastName = value;
            OnPropertyChanged("StudentLastName");
         }
      }
   }

   public Student() { }

   public void changeStudent()
   {
      StudentLastName = "McRonald";
   }

   public event PropertyChangedEventHandler PropertyChanged;
   internal void OnPropertyChanged(String propertyName)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

Pat*_*iek 7

首先,我想使用MVVM框架的一个建议(我个人喜欢和使用Caliburn.Micro,但也有MVVM Light别人的无数).

(我Caliburn.Micro将从现在开始使用实现作为示例,因为这是我或多或少知道的一个框架)

为什么?好吧,它为您提供强类型NotifyOfPropertyChange(),内置事件聚合器,窗口管理器等等.这样你就不必每次都重新发明轮子.此外,Caliburn.Micro的引导程序允许您烘焙您选择的IoC容器,如果您想要使用MVVM而不使用框架方式,那么使用WPF并不是那么容易.作为奖励,您可以拦截GUI控件中的事件,因此您实际上不必在代码隐藏中编写任何内容.

有些框架允许您按惯例绑定并简化命令,但您必须阅读它,具体取决于您使用的框架.

第二件事,我强烈赞成完全重写视图模型,因此它们是独立的类而不是数据模型的包装器.您可以使用AutomapperValueInjecter稍后进行映射.

那么你有视图模型,例如:

public class StudentViewModel : PropertyChangedBase
{
    private string firstName;
    public string FirstName
    {
        get { return firstName; }
        set
        {
            firstName = value;
            NotifyOfPropertyChange(() => FirstName);
        }       
    }

    private string lastName
    public string LastName
    {
        get { return lastName; }
        set
        {
            lastName = value;
            NotifyOfPropertyChange(() => LastName);
        }       
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是视图模型.然后在视图模型中使用数据绑定,验证等.

您的Student课程可以是简单的DTO或EF级或其他类.为简单起见,让我们选择哑巴DTO:

public class Student
{
    public string FirstName { get;set; }
    public string LastName { get;set; }
}
Run Code Online (Sandbox Code Playgroud)

因此,仅在保存到数据库时才使用DTO.就是这样.对于"常规"应用程序使用,GUI交互(绑定),您使用视图模型.

这就是Automapper/ValueInjecter发挥作用的地方,因为当您想要'保存'更改/在任何地方添加新学生时,您必须将视图模型映射到模型,例如:

//ValueInjecter
var dataModel = new Student().InjectFrom(this) as Student;
//AutoMapper
var dataModel = Mapper.Map<StudentViewModel, Student>(this);
Run Code Online (Sandbox Code Playgroud)

就是这样.简单,容易,干净.您描述它的方式,您想要更改基础模型.我建议不要这样做,操作视图模型而不是可以通知你的UI.您仅使用模型"修改"数据存储中的数据(保存/更新/获取/删除)或以某种方式"传输"数据(例如,使用REST Web服务),并使用视图模型进行交互.