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.BelowCursor
ButKeepCursorLocation
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");
Draw.RectangleFromCursor(1, 0, 2, 2);
Console.WriteLine(" <--Cursor after test 1 was here.");
Draw.RectangleFromTop(4, 2, 2, 2, ConsoleColor.DarkYellow);
Console.WriteLine(" <--Cursor after test 2 was here.");
Draw.RectangleFromCursor(1, 3, 2, 2, keepOriginalCursorLocation: true, color: ConsoleColor.Red);
Console.WriteLine(" <--Cursor after test 3 was here.");
Draw.Rectangle(4, 2, 2, 2, Draw.DrawKind.BelowCursorButKeepCursorLocation, color: ConsoleColor.Green);
Console.WriteLine(" <--Cursor after test 4 was here.");
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
{
public static void RectangleFromCursor(int width,
int height,
int xLocation = 0,
int yLocation = 0,
bool keepOriginalCursorLocation = false,
ConsoleColor? color = null,
bool useDoubleLines = false)
{
{
int savedCursorTop = Console.CursorTop;
int savedCursorLeft = Console.CursorLeft;
if (width < 1 || height < 1)
{
return;
}
ConsoleColor savedColor = Console.ForegroundColor;
if (color.HasValue)
{
Console.ForegroundColor = color.Value;
}
char tl, tt, tr, mm, bl, br;
if (useDoubleLines)
{
tl = '+'; tt = '-'; tr = '+'; mm = '¦'; bl = '+'; br = '+';
}
else
{
tl = '+'; tt = '-'; tr = '+'; mm = '¦'; 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);
}
}
}
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);
}
public enum DrawKind
{
BelowCursor,
BelowCursorButKeepCursorLocation,
FromTop,
}
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 (width < 1 || height < 1)
{
return;
}
int savedCursorTop = Console.CursorTop;
int savedCursorLeft = Console.CursorLeft;
if (drawKind == DrawKind.BelowCursor || drawKind == DrawKind.BelowCursorButKeepCursorLocation)
{
yLocation += Console.CursorTop;
}
ConsoleColor savedColor = Console.ForegroundColor;
if (color.HasValue)
{
Console.ForegroundColor = color.Value;
}
char tl, tt, tr, mm, bl, br;
if (useDoubleLines)
{
tl = '+'; tt = '-'; tr = '+'; mm = '¦'; bl = '+'; br = '+';
}
else
{
tl = '+'; tt = '-'; tr = '+'; mm = '¦'; 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);
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);
}
}
}