1
votes

I just started using Cliburn Micro in my wpf application. I develop using dependency injection in order to help keep things abstract using interfaces.

How can I use the Caliburn Micros Bootstrappers Container to instantiate my own instances of classes.

For example

public interface IFoo
{
    int ReturnNumber();
}

public class Foo : IFoo
{
    public int ReturnNumber()
    {
        return 1;
    }
}

public class SomeViewModel()
{
    public SomeViewMode()
    {
        IFoo = //instance from caliburn
    }
}

Within my bootstrapper I am using a CompositionContainer

----UPDATE------

I am Using Modern UI and Caliburn Micro frameworks side by side but get an null value exception in the bootstrapper at the GetInstance( Type, String ) method when the bootstrapper tried to resolve an instance for a type that contains a parameter for example IWindowManager. I believe this is due to the nature of ModernUI taking a View first approach using a content loader to communicate with Caliburn.Micro however I am struggling to find a solution that allows my bootstrapper to function properly.

Here is the configuration of the bootstrapper

    public class AppBootstrapper : Bootstrapper<IShellViewModel>
{
    private static CompositionContainer _container;
    ....

    protected override void Configure()
    {
        // Add New ViewLocator Rule
        ViewLocator.NameTransformer.AddRule(
            @"(?<nsbefore>([A-Za-z_]\w*\.)*)?(?<nsvm>ViewModels\.)(?<nsafter>([A-Za-z_]\w*\.)*)(?<basename>[A-Za-z_]\w*)(?<suffix>ViewModel$)",
            @"${nsbefore}Views.${nsafter}${basename}View",
            @"(([A-Za-z_]\w*\.)*)?ViewModels\.([A-Za-z_]\w*\.)*[A-Za-z_]\w*ViewModel$"
        );

        _container = new CompositionContainer(
                new AggregateCatalog(
                new AssemblyCatalog( typeof( IShellViewModel ).Assembly ),
                AssemblySource.Instance.Select( x => new AssemblyCatalog( x ) ).OfType<ComposablePartCatalog>().FirstOrDefault()
            )
        );

        var batch = new CompositionBatch();
        batch.AddExportedValue<IWindowManager>( new WindowManager() );
        batch.AddExportedValue<IEventAggregator>( new EventAggregator() );
        _container.Compose( batch );
    }

    protected override object GetInstance( Type serviceType, string key )
    {
        string contract = string.IsNullOrEmpty( key ) ? AttributedModelServices.GetContractName( serviceType ) : key;

        var exports = _container.GetExportedValues<object>( contract );
        return exports.FirstOrDefault();
    }

Am I missing something obvious?

2

2 Answers

1
votes

You could inject your dependency via constructor injection (assuming it is a required dependency):

public class SomeViewModel : Screen
{
    private readonly IFoo foo;

    public SomeViewModel(IFoo foo)
    {
       // Can use foo later, e.g. in OnActivate for your screen
       this.foo = foo;            
    }
}

The Caliburn.Micro generic bootstrapper resolves the your root view model (e.g. ShellViewModel) via the configured container. If SomeViewModel is a dependency of ShellViewModel, then all of it's dependencies will be resolved by the container also, and any further chained dependencies.

If you wish to control the lifetime of types, then inject a factory instead that is registered in your container, and create new instances of that type via the factory.

1
votes

Ok I found the solution to this problem so thought I would post it for any one else. By defualt Caliburn.Micro looks for a default constructor containing no parameters but that does not exist in the class. So the [ImportingConstructor] attribute needs to be used to tell caliburn micro to build an instance of the class using injection to fill the parameters.

Example using devdigitals example

[ImportingConstructor]
public class SomeViewModel : Screen
{
    private readonly IFoo foo;

    public SomeViewModel(IFoo foo)
    {
       // Can use foo later, e.g. in OnActivate for your screen
       this.foo = foo;            
    }
}