WPF / MVVM:在多个控制器和关注点分离中重用ViewModel

时间:2010-11-26 20:56:13

标签: wpf mvvm viewmodel reusability

在我的 AdministrationController 中,我使用 PupilViewModel ,如:

_adminRepo.GetSchoolclassPupilList().ForEach(s =>
            {
                SchoolclassViewModel sVM = new SchoolclassViewModel(s, _adminRepo);

                foreach (PupilViewModel pVM in sVM.PupilListViewModel)
                {
                    pVM.Documents.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
                    pVM.Documents.AddDocumentDelegate += new Action(OnAddDocument);
                    pVM.Documents.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
                }
                SchoolclassList.Add(sVM);
            }); 

以这样的方式创建PupilViewModel

public SchoolclassViewModel(Schoolclass schoolclass, IAdministrationRepository adminRepo)
        {
            _schoolclass = schoolclass;
            _adminRepo = adminRepo;  

            PupilListViewModel = new ObservableCollection<PupilViewModel>();
            schoolclass.Pupils.ForEach(p => PupilListViewModel.Add(new PupilViewModel(p, _adminRepo)));                      
        }

您肯定注意到PupilViewModel在其构造函数中占用了2个参数。 重要的是第二个参数,它是一个特定于IAdministrationRepository实例的服务/存储库。

现在存在另一个名为 IncidentReportingController 控制器。 在它的构造函数中,我执行与AdministrationController相同的操作:

// When I now try to wrap my pupils into a ViewModel I have a problem:

IEnumerable<Pupil> pupils = incidentRepo.GetPupilIncidentReportDocumentList();         
PupilViewModels = new ObservableCollection<PupilViewModel>(pupils.Select(p => new PupilViewModel(p, ???)));

A。)要么我不想将服务传递给PupilViewModel,因为没有理由更新PupilViewModel的属性,因为它在View中是只读的。

B。)在我的AdministrationController中,我从此Aggregation获取服务中的数据:     1 Schoolclass有N Pupils,1 Pupil有N Documents。那些实体被包裹进去     SchoolclassViewmodelsPupilViewModelsDocumentViewModels ...

现在我的IncidentController我也从服务中获取数据,而我的Aggregation非常相似: 1 Pupil有N IncidentReports,1 IncidentReport有N Documents。那些实体被包裹进去 PupilViewModelsIncidentReportViewModelsDocumentViewModels ...

问题是=&gt; 在PupilViewModel类中,它已经包装了DocumentListViewModel。现在我需要再次使用PupilViewModel来包装IncidentReportListViewModel,然后再次让我有1个Pupil有1个SeatingChair并再次包装它们。这意味着我必须将三个服务传递给PupilViewModel,尽管我并不总是需要它们。

我很难切断对问题的追逐,但不知怎的,我觉得这不是正确的方法。

那么如何重新使用相同的ViewModel来包装具有不同服务的不同聚合的实体呢?

3 个答案:

答案 0 :(得分:1)

不知道你走的路有多远......我强烈建议你看一下使用PrismUnity。正如Unitys网站上所述......

  

Unity应用程序块(Unity)是   轻量级可扩展依赖项   注射容器支持   构造函数,属性和方法调用   注射。

在使用这些不同的框架时,您获得的是广泛的解耦和实例化的“责任”等。然后,您不再需要担心传递给构造函数的参数,因为框架将为您解决这些问题。

您还可以将注册类型的生命周期(例如IDoSomethingController)设置为实际类型DoSomethingController ...并将其生命周期设置为Singleton,如果您需要在有人请求时传递单个实例IDoSomethingController类型的对象。

一旦您使用这些框架,您就不再“新建”实例,您正在利用框架来提供您正在寻找的重用。

IDoSomethingController controller = IUnityContainer.Resolve<IDoSomethingController>();

编辑:既然你说你正在使用MEFedMVVM; DI框架存在。您的PupilViewModel是ObservableCollection的一个实例。这是过度的,通过这个。在您离开之前,ViewModel应该需要额外的重量来传递数据。您的ViewModel似乎试图表示对象verusu概念。我的意思是你可以简单地使用一个暴露学生,课程等的SchoolViewModel。这些项目成为可以在ViewModel上以某种形式聚合的模型。 ViewModel旨在成为View的中介。它可以包含不同模型和服务的大量信息,成为View的单点数据。

