Help with ApplicationCloseStrategy from the HelloScreens Sample

Topics: Framework Services, UI Architecture
May 13, 2013 at 4:04 PM
The ApplicationCloseStrategy is driving me crazy, i understand what rob has explained in general but the details of the execute method is killing me, can somebody please explain it in detail or give a sample run when and when there isn't any documents open, or at least explain how that SequentialResult is working ?
Thanks
May 14, 2013 at 9:29 AM
Hi abnajjar,

Essentially the ApplicationCloseStrategy is built around Coroutines with SequentialResult being a set of IResult that happen in sequence. Coroutines do some magic with .net enumerators that basically allow you to run logic in a state machine.

Essentially what is happening is every time the application tries to close it asks the shell can it close, this in turn asks each conductor which may ask each screen. All this happens in a state machine sequentially.

Have a look at the Coroutines wiki section, that is the key to understanding this as it goes through how Coroutines work and details IResult usuage.
May 14, 2013 at 9:32 AM
If you scroll to the end of this wiki doc it has the steps the ApplicationCloseStrategy uses to close out as a set of steps.

https://caliburnmicro.codeplex.com/wikipage?title=Screens%2c%20Conductors%20and%20Composition&referringTitle=Documentation
May 14, 2013 at 1:28 PM
Hello McDonnelDean,
Thank you for the explanation, but my problem is that i tried to port the application to WPF, then i understood that the window manager has an internal conductor that does the checking for you to see if the shell view model implements iguard close, now that is great, but the problem is the application is shutting down anyway even though there are things to be done ? how can i solve this ?
May 14, 2013 at 1:52 PM
Without seeing your code it would be impossible to say what is wrong!
May 14, 2013 at 2:25 PM
Ok, this is my code, it is a modified version of Rob original Hybrid Shell example, notice that i made ApplicationCloseStrategy implement IResult
so i could add it to the end of the SequentialResult list, this way if all other IResults before complete successfully without an error or canceling, the result will initiate the callback method responsible for determining if the application is allowed to close or not.
namespace HelloScreensWPF.Shell {
  

    public class ApplicationCloseStrategy : ICloseStrategy<IWorkspace>, IResult {

        private IEnumerable<IWorkspace> _workspaces;
        private Action<bool, IEnumerable<IWorkspace>> _toCloseCallback;

        public void Execute(IEnumerable<IWorkspace> toClose, Action<bool, IEnumerable<IWorkspace>> callback) {
            _workspaces = toClose;
            _toCloseCallback = callback;
            _DoExecute();
        }

        private void _DoExecute()
        {
            foreach (var workspace in _workspaces)
            {
                if (_CurrentItemIsConductor(workspace))
                {
                    var conductor = workspace as IConductor;
                    var tasks = _GetConductedItemsWithShutdownTasks(conductor);
                    var sequential = new SequentialResult(tasks.GetEnumerator());
                    sequential.Completed += (s, e) =>
                    {
                        if (e.WasCancelled == true)
                            _toCloseCallback(false, new List<IWorkspace>());
                    };
                    sequential.Execute(new ActionExecutionContext());
                }
            }
        }

        private IEnumerable<IResult> _GetConductedItemsWithShutdownTasks(IConductor conductor)
        {
            var tasks = conductor.GetChildren()
                .OfType<IHaveShutdownTask>()
                .Select(x => x.GetShutdownTask())
                .Where(x => x != null).Concat<IResult>(new IResult[] {this});
            return tasks;
        }

        private bool _CurrentItemIsConductor(IWorkspace workspace)
        {
            return ((workspace as IConductor) != null);
        }

        #region IResult implementation

        public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };

        public void Execute(ActionExecutionContext context)
        {
            _toCloseCallback(true, new List<IWorkspace>());
            Completed(this, new ResultCompletionEventArgs());
        }

        #endregion


    }
}
I Hope i am not asking for too much.
One last thing, i really love the Caliburn.Micro project. and would like to participate in development if possible.
I wonder how might i be able to do so, especially providing detailed documentation.
Thanks
abnajjar
May 14, 2013 at 3:12 PM
Hi Abnajjar,

That all looks ok from first glance,

I wonder is your problem because of hooking into the main closing method. In Silverlight it is done like so:
protected override void OnStartup(object sender, StartupEventArgs e) {
    base.OnStartup(sender, e);

    if(Application.IsRunningOutOfBrowser) {
        mainWindow = Application.MainWindow;
        mainWindow.Closing += MainWindowClosing;
    }
}

void MainWindowClosing(object sender, ClosingEventArgs e) {
    if (actuallyClosing)
        return;

    e.Cancel = true;

    Execute.OnUIThread(() => {
        var shell = IoC.Get<IShell>();

        shell.CanClose(result => {
            if(result) {
                actuallyClosing = true;
                mainWindow.Close();
            }
        });
    });
}
You would need to do the same in WPF otherwise it wont ran.

Regarding helping out. I wouldn't be the one to talk to about it. You would need to speak to Rob.
May 14, 2013 at 3:43 PM
I was doing exactly that, but because of the WindowConductor, the same code is getting called three times, two times from the WindowConductor class and one time from the my code. Now another thing is confusing me, you see the IResult in the class i showed you is getting called twice !!.
I think the main confusion point is coming from the SequentialResult and how it is treated by the framework.
Anyway, i don't want to bother you with this any more.
I will try to track down the issue to see what is going on.
Thank you for you help, i will try to talk to Rob and see.
abnajjar