Search code examples
c#wpfmultithreadingprismeventaggregator

Prism EventAggregator. Publishing event while loading modules


I'd like to keep track of the number of modules that have been loaded over the total so the user can see how much loading is left. So I've decided to load each module individually and then publish an event.

I can see that the Subscriber is getting hit and running through all the correct code, but the UI wont update until all modules have been loaded. So my progress bar just goes from 0 straight to 100.

So to clarify, the problem I have is that the UI appears to be frozen throughout the entire module loading process. Is there any way around this so I can utilize a progress bar?

Bootstrapper

{
    protected override void InitializeModules()
    {         
        FillModuleCatalogFromConfig();

        // begin the initialization process
        eventAggregator = this.Container.Resolve<IEventAggregator>();
        eventAggregator.GetEvent<BeginLoadingModulesEvent>().Publish(true);

        // load the rest of the modules
        InitializeRemainingModules();
        eventAggregator.GetEvent<BeginLoadingModulesEvent>().Publish(false);
    }

    private void InitializeRemainingModules()
    {
        foreach (var module in ModuleCatalog.Modules)
        {
            InitializeModule(module);
        }
    }

    private void InitializeModule(ModuleInfo moduleInfo)
    {
        if (moduleInfo.State == ModuleState.Initialized)
            return;

        if (moduleInfo.DependsOn.Count > 0)
        {
            // Load any dependencies first
            foreach (var dependenciesModulesName in moduleInfo.DependsOn)
            {
                // if the dependency isn't loaded then we'll have to load that first
                ModuleInfo module = ModuleCatalog.Modules.First(x => x.ModuleName == dependenciesModulesName);
                if (module.State != ModuleState.Initialized)
                {
                    // must initialize this module first                         
                    InitializeModule(module);
                }
            }
        }

        eventAggregator.GetEvent<MyEvent>().Publish(new ProgressChangedEventArgs(CalculateModulesLoadedProgress(), moduleInfo.ModuleName));
        moduleManager.LoadModule(moduleInfo.ModuleName);
    }

    private int CalculateModulesLoadedProgress()
    {
        decimal progress = Decimal.Divide(ModuleCatalog.Modules.Where(x => x.State == ModuleState.Initialized).Count(), ModuleCatalog.Modules.Count()) * 100;
        return (int)(Math.Round(progress));
    }
}

ViewModel to the shell that displays the progress bar

public Class ShellViewModel
{
    IEventAggregator ea;

    ShellViewModel(IEventAggregator ea)
    {
        this.ea = ea;
        this.ea.GetEvent<MyEvent>().Subscribe(this.UpdateProgressBar);
    }

    public int ProgressValue
    {
        get { return progressValue; }
        set { SetProperty(ref progressValue, value); }
    }

    private void UpdateProgressBar(ProgressChangedEventArgs args)
    {
        // this all gets hit and runs fine, but the actual UI bar for progress
        // wont get hit until all modules are done loading
        this.ProgressValue = args.ProgressPercentage;
    }
}

Solution

  • I did end up figuring this out. Or at least getting working the way I wanted it to. I just used the Dispatcher and set it to a background priority.

    Instead of

    moduleManager.LoadModule(moduleInfo.ModuleName);
    

    I used

    Application.Current.Dispatcher.Invoke(() => moduleManager.LoadModule(moduleInfo.ModuleName), DispatcherPriority.Background);