初始化视图模型

时间:2010-09-01 05:39:46

标签: mvvm silverlight-4.0

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

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

假设我的应用程序是PeopleEditor。它用于加载和编辑People对象,这些对象很复杂。当你加载应用程序时,你得到一个主屏幕,将一堆人加载到内存中 - 让我们说这些都可以通过我可以从我的容器中访问的集合访问。通过单击Person,您将进入编辑器屏幕。编辑器很复杂,因此这不是在一个屏幕上实现的简单的主 - 详细视图。

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

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

2 个答案:

答案 0 :(得分:3)

nlawalker,

我不是专家,但我对View-First和Model-First的了解是:

  1. View-First:查看程序ViewModel,您创建视图,然后自动创建viewmodel。
  2. Model-First:ViewModel程序View,您可以在根应用程序中创建ViewModel对象图,将其分配给根视图数据上下文。然后让视图呈现其相关的子视图取决于视图模型。
  3. 并不是说Model-First方法很糟糕但是,我更喜欢View-First方法,因为viewmodel可以位于代码后面,所以当某些进程需要非绑定友好任务(PasswordBox,DialogConfirmation,ClosingForm等)时,我可以在代码中写下我的逻辑。

    无论如何,为了解决这个问题,我经常使用IOC和Event Aggregator的组合。这是:

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

      <强>更新

        

      然后用任何类型的本地上下文填充它我需要使用全局工具向它发送事件吗?

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

      //selectedPeople is contextual object
      myPeopleDetailVM.LoadData(selectedPeople)
      

      selectedPeople传递给事件总线的参数时,它几乎相同。

      如果您考虑性能,那么在这种情况下,您可以将其与WPF Routed Event System进行比较。路由策略比事件总线更复杂,我认为如果您对使用WPF路由事件有足够的信心,那么事件聚合器应该如此。< / p>

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

      希望有所帮助。

答案 1 :(得分:0)

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

您可以在Prism下载附带的“View-Swithing Navigation”示例应用中看到这一点。它位于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;
    }