I have a c# .net 3.5 application that writes text to the console using a StreamWriter. Is there a way I can add text decorations like underline and strikethrough to the text that is printed to the console? Possibly using ANSI escape sequences?
TextWriter writer = new StreamWriter(Console.OpenStandardOutput());
writer.WriteLine("some underlined text");
Thanks, PaulH
I found this question and thought I would add to the answers for pre-windows 10 terminal using kernel32 functions,
using System;
using System.Runtime.InteropServices;
namespace color_console
{
class Class1
{
static void Main(string[] args)
{
Class1 c = new Class1();
c.change();
}
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, CharacterAttributes wAttributes);
[DllImport("kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
public static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput,
ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
void change()
{
const int STD_OUTPUT_HANDLE = -11;
IntPtr hOut;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(hOut, ref ConsoleInfo);
CharacterAttributes originalAttributes = (CharacterAttributes)ConsoleInfo.wAttributes;
//write some text
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_BLUE);
Console.WriteLine("Blue text");
SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_RED);
Console.WriteLine("Red background");
SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN);
Console.WriteLine("Green background");
SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN | CharacterAttributes.BACKGROUND_RED);
Console.WriteLine("Yellow background");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED | CharacterAttributes.COMMON_LVB_UNDERSCORE);
Console.WriteLine("Red underlined text");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
| CharacterAttributes.FOREGROUND_BLUE);
Console.WriteLine("Purple text");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
| CharacterAttributes.FOREGROUND_BLUE
| CharacterAttributes.FOREGROUND_INTENSITY);
Console.WriteLine("Purple text intense");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
| CharacterAttributes.FOREGROUND_BLUE
| CharacterAttributes.COMMON_LVB_REVERSE_VIDEO);
Console.WriteLine("Aqua reversed text ");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
| CharacterAttributes.FOREGROUND_BLUE
| CharacterAttributes.COMMON_LVB_REVERSE_VIDEO
| CharacterAttributes.FOREGROUND_INTENSITY);
Console.WriteLine("Aqua reversed intense text ");
SetConsoleTextAttribute(hOut, CharacterAttributes.COMMON_LVB_GRID_LVERTICAL
| CharacterAttributes.FOREGROUND_GREEN);
Console.WriteLine("What does this do");
SetConsoleTextAttribute(hOut, originalAttributes);
Console.WriteLine("Back to the shire");
}
public enum CharacterAttributes
{
FOREGROUND_BLUE = 0x0001,
FOREGROUND_GREEN = 0x0002,
FOREGROUND_RED = 0x0004,
FOREGROUND_INTENSITY = 0x0008,
BACKGROUND_BLUE = 0x0010,
BACKGROUND_GREEN = 0x0020,
BACKGROUND_RED = 0x0040,
BACKGROUND_INTENSITY = 0x0080,
COMMON_LVB_LEADING_BYTE = 0x0100,
COMMON_LVB_TRAILING_BYTE = 0x0200,
COMMON_LVB_GRID_HORIZONTAL = 0x0400,
COMMON_LVB_GRID_LVERTICAL = 0x0800,
COMMON_LVB_GRID_RVERTICAL = 0x1000,
COMMON_LVB_REVERSE_VIDEO = 0x4000,
COMMON_LVB_UNDERSCORE = 0x8000
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public int wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
// Standard structures used for interop with kernel32
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short x;
public short y;
}
[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
}
}
Output on my PC
I use this code. It's a fixed version of Vladimir Reshetnikov's answer, using the correct escape code for the reset.
private static void WriteUnderline(string s)
{
var handle = GetStdHandle(STD_OUTPUT_HANDLE);
uint mode;
GetConsoleMode(handle, out mode);
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(handle, mode);
Console.WriteLine($"\x1B[4m{s}\x1B[24m");
}
This will do underlined text, and has the benefit of not resetting any colors you have set.
The Windows console does not support ANSI escape sequences. To my knowledge, the only way to change the attributes of an output character is to call SetConsoleTextAttribute
before writing the character. Or, in .NET, modify the Console.ForegroundColor
or Console.BackgroundColor
attributes.
It might be possible to set those properties to custom values (i.e. values not defined by ConsoleColor
) with a type cast. But I don't know what good that would do you.
I don't know that I've ever seen strikethrough text on a Windows console, and it's been years since I saw underline. I suppose it's possible, but I don't know how it's done.
Short answer, no; the console doesn't allow the use of underlined characters in output.
Longer answer: The screen buffer used by the console is little more than a byte array. Each cursor position is one byte or one character. To create an underline, you either need two characters overlapping (which isn't possible in the console), or you need access to a codepage that uses the upper 128 character values as underlined or strikethrough versions of the lower 128 (I don't know of one).
You can work around this if you are willing to go "double-spaced" for lines that have underlines. Character code 0x00AF (decimal 175) is a "text art" character representing a border across the top of the character space. If you use those in the line underneath your text, presto, underlines.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With