1

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?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
maxhap
  • 221
  • 3
  • 15

2 Answers2

1

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.

devdigital
  • 34,151
  • 9
  • 98
  • 120
  • Hi I am a large user of construction injection as have used Ninject as my primary framework in the past, however this project is to be developed using ModernUI and Caliburn Micro. I have a bootstrapper that works as long as I don't use any parameters in the ViewModel Constructor. With a parameter any ViewModel that's instances is resolved by the Bootstrapper but loaded through a MUI content Loader throws an exeption in the GetInstance() method of the Bootstrapper. Any thoughts on why that might be? I will update my question above to show an example, thanks again. – maxhap Jan 23 '14 at 23:43
  • You could create a custom content loader that uses the Caliburn.Micro view model locator (which resolves view models via the configured container). See http://stackoverflow.com/questions/16421582/caliburn-micro-and-modernui-examples-tutorials. Alternatively, you might consider a UI framework that supports a view model first approach, such as MahApps.Metro - http://mahapps.com/MahApps.Metro/. – devdigital Jan 24 '14 at 13:45
  • I am actually using a Custom Loader, nearly the same as that post (already looked at that in the past). Sadly using MahApps is not an option. – maxhap Jan 24 '14 at 14:12
  • haha I'm an utter tool! After going through the documentation on CM again I noticed I was missed the [ImportingConstructor] attribute above the class – maxhap Jan 24 '14 at 14:39
  • Yes that would do it! It's actually MEF that requires the attribute for types without a default parameterless constructor. – devdigital Jan 24 '14 at 14:44
  • Thanks for your help though devdigital :) – maxhap Jan 24 '14 at 14:45
1

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;            
    }
}
maxhap
  • 221
  • 3
  • 15