Caliburn Micro:MEF场景下与插件相关的问题

3
我有一个场景,其中包含一个WPF应用程序,它托管了一些视图(用户控件)及其视图模型,在其插件文件夹中作为MEF导出部分找到。该应用程序与配置文件一起加载其数据,该配置文件还告诉哪些部分应导入可用的部分。
第一个问题与MEF引导程序相关:我如何自定义它以让它知道插件文件夹?我知道它的SelectAssemblies重写,但它需要装配件,而我的典型方法将是MEF目录目录。我不想为在目录中找到的每个DLL使用像Assembly.LoadFrom这样的方法:MEF就是为此目的而存在的(生命周期管理等)。那么,我如何做类似于在引导程序中构建DirectoryCatalog的MEF AggregateCatalog呢?

第二个问题: 一旦我拥有所需的VM列表,我想要实例化它们。其中一些需要注入CM服务,例如 IEventAggregatorIWindowManager,因此它们具有相应的导入构造函数,因此我需要CM来为我实例化它们:但我需要以编程方式执行此操作,因此我不能只是在属性或导入构造函数上使用Import属性。

对于视图也是如此:一旦我获得了我的VM,我需要CM创建它们并将相应的VM设置为它们的数据上下文;但是我不能使用窗口管理器,因为我只想获取它们,然后以编程方式将它们(它们是用户控件)添加到选项卡控件中,该控件根据数据配置以不同的方式组合。

我使用MEF,因为应用程序是基于插件的,因此当作为IoC使用时,我可以坚持其限制。但是我想利用CM来实例化我的视图和视图模型(全部包含在几个插件DLL中)并正确绑定它们。是否有人可以给出一些提示或指向有关此事的示例或文档?

第二次更新,如承诺的 :):

我基本上按照以下步骤进行:

  1. 根据某些应用逻辑实例化所需的VM(使用MEF)。所有VM都是MEF导出项,托管在插件文件夹中的多个插件中。

  2. 对于每个VM,调用:

static private object LocateViewFor(object viewmodel)
{
  UIElement view = ViewLocator.LocateForModel(viewmodel, null, null);
    ViewModelBinder.Bind(viewmodel, view, null);
  return view;
}

这应该通过CM实例化我的视图,这也将满足它们的导入并将每个视图绑定到其VM。使用“标准”的MefBootstrapper(修改为WPF),例如这里。无论如何,这不起作用并返回null。

我必须告诉引导程序在哪里可以找到我的MEF导出。它们在插件文件夹中,如果我不使用CM,我将使用MEF DirectoryCatalog来检查其内容。在CM中,引导程序的典型扩展点是SelectAssemblies重写,这要求我返回一个Assembly对象列表。从文件夹加载所有程序集不是一个选项。根据上述引用页面的建议,我可以像下面这样添加一个方法到我的引导程序中:
private IEnumerable GetDirectoryCatalogs()
{
 return new ComposablePartCatalog[]
 {
 new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory)
 // TODO other plugins folders...
  };
}
并修改其配置代码如下:
_container = new CompositionContainer(
  new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType()
  .Union(GetDirectoryCatalogs())));
这似乎有效地加载了所需的MEF程序集,但这不足以将它们“注册”到CM AssemblySource.Instance中:事实上,我可以通过MEF检索VM,但当我要求CM获取相应的视图时(使用上述方法或调用IWindowManager.ShowDialog(myviewmodel, null)),该方法返回null,或者(对于第二个示例)返回“占位符”文本框,表示找不到VM的视图。
这似乎与视图(如视图模型一样)托管在不同的MEF插件程序集中有关。我确保视图和视图模型共享相同的命名空间遵循类型SomeNamespace.SampleViewModel - SomeNamespace.SampleView的命名约定;此外,视图和视图模型都通过装饰它们使用[Export]导出,并从公共接口派生。尽管如此,在处理外部程序集时,我无法让CM按预期工作。MEF正常工作,具有所有的导入和导出,但只要CM进入方程式,它就无法执行其“魔法”,从VM中定位视图。
有任何提示吗?

添加一些考虑:我在想像 UIElement view = ViewLocator.LocateForModel(viewmodel, null, null) 这样的东西来从 VM 中获取视图,然后使用 ViewModelBinder.Bind(viewmodel, view, null) 来绑定它们。这样可以吗?无论如何,我的问题仍然存在... - Naftis
1个回答

1

我以前从未使用过Caliburn Micro,但它是否使用CompositionInitializer组件来满足导入需求呢?如果是这样,你可以使用CompositionHost手动初始化用于满足导入需求的容器,例如:

var catalog = new AggregateCatalog(
    new DirectoryCatalog("bin"),
    new DirectoryCatalog("Plugins"));

CompositionHost.Initialise(catalog);

任何对CompositionInitialiser.SatisfyImports(...)的调用都将使用从您创建的目录构建的容器。
如果您希望能够以编程方式创建导出实例,则需要引用实际的CompositionContainer本身。但是,为了做到这一点,我们需要另一种初始化组合的替代方法。您可以创建一个容器并保留对它的引用,例如:
var catalog = new AggregateCatalog(
    new DirectoryCatalog("bin"),
    new DirectoryCatalog("Plugins"));
var container = new CompositionContainer(catalog);
CompositionHost.Initialise(container);

Container = container;

...其中Container是对你的CompositionContainer实例的静态引用。我们已经更改了对CompositionHost的调用,以指定CompositionInitializer将使用的确切容器,这样所有内容仍然可以正确地工作,但我们随后拥有一个容器,可以用来创建特定的实例,例如:

var viewModel = Container.GetExport<ISomeViewModel>();

这有帮助吗?


谢谢,这可能是一个好的提示,但可悲的是我目前正在开发的应用程序是一个WPF应用程序,而CompositionHost不可用。CM使用的典型MEF引导程序对于SL使用CompositionHost,对于WPF使用像这样的代码:_container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x))。OfType < ComposablePartCatalog>())); - Naftis
你能否更新你的代码来展示你是如何构建你的容器以及如何将其连接到 CM? - Matthew Abbott
我编辑后的帖子的最后一部分可能就是解决问题的关键。无论如何,我仍然在CM和MEF方面遇到一些问题,其中一些是MEF方面的,所以这成为了另一个帖子的争议点...:) 当我有更清晰的见解时,我会尝试更新这篇帖子。 - Naftis
对于任何可能感兴趣的人,我在第一篇帖子中更新了我的问题。看起来问题在于当视图托管到另一个程序集中时,CM无法定位视图,即使视图模型位于同一个程序集中,它们遵循命名约定并且都使用MEF进行了[Export]。 - Naftis
如果你在讨论区搜索,我看到有关于这个问题的帖子和如何解决它的讨论。 - Derek Beattie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接