c# - Caliburn Micro 无法在不同的程序集中找到 View 模型的 View

标签 c# mvvm caliburn.micro

最近我开始将我的一个项目重组为更小的程序集。在一个步骤中,我将我的 View 和 View 模型移动到单独的程序集中,同时将属于一起的 View 和 VM 保留在一个公共(public)程序集中。因此,我的项目结构如下所示:

  • 主要
  • 核心
  • 模块 <- 文件夹
    • 人物介绍
    • 位置演示者

命名空间是这样的:

  • RpgTools
    • 主要
      • View 模型
      • 浏览量
    • LocationPresenter
      • View 模型
      • 浏览量
    • 其他项目遵循相同的系统。

“Main”包含 bootstrapper 和 d minimalistic vm 以及用于选择模块的 View 。每个演示者都包含该演示者所需的所有 View 和 View 模型。 “核心”包含文件夹中每个项目使用的 Assets (例如元数据定义、导出接口(interface)等)

现在在移动 Caliburn.Micro 之后找不到 View 模型的 View ,无论它多么简单。这是一个 View 模型和 View 的示例:

namespace RpgTools.LocationPresenter.ViewModels
{
    using System.ComponentModel.Composition;
    using RpgTools.Core.Contracts;

    [RpgModuleMetadata(Name = "Module C")]
    [Export(typeof(IRpgModuleContract))]
    public class ModuleCViewModel :IRpgModuleContract
    {
    }
}

<UserControl x:Class="RpgTools.LocationPresenter.Views.ModuleCView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
            <TextBlock Text="ModuleC" />
    </Grid>
</UserControl>

每次加载模块时都会出现以下错误:

Cannot find view for RpgTools.LocationPresenter.ViewModels.ModuleCViewModel.

如果我将模型移回“主”,它就可以正常工作。因为它可能与 Bootstrap 有关,所以这里是它的完整代码:

namespace RpgTools.Main
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Windows;

    using Caliburn.Micro;

    using RpgTools.Core.Contracts;

    /// <summary>The MEF bootstrapper.</summary>
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
    public class MefBootstrapper : BootstrapperBase
    {
        /// <summary>The composition container.</summary>
        private CompositionContainer compositionContainer;

        /// <summary>Initialises a new instance of the <see cref="MefBootstrapper"/> class.</summary>
        public MefBootstrapper()
        {
            // this.CheckModuleDirectory();

            this.Initialize();
        }

        /// <summary>Override to configure the framework and setup your IoC container.</summary>
        protected override void Configure()
        {
            // Get the modules from the module directory
            // ToDo: Implement dynamic loading from modules directory.
            DirectoryCatalog dirCatalog = new DirectoryCatalog(@".");

            // Create a combinable catalog
            // ReSharper disable once RedundantEnumerableCastCall
            AggregateCatalog catalog = new AggregateCatalog(AssemblySource.Instance.Select(s => new AssemblyCatalog(s)).OfType<ComposablePartCatalog>());
            catalog.Catalogs.Add(dirCatalog);

            // Create a new composition container.
            // ReSharper disable once RedundantEnumerableCastCall
            this.compositionContainer = new CompositionContainer();

            // Create a new composition container.
            this.compositionContainer = new CompositionContainer(catalog);

            CompositionBatch compositionBatch = new CompositionBatch();

            // Add window manager to composition batch.
            compositionBatch.AddExportedValue<IWindowManager>(new ToolsWindowManager());

            // Add EventAggregator to composition batch.
            compositionBatch.AddExportedValue<IEventAggregator>(new EventAggregator());

            // Add the container itself.
            compositionBatch.AddExportedValue(this.compositionContainer);

            // Compose the container.
            this.compositionContainer.Compose(compositionBatch);
        }

        /// <summary>Override this to provide an IoC specific implementation.</summary>
        /// <param name="service">The service to locate.</param>
        /// <param name="key">The key to locate.</param>
        /// <returns>The located service.</returns>
        protected override object GetInstance(Type service, string key)
        {
            // Check if the contract is null or an empty string, if so return the contract name from the service itself.
            string contractName = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(service) : key;

            // Get a collection of exported values with the goven contract name. 
            IList<object> exports = this.compositionContainer.GetExportedValues<object>(contractName).ToList();

            if (exports.Any())
            {
                return exports.First();
            }

            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contractName));
        }

        /// <summary>Override this to provide an IoC specific implementation</summary>
        /// <param name="serviceType">The service to locate.</param> 
        /// <returns>The located services.</returns>
        protected override IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return this.compositionContainer.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }

        /// <summary>Override this to provide an IoC specific implementation.</summary>
        /// <param name="instance"> The instance to perform injection on.</param>
        protected override void BuildUp(object instance)
        {
            this.compositionContainer.SatisfyImportsOnce(instance);
        }

        /// <summary>Override this to add custom behaviour to execute after the application starts.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The args.</param>
        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            this.DisplayRootViewFor<IShell>();
        }

        /// <summary>Checks if the modules directory exists and if not create it.</summary>
        private void CheckModuleDirectory()
        {
            if (!Directory.Exists(@".\Modules"))
            {
                Directory.CreateDirectory(@".\Modules");
            }
        }
    }
}

我可能会补充一点,模块发现工作正常。我的 ShellView 模型显示了我添加的每个项目中的每个模块,如果 View 位于与“Main”不同的程序集中,则只是加载 View 不起作用


我通过使用以下代码覆盖 SelectAssemblies() 方法修复了原始问题:

protected override IEnumerable<Assembly> SelectAssemblies()
{
    var assemblies = Directory.GetFiles(ModuleDirectory, "*.dll", SearchOption.AllDirectories).Select(Assembly.LoadFrom).ToList();
    assemblies.Add(Assembly.GetExecutingAssembly());
    return assemblies;
}

但是现在我所有的模块都被加载了两次!这是我对代码所做的唯一更改。我做错了什么?

最佳答案

您需要覆盖 Bootstrapper 中的 SelectAssemblies 以包含包含您的 View 的程序集。默认情况下,Caliburn Micro 将仅包含定义了 Bootstrapper 的程序集。

来自documentation :

So, what is AssemblySource.Instance? This is the place that Caliburn.Micro looks for Views. You can add assemblies to this at any time during your application to make them available to the framework, but there is also a special place to do it in the Bootstrapper. Simply override SelectAssemblies like this:

protected override IEnumerable<Assembly> SelectAssemblies()
{
    return new[] {
        Assembly.GetExecutingAssembly()
    };
}

All you have to do is return a list of searchable assemblies. By default, the base class returns the assembly that your Application exists in. So, if all your views are in the same assembly as your application, you don’t even need to worry about this. If you have multiple referenced assemblies that contain views, this is an extension point you need to remember.

关于c# - Caliburn Micro 无法在不同的程序集中找到 View 模型的 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29458292/

相关文章:

wpf - Prism with MVVM - 如何从外壳激活加载模块中的 View

c# - Observable.Interval 未以预期频率更新 UI

c# - 重用具有不同 ViewModel 的 usercontrol(View)

c# - Caliburn 微 : DialogResult

c# - Caliburn 对模型-对象的微 Action

c# - 通过OpenXML生成word文档

c# - 在 Console.WriteLine 上使用三元运算符

c# - 如何以编程方式将文本转换为 prc/mobi 文件?

c# - 在没有打开 Outlook 应用程序的情况下阅读电子邮件

c# - 为什么动态更改的数据未在此处更新 MVVM 绑定(bind)