【问题标题】:Prism 2.1 Injecting Modules into ViewModelPrism 2.1 将模块注入 ViewModel
【发布时间】:2010-01-21 21:20:31
【问题描述】:

我一直在尝试将 ModuleCatalog 中的模块注入到我的 Shell 的 ViewModel 中,但我运气不佳...

我正在我的引导程序中创建 ModuleCatalog,并且我的模块从它的初始化程序进入屏幕没有问题。但是,我希望能够将我的模块列表绑定到一个带有 DataTemplate 的容器,从而允许它们从菜单中启动!

这是我的 Boostrapper 文件,随着时间的推移,我会添加更多模块,但现在,它只包含我相当做作的“ProductAModule”:

public class Bootstrapper : UnityBootstrapper
{
    protected override void ConfigureContainer()
    {
        Container.RegisterType<IProductModule>();

        base.ConfigureContainer();
    }

    protected override IModuleCatalog GetModuleCatalog()
    {
        return new ModuleCatalog()
            .AddModule(typeof(ProductAModule));
    }

    protected override DependencyObject CreateShell()
    {
        var view = Container.Resolve<ShellView>();
        var viewModel = Container.Resolve<ShellViewModel>();
        view.DataContext = viewModel;
        view.Show();

        return view;
    }
}

接下来,这是我的 Shell 的 ViewModel:

public class ShellViewModel : ViewModelBase
{
    public List<IProductModule> Modules { get; set; }

    public ShellViewModel(List<IProductModule> modules)
    {
        modules.Sort((a, b) => a.Name.CompareTo(b));
        Modules = modules;
    }
}

如您所见,我正在尝试注入一个 IProductModule 列表(ProductAModule 继承了它的一些属性和方法),以便它可以绑定到我的 Shell 视图。有没有我真的很简单的东西,或者不能使用 Unity IoC 完成? (我已经看到它使用 StructureMap 的 Prism 扩展来完成)

还有一件事...运行应用程序时,当 ShellViewModel 被 Bootstrapper 中的容器解析时,我收到以下异常:

