在 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 作为容器时。 –
由于某种原因,当尝试使用 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:
中绑定此 ViewModelpublic 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 作为容器时。 –