Let's take a look at how coroutines simplify ViewModel code in Halfwit.

Blocking a User with Coroutines

Yesterday I took the time to introduce MadProps.MvvmLight into Halfwit, so I could replace some of the convoluted asynchronous code with simple coroutines. Let's take a look at one example.

When you choose to "block" a user in Halfwit, a couple of things need to happen:

  1. You're prompted with a dialog box asking you to confirm your action
  2. If you click "No", we don't do anything
  3. If you answered positively, we make an asynchronous call to Twitter to block the user
  4. If the call didn't succeed, we report the error and stop work
  5. If the call succeeded, we tell you that the user is now blocked and remove any tweets of theirs from your timestream.

A lot of those steps require asynchronous work. Either explicitly asynchronous, like the call to Twitter, or asynchronous as far as the ViewModel is concerned, like the showing of the dialog box. Previously there were two or three different methods involved, since each asynchronous step needed to provide a callback method for the next step.

Coroutines have simplified this procedure down to a single, sequential-looking method.

First, let's take a look at our BlockCommand initialization:

// processor is an ActivitiyProcessor instance
BlockCommand = processor.CreateCommand<IHalfwitUser>(Block);

That's creating a new kind of ICommand implementaton that knows about coroutines, and takes an IHalfwitUser as a parameter.

Now the execution:

IEnumerable<IActivity> Block(IHalfwitUser user)
{
    if (user == null) yield break;

    var mbox = new MessageBoxActivity("Block " + user.Name + "?", 
            "Confirm Block", 
            MessageBoxButton.YesNo, 
            MessageBoxImage.Question, 
            MessageBoxResult.No);
    yield return mbox;

    if (mbox.Result == MessageBoxResult.No) yield break;

    var block = new BlockActivity(Service, user.ScreenName);
    yield return block;

    if (block.Response.StatusCode != System.Net.HttpStatusCode.OK)
    {
        Status = block.Response.StatusDescription;
        yield break;
    }

    Status = "Blocked " + block.User.Name;

    foreach (var item in Items.Where(i => i.User.ScreenName == block.User.ScreenName).ToList())
    {
        _items.Remove(item);
    }
}

Each "activity" is yielded from our method and processed in turn, and control is not returned to the method until that activitiy completes. So we can show our message box and wait for it to return, then ask Twitter to block the user and wait for that to return, and finally take some action based on the response from Twitter.

Take it from me: This is a big improvement over the series of methods that used to make up this procedure. Callbacks inside callbacks is no way to code.

If you haven't already played with the MVVM Light coroutine support in MadProps.MvvmLight, go ahead and download it! If you grab the complete source there's a sample project to try out.

mvvm halfwit coroutines wpf
Posted by: Matt Hamilton
Last revised: 22 Sep, 2017 04:19 AM History

Trackbacks