0

models from shell-view-model with abstract factory pattern. I need inject in view-models classes from external assembly. If I use abstract factory pattern on creation view-models. Problem is imported classes in view-models are null.

Shell-view-models look like this:

public interface IViewModelFactory
    {
        ILogOnViewModel CreateLogOnViewModel(IShellViewModel shellViewModel);
        IMessengerViewModel CreateMessengerViewModel(IShellViewModel shellViewModel);
    }

    [Export(typeof(IViewModelFactory))]
    public class DefaulFactoryViewModel:IViewModelFactory
    {
        #region Implementation of IViewModelFactory

        public ILogOnViewModel CreateLogOnViewModel(IShellViewModel shellViewModel)
        {
            return  new LogOnViewModel(shellViewModel);
        }

        public IMessengerViewModel CreateMessengerViewModel(IShellViewModel shellViewModel)
        {
            return new MessengerViewModel(shellViewModel);
        }

        #endregion
    }


    public interface IShellViewModel
    {
        void ShowLogOnView();
        void ShowMessengerView();
    }

    [Export(typeof(IShellViewModel))]
    public class ShellViewModel : Conductor<IScreen>, IShellViewModel
    {

        private readonly IViewModelFactory _factory;

        [ImportingConstructor]
        public ShellViewModel(IViewModelFactory factory)
        {
            _factory = factory;
            ShowLogOnView();
        }

        public void ShowLogOnView()
        {
            var model = _factory.CreateLogOnViewModel(this);
            // var model = IoC.Get<LogOnViewModel>();
            ActivateItem(model);
        }

        public void ShowMessengerView()
        {
            var model = _factory.CreateMessengerViewModel(this);
            ActivateItem(model);
        }
    }

Some view-model.:

public class LogOnViewModel : Screen,ILogOnViewModel
{
    [Import]//inject class from external assembly
    private IPokecConnection _pokecConn;

    private readonly IShellViewModel _shellViewModel=null;

    private User _user=null;

    public LogOnViewModel(IShellViewModel shellViewModel)
    {
        _shellViewModel = shellViewModel;
        _user = new User();
    }
}

variable _pokecConn are null becasuse I use abstract factory on creation new view-models.

if I use in shell-view model this:

var model = IoC.Get<LogOnViewModel>();

instead this:

var model = _factory.CreateLogOnViewModel(this);

and add Export attribute on view-models classes it works good, but I would like use abstract factory, and inject in view-model only classes from extrenal assembly. It exist solution on this problem, or I must create view-models from IoC and export all class? Thanl for advance.

EDITED :

MEF BOOTSTRAPER CLASS:

 public class MefBootStrapper : Bootstrapper<IShellViewModel>
    {
    #region Fields
    private CompositionContainer _container;
    #endregion

    #region Overrides
    protected override void Configure()
    { //  configure container
    #if SILVERLIGHT
        _container = CompositionHost.Initialize(
        new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
    #else

        var catalog =
            new AggregateCatalog(
                AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());

        //add external DLL
        catalog.Catalogs.Add(
            new AssemblyCatalog(string.Format(
                CultureInfo.InvariantCulture, "{0}{1}", System.IO.Directory.GetCurrentDirectory(), @"\Pokec_Toolkit.dll")));

        _container = new CompositionContainer(catalog);
    #endif

        var batch = new CompositionBatch();

        batch.AddExportedValue<IWindowManager>(new WindowManager());
        batch.AddExportedValue<IEventAggregator>(new EventAggregator());
        batch.AddExportedValue(_container);

        _container.Compose(batch);
        _container.SatisfyImportsOnce(this);  
    }

    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);
    }
    #endregion
    }

1 Answers1

0

Did you forget the attribute ImportingConstructor for the LogOnViewModel constructor?

EDIT: Import property always null (MEF import issue)

Community
  • 1
  • 1
