Simplified XNA Message Boxes
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.
Comments
Tell me what you're thinking...
