Simplified XNA Message Boxes

June 11, 2009 by Dave
Filed under: C#, Game Programming, XNA 

Shawn Hargreaves brings up the subject of how annoying async coding can be.  Calling a “begin” method, dealing with the completion callback function, handling the results – it’s all very ugly to keep track of, and often leads to very ugly code.

He wants to be able to write code like this (and so do I)…

 

 int? button = Guide.ShowMessageBox("Save Game",
                                       "Do you want to save your progress?",
                                       new string[] { "OK", "Cancel" },
                                       0, MessageBoxIcon.None);

    if (button == 0)
    {
        StorageDevice storageDevice = Guide.ShowStorageDeviceSelector();

        if (storageDevice != null)
        {
            using (StorageContainer storageContainer = storageDevice.OpenContainer("foo"))
            {
                ...
            }
        }
    }

 

 

It turns out that making async code work almost like this isn’t too bad to do. It basically involves creating a static class to encapsulate all of the various things you need to keep track of. Here is the fairly well commented code for the static class.

class SimpleMessageBox
  {
    private static int? dialogResult = null;
    public static bool Showing { get; set; }

    public static int? ShowMessageBox(string title, string text, IEnumerable  buttons, int focusButton, MessageBoxIcon icon)
    {
      // don't do anything if the guide is visible - one issue this handles is showing dialogs in quick
      // succession, we have to wait for the guide to go away before the next dialog can display
      if (Guide.IsVisible) return null;

      // if we have a result then we're all done and we want to return it
      if (dialogResult != null)
      {
        // preserve the result
        int? saveResult = dialogResult;

        // reset everything for the next message box
        dialogResult = null;
        Showing = false;

        // return the result
        return saveResult;
      }

      // return nothing if the message box is still being displayed
      if (Showing) return null;

      // otherwise show it
      Showing = true;
      Guide.BeginShowMessageBox(title, text, buttons, focusButton, icon, MessageBoxEnd, null);
      return null;
    }

    private static void MessageBoxEnd(IAsyncResult result)
    {
      dialogResult = Guide.EndShowMessageBox(result);

      // if no button was pressed then we want the result to be -1
      if (dialogResult == null)
        dialogResult = -1;
    }

 

Using the class involves calling SimpleMessageBox.ShowMessage(…) in your Update() method. You continue to call it each frame until it returns a result. This does require some game state information (i.e. your game state is SaveGameState or something similar) so it takes a little extra work, but you have to keep track of those sorts of states anyway.

Here’s a sample of the usage:

protected override void Update(GameTime gameTime)
    {
      base.Update(gameTime);

      if (saveGame)
      {
        // show the message box - we end up calling this each frame as long as we're in the saveGame state - it will
        // return null until the user presses a button or closes the guide - it returns -1 if the guide
        // is closed, otherwise it returns the button number
        int? button = SimpleMessageBox.ShowMessageBox("Save Game", "Do you want to save your progress?",
                                                      new string[] { "OK", "Cancel", "Repeat" }, 0, MessageBoxIcon.None);

        switch (button)
        {
          case -1:
            message = "No Button";
            saveGame = false;
            break;

          case 0:
            message = "Saved";
            saveGame = false;
            break;

          case 1:
            message = "Cancelled";
            saveGame = false;
            break;

          case 2:
            message = "Repeat";
            break;
        }
      }
    }

 

I haven’t use this code in a real project yet (just the sample), but it seems like it would work in quite a few situations. It’s a bit different than doing a message box in Windows since you have to realize you’re calling the ShowMessageBox method each frame. That aside, you can almost imagine that you’re using a blocking message box function.

Download Sample Project

Comments

Tell me what you're thinking...