Drawing Dialog Backgrounds in XNA

Written by Dean Edis.

The fact that XNA comes with minimal built-in ability to display attractive-looking UIs can be seen as both an advantage and a disadvantage.

When you're writing an application that needs to put up a dialog though (and you don't want to break XNA's cross-platform support by using WIN32 calls!), it can be annoying!

We're in the process of writing the infrastructure for our next game, and in it we'd like to be able to display a range of attractive UI styles, without too much overhead.

So to help with this I've written code to display a dialog background using only a template image. The class ensures that the dialog can be displayed at any sensible size, without introducing unpleasant 'stretch' artifacts.

 

To start off with, let's see an example of a dialog template, and how it could be made to appear on the screen:

To display this on our XNA screen at any given size, we first need to split the bitmap into a 3x3 grid of 'virtual' regions. (I've also reduced the size of the template, as it doesn't need to be too big.)

The corner areas will be blitted to the screen without any scaling applied to them. The top and left edges will be stretched in one direction only, and the centre-most region will be scaled in both direction as appropriate.

The result is nice-looking dialog background, regardless of the dimensions of the original template image - You only need to define the size of the 'margin' on your original template image such that it incloses each of the corners.

The code is quite straight-forward, and easy to import into your own projects. Just create an instance of BorderBitmap, passing in your dialog texture and the pixel size of a ‘corner’, then call Draw() from your display code.

Finding dialog templates online isn't too tricky. I certainly recommend here:
http://opengameart.org/

 

I hope you find this useful!

/// <summary>
/// For drawing a textured bitmap where the corner sections remain unscaled but the remaining area is stretched to the desired screen size.
/// </summary>
public class BorderBitmap
{
    private readonly Texture2D m_texture;
    private readonly int m_cornerPixels;

    public BorderBitmap(Texture2D texture, int cornerPixels)
    {
        if (texture == null)
            throw new ArgumentNullException("texture");

        m_texture = texture;
        m_cornerPixels = cornerPixels;
    }

    public void Draw(SpriteBatch spriteBatch, int x, int y, int width, int height)
    {
        var sourceArea = new Rectangle(0, 0, m_texture.Width, m_texture.Height);
        var sourceInnerArea = new Rectangle(m_cornerPixels, m_cornerPixels, sourceArea.Width - 2 * m_cornerPixels, sourceArea.Height - 2 * m_cornerPixels);

        spriteBatch.Draw(m_texture, new Rectangle(x + m_cornerPixels, y + m_cornerPixels, width - 2 * m_cornerPixels, height - 2 * m_cornerPixels), sourceInnerArea, Color.White);

        if (m_cornerPixels > 0)
        {
            DrawSides(spriteBatch, x, y, width, height, sourceInnerArea);
            DrawCorners(spriteBatch, x, y, width, height, sourceInnerArea);
        }
    }

    private void DrawCorners(SpriteBatch spriteBatch, int x, int y, int width, int height, Rectangle sourceInnerArea)
    {
        // Top-left
        spriteBatch.Draw(m_texture, new Vector2(x, y), new Rectangle(0, 0, m_cornerPixels, m_cornerPixels), Color.White);

        // Top-right
        spriteBatch.Draw(m_texture, new Vector2(x + width - m_cornerPixels, y), new Rectangle(sourceInnerArea.Right, 0, m_cornerPixels, m_cornerPixels), Color.White);

        // Bottom-right
        spriteBatch.Draw(m_texture, new Vector2(x + width - m_cornerPixels, y + height - m_cornerPixels), new Rectangle(sourceInnerArea.Right, sourceInnerArea.Bottom, m_cornerPixels, m_cornerPixels), Color.White);

        // Bottom-left
        spriteBatch.Draw(m_texture, new Vector2(x, y + height - m_cornerPixels), new Rectangle(0, sourceInnerArea.Bottom, m_cornerPixels, m_cornerPixels), Color.White);
    }

    private void DrawSides(SpriteBatch spriteBatch, int x, int y, int width, int height, Rectangle sourceInnerArea)
    {
        // Top
        spriteBatch.Draw(m_texture, new Rectangle(x + m_cornerPixels, y, width - 2 * m_cornerPixels, m_cornerPixels), new Rectangle(sourceInnerArea.Left, 0, sourceInnerArea.Width, m_cornerPixels), Color.White);

        // Bottom
        spriteBatch.Draw(m_texture, new Rectangle(x + m_cornerPixels, y + height - m_cornerPixels, width - 2 * m_cornerPixels, m_cornerPixels), new Rectangle(sourceInnerArea.Left, sourceInnerArea.Bottom, sourceInnerArea.Width, m_cornerPixels), Color.White);

        // Left
        spriteBatch.Draw(m_texture, new Rectangle(x, y + m_cornerPixels, m_cornerPixels, height - 2 * m_cornerPixels), new Rectangle(0, m_cornerPixels, m_cornerPixels, sourceInnerArea.Height), Color.White);

        // Right
        spriteBatch.Draw(m_texture, new Rectangle(x + width - m_cornerPixels, y + m_cornerPixels, m_cornerPixels, height - 2 * m_cornerPixels), new Rectangle(sourceInnerArea.Right, m_cornerPixels, m_cornerPixels, sourceInnerArea.Height), Color.White);
    }
}