Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Drawing a Rectangle in the C# Console

0.00/5 (No votes)
18 Apr 2016 1  
Draw rectangles in a C# console window with this drop-in class

Tip/Trick: Draw Boxes in a C# Console with this Drop-in Class

This is a drop-in class that allows the drawing of rectangles in a console window. I originally built it for my own needs but decided to share it for others that might need it.

Two different drawing modes are supported:

  • WriteLine() Output Style - The function for this is RectangleFromCursor(...). When a new box needs to be drawn on some new appended lines, similar to how WriteLine() performs, then this function can be used. It basically calls WriteLine() repeatedly to draw the new box. One drawback to the WriteLine() version is that multiple rectangles cannot be drawn on the same line. For this, the Overwrite style is needed.
  • Overwrite Style - RectangleFromTop(...) and Rectangle(...) do not use WriteLine(). Instead they set the cursor to a specific location and then write. These functions are best utilized when a rectangle needs to be drawn in a specific location of the console window. If several boxes need to be on the same row, then these functions will serve best. The overwrite style has two types:
    • Draw below offset 0,0 - When using Rectangle(...), use the DrawKind.FromTop option for this. It will draw based on the top of the console buffer area. Basically, if you scroll to the top, 0,0 is the top left corner.
    • Draw below offset of cursor - Use DrawKind.BelowCursor or DrawKind.BelowCursorButKeepCursorLocation for this. This will draw the rectangles using the cursor row as an offset.

Code Features

When building the rectangle drawing class, I tried to make it as flexible as possible.

  • It can draw single or double boarder boxes
  • Draw boxes in different colors
  • It restores the original draw color of the curser when done
  • Supports writing from the top of the window or for appending at the curser
  • Option to keep the curser at end of the rectangle or it can be returned to the original location

Example Usage

Here is some example code...

Console.WriteLine();
Console.WriteLine("01234567890123456789");

// Test 1: draw 2 x 2 square at 1,1
Draw.RectangleFromCursor(1, 0, 2, 2);
Console.WriteLine(" <--Cursor after test 1 was here.");

// Test 2: draw a yellow 2 x 2 square at 4,2
Draw.RectangleFromTop(4, 2, 2, 2, ConsoleColor.DarkYellow);
Console.WriteLine(" <--Cursor after test 2 was here.");

// Test 3: draw a red 2 x 2 square below
Draw.RectangleFromCursor(1, 3, 2, 2, keepOriginalCursorLocation: true, color: ConsoleColor.Red);
Console.WriteLine(" <--Cursor after test 3 was here.");

// Test 4: draw a green 2 x 2 square below
Draw.Rectangle(4, 2, 2, 2, Draw.DrawKind.BelowCursorButKeepCursorLocation, color: ConsoleColor.Green);
Console.WriteLine(" <--Cursor after test 4 was here.");

// Test 5: draw a double-boarder cyan rectangle around everything
Draw.RectangleFromTop(0, 0, 33, 15, ConsoleColor.Cyan, useDoubleLines: true);
Console.WriteLine(" <--Cursor after test 5 was here.");

The output of the above looks like this:

The Code (Drop-in Class)

Since the class is not too large, it is being posted here in its entirety. Just drop this in your console project and enjoy.

public static class Draw
{
    /// <summary>
    /// Draws a rectangle in the console using several WriteLine() calls.
    /// </summary>
    /// <param name="width">The width of the rectangle.</param>
    /// <param name="height">The right of the rectangle.</param>
    /// <param name="xLocation">The left side position.</param>
    /// <param name="yLocation">The top position.</param>
    /// <param name="keepOriginalCursorLocation">If true, 
    /// the cursor will return back to the starting location.</param>
    /// <param name="color">The color to use. null=uses current color Default: null</param>
    /// <param name="useDoubleLines">Enables double line boarders. Default: false</param>
    public static void RectangleFromCursor(int width,
        int height,
        int xLocation = 0,
        int yLocation = 0,
        bool keepOriginalCursorLocation = false,
        ConsoleColor? color = null,
        bool useDoubleLines = false)
    {
        {
            // Save original cursor location
            int savedCursorTop = Console.CursorTop;
            int savedCursorLeft = Console.CursorLeft;

            // if the size is smaller then 1 then don't do anything
            if (width < 1 || height < 1)
            {
                return;
            }

            // Save and then set cursor color
            ConsoleColor savedColor = Console.ForegroundColor;
            if (color.HasValue)
            {
                Console.ForegroundColor = color.Value;
            }

            char tl, tt, tr, mm, bl, br;

            if (useDoubleLines)
            {
                tl = '+'; tt = '-'; tr = '+'; mm = '&brvbar;'; bl = '+'; br = '+';
            }
            else
            {
                tl = '+'; tt = '-'; tr = '+'; mm = '&brvbar;'; bl = '+'; br = '+';
            }

            for (int i = 0; i < yLocation; i++)
            {
                Console.WriteLine();
            }

            Console.WriteLine(
                string.Empty.PadLeft(xLocation, ' ')
                + tl
                + string.Empty.PadLeft(width-1, tt)
                + tr);

            for (int i = 0; i < height; i++)
            {
                Console.WriteLine(
                    string.Empty.PadLeft(xLocation, ' ')
                    + mm
                    + string.Empty.PadLeft(width - 1, ' ')
                    + mm);
            }

            Console.WriteLine(
                string.Empty.PadLeft(xLocation, ' ')
                + bl
                + string.Empty.PadLeft(width - 1, tt)
                + br);


            if (color.HasValue)
            {
                Console.ForegroundColor = savedColor;
            }

            if (keepOriginalCursorLocation)
            {
                Console.SetCursorPosition(savedCursorLeft, savedCursorTop);
            }
        }
    }

