Search code examples
c#wpfdependency-injectionprismprism-6

Initialize Shell with ViewModel after all modules loaded in Prism 6


The ViewModel (set via AutoWireViewModel="True") of my Shell / MainWindow requests a dependency which gets loaded in a module at startup using the ConfigurationModuleCatalog.
Because the Shell is initialzed before the modules, the DI container obviously can't resolve it, so the application crashes.

public class MainWindowViewModel : BindableBase
{
    // Cannot resolve IService
    public MainWindowViewModel(IService service)
    {
    }
}

I already tried the two approaches of this post, but both didn't worked.

I tried it this way:

public interface IShellService
{
    int NumberOfLoadedModules { get; }

    void FlagModuleAsLoaded();
}

public class ShellService : IShellService
{
    private readonly IModuleCatalog _moduleCatalog;

    public ShellService(IModuleCatalog moduleCatalog)
    {
        _moduleCatalog = moduleCatalog;
    }

    public int NumberOfLoadedModules { get; private set; }

    public void FlagModuleAsLoaded()
    {
        NumberOfLoadedModules++;

        if (NumberOfLoadedModules != _moduleCatalog.Modules.Count())
            return;

        InitializeShell();
    }

    private static void InitializeShell()
    {
         Application.Current.MainWindow.Show();
    }
}

internal class Bootstrapper : UnityBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        return null;
    }

    protected override void InitializeShell()
    {
    }

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        Container.RegisterInstance<IShellService>(new ShellService(ModuleCatalog), new ContainerControlledLifetimeManager());
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        return new ConfigurationModuleCatalog();
    }
}

Usage

public abstract class ModuleBase : IModule
{
    private readonly IShellService _shellService;

    protected ModuleBase(IShellService shellService)
    {
        _shellService = shellService;
    }

    public void Initialize()
    {
        InitializeInternal();
        FlagAsLoaded();
    }

    public abstract void InitializeInternal();

    public void FlagAsLoaded()
    {
        _shellService.FlagModuleAsLoaded();
    }
}

public class FooModule : ModuleBase
{
    IUnityContainer _container;

    public MusicUIModule(IUnityContainer container, IShellService shellService) : base(shellService)
    {
        _container = container;
    }

    public override void InitializeInternal()
    {
        _container.RegisterType<IService, Service>();
    }
}

It starts counting the modules and then the application crashes because of the same reason.
If the approach above isn't fitting for my purpose, how could that problem be solved?

Thanks!


Solution

  • I tried to implement Haukinger's suggestion and did it this way which works just fine:

    // Factory --------------------------------------------------------
    
    public interface IDependencyFactory
    {
        IService GetService();
    }
    
    public class DependencyFactory : IDependencyFactory
    {
        private readonly IUnityContainer _container;
    
        public DependencyFactory(IUnityContainer container)
        {
            _container = container;
        }
    
        public IService GetService()
        {
            return _container.Resolve<IService>();
        }
    }
    
    // PubSubEvent ------------------------------------------------------
    
    public class AllModulesLoaded : PubSubEvent
    {
    }
    
    // Bootstrapper -----------------------------------------------------
    
    internal class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }
    
        protected override void InitializeShell()
        {
            Application.Current.MainWindow.Show();
        }
    
        protected override void InitializeModules()
        {
            base.InitializeModules();
    
            // Publishing event to tell subscribers that the modules are loaded
            var eventAggregator = Container.Resolve<IEventAggregator>();
            eventAggregator?.GetEvent<AllModulesLoaded>().Publish();
        }
    
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();
    
            // ...
            Container.RegisterType<IDependencyFactory, DependencyFactory>();
        }
    
        protected override IModuleCatalog CreateModuleCatalog()
        {
            return new ConfigurationModuleCatalog();
        }
    }
    
    // ViewModel ---------------------------------------------------------
    
    public class MainWindowViewModel : BindableBase
    {
        private IService _service;
        private readonly IEventAggregator _eventAggregator;
        private readonly IDependencyFactory _dependencyFactory;
    
        public MainWindowViewModel(IEventAggregator eventAggregator, IDependencyFactory dependencyFactory)
        {
            _eventAggregator = eventAggregator;
            _dependencyFactory = dependencyFactory;
    
            _eventAggregator.GetEvent<AllModulesLoaded>().Subscribe(OnAllModulesLoaded);
        }
    
        private void OnAllModulesLoaded()
        {
            var service = _dependencyFactory.GetService();
            if (service != null)
                _service = service ;
    
            _eventAggregator.GetEvent<AllModulesLoaded>().Unsubscribe(OnAllModulesLoaded);
        }
    }