Search code examples
c#wpfninjectcaliburn.micro

Using Ninject as the DI Container with Calburn.Micro and MVVM in WPF


All I have some experience with Caliburn.Micro using System.ComponentModel.Composition as an IoC container. This time I want to have some fun and use Niject. To setup the Calburn.Micro bootstrapper, I have the following class

public class Bootstrapper : BootstrapperBase
{
    private IKernel _kernel;

    public Bootstrapper()
    {
        Initialize();
    }

    protected override void Configure()
    {
        _kernel = new StandardKernel();
        _kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
        _kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
        _kernel.Bind<IMainWindowViewModel>().To<MainWindowViewModel>().InSingletonScope();
    }

    protected override object GetInstance(Type service, string key)
    {
        return _kernel.Get(service);
    }

    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        return _kernel.GetAll(service);
    }

    protected override void OnStartup(object sender, StartupEventArgs suea)
    {
        base.OnStartup(sender, suea);
        DisplayRootViewFor<IMainWindowViewModel>();
    }

    protected override void OnExit(object sender, EventArgs e)
    {
        _kernel.Dispose();
        base.OnExit(sender, e);
    }
}

This seem to be called fine, but when the line

DisplayRootViewFor<IMainWindowViewModel>();

Is hit, it seems to launch the view IMainWindowView okay, but

public partial class MainWindowView : Window
{
    public MainWindowView()
    {
        InitializeComponent();
    }
}

public interface IMainWindowViewModel { }

and MainWindowViewModel as

public class MainWindowViewModel : Conductor<IMainWindowViewModel>, IMainWindowViewModel { }

with the XAML of IMainWindowView as

<Window x:Class="Mole.Replay.Framework.MainWindowView"
        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" 
        xmlns:ViewModels="clr-namespace:Mole.Replay.Framework"
        xmlns:local="clr-namespace:Mole.Replay.Framework"
        mc:Ignorable="d" 
        d:DesignHeight="450" d:DesignWidth="800">
    <Window.DataContext>
        <ViewModels:MainWindowView/>
    </Window.DataContext>
    <Grid>

    </Grid>
</Window>

The ctor is called over and over again and results in a StackOverflow exception, there is no clear cause for this what-so-ever. This type is bound in singleton scope. Why is this happening am I missing something?


Solution

  • <Window.DataContext>
        <ViewModels:MainWindowView/>
    </Window.DataContext>
    

    it doesn't make sense to set DataContext of MainWindowView to another instance of MainWindowView which will also try to set DataContext etc, until you get StackOverflow exception.

    it should be a view model in DataContext. I don't know if caliburn.micro creates view models for view based on conventions, but at least remove current <Window.DataContext> assignment.

    it is not clear to me why view namespace is aliased as ViewModels. If real view model is in the same namespace and it is not resolved automatically, assign view model

    <Window.DataContext>
        <ViewModels:MainWindowViewModel/>
    </Window.DataContext>
    

    DI container really should provide constructor arguments.Use them to assign DataContext:

    public MainWindowView(IMainWindowViewModel vm)
    {
        InitializeComponent();
        DataContext = vm;
    }