Mvvm 初始化视图模型

Mvvm 初始化视图模型,mvvm,silverlight-4.0,Mvvm,Silverlight 4.0,MVVM一直让我感到困惑——如果我使用视图优先的方法来构建我的对象(这似乎是最常见的方法,至少在经过大量阅读和搜索之后),我如何将上下文信息获取到viewmodel中 我看到许多类似问题的答案都是“使用DI容器注入模型”,但这对我没有帮助,所以我将提供一个小示例 假设我的应用程序是PeopleEditor。它用于加载和编辑复杂的人物对象。当你加载应用程序时,你会看到一个主屏幕,它将一群人加载到内存中——比如说,这些人都可以通过我可以从我的容器中访问的集合来访问。通过单击一个人,您将进入编辑器屏幕

MVVM一直让我感到困惑——如果我使用视图优先的方法来构建我的对象(这似乎是最常见的方法,至少在经过大量阅读和搜索之后),我如何将上下文信息获取到viewmodel中

我看到许多类似问题的答案都是“使用DI容器注入模型”,但这对我没有帮助,所以我将提供一个小示例

假设我的应用程序是PeopleEditor。它用于加载和编辑复杂的人物对象。当你加载应用程序时,你会看到一个主屏幕,它将一群人加载到内存中——比如说,这些人都可以通过我可以从我的容器中访问的集合来访问。通过单击一个人,您将进入编辑器屏幕。编辑器很复杂,因此这不是一个在一个屏幕中实现的简单的主细节视图

因此,在主屏幕上,当我单击一个人时,应用程序需要创建一个新的视图和viewmodel并显示视图。如果我首先通过容器或不通过容器创建viewmodel,我可以使用适当的person对象初始化它这对我来说很自然,所以我很难弄清楚为什么“视图优先”似乎是主要的模式。如何使用“视图优先”方法?视图将创建viewmodel,它可以访问人员集合,但不知道编辑的是谁。为清晰起见进行编辑:一次可以存在多个人员编辑器,每个编辑器编辑不同的人员

Prism 4.0 alpha的MVVM参考实现使用“状态处理程序”,这基本上是应用程序用于在容器中存储构造函数参数的服务。它保存状态并调用ShowView,最终创建的viewmodel导入状态对象。这对我来说似乎很笨拙——就像它试图假装它是松散耦合的,而实际上它不是。还有其他人有其他指导吗?

nlawalker

我不是专家,但我对“视图优先”和“模型优先”的了解是:

  • 先查看:查看程序ViewModel,创建视图,然后自动创建ViewModel
  • 模型优先:ViewModel程序视图,在根应用程序中创建ViewModel对象图,并将其分配给根视图数据上下文。然后让视图根据视图模型渲染其相关子级 我不是说模型优先的方法不好,但我更喜欢视图优先的方法,因为viewmodel可以隐藏在代码中,所以当某些进程需要非绑定友好任务(密码箱、对话框确认、ClosingForm等)时,我可以用代码隐藏编写逻辑

    无论如何,为了解决这个问题,我通常使用IOC和事件聚合器的组合。这是:

  • 对于viewmodel,需要上下文信息在IOC容器中注册其实例,而不是注册其类型。因此,它已经准备好了,即使它的观点没有准备好
  • 当发生导航操作时(通过单击人员列表项),使用IOC容器解析器解析您的视图。并使用指定的参数向导航总线发送事件。此外,此事件将捕获目标ViewModel并执行某些操作
  • 注册viewmodel的实例并不是真正必要的。只有在上一个viewmodel调度事件时,才能确保viewmodel已就绪

    更新

    但是,要用任何类型的本地上下文填充它,我需要使用一个全局工具向它发送一个事件

    在您的例子中,上下文对象不是本地的,而是在对象调用之间传递的消息。显然,在模型优先方法中,您会:

    //selectedPeople is contextual object
    myPeopleDetailVM.LoadData(selectedPeople)
    
    selectedPeople
    传递给事件总线的参数时,情况基本相同

    若您考虑性能,那个么您可以将其和在这种情况下的路由策略进行比较。路由策略比事件总线更复杂。我认为,若您对使用WPF路由事件有足够的信心,那个么使用事件聚合器应该比使用事件聚合器更有信心

    我看到的唯一问题是,如果您使用内置框架事件聚合器(prism、mvvmlight),您的viewmodel会被事件总线污染,如果您对此抱怨,那么我同意您的看法


    希望有帮助。

    如果您使用的是Prism,您可以通过使用其导航功能轻松、灵活地解决此问题。使用IRegionManager.RequestNavigate,通过构造目标视图的Uri以包含用于相应人员标识的查询字符串参数,从主视图导航到编辑视图。您可以在目标视图模型的OnNavigatedTo()方法实现中提取该id(INavigationAware成员。视图模型应实现此接口)

    您可以在Prism下载附带的“查看切换导航”示例应用程序中看到这一点。它位于Quickstarts文件夹下

    从同一示例应用程序(模仿Outlook)中,以下代码用于从InboxView导航到EmailView,以便从收件箱打开特定的电子邮件:

    var builder = new StringBuilder();
    builder.Append(EmailViewKey);
    var query = new UriQuery();
    query.Add(EmailIdKey, document.Id.ToString("N"));
    builder.Append(query);
    this.regionManager.RequestNavigate(RegionNames.MainContentRegion, new Uri(builder.ToString(), UriKind.Relative));
    
    在EmailView的视图模型EmailViewModel中,要打开的电子邮件是从导航上下文中提取的,如下所示:

     void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
        {
            // todo: 15 - Orient to the right context
            //
            // When this view model is navigated to, it gathers the
            // requested EmailId from the navigation context's parameters.
            //
            // It also captures the navigation Journal so it
            // can offer a 'go back' command.
            var emailId = GetRequestedEmailId(navigationContext);
            if (emailId.HasValue)
            {
                this.Email = this.emailService.GetEmailDocument(emailId.Value);
            }
    
            this.navigationJournal = navigationContext.NavigationService.Journal;
        }
    
     private Guid? GetRequestedEmailId(NavigationContext navigationContext)
        {
            var email = navigationContext.Parameters[EmailIdKey];
            Guid emailId;
            if (email != null && Guid.TryParse(email, out emailId))
            {
                return emailId;
            }
    
            return null;
        }
    

    当你说viewmodel对你来说更自然时,为什么不想使用viewmodel优先的方法呢?marlon grech说view或viewmodel first是个人偏好的选择(viewfirst更容易与之混合使用)。我也这么认为,我使用的方法最适合我的场景。所以我在我的应用程序中混合了这两种,这取决于我想做什么。有趣的问题。我总是问自己关于“视图优先”方法的同样问题。。。viewmodel首先看起来更自然,所以我总是这样use@blindmeis:谢谢你的回复。这并不是说我不想先使用viewmodel——事实上,这就是我现在构建应用程序的方式——只是我很好奇如何以视图优先的方式实现它。我试图在我的应用程序中实现的目标对我来说似乎相当普遍,而viewmodel首先似乎非常简单