Get ViewModel through IoC isssue

Topics: Bootstrappers & IoC
Nov 28, 2012 at 10:34 AM

I'm newcommer in Caliburn.

You can see ViewModel code below.

[Export(typeof(CardValidatingViewModel))]
public class CardValidatingViewModel
{
       [ImportingConstructor]
        public CardValidatingViewModel(string password)
        {            
        }
}

Associated view code:

  public sealed partial class CardValidatingView { public CardValidatingView() { InitializeComponent(); CardValidatingViewModel viewModel = IoC.Get<CardValidatingViewModel>();; viewModel.ApplicationLaunchBranch(); } }

Namespaces are correct. IoC.Get() throws Exception from the code of Bootstrapper:

public class Bootstrapper : Bootstrapper<PrimaryLaunchViewModel>
    {
        private CompositionContainer container;

        protected override void Configure()
        {
            container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));

            CompositionBatch batch = new CompositionBatch();

            batch.AddExportedValue<IWindowManager>(new WindowManager());
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());    
            batch.AddExportedValue(container);
            
            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);

            if (exports.Count() > 0)
            {
                return exports.First();
            }

            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
        }

        protected override IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }

        protected override void BuildUp(object instance)
        {
            container.SatisfyImportsOnce(instance);
        }
    }
IoC.Get() could not locate any instances of ViewModel. Without IoC.Get() everything is fine.
I mean see the View. But I really need to get the ViewModel. What is the problem?

Nov 28, 2012 at 7:08 PM

In your importing constructor, you have a parameter that MEF can't resolve. It's looking in the container for a string part and doesn't find one. You typically inject  into the constructor using interfaces. There's a sample below. MEF will see that in order to create a CardValidatingViewModel it needs an IPassword object created first. It will look in the container and find one since we have a class that exports as an IPassword.

interface IPassword
{
   string password;
}

[Export(typeof(IPassword))]
public class UserPassword : IPassword
{
   string password;
}

[Export(typeof(CardValidatingViewModel))]
public class CardValidatingViewModel
{
   [ImportingConstructor]
   public CardValidatingViewModel(IPassword password)
   {
      //do something with password
   }
}
Nov 28, 2012 at 10:18 PM
Edited Nov 28, 2012 at 10:19 PM

Can you explain why you need to get the viewmodel from the IOC, in that nature?  Its kind of counter to what MEF and CM are normally doing behind the scenes.  CM will automatically bind the viewmodel to the view.  Therefore what you are doing with the IOC is actually causing some grief.  But what iamdragonwolf is correct it can't find the value of the parameter passed therefore MEF fails epically and doesn't give you a reason (one of the enhancements v2 was to bring better debug information). 

the viewmodel can do what you are attempting on the codebehind without having to pull from the IOC the object in question into the view.  look at the overrides for the viewmodel assuming its based off Screen type, you will have OnActivate, OnDeactivate, OnInitialize, etc, to do the things you need to without having tap IoC (which can actually lead to issues, example is your current error. ).

 

Morgan.

Nov 29, 2012 at 7:30 AM

Thank you very much guys! mvermef, thank you especially, now it's clear for me how to interact within viewmodel with view's lifecycle events.

Nov 29, 2012 at 8:33 AM

Hm... I faced the issue with OnActivated overriding or OnLoaded overriding. If I invoke required method I don't see the view. The method I invoke is a long running computational and I\O bound task. It's very uncommon situtation. Yes it's a long running task but I really don't need the separate thread for it. Otherwise the view is not displayed. I don't want use separete thread even from thread pool, because of need to syncronize many parts of code and because the machine is very slow despite of that we run WPF app on it.

Nov 29, 2012 at 4:51 PM
Edited Nov 29, 2012 at 4:53 PM

It all boils down to design decisions.  We have all been there, but if you are trying to keep the UI responsive then you are going to want to play with tasks or threads (framework version dependent), I have been learning RX a little to aid my UI responsiveness it might help you as well but then again it might not.  If its an application that you only use then that is one thing but if you are publishing for others then you might consider the above advice.

 Take a look at Dispatcher class in WPF or Execute.OnUIThread in CM