Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Console Magic, Part 1: Messages in Living Color

4.67/5 (7 votes)
10 May 2015CPOL8 min read 18.3K   214  
This article describes a class that adds color to console program displays.

Introduction

This article is the first of a series, which I shall call Console Magic, that covers a number of techniques that I have developed over the last ten years or so to simplify and expedite writing utility programs in C# and VB.NET.

Having spent quite a bit of time in front of a NetWare console and written my share of QuickBASIC utility programs, I knew that it was possible to output text in colors other than the default text and background colors set for the console window in which it runs. While doing so isn't all that hard, I decided to discover what it would take to make the task transparent. Figure 1 (below) shows the MessageInColor class in action.

Figure 1 is the 1st of 4 screens that use four similar sets of methods to write in color. This first screen demonstrates the color version of Console.WriteLine, which I called MessageInColor.WriteLine, which is static, but requires two extra arguments.

Figure 1 is the 1st of 4 screens that use four similar sets of methods to write in color in a console window. This first screen demonstrates the color version of Console.WriteLine, which I called MessageInColor.RGBWriteLine, which is static, but requires two extra arguments to specify the foreground and background colors. For the eagle eyes among you, I took this screen capture before I settled on the final naming scheme for the static and instance methods.

The MessageInColor class is intended for use as a more or less drop-in replacement for the System.Console.Write and System.Console.WriteLine methods, with convenience features to simplify displaying messages in something other than the default screen colors. It is intended for adding “spot color,” such as when you need to call attention to something important, such as an error message. Everything this class does can be achieved using built-in properties and methods of the System.Console class, but it’s a tad more work.

Along the same lines, I wanted my programs to be able to continuously display progress messages without scrolling the text above them, such as the way most command line archiving programs report progress when asked to compress a big file. It was no surprise to me that this was a lot more work. Nevertheless, the result was a small class that is easy to apply to new projects or retrofit to existing ones. The next article in this series explains the FixedConsoleWriter class.

Future articles will discuss other helper classes for use with console programs that add timed pauses, with and without options to interrupt them, a replacement for the lame "Press any key to continue." message, exception logging, and other features, all exposed by the same handful of class libraries.

Background

The classes presented here take advantage of four long established features of every console of which I know that supports color displays. These features were well established when IBM PC-DOS hit the market in 1981, and were carried forward into Windows from the very first version, and the last two are inherited from the typewriter.

  1. Every character has a color attribute. Strictly speaking each character has two, a text (foreground) and background color. Although modern displays support 24 bit color, this article confines itself to the original 16 colors defined by the ConsoleColor enumeration of the System.Console class.
  2. The console is a rectangular array of (typically) 80 columns by 25 rows or 132 columns by 43 lines. Like all good arrays, the coordinate system starts at zero. In the interest of clearly documenting the code, I defined LEFT_EDGE as an Integer constant with a value of zero. The actual width of the console is a user configurable value, which is tracked by the Console.WindowWidth property.
  3. An unadorned Carriage Return character behaves exactly like the carriage return of a typewriter. If you paid close attention to the behavior of a typewriter, carriage return and line feed were two separate actions, which you used to your advantage when you wanted to make some or all of a line bold. The same behavior carried over to line printers, and the same tricks were used to make them print bold text.
  4. The cursor advances to the next position afer each letter is written. So, when you write "hello" on a console, the cursor stops at position 6. Therefore, when you write the 80th character on a line, the text wraps, and you get an implicit line feed, so that the 81st character goes into column 1 of the next line.

These behaviors are not unique to the Console.Write and Console.WriteLine methods; the corresponding routines in the C runtime library and the Windows Platform SDK exhibit the same behavior.

Using the code

The accompanying package contains two demonstration programs that, between them, demonstrate every method and class discussed in these articles,.

  • Console_Color_Writer\TestStand\bin\Release\TestStand.exe demonstrates the 18 overloads of static methods MessageInColor.RGBWriteLine() and MessageInColor.RGBWrite(), instance methods MessageInColor.WriteLine() and MessageInColor.Write(), the ErrorMessageColors property of the AppExceptionLogger class, and the FixedConsoleWriter class.
  • DLLServices2\DLLServices2TestStand\bin\Release\DLLServices2TestStand.exe is a basic demonstration of the MessageInColor class and a thorough treatment of the ErrorMessagesInColor class, which implements default colors for fatal and nonfatal error messages that can be overridden on a per-application basis.

The MessageInColor class exposes two functionally identical sets of methods, both of which closely follow the static Write and WriteLine methods of the System.Console class.

  • MessageInColor instances have MessageForegroundColor and MessageBackgroundColor properties, which store the foreground (text) and background colors that you designate when you call the constructor. You can implemnet a color scheme by defining a set of appropriately named MessageInColor instances.
  • I named the instance methods Write and WriteLine, to make clear that their signatures are analogous to those of the like named static methods of System.Console.
  • The corresponding static methods are RGBWriteLine and RGBWrite, to remind you that you must speicify foreground and background colors on each call.

Why Two Identical Classes?

Superficially, MessageInColor and ErrorMessagesInColor are identical. However, on closer examination, you will discover that the latter uses the Console.Error property to write to the Standard Errror (STDERR) stream, while MessageInColor writes to Standard Output (STDOUT) through the Console.Out property. Writing error messages to the STDERR stream makes them visible even when STDOUT is redirected to a file.

A significant consequence of this behavior is that error messages are not captured in normal redirected output.. Ususally, this is desireable, since the redirected output is intended to capture a report, while you want the error messages to be visible. Take heart, however, because the AppExceptionLogger class can be configured to record errors in the Windows event log of your choosing, which was its original motivation, and additional, but sparsely documented, featurex exist for capturing the Standard Errror stream separately.

What's This About Default Colors?

I believe very strongly that, whenever practical, programs should store values tha might change, ever, as data, to be read at run time. To that end, I established default colors for fatal and recoverable (nonfatal) error messages that are read from a configuration file. Since my intention was for these defaults to be more or less global, I wanted them to accompany the DLL, and be automatically available to any appluication that sets a reference to it.

Figure 2 is a full color rendition of the output generated by the ErrorMessagesInColor objeect.

Figure 2 shows the ErrorMessagesInColor class in action, using the default colors specified in WizardWrx.DLLServices2.dll.config,.

The library that hosts the color message writers, WizardWrx.DLLServices2.dll. has its own configuratin file, WizardWrx.DLLServices2.dll.config, which it knows how to find, so long as a copy travels with it. To that end, its CopyToOutputDirectory property is set to Always in the project configuration file.

The working part of the configuration file is very straightworward.

XML
<configuration>
  <appSettings>
    <add key="FatalExceptionTextColor"             value="White"/>
    <add key="FatalExceptionBackgroundColor"       value="DarkRed"/>

    <add key="RecoverablelExceptionTextColor"      value="Black"/>
    <add key="RecoverableExceptionBackgroundColor" value="Yellow"/>
  </appSettings>
</configuration>

To keep things simple, the colors are stored as string literals that correspond to the string representation of the ConsoleColors enumeration, and the key names correspond to the property names.

The colors reflect choices that I made after studying many combinations and solicitation of input from computer users regarding legibility. However, they aren't sacred; please feel free to aubstitute your own preferences. You override the default colurs by setting the ErrorMessageColors property of the AppExceptionLogger object, as shown below.

C#
s_theApp.BaseStateManager.AppExceptionLogger.ErrorMessageColors = new ErrorMessagesInColor (
    ConsoleColor.DarkRed ,
    ConsoleColor.White );

Figure 3 shows what happens when the settings shown above are in force when an exception is reported.

 

Figure 3 shows the ErrrorMessagesInColor class, using application defined colors.

Figure 3 shows the ErrrorMessagesInColor class, using application defined colors.

Points of Interest

The default console colors must be preserved and restored at all times. To that end, the MessageInColor constructor saves the current console colors into a pair of private ConsoleColor variables, _clrOrigForeColor and _clrOrigForeColor. These are reference values, and are not otherwise used directly, apart from read access through a pair of public properties. They exist to give you a method of restoring the original console colors before your program exits.

The instance writers use another pair, _clrSaveForeColor and _clrSaveBackColor, to save and restore the current colors before and after each operation. Hence, an intervening call to Console.WriteLine or Console.Write displays text in the current console colors. This feature permits you to freely mix and match the methods and properties of this class and those of System.Console.

Before it writes anything, every instance method calls SetMessageColors to save the current console colors, then set the colors defined for the instance.

C#
private void SetMessageColors ( )
{
    _clrSaveBackColor = Console.BackgroundColor;
    _clrSaveForeColor = Console.ForegroundColor;

    Console.BackgroundColor = _clrTextBackColor;
    Console.ForegroundColor = _clrTextForeColor;
}   // private void SetMessageColors

A similar, but simpler, companion method, RestoreMessageColors, restores the original colors after the writer has finished writing.

C#
private void RestoreMessageColors ( )
{
    Console.BackgroundColor = _clrSaveBackColor;
    Console.ForegroundColor = _clrSaveForeColor;
}   // private void RestoreMessageColors

The WriteLine methods have one last task to perform, which is to call Console.WriteLine with an empty argument list to finish the line, as shown in the WriteLine overload that writes a single string.

C#
public void WriteLine (
    string value )
{
    SetMessageColors ( );
    Console.Write ( value );
    RestoreMessageColors ( );
    Console.WriteLine ( );
}   // public void WriteLine (10 of 18)

The corrsponding Console.Write method gets the colored text onto the console, and the cursor is advanced by calling Console.WriteLine immediately after RestoreMessageColors. These two must happen in this order, or a call to either Console.Write or Console.WriteLine would render at least the next line of text in the wrong colors.

To support appending text with a subsequent method call, the Write methods don't call Console.WriteLine. However, since the default colors are restored, if the next routine that writes to the console is Console.Write or Console.WriteLine, the text is rendered in the expected console colors.

The static methods employ similar techniques to save and restore console colors.

History

Sunday, 10 May 2015 is the release date of this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)