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

时间:2015-07-12 12:54:00

标签: c# .net caliburn.micro design-time

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

  

bei Caliburn.Micro.IoC。< .cctor> b__1(类型服务)bei   Caliburn.Micro.ViewLocator。< .cctor> b__2(Type viewType)bei   Caliburn.Micro.ViewLocator。< .cctor> b__9(TypeTypeType,   DependencyObject displayLocation,Object context)bei   Caliburn.Micro.ViewLocator。< .cctor> b__a(对象模型,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元数据,   EffectiveValueEntry oldEntry,EffectiveValueEntry& newEntry,布尔值   coerceWithDeferredReference,Boolean coerceWithCurrentValue,   OperationType operationType)bei   System.Windows.DependencyObject.InvalidateProperty(的DependencyProperty   dp,Boolean preserveCurrentValue)bei   System.Windows.Data.BindingExpressionBase.Invalidate(布尔   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   尝试)bei   System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(布尔   lastChance)bei MS.Internal.Data.DataBindEngine.Task.Run(布尔值   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的视图(就像它在运行时一样,当名称错误或类未找到/缺失时......但是它存在于同一个库中)。

以下是@mvermef提出的 Bootstrapper 的一部分:

// 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视图程序集添加到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;
    }

1 个答案:

答案 0 :(得分:1)

您是否使用Override for SelectedAssemblies,这就是Caliburn如何找到您需要的其他项目(如视图/视图模型),而不管是否为引用的程序集,尤其是在您使用MEF作为容器时。 -