在 Caliburn.Micro 中尝试使用 DesignTime 支持时如何解决 IoC 异常?

How to solve IoC exception when trying to use DesignTime support in Caliburn.Micro?

由于某种原因,当尝试使用 Caliburn.Micro 中的设计时支持时,出现 IoC 异常。

bei Caliburn.Micro.IoC.<.cctor>b__1(Type service) bei Caliburn.Micro.ViewLocator.<.cctor>b__2(Type viewType) bei Caliburn.Micro.ViewLocator.<.cctor>b__9(Type modelType, DependencyObject displayLocation, Object context) bei Caliburn.Micro.ViewLocator.<.cctor>b__a(Object model, DependencyObject displayLocation, Object context) bei Caliburn.Micro.View.OnModelChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs args) bei System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) bei System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) bei System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) bei System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) bei System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue) bei System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange) bei System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange) bei System.Windows.Data.BindingExpression.Activate(Object item) bei System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt) bei System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance) bei MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance) bei MS.Internal.Data.DataBindEngine.Run(Object arg)
bei MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e) bei System.Windows.ContextLayoutManager.fireLayoutUpdateEvent() bei System.Windows.ContextLayoutManager.UpdateLayout() bei System.Windows.UIElement.UpdateLayout()

我想为包含单个 ContentControl 的 Window 启用 DesignTime 支持 here

<Window x:Class="Test.MyShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.caliburnproject.org"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:viewModels="clr-namespace:Test.DesignData.ViewModels"
        mc:Ignorable="d"
        ResizeMode="CanResizeWithGrip"
        d:DataContext="{d:DesignInstance Type=viewModels:DesignShellViewModel, IsDesignTimeCreatable=True}"
        cal:Bind.AtDesignTime="True">


    <ContentControl cal:View.Model="{Binding ActiveItem}" />

</Window>

它在运行时与以下 ViewModel 一起使用:

public class MyShellViewModel : ShellViewModel // derrived from own base class
{
    public MyShellViewModel(IThemeManager themeManager)
        : base(themeManager)
    {
    }
}

但视图应在设计时在命名空间 DesignData:

中绑定此 ViewModel
public sealed class DesignShellViewModel : Conductor<IScreen>.Collection.OneActive
{
    public DesignShellViewModel()
    {
        AssemblySource.Instance.Add(this.GetType().Assembly);

        this.ActivateItem(new TestViewModel());
    }
}

这是一个简单的导体,包含一个屏幕(一个 TestViewModel) 在 Test.DesignData 命名空间中:

public class TestViewModel : Screen
{
// nothing interesting to see here
}

和相应的视图

<UserControl x:Class="Test.DesignData.Views.TestView"
             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 VerticalAlignment="Center" HorizontalAlignment="Center">It's working at design time!</TextBlock>
    </Grid>
</UserControl>

有人知道问题出在哪里吗?

编辑:mvermef 的帖子让我更接近解决方案(我认为)

现在出现一条消息,指出 Caliburn 无法找到 ViewModel 的视图(就像它在运行时所做的那样,当名称错误或 class 不是 found/missing... 但它在那里在同一个图书馆)。

这是我的 Bootstrapper 的一部分,正如@mvermef 所问:

// nothing special here, is empty... just a concrete class to create it via Xaml
public class Bootstrapper : Bootstrapper<IShell>
{
}

// contains the important parts (Autofac, MEF integration etc.)
public class Bootstrapper<TViewModel> : BootstrapperBase
{
    // I use Common.Logging
    private static readonly ILog Log = LogManager.GetLogger(typeof(Bootstrapper<TViewModel>));

    // the Autofac container
    private IContainer container;

    static Bootstrapper()
    {
        // Redirect the default log output of Caliburn and use Common.Logging instead
        Caliburn.Micro.LogManager.GetLog = type => new CommonLoggingAdapter(type);
    }

    protected Bootstrapper()
    {
        this.Initialize();
    }

    protected override void Configure()
    {
        var builder = new ContainerBuilder();

        var catalogs = new List<ComposablePartCatalog>();

        // Register basic services required by Caliburn
        builder.RegisterType<WindowManager>()
            .As<IWindowManager>()
            .InstancePerLifetimeScope();

        builder.RegisterType<EventAggregator>()
            .As<IEventAggregator>()
            .InstancePerLifetimeScope();

        if (Execute.InDesignMode)
        {
            // code that follows next should not execute in Design Mode, so I jump over (thanks to mvermev, the Exception is gone because of this => but still the problem with the missing View)
            return;
        }
        ...
    }

    protected TInstance Get<TInstance>(string key = null)
    {
        return (TInstance)this.GetInstance(typeof(TInstance), key);
    }

    protected override object GetInstance(Type service, string key)
    {
        if (Execute.InDesignMode)
        {
            return base.GetInstance(service, key);
        }

        return string.IsNullOrWhiteSpace(key)
                   ? this.container.Resolve(service)
                   : this.container.ResolveNamed(key, service);
    }

    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        return Execute.InDesignMode
                   ? base.GetAllInstances(service)
                   : this.container.Resolve(typeof(IEnumerable<>).MakeGenericType(service)) as IEnumerable<object>;
    }

    protected override void BuildUp(object instance)
    {
        if (Execute.InDesignMode)
        {
            base.BuildUp(instance);
        }
        else
        {
            this.container.InjectProperties(instance);
        }
    }

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
        this.DisplayRootViewFor<TViewModel>();
    }

    protected override IEnumerable<Assembly> SelectAssemblies()
    {
        foreach (var assembly in base.SelectAssemblies())
        {
            yield return assembly;
        }

        yield return Assembly.GetEntryAssembly();
        yield return typeof(TViewModel).Assembly;
    }

   // other methods are only executed at runtime / not design time relevant
}

编辑 2:已解决! 需要将 DesignTime View 程序集添加到 Boostrapper 的 SelectAssemblies 方法。 Bootstrapper 和 Views 不在同一个程序集中(在我的例子中)。

    protected override IEnumerable<Assembly> SelectAssemblies()
    {
        foreach (var assembly in base.SelectAssemblies())
        {
            yield return assembly;
        }

        yield return typeof(MyDesignTimeView).Assembly; // !!!IMPORTANT!!!
        yield return Assembly.GetEntryAssembly();
        yield return typeof(TViewModel).Assembly;
    }

您是否对 SelectedAssemblies 使用了 Override,这就是 Caliburn 查找您需要的其他项目(如 views/viewmodels 的方式),无论是引用程序集,尤其是当您使用 MEF 作为容器时。 –