L-Four
  • 13,345
  • 9
  • 65
  • 109
  • No, I create new view-model with factory no with MEF. I need only import one class into view-model class. private IPokecConnection _pokecConn = import class here. Vie-Model class is created in shell-view-model. –  Dec 17 '10 at 12:54
  • ok sorry, i always used constructor injection. What if you make it a public property like [Import] public IPokecConnection _pokecConn {get; set; } – L-Four Dec 17 '10 at 12:58
  • ah this can help: http://stackoverflow.com/questions/1845892/import-property-always-null-mef-import-issue – L-Four Dec 17 '10 at 13:01
  • Oki, it’s OK, but if I make public property it’s still null :( MEF doen’t injected in this interface object. –  Dec 17 '10 at 13:05
  • I think this solution (http://stackoverflow.com/questions/1845892/import-property-always-null-mef-import-issue) doesn’t solve my problem. Because in usefull advance he export class,in which he want import some class. I create instaces of view-model by myself no with MEF ;) I need MEF only on inport one/two classes into view-model class. –  Dec 17 '10 at 13:14
  • the accepted answer starts with using SatisfyImports because he also instantiates a class without MEF, that is what you need I think – L-Four Dec 17 '10 at 13:18
  • Oki, but I have container in external class, in MefBoostraper class. View-model is in another class. So I don’t have a way how access to varibale/instance of MEF container. –  Dec 17 '10 at 13:20
  • See my edited question, MEF BOOSTRAPER CLASS on the bottom. I don’t know how can I use variable _container in view-model class. –  Dec 17 '10 at 13:24
  • so why are you not using MEF to instantiate the view models in the factory? – L-Four Dec 17 '10 at 13:28
  • Because I need pass 3-4 arguments from one view-model to another. Something like this var model = _factory.CreateLogOnViewModel(this, arg1, arg2, argn);. If you use MEF :var model = IoC.Get(string var); you can pass only one string variable. –  Dec 17 '10 at 13:38
  • ok so why don't you just use MEF for every dependency injection to keep it clean, and set additional properties on the viewmodel when needed? – L-Four Dec 17 '10 at 13:42
  • Oh, we do not understand about what is my problem. Simple scenarion. In shell is active view1 (view-model1) u create with MEF new view2 and also set as active item. But how you pass 3 object from view-model1 to view-model2 constructor/ properties.Would you be so good and showed me a code example? –  Dec 17 '10 at 14:57
  • I use PRISM to develop WPF/Silverlight apps; and if I had to implement your requirements; I would create independent views and independent view models; and use the publish/subscribe mechanism. This means that if the active item in first view changes, it publishes the change event. The second viewmodel subscribes to these events and react upon them; by (for example) also setting the active item. If you are not using PRISM, google for 'wpf publish subscribe' for non-prism examples: http://weblogs.asp.net/guystarbuck/archive/2008/10/08/shared-message-area-in-wpf-using-publish-subscribe.aspx – L-Four Dec 17 '10 at 15:13
  • By doing so you cleany separate your views, and you can use the publish/subscribe mechanism for other events like status changes etc. – L-Four Dec 17 '10 at 15:16
  • I don’t use PRIM, I can use EventAggregator(implement Mediator pattern) for Caliburn on publish/handle messages. But how can publish view1 to view2 something, when view1 is closed and the is actived view2? –  Dec 17 '10 at 16:17
  • I don't know your use case well enough, so I don't really understand what you are trying to do. Maybe you can elaborate and be more specific what you want to do? – L-Four Dec 17 '10 at 16:23
  • From view-model1 I create new view-model-2. I need pass from view-model1 to view-model2 some arguments. If you use MEF and export view-model class, you must create view-model in shell with IoC.get<>(), and u can not pass any object arguments. You don't use mediator pattern for publish/handle message on some view, which doesn’t exist yet. You must send object as argument type of construtor. –  Dec 17 '10 at 17:00
  • you publish a message with the needed information, your controller is subscribed to this message, takes care of creating the new view/view model and passes the needed parameters to the created viewmodel; this viewmodel takes care of loading needed data and setting the selection. – L-Four Dec 19 '10 at 08:20
  • So I use another solution. Here is it : http://caliburnmicro.codeplex.com/Thread/View.aspx?ThreadId=238908 –  Dec 21 '10 at 14:18
  • that's about the same solution I wrote a few messages ago: "ok so why don't you just use MEF for every dependency injection to keep it clean, and set additional properties on the viewmodel when needed? " – L-Four Dec 21 '10 at 19:05