【问题标题】:How do I avoid coupling XAML Views to start up code in a WPF MVVM application?如何避免耦合 XAML 视图以在 WPF MVVM 应用程序中启动代码?
【发布时间】:2011-06-03 16:50:59
【问题描述】:

我真的很喜欢通过 XAML 在视图的 DataContext 上声明我的 ViewModel 的灵活性,但我很难弄清楚如何将此 ViewModel 绑定到系统的其余部分。

例如

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ViewModels">

<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>    

这里的问题是,我的 ViewModel 经常与系统的其他部分具有并共享依赖关系,我不确定如何将另一个对象的引用注入到上面声明的 MainViewModel 中。

如果我正在执行手动依赖注入,那么我会在应用程序开始时创建一堆工厂,负责将所有内容连接起来。

如何在 MVVM WPF 应用程序中采用相同的方法?处理我的所有 ViewModel 的最有效方法是什么?

【问题讨论】:

    标签: wpf xaml dependency-injection datacontext bootstrapping


    【解决方案1】:

    我猜你有一个依赖项,你想像这样构造函数注入:

    class MainViewModel
    {
        public interface IDependency { };
        readonly IDependency _dep;
        public MainViewModel (IDependency dep)
        {
            if (dep == null)
                throw new ArgumentNullException("IDependency dep cannot be null");
            _dep = dep;
        }
    }
    
    1. 这就是你想要做的吗??

    其次,据我了解,要能够在 XAML 中使用您的 MainViewModel,您需要提供一个默认构造函数,它与注入的依赖项相结合是“混蛋注入”:What is the real difference between "Bastard Injection" and "Poor Man's Injection"

    到目前为止,我还没有看到避免“混蛋注射”并且仍然能够做到这一点的方法:

    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext> 
    

    或者那个:

    <UserControl.Resources>
        <DataTemplate DataType="{x:Type vm:ProgrammingViewModel}">
            <local:ProgrammingView />
        </DataTemplate>
    </UserControl.Resources>
    

    【讨论】:

      【解决方案2】:

      要实现完全解耦,请在主 App 类上的 ResourceDictionary 上设置 ViewModels。有两种方法可以做到这一点,在大多数情况下,使用哪种方法并不重要。然而,也有取舍。

      方法一

      如果以编程方式完成,则必须确保字典键匹配。这会导致 XAML 中定义的字符串与以编程方式定义的字符串之间存在弱耦合。不理想,但也不是世界末日。这里的优点是能够使用构造函数注入。

      //App.xaml.cs
      //This line invoked somewhere after App OnStartUp function is called. 
      MainViewModel mainViewModel = new MainViewModel( new Dependency() );
      Current.ResourceDictionary["MainViewModel"] = mainViewModel; 
      
      //MainView.xaml
      <Window DataContext="{StaticResource MainViewModel}">
      

      业务逻辑不关心视图是什么,应用程序可以以任何方式引导...使用工厂/构建器对象或任何 IOC 容器。 (只要一切都在 OnStartUp 函数中开始)。

      方法二

      使用Application.ResourceApp.xaml 中定义视图模型。使用这种方法,所有键名都将位于 XAML 中,感觉非常好。唯一的负面结果是 .NET 自动构建 ViewModel,强制提供默认构造函数。有时需要 IOC 容器来构建您的对象,或者在自定义工厂/构建器中使用构造函数注入。

      //App.xaml
      <Application 
          xmlns:local="clr-namespace:ViewModels" />
      
      <Application.Resources>
           <ResourceDictionary>
              <ResourceDictionary.MergedDictionaries>
                  <ResourceDictionary>
                      <local:MainViewModel x:key="MainViewModel" />
                  </ResourceDictionary>
              </ResourceDictionary.MergedDictionaries>
          </ResourceDictionary>
      </Application.Resources>
      
      //App.xaml.cs
      MainViewModel mainViewModel = Current.ResourceDictionary["MainViewModel"];
      mainViewModel.propertyInjection = new Dependency();
      
      //MainView.xaml
      <Window DataContext="{StaticResource MainViewModel}">
      

      这两种方式都是有效的选择,并且通过一点密钥管理,可以使用混合搭配来满足要求。

      【讨论】:

        【解决方案3】:

        我为整个应用程序创建了一个视图模型,要么将其作为我的主窗口的数据上下文,要么(如果我不打算只有一个主窗口)将它粘贴到应用程序的资源字典中。所有启动代码都在应用程序视图模型的构造函数中,AVM 将其他视图模型作为属性公开,以便可以通过绑定访问它们。

        这是一种钝器,但它具有简单的优点。

        【讨论】:

          【解决方案4】:

          我喜欢使用在 App.xaml(或其他地方)内的资源字典中定义的 DataTemplate 将 ViewModel 映射到如下所示的 View:

          <DataTemplate DataType="{x:Type vm:CustomerViewModel}">
              <vw:CustomerView />
          </DataTemplate>
          

          这具有在运行时自动将 ViewModel 分配为视图的 DataContext 的效果。在这种情况下,我将在 ViewModel 中为窗口或其他承载 CustomerView 的用户控件实例化一个名为 myCustomerViewModel 的对象,然后在该窗口的 View 中,使用如下所示的 ContentControl:

          <ContentControl Content="{Binding Path=myCustomerViewModel}" />
          

          【讨论】:

            【解决方案5】:

            您可以使用视图模型优先的方法,在其中实例化您的视图模型(通过构造函数注入注入任何依赖项),然后实例化您的视图,并以编程方式将其 DataContext 设置为您的视图模型实例。

            我使用 Caliburn.Micro 框架,它通过约定自动查看位置和绑定。你可以在http://caliburnmicro.codeplex.com/找到它

            Rob Eisenburg 最初的 Build Your Own MVVM Framework 演讲很好地概述了后来的 Caliburn.Micro - http://live.visitmix.com/MIX10/Sessions/EX15

            【讨论】:

            • 我将仔细研究您的框架,但我试图避免将 XAML 耦合到启动代码。理想情况下,我将能够使用上述方法创建 ViewModel,并且仍然能够通过属性注入依赖项。这样一来,我的启动就完全不用关心视图了,可以随意调入。事实上,现在只是谈论这个让我觉得我可能能够在我的 MainWindow 初始化之前在 App 文件中执行大部分启动。无论哪种方式,我都感谢您的回复。谢谢。
            • 使用 Caliburn.Micro,您可以将起始视图模型指定为通用 BootStrapper 的类型,因此您可以轻松地将其换出。事实上,Caliburn.Micro 将使用您配置的任何 IoC 容器来解析启动视图模型,因此您可以将抽象指定为 BootStrapper 类型,这将在启动时通过容器解决,因此您甚至可以将其换掉更简单。见caliburnmicro.codeplex.com/…
            • 好的,这很酷。它类似于自动装配,因为它通过命名约定解决 View - ViewModel 依赖关系。通常我会尽量避免这种事情,因为它会用另一个问题交换一个问题。在这种情况下,我不一定希望我的 View 在语义上被我的 ViewModel 按名称喜欢。话虽如此,它似乎仍然很好地解决了我的问题。
            猜你喜欢
            • 2018-03-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-09-13
            • 2016-05-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多