解析依赖失败,type = "PrismBasic.Shell.ViewModels.ShellViewModel", name = ""。异常消息是:当前构建操作(构建密钥 Build Key[PrismBasic.Shell.ViewModels.ShellViewModel, null])失败:尝试调用构造函数 PrismBasic.Shell.ViewModels.ShellViewModel(System.Collections) 时无法解析参数模块.Generic.List`1[[PrismBasic.ModuleBase.IProductModule, PrismBasic.ModuleBase, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] 模块)。 (策略类型 BuildPlanStrategy,索引 3)

总之,简单吧...看起来很困惑...

任何帮助将不胜感激!

罗伯

【问题讨论】:

    标签: c# mvvm prism


    【解决方案1】:

    我想你可以这样做:

    public class Bootstrapper : UnityBootstrapper
    {
        protected override void ConfigureContainer()
        {
            Container.RegisterType<IProductModule>();
    
            base.ConfigureContainer();
        }
    
        private static ObservableCollection<IProductModule> _productModules = new Obser...();
        public static ObservableCollection<IProductModule> ProductModules
        { 
             get { return _productModules; } 
        }
        protected override IModuleCatalog GetModuleCatalog()
        {
            var modCatalog = new ModuleCatalog()
                .AddModule(typeof(ProductAModule));
            //TODO: add all modules to ProductModules collection
    
            return modCatalog;
        }
    
       ...
    }
    

    然后你会有一个静态属性,任何东西都可以直接绑定到,或者可以从你的 ViewModel 中使用。


    这里是如何获取已在模块目录中注册的模块名称列表。

    public class MyViewModel : ViewModel
    {
    
          public ObservableCollection<string> ModuleNames { ... }
          public MyViewModel(IModuleCatalog catalog)
          {
               ModuleNames = new ObservableCollection<string>(catalog.Modules.Select(mod => mod.ModuleName));
          }
    }
    

    差不多就是这样。 IModuleCatalog 和 IModuleManager 是容器中设置的唯一内容,您可以根据模块访问。不过,正如我所说,您不会获得任何实例数据,因为这些模块(希望如此)尚未创建。您只能访问 Type 数据。

    希望这会有所帮助。

    【讨论】:

    • 嗨安德森,我不这样做的原因有两个:1)我写这篇文章是希望用 MVVM 揭开 Prism 的神秘面纱,所以我希望保留我所有的绑定在视图模型(即 ShellViewModel)中。 2)如果我要承担 //TODO: add... 我不会使用在 UnityContainer 中注册的 ModuleCatalog... 我确实知道您来自哪里,并感谢您的建议!跨度>
    • 我认为我面临的问题是将 ModuleInfo 转换为 IProductModule。它们继承自 IProductModule,后者继承自 IModule。这只会让整个情况更令人恼火!
    • @Robert Reid:关于我提出的解决方案,没有什么不是 MVVM。此静态仅可供 ViewModel 使用并公开给其视图,或者供 View 使用 {x:Static...} 直接使用。至于模块,API 不存储 IModule (或 IProductModule,在您的情况下)的实例,只存储类型(ModuleInfo.ModuleType)作为字符串。此时它已经得到了 ModuleName(没有命名空间的类型名称或您在属性中指定的名称)。您可以请求 IModuleCatalog 或 IModuleManager 作为依赖项,但仅此而已。
    • @Robert Reid:我将发布一个使用 IModuleCatalog 获取可加载模块列表的示例,但这几乎是你的限制......直到你真正初始化模块,它们还不存在。
    • 您好,安德森,我们将不胜感激。当然,我知道它不会破坏 MVVM 最佳实践(如果有严格的实践的话),我只是想让我的 ViewModel(s) 不依赖于模型以外的类来获取信息。尽管我想没有什么可以说 Bootstrapper 不可能!非常感谢您的贡献。
    【解决方案2】:

    我认为您误解了模块的用途。这些模块只是您希望使用的视图和服务的容器。另一方面,shell 应该只包含应用程序的主要布局。

    我认为您应该做的是在您的外壳中定义一个区域,然后在该区域中注册视图(在您的情况下是按钮)。

    您希望如何根据模块部署视图和服务与您正在寻找的模块化级别更相关,即,如果您希望能够独立于视图和视图部署 ModuleA 的视图和服务, ModuleB等的服务。在您的情况下,将所有内容注册在一个模块中可能就足够了。

    花点时间玩一下文档中提供的示例,它们非常好。

    您的示例抛出示例的原因是因为您的 ShellViewModel 依赖于 List 并且该类型未在 Unity 中注册。此外,您正在向 Unity 注册 IProductModule,这没有任何意义,因为无法构造接口。

    【讨论】:

      【解决方案3】:

      我想我今天遇到了类似的问题,原来 PRISM 在初始化模块之前创建了 shell,所以你不能从模块中将任何服务注入到 shell 本身。

      尝试创建另一个依赖于所有其他模块并实现您想要的功能的模块,然后您可以将其添加到外壳中的一个区域以显示您的服务列表。不幸的是,我还没有机会尝试,但这是我计划实施的解决方案。

      附带说明一下,我认为您需要使用属性标记属性才能使用属性注入,但我可能会弄错(我已经有一段时间没有直接使用 Unity 了)。


      编辑: 您需要将 DependencyAttribute 应用于属性才能在 Unity 中使用 setter 注入;你可以阅读它here

      【讨论】:

      • 您好,Rory,感谢您的回答。抱歉,我的问题可能有点太模棱两可了...我希望将实际模块本身放入 ShellViewModule,我将在某个地方处理服务! Bootstrapper 中初始化的运行顺序如下: - ConfigureContainer - GetModuleCatalog - CreateShell 所以我已经确定了我的 ModuleInfo 数组,但如果没有我的 IProductModule 包含的其他属性,它们对于绑定就不够用了。谢谢罗里!
      • 您好,我之前使用过 [Dependency] 属性(用于属性注入)。我在这里尝试注入构造函数,我猜即使我使用属性注入,它也不会是灵丹妙药!
      • 嗨罗伯特,很抱歉造成误会。我刚刚注意到该属性的文档无论如何都已过时,所以我稍后会删除链接。如果您查看 UnityBootloader 的源代码,您会注意到容器配置了 ModuleCatalog,而不是单个模块。您最好的选择可能是覆盖 ConfigureContainer 并自己向容器注册模块,只是不要忘记调用基本实现。
      • 感谢您的建议 Rory,我有点犹豫要自己手动添加模块,因为我负责注册和创建 LoggerFacade 并添加所有 RegistersTypeIfMissings 等(因为调用base.ConfigureContainer() 将重新加载我的所有模块,导致它们初始化(更重要的是)将它们的视图添加到 ShellView 区域两次)。不过,我真的很感谢你的帮助。
      • 不确定是否有帮助,但 IModuleCatalog 接口有一个可枚举的 Modules 属性。如果您只想要模块的信息,您可以让容器注入 IModuleCatalog 并使用它包含的 ModuleInfo 项来列出模块。
      【解决方案4】:
      var modules = new IProductModule[]
      {
          Container.Resolve<ProductAModule>()
          //Add more modules here...
      };
      Container.RegisterInstance<IProductModule[]>(modules);
      

      就是这样!使用此代码,我可以将我的模块注入到 ShellViewModel 中,并将每个模块显示为我的应用程序中的按钮!

      如此简单的解决方案!来自 CompositeWPF 讨论组的一位好人。我毫无保留地推荐他们^_^

      【讨论】:

      • 除非您要创建每个模块的多个实例,如果您打算根据显示的信息激活它们。这可能会起作用,但是您在 constrictor 中为每个模块所做的任何事情都将执行两次,并且您将在内存中永久拥有每个模块类型的 2 个实例。这就是为什么我没有建议这种方法。这表明对 Prism 的实际工作原理缺乏了解。
      • 嗨安德森,我对 Prism 确实缺乏了解!我已经测试了您的理论,并且确实创建了两个我的模块实例。但这是迄今为止我能想到的最好的解决方案(它允许我将我的模块绑定到它们自己的按钮,每个按钮都有一个命令)。
      猜你喜欢
      • 2015-10-28
      • 1970-01-01
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-18
      • 1970-01-01
      • 2023-03-14
      相关资源
      最近更新 更多