Using colors in console, how to store in a simplified notation
The code below shows a line in different colors, but that's a lot of code to type just for one line and to repeat that all over a program again.
How exact开发者_运维百科ly can I simplify this, so I don't need to write the same amount of code over and over?Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write(">>> Order: ");
Console.ResetColor();
Console.Write("Data");
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.Write("Parity");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write(" <<<");
Is there any way to store ... = Console.ForegroundColor = ConsoleColor.Cyan;
?
"text" + color? + "text"; etc...
It's not entirely clear what you mean, but you could always create helper methods:
public static void ColoredConsoleWrite(ConsoleColor color, string text)
{
ConsoleColor originalColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.Write(text);
Console.ForegroundColor = originalColor;
}
Use can use my library Edokan.KaiZen.Colors . It is a shameless copy of colors npm module for node.js.
It has some problems but it works. Actually it works just the way you want.
Console.WriteLine("INFO: ".Cyan().Bold() + " This is just a message");
Another idea is to embed something like the ANSI escape codes but they are quite extensive, with color just a smallish subset of them.
So I'd recommend embedding color codes in the output strings, using a section of the Unicode Private Use Area (PUA) in the Basic Multilingual Plane (BMP) and a basic parser/renderer.
BTW the console color settings are global to the process, so if multiple threads are generating colored output each block needs to be surrounded by a critical section to avoid "color confusion".
Since its a bit long to put inline I've uploaded the sample source to my site.
Ugly, but simple solution.
Begin of foreground color tag: <*color*>
.
End of foreground color tag: <*/*>
.
Begin of background color tag: <*!color*>
.
End of background color tag: <*/!*>
.
Usage example:
WriteLineColor("<*!red*><*yellow*>Yellow text<*/*> and <*cyan*>cyan text<*/*> on red background.<*/!*>");
List of acceptable colors (System.ConsoleColor), case insensitive: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White.
static void WriteColor(string str)
{
var fgStack = new Stack<ConsoleColor>();
var bgStack = new Stack<ConsoleColor>();
var parts = str.Split(new[] { "<*" }, StringSplitOptions.None);
foreach (var part in parts)
{
var tokens = part.Split(new[] { "*>" }, StringSplitOptions.None);
if (tokens.Length == 1)
{
Console.Write(tokens[0]);
}
else
{
if (!String.IsNullOrEmpty(tokens[0]))
{
ConsoleColor color;
if (tokens[0][0] == '!')
{
if (Enum.TryParse(tokens[0].Substring(1), true, out color))
{
bgStack.Push(Console.BackgroundColor);
Console.BackgroundColor = color;
}
}
else if (tokens[0][0] == '/')
{
if (tokens[0].Length > 1 && tokens[0][1] == '!')
{
if (bgStack.Count > 0)
Console.BackgroundColor = bgStack.Pop();
}
else
{
if (fgStack.Count > 0)
Console.ForegroundColor = fgStack.Pop();
}
}
else
{
if (Enum.TryParse(tokens[0], true, out color))
{
fgStack.Push(Console.ForegroundColor);
Console.ForegroundColor = color;
}
}
}
for (int j = 1; j < tokens.Length; j++)
Console.Write(tokens[j]);
}
}
}
static void WriteLineColor(string str)
{
WriteColor(str);
Console.WriteLine();
}
static void WriteColor(string str, params object[] arg)
{
WriteColor(String.Format(str, arg));
}
static void WriteLineColor(string str, params object[] arg)
{
WriteColor(String.Format(str, arg));
Console.WriteLine();
}
You can use CsConsoleFormat† library to format console output with colors.
Here's what your code would look like:
using static System.ConsoleColor;
ConsoleRenderer.RenderDocument(new Document().AddChildren(
new Span(">>> Order: ") { Color = Cyan },
new Span("Data") { Color = Gray },
new Span("Parity") { Color = DarkGreen },
new Span(" <<<") { Color = Cyan }
));
or, alternatively:
ConsoleRenderer.RenderDocument(new Document().AddChildren(
new Span { Color = Cyan }.AddChildren(
">>> Order: ",
new Span("Data") { Color = Gray },
new Span("Parity") { Color = DarkGreen },
" <<<"
)
));
or, with helper code (see below):
ColoredSpans.Render(">>> Order: ".Cyan(), "Data".Gray(), "Parity".DarkGreen(), " <<<".Cyan());
The top two methods, while not being very conscise, have many benefits:
- Make the intetion clear and support hierarchy, for example, child spans added to a parent span will inherit the parent's colors.
- Unlike
ColoredConsoleWrite
helper method, generate a complete document which can be adjusted as a whole (for example, word wrapping, background). - Don't rely on "inverse console color" concept, which is alien for .NET developers on Windows, to set background color.
If you want more conscise code (for example, you write a lot of formatted paragraphs with many highlighted words), you can use this collection of helper methods, inspired by Edokan.KaiZen.Colors, to write code like in the 3rd example above:
public static class Spans
{
public static Span Black(this string text) => new Span(text) { Color = ConsoleColor.Black };
public static Span DarkBlue(this string text) => new Span(text) { Color = ConsoleColor.DarkBlue };
public static Span DarkGreen(this string text) => new Span(text) { Color = ConsoleColor.DarkGreen };
public static Span DarkCyan(this string text) => new Span(text) { Color = ConsoleColor.DarkCyan };
public static Span DarkRed(this string text) => new Span(text) { Color = ConsoleColor.DarkRed };
public static Span DarkMagenta(this string text) => new Span(text) { Color = ConsoleColor.DarkMagenta };
public static Span DarkYellow(this string text) => new Span(text) { Color = ConsoleColor.DarkYellow };
public static Span Gray(this string text) => new Span(text) { Color = ConsoleColor.Gray };
public static Span DarkGray(this string text) => new Span(text) { Color = ConsoleColor.DarkGray };
public static Span Blue(this string text) => new Span(text) { Color = ConsoleColor.Blue };
public static Span Green(this string text) => new Span(text) { Color = ConsoleColor.Green };
public static Span Cyan(this string text) => new Span(text) { Color = ConsoleColor.Cyan };
public static Span Red(this string text) => new Span(text) { Color = ConsoleColor.Red };
public static Span Magenta(this string text) => new Span(text) { Color = ConsoleColor.Magenta };
public static Span Yellow(this string text) => new Span(text) { Color = ConsoleColor.Yellow };
public static Span White(this string text) => new Span(text) { Color = ConsoleColor.White };
public static void Render(object[] elements) => ConsoleRenderer.RenderDocument(new Document().AddChildren(elements));
}
† CsConsoleFormat was developed by me.
I've created a small plugin (available on NuGet) that allows you to easily wrap your strings in ANSI color codes.
You can specify any color you like, but it's up to the terminal whether it supports it or not. If it doesn't, it will approximate to the nearest color. 24-bit colors are available in Windows 10 since v1511. PowerShell supports them by default.
It works by extending the String
object, and the syntax is very simple:
"color me".Pastel("#1E90FF");
After that it's ready to be printed to the console.
Here's my contribution, albeit in Visual Basic:
Sub WriteColor(ByRef part1 As String, ByRef part2 As String)
Console.Write(part1)
Console.ForegroundColor = ConsoleColor.Green
Console.WriteLine(part2)
Console.ResetColor()
End Sub
I call it as follows:
WriteColor("DEBUGMODE INFO: SQL query statement result = ", result.ToString)
The green color lets me pick out the result of the query within all the other debug messages that I print out.
Obviously, a more general solution, perhaps one that allows variable number of arguments, would be more useful, but even this particular code helps me avoid repetition and makes life easier.
Try this one it works for me
public static void LogInfo(this IDebugLogger logger , string message)
{
var defaultColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
logger.Log(message, "Information");
Console.ForegroundColor = defaultColor;
}
Just ran across this looking for a quick and dirty way to colorize fragments of the console output of an internal tool and I found that the ANSI escape sequences previously mentioned work in the Windows console.
public static int Main(string[] args)
{
string NL = Environment.NewLine; // shortcut
string NORMAL = Console.IsOutputRedirected ? "" : "\x1b[39m";
string RED = Console.IsOutputRedirected ? "" : "\x1b[91m";
string GREEN = Console.IsOutputRedirected ? "" : "\x1b[92m";
string YELLOW = Console.IsOutputRedirected ? "" : "\x1b[93m";
string BLUE = Console.IsOutputRedirected ? "" : "\x1b[94m";
string MAGENTA = Console.IsOutputRedirected ? "" : "\x1b[95m";
string CYAN = Console.IsOutputRedirected ? "" : "\x1b[96m";
string GREY = Console.IsOutputRedirected ? "" : "\x1b[97m";
string BOLD = Console.IsOutputRedirected ? "" : "\x1b[1m";
string NOBOLD = Console.IsOutputRedirected ? "" : "\x1b[22m";
string UNDERLINE = Console.IsOutputRedirected ? "" : "\x1b[4m";
string NOUNDERLINE = Console.IsOutputRedirected ? "" : "\x1b[24m";
string REVERSE = Console.IsOutputRedirected ? "" : "\x1b[7m";
string NOREVERSE = Console.IsOutputRedirected ? "" : "\x1b[27m";
Console.WriteLine($"This is {RED}Red{NORMAL}, {GREEN}Green{NORMAL}, {YELLOW}Yellow{NORMAL}, {BLUE}Blue{NORMAL}, {MAGENTA}Magenta{NORMAL}, {CYAN}Cyan{NORMAL}, {GREY}Grey{NORMAL}! ");
Console.WriteLine($"This is {BOLD}Bold{NOBOLD}, {UNDERLINE}Underline{NOUNDERLINE}, {REVERSE}Reverse{NOREVERSE}! ");
}
The output:
The NOBOLD
code is really "normal intensity". See the "SGR (Select Graphic Rendition) parameters" section on the linked wikipedia page for details.
The redirection test avoids outputting escape sequences into a file, should the output be redirected. If the user has a color scheme other than white-on-black, it won't get reset but you could maybe use the Console functions to save/restore the user's color scheme at the beginning and end of the program if that matters.
精彩评论