答案 1 :(得分:0)

我将回答我自己的问题以及那些关注我的问题或在此回答的人请测试/检查我的解决方案并发表评论,这是一个值得解决的问题:

原始问题是我在不同的上下文中的3个地方重用PupilViewModel。有一次PVM的ctor需要一个AdministrationService,其他用法需要一个IncidentReportingService,而上一次我忘记了第三次服务......

现在在所有3个地方注入所有3项服务对我来说似乎很蠢:

PupilViewModel pVM = new PupilViewModel(pupil,adminService,IncidentReportingService,3rdService);

这样做3次看起来很糟糕。其实我需要这个: 当我需要他们时,只需包住学生并注入所有3项服务!!!

http://marlongrech.wordpress.com/2010/05/23/mefedmvvm-v1-0-explained/

向下滚动人们,你会发现MEFedMVVM可以做Ctor和Prop注射! 完全忽略了该选项及其MEF默认注入(Property Injection)。

编辑:这不起作用,因为:

public SchoolclassViewModel(Schoolclass schoolclass, IAdministrationRepository adminRepo)
        {
            _schoolclass = schoolclass;
            _adminRepo = adminRepo;          

            //this.PropertyChangedHandler(object o, PropertyChangedEventArgs e)
            //{
            //    switch (e.PropertyName)
            //    {
            //        case "SchoolclassCode" : _saRepo.UpdateSchoolclass(_schoolclass); break;       
            //        default: break;
            //    }
            //}  

            PupilListViewModel = new ObservableCollection<PupilViewModel>();
            schoolclass.Pupils.ForEach(p => PupilListViewModel.Add(new PupilViewModel(p, _adminRepo)));
        }

        public IAdministrationRepository AdministrationRepository { get; set; }

您是否看到_adminRepo如何传递给新创建的PupilViewModel?在运行Ctor的那个时候,Property AdministrationRepository仍为null以从那里获取Repo ... bad:/

答案 2 :(得分:0)

您似乎在视图模型上没有必要使用存储库?我希望在您的业务类型上看到一个存储库,而不是您的视图模型。

例如......

PubilsViewModel(ISchool school) { }

学生视图模型只需要一个学校实例,由Unity(或您选择的IOC)注入,所有保存,更新等方法都存储在业务对象中。

var school = new School();
school.Save();
school.Update();

甚至是业务对象上的静态方法,它们在构造函数中接受学校类型?无论哪种方式,我希望你的视图模式是在Business Objects上调用方法,学校对象似乎更合乎逻辑地知道它的保存方式(包括在最终允许保存之前可能需要完成的验证)到数据库)。

在业务对象上,您将拥有School Repository的实例,业务对象不需要知道它是如何被追求的机制,那就是放弃存储库来担心,业务对象会简单对自己执行业务规则的任何验证,然后做出决定,它应该通过调用存储库中的方法将其自身保存到数据库中。

示例:

public class School
{
   private ISchoolRepository _repository;

   public string Name { get; set; }

   public School()
   {
      this._repository = IoC.Resolve<ISchoolRepository>();
   }

   public bool IsValid()
   {
     // Some kind of business logic?
     if (this.Name != null)
     {
       return true;
     }

      return false;
   }

   public void Save()
   {
      if (this.isValid())
      {
         this._repository.Save(this)
      }
}

存储库将处理将记录保存到数据库所需的任何代码。

学校实体应该不知道如何保存自己,没有理由为什么它不能知道它需要什么以保存自己的细节...... ViewModel不需要知道这个细节水平(并且不应该),使用依赖注入,业务对象(School)只知道它需要一个ISchoolRepository,实例化它然后调用它上面的方法。

这可以解决您的问题......

// Knows it needs a school.
// When needing to start the save process for a School, would call the methods on the
// school instance provided in the constructor.
PupilViewModel(School school) { }

// Knows it needs a Repository.
// Would perform validation of business rules and call methods on the repository when//
// it is ready to be pursisted.
School(ISchoolRepository repository) {}

// Repository
// Would perform the read / write of the data.
SchoolRepository() {}

这有意义吗?希望你能看到关注的分离,这是你已经开始尝试和实现的...希望它有所帮助!