    /// <summary>
    /// Draws a rectangle in a console window using the top line of the buffer as the offset.
    /// </summary>
    /// <param name="xLocation">The left side position.</param>
    /// <param name="yLocation">The top position.</param>
    /// <param name="width">The width of the rectangle.</param>
    /// <param name="height">The right of the rectangle.</param>
    /// <param name="color">The color to use. null=uses current color Default: null</param>
    public static void RectangleFromTop(
        int width,
        int height,
        int xLocation = 0,
        int yLocation = 0,
        ConsoleColor? color = null,
        bool useDoubleLines = false)
    {
        Rectangle(width, height, xLocation, yLocation, DrawKind.FromTop, color, useDoubleLines);
    }

    /// <summary>
    /// Specifies if the draw location should be based on the current cursor location or the
    /// top of the window.
    /// </summary>
    public enum DrawKind
    {
        BelowCursor,
        BelowCursorButKeepCursorLocation,
        FromTop,
    }

    /// <summary>
    /// Draws a rectangle in the console window.
    /// </summary>
    /// <param name="width">The width of the rectangle.</param>
    /// <param name="height">The right of the rectangle.</param>
    /// <param name="xLocation">The left side position.</param>
    /// <param name="yLocation">The top position.</param>
    /// <param name="drawKind">Where to draw the rectangle and 
    /// where to leave the cursor when finished.</param>
    /// <param name="color">The color to use. null=uses current color Default: null</param>
    /// <param name="useDoubleLines">Enables double line boarders. Default: false</param>
    public static void Rectangle(
        int width, 
        int height, 
        int xLocation = 0, 
        int yLocation = 0, 
        DrawKind drawKind = DrawKind.FromTop, 
        ConsoleColor? color = null, 
        bool useDoubleLines = false)
    {        
        // if the size is smaller then 1 than don't do anything
        if (width < 1 || height < 1)
        {
           return;
        }

        // Save original cursor location
        int savedCursorTop = Console.CursorTop;
        int savedCursorLeft = Console.CursorLeft;

        if (drawKind == DrawKind.BelowCursor || drawKind == DrawKind.BelowCursorButKeepCursorLocation)
        {
            yLocation += Console.CursorTop;
        }

        // Save and then set cursor color
        ConsoleColor savedColor = Console.ForegroundColor;
        if (color.HasValue)
        {
            Console.ForegroundColor = color.Value;
        }

        char tl, tt, tr, mm, bl, br;

        if (useDoubleLines)
        {
            tl = '+'; tt = '-'; tr = '+'; mm = '&brvbar;'; bl = '+'; br = '+';
        }
        else
        {
            tl = '+'; tt = '-'; tr = '+'; mm = '&brvbar;'; bl = '+'; br = '+';
        }

        SafeDraw(xLocation, yLocation, tl);
        for (int x = xLocation + 1; x < xLocation + width; x++)
        {
            SafeDraw(x, yLocation, tt);
        }
        SafeDraw(xLocation + width, yLocation, tr);

        for (int y = yLocation + height; y > yLocation; y--)
        {
            SafeDraw(xLocation, y, mm);
            SafeDraw(xLocation + width, y, mm);
        }

        SafeDraw(xLocation, yLocation + height + 1, bl);
        for (int x = xLocation + 1; x < xLocation + width; x++)
        {
            SafeDraw(x, yLocation + height + 1, tt);
        }
        SafeDraw(xLocation + width, yLocation + height + 1, br);

        // Restore cursor
        if (drawKind != DrawKind.BelowCursor)
        {
            Console.SetCursorPosition(savedCursorLeft, savedCursorTop);
        }

        if (color.HasValue)
        {
            Console.ForegroundColor = savedColor;
        }
    }

    private static void SafeDraw(int xLocation, int yLocation, char ch)
    {
        if (xLocation < Console.BufferWidth && yLocation < Console.BufferHeight)
        {
            Console.SetCursorPosition(xLocation, yLocation);
            Console.Write(ch);
        }
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here