【问题标题】:Initialize Shell with ViewModel after all modules loaded in Prism 6在 Prism 6 中加载所有模块后使用 ViewModel 初始化 Shell
【发布时间】:2016-10-11 14:27:21
【问题描述】:

我的 Shell / MainWindow 的 ViewModel(通过 AutoWireViewModel="True" 设置)请求一个依赖项,该依赖项在启动时使用 ConfigurationModuleCatalog 加载到模块中。
因为Shell在模块之前初始化,DI容器显然无法解析,所以应用崩溃了。

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

我已经尝试了this post 的两种方法,但都没有奏效。

我是这样尝试的:

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();
    }
}

用法

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>();
    }
}

它开始计算模块,然后应用程序由于同样的原因而崩溃。
如果上述方法不适合我的目的,该问题如何解决?

谢谢!

【问题讨论】:

    标签: c# wpf dependency-injection prism prism-6


    【解决方案1】:

    我尝试实施Haukinger's 建议并以这种方式进行,效果很好:

    // 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);
        }
    }
    

    【讨论】:

    • 要取消订阅,也可以将SubscribeDisposeit返回的订阅令牌存储起来进行取消订阅。
    【解决方案2】:

    我会将 shell 的依赖项隐藏在工厂/提供者后面,然后在加载最后一个模块时创建/获取它。当base.InitializeModules 返回时,您的shell 的视图模型订阅了一个AllModulesLoaded 事件,该事件从您的引导程序的InitializeModules 触发,以获取依赖项可用的通知。或者工厂/提供者订阅事件,shell 轮询它,这取决于您希望如何使用依赖项。

    【讨论】:

    • 感谢您的回答!您是指普通事件委托还是 PubSubEvent?我还不知道工厂类是如何工作/使用的..您能否在答案中添加一个简短的示例,说明我将如何通过 MainWindowViewModelFactory 获得例如 IService?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多