【问题标题】:Initializing a viewmodel初始化视图模型
【发布时间】:2010-09-01 05:39:46
【问题描述】:

关于 MVVM 的一些问题让我继续感到困惑 - 如果我使用视图优先的方法来构造我的对象(这似乎是最常见的方法,至少在大量阅读和搜索之后),我如何将上下文信息放入视图模型?

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

假设我的应用程序是 PeopleEditor。它用于加载和编辑复杂的 People 对象。当您加载应用程序时,您会看到一个主屏幕,它将一堆人加载到内存中 - 假设所有这些都可以通过我可以从我的容器中获取的集合进行访问。通过单击一个人,您将进入编辑器屏幕。编辑器很复杂,所以这不是在一个屏幕中实现的简单的主从视图。

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

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

【问题讨论】:

  • 当你说它对你来说更自然时,为什么你不想使用视图模型优先方法? marlon grech 说 view 或 viewmodel first 是个人喜好的选择(viewfirst 更容易与之混合)。我也这么认为,我使用最适合我的场景的方法。所以我根据我想要做什么在我的应用程序中混合使用。
  • 有趣的问题。关于视图优先方法,我总是问自己同样的问题......视图模型优先似乎更自然,所以这就是我一直使用的方法
  • @blindmeis:感谢您的回复。并不是我不想使用视图模型优先——事实上,这就是我现在构建应用程序的方式——只是我很好奇如何以视图优先的方式进行操作。我试图在我的应用程序中实现的目标对我来说似乎相当普遍,并且 viewmodel-first 似乎是一个如此自然的答案,以至于我无法弄清楚为什么“viewmodel composition”或“viewmodel-first”会有更多更好的命中。跨度>
  • @blindmeis:另外,我看到 Blendability 提到了很多关于 view-first 和 viewmodel-first 的问题,我不明白为什么 Blendability 是一个问题了,现在我们有d:DataContext、d:DesignInstance 和 d:DesignData。我已经发布了一个后续问题:stackoverflow.com/questions/3619031/…

标签: mvvm silverlight-4.0


【解决方案1】:

nlawalker,

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

  1. View-First:查看程序 ViewModel,您创建视图,然后自动创建视图模型。
  2. Model-First:ViewModel 程序 View,您在根应用程序中创建 ViewModel 对象图,将其分配给根视图数据上下文。然后让视图渲染其相关子视图取决于视图模型。

并不是说 Model-First 方法不好,但我更喜欢 View-First 方法,因为 viewmodel 可以位于代码后面,所以当某些进程需要非绑定友好任务(PasswordBox、DialogConfirmation、ClosingForm 等)时,我可以在后面的代码中编写我的逻辑。

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

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

注册视图模型的实例并不是真正需要的。只是为了确保在前一个视图模型调度事件时视图模型已准备就绪。

更新

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

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

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

当你将selectedPeople 传递给事件总线的参数时,它几乎是一样的。

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

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

希望有所帮助。

【讨论】:

  • 感谢您的回复。这几乎是我的想法,但这对我来说似乎很奇怪 - 使用 IOC 来创建你的视图模型,很好,但是为了用任何类型的本地上下文填充它,我需要使用全局工具来发送它事件?我觉得这很奇怪。
【解决方案2】:

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

您可以在 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;
    }

【讨论】:

    猜你喜欢
    • 2017-08-28
    • 2012-10-10
    • 1970-01-01
    • 2013-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-12
    • 1970-01-01
    相关资源
    最近更新 更多