Wensus Analytics

Caliburn.Micro & WP7: Executing coroutines without user input

This tutorial will show you how to execute a coroutine without user input. 

Background

Coroutines in Caliburn.Micro offer an excellent way of creating stateful tasks. These tasks can contain multiple asynchronous method calls and show multiple different dialogs, Caliburn.Micro taking care of the in-order processing. But most of the examples only cover one situation: The coroutine is executed when a user clicks a button which is attached to the coroutine-method.

But what if you want to execute the coroutine automatically when the view model is activated? Let’s go through couple of different cases.

First case: ViewModel contains the IEnumerator<IResult> –method

In our first case the coroutine-executing method lives inside the view model class. It runs a coroutine called LoginTask which implements the Caliburn.Micro’s IResult-interface.

        private IEnumerator<IResult> FolderSelection()
        {
            yield return new LoginTask();

            if (LoginTask.LoginCredentials == null)
                yield break;

            service.LoadFolderContentsCompleted += OnLoadFolderContentsCompleted;
            ThreadPool.QueueUserWorkItem(x => service.LoadFolderContentsBegin(LoginTask.LoginCredentials));
        }

We want to execute this when the view model is activated. This can be achieved by calling the static BeginExecute-method inside the Coroutine-class:

        protected override void OnActivate()
        {
            Coroutine.BeginExecute(FolderSelection());
        }

Second case: ViewModel needs to execute a single IResult-implementing class

In our second case we have a class implementing the IResult-interface. We again want to execute this when a view model is activated. Here’s the coroutine:

    public class LoginServiceCallTask : IResult
    {
        private readonly ILoginService service;
        private string userName;
        private string password;

        public LoginResult LoginResult { get; private set; }
        public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };

        public LoginServiceCallTask(string userName, string password)
        {
            this.userName = userName;
            this.password = password;

            this.service = IoC.Get<ILoginService>();
        }

        public void Execute(ActionExecutionContext context)
        {
            service.DoLoginCompleted += ServiceLoginCompleted;
            ThreadPool.QueueUserWorkItem(x => service.DoLoginBegin(userName, password));
        }

        void ServiceLoginCompleted(object sender, LoginEventArgs e)
        {
            service.DoLoginCompleted -= ServiceLoginCompleted;

            this.LoginResult = e.Result;
            Caliburn.Micro.Execute.OnUIThread(() => Completed(this, new ResultCompletionEventArgs()));
        }
    }

To execute it, we need to create an instance of it, add it to a collection, get the enumerator for the collection and run it with the Coroutine.BeginExecute-method:

        protected override void OnActivate()
        {
            var loginTask = new LoginServiceCallTask("username", "password");
            var list = new List<IResult> { loginTask };

            Coroutine.BeginExecute(list.GetEnumerator());
        }
<br />

Conclusion

When you need to execute a coroutine and you can’t require user input to do it, Coroutine.Beginexecute allows you to execute the coroutine whenever needed.

Links