tags:

views:

463

answers:

3

I have a WPF application built on top of PRISM.

When the user tries to close the application I need to check the dirty status of any loaded views.

I was hoping to enumerate a list of the loaded modules and ask them in turn whether it's OK to exit and save any changes, but I'm having trouble finding a list of references to the loaded modules.

The closest I could find was IModuleCatalog which gives me a list of modules, but not the actual object references these modules

Any suggestions on how I can do this?

Thanks in advance

Ian

+1  A: 

Have you considered a composite command for this, rather than your current approach? It seems like your views ought to be participating in the closing of the application, rather than some central bit of module logic.

This sample (called the Commanding Sample, which I think is great to say... it's very commanding) illustrates a "Save All" which is very similar to your "Close All" (basically what you are doing). The thing that is great here is that this is functionality baked in that you don't have to build yourself: http://msdn.microsoft.com/en-us/library/dd458890.aspx

Anderson Imes
Thanks, I'll take a look tomorrow, what I need is to be able to hook the Closing event of the Shell window and the SessionEnding event on the Application object and be able to cancel each of these events if the user Clicks cancel on a Do you want to save your changes? prompt. So this is where the central piece of logic came from, ask each view if it's OK to close and be able to cancel if required.
Ian Oakes
Exactly. What is nice about the composite command is the "asking" is already handled.
Anderson Imes
In this case the CompositeCommand does not work because it would require knowledge of the commands within the view models. In my case we are responding to the user shutting down the application and we need some way asking each loaded view whether it's ok to exit and for them to pack up and save any changes. We also need to allow the view the ability to stop the application from shutting down. I have a partial solution involving enumerating the views inside the main region on the shell. I'll post here next week when complete.
Ian Oakes
If you look at the sample, when each command is created it is added at that time to a static CompositeCollection elsewhere in the application by the code that created the Command. It works. You could even put a service for doing this in your IoC container... something like IApplicationEvents with a RegisterCloseCommand(ICommand closeCommand) or something similar so you didn't have your modules needing to know about some static command to register with.
Anderson Imes
A: 

If what you want to do is to obtain the existing instance of all the loaded modules, you can do the following:

  1. Get the existing instance of IServiceLocator from the Unity container.
  2. For each instance of ModuleInfo in the module catalog where the state is Initialized, get the module instance by using serviceLocator.GetInstance(moduleInfo.ModuleType)
Konamiman
Does the module manager insert the created instance of the module into the container (IServiceLocator is just a fascade for the container, so this is actually an extra step)? I've never seen it do this. I'll have to get back in the code and look. My gut says that the code you are proposing will give you a *new* instance of the module, rather than the instance Initialize was called on.
Anderson Imes
Anderson is correct, when I use IServiceLocator.GetInstance(Type), I get a new instance of the module.
Ian Oakes
A: 

I've implemented modules cleaning up on application shutdown in the following way.

I create "cleanup service" where modules can register their cleanup actions. public interface IModuleCleanupService { void RegisterCleanupAction(Action action); } public class ModuleCleanupService: IModuleCleanupService { private readonly List m_cleanupActions = new List();

    public void RegisterCleanupAction(Action action)
    {
        m_cleanupActions.Add(action);
    }

    public void Cleanup()
    {
        List<Exception> exceptions = null;
        foreach (Action action in m_cleanupActions)
        {
            try
            {
                action();
            }
            catch (Exception ex)
            {
                if (exceptions==null)
                    exceptions = new List<Exception>();
                exceptions.Add(ex);
            }
        }
        if (exceptions != null)
            throw new AggregateException(exceptions);
    }
}

Then any module can import IModuleCleanupService-instance. This can be done in different ways (with MEF/Unity via contructor injection or through asking the ServiceLocalor.Current)

The service's instance is created in Bootstapper's ConfigureContainer (here I'm using MefBootstapper deviced type, but this isn't important):

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();

        m_moduleCleanupService = new ModuleCleanupService();
        Container.ComposeExportedValue<IModuleCleanupService>(m_moduleCleanupService);
    }

Then I add GetDisposable method to my bootstapper which returns IDisposable-object. That IDisposable-object simple calls Cleanup on ModuleCleanupService:

    public IDisposable GetDisposable()
    {
        return new DisposableDelegate(() => m_moduleInitializationService.Cleanup());
    }

    class DisposableDelegate: IDisposable
    {
        private readonly Action m_action;

        public DisposableDelegate(Action action)
        {
            m_action = action;
        }

        public void Dispose()
        {
            m_action();
        }
    }
Shrike