在 .NET Framework Base Class Library 的 System 命名空间中定义了 ConsoleColor 枚举,该枚举用来指定控制台(System.Console)前景色(Console.ForegroundColor)和背景色(Console.BackgroundColor)。MSDN 文档中有一个示例程序 ConsoleColorSample.cs:
// This example demonstrates the ConsoleColor enumeration. using System; class Sample { public static void Main() { String nl = Environment.NewLine; String[] colorNames = ConsoleColor.GetNames(typeof(ConsoleColor)); // --------------------------------------------------------------------------------------- Console.WriteLine("{0}All the foreground colors on a constant black background.", nl); Console.WriteLine(" (Black on black is not readable.){0}", nl); for (int x = 0; x < colorNames.Length; x++) { Console.Write("{0,2}: ", x); Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), colorNames[x]); Console.Write("This is foreground color {0}.", colorNames[x]); Console.ResetColor(); Console.WriteLine(); } // --------------------------------------------------------------------------------------- Console.WriteLine("{0}A constant white foreground on all the background colors.", nl); Console.WriteLine(" (White on white is not readable.){0}", nl); for (int x = 0; x < colorNames.Length; x++) { Console.Write("{0,2}: ", x); Console.ForegroundColor = ConsoleColor.White; Console.BackgroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), colorNames[x]); Console.Write("This is background color {0}.", colorNames[x]); Console.ResetColor(); Console.WriteLine(); } // --------------------------------------------------------------------------------------- } }
这个程序的运行效果如下所示:
从上图中可以看出,ConsoleColor 枚举表示十六种不同的颜色。
现在让我们写一个程序来搞清楚这十六种颜色的细节吧。下面就是 ConsoleColorTester.cs:
using System; using System.Data; using System.Drawing; using System.Windows.Forms; namespace Skyiv.Tester { sealed class ConsoleColorTester : Form { DataGridView dgv; ConsoleColorTester() { Text = "ConsoleColor - " + Environment.OSVersion; Size = new Size(600, 380); dgv = new DataGridView(); dgv.Dock = DockStyle.Fill; Controls.Add(dgv); } protected override void OnLoad(EventArgs e) { dgv.DataSource = GetConsoleColors(); dgv.RowHeadersVisible = false; dgv.AllowUserToAddRows = false; dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; dgv.ReadOnly = true; foreach (DataGridViewColumn column in dgv.Columns) column.SortMode = DataGridViewColumnSortMode.NotSortable; foreach (DataGridViewRow row in dgv.Rows) row.Cells[10].Style.BackColor = (Color)row.Cells[2].Value; base.OnLoad(e); } DataTable GetConsoleColors() { var dt = GetDataTable(); foreach (var consoleColor in Enum.GetValues(typeof(ConsoleColor))) { var color = Color.FromName(consoleColor.ToString()); var dr = dt.NewRow(); dr[0] = consoleColor; dr[1] = consoleColor; dr[2] = color; dr[3] = color.A; dr[4] = color.R; dr[5] = color.G; dr[6] = color.B; dr[7] = color.GetHue(); dr[8] = color.GetSaturation(); dr[9] = color.GetBrightness(); dt.Rows.Add(dr); } return dt; } DataTable GetDataTable() { var dt = new DataTable(); dt.Columns.Add("值", typeof(int)); dt.Columns.Add("名称", typeof(string)); dt.Columns.Add("Color", typeof(Color)); dt.Columns.Add("Alpha", typeof(byte)); dt.Columns.Add("Red", typeof(byte)); dt.Columns.Add("Green", typeof(byte)); dt.Columns.Add("Blue", typeof(byte)); dt.Columns.Add("色调", typeof(float)); dt.Columns.Add("饱和度", typeof(float)); dt.Columns.Add("亮度", typeof(float)); dt.Columns.Add(" 颜色 ", typeof(string)); return dt; } static void Main() { Application.Run(new ConsoleColorTester()); } } }
在 Window Server 2003 操作系统的 .NET Framework 4 环境中编译和运行:
C:\CS\ConsoleColorTester> csc /t:winexe ConsoleColorTester.cs Microsoft(R) Visual C# 2010 编译器 4.0.30319.1 版 版权所有(C) Microsoft Corporation。保留所有权利。 C:\CS\ConsoleColorTester> ConsoleColorTester
上图显示了这十六种颜色的 ARGB 值和 HSB 值(不能简单地根据枚举元素的名称将 ConsoleColor 枚举和 KnownColor 枚举对应起来,请参见:再谈 ConsoleColor)。咦,ConsoleColor.DarkYellow 有些不正常,它的 ARGB 值和 HSB 值是全零,也就是它是完全透明的,上图中“颜色”那栏显示的“xt = En”其实是透过去看到后面的东东。在前面的 ConsoleColorTester.cs 程序的第 41 行中使用 Color.FromName 方法来得到一个 System.Drawing.Color 结构。Color.FromName 方法基于预定义颜色的指定名称创建 Color 结构,预定义颜色又称为已知颜色,由 System.Drawing.KnownColor 枚举的一个元素表示。 如果参数不是预定义颜色的有效名称,那么 Color.FromName 方法将创建一个 ARGB 值为 0(即所有的 ARGB 分量都为 0)的 Color 结构。由此看来,DarkYellow 不是已知的系统颜色。
我们将前面的 ConsoleColorTester.cs 稍做修改,得到如下的 KnownColorTester.cs:
using System; using System.Data; using System.Drawing; using System.Windows.Forms; namespace Skyiv.Tester { sealed class KnownColorTester : Form { DataGridView dgv; KnownColorTester() { Size = new Size(660, 753); dgv = new DataGridView(); dgv.Dock = DockStyle.Fill; Controls.Add(dgv); } protected override void OnLoad(EventArgs e) { var view = GetKnownColors().DefaultView; view.Sort = "Alpha,Red,Green,Blue"; Text = "KnownColor (" + view.Count + ") - " + Environment.OSVersion; dgv.DataSource = view; dgv.RowHeadersVisible = false; dgv.AllowUserToAddRows = false; dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; dgv.ReadOnly = true; dgv.Columns[2].Visible = false; foreach (DataGridViewColumn column in dgv.Columns) column.SortMode = DataGridViewColumnSortMode.NotSortable; foreach (DataGridViewRow row in dgv.Rows) row.Cells[10].Style.BackColor = (Color)row.Cells[2].Value; base.OnLoad(e); } DataTable GetKnownColors() { var dt = GetDataTable(); foreach (var knownColor in Enum.GetValues(typeof(KnownColor))) { var color = Color.FromName(knownColor.ToString()); var dr = dt.NewRow(); dr[0] = knownColor; dr[1] = knownColor; dr[2] = color; dr[3] = color.A; dr[4] = color.R; dr[5] = color.G; dr[6] = color.B; dr[7] = color.GetHue(); dr[8] = color.GetSaturation(); dr[9] = color.GetBrightness(); dt.Rows.Add(dr); } return dt; } DataTable GetDataTable() { var dt = new DataTable(); dt.Columns.Add("值", typeof(int)); dt.Columns.Add("名称", typeof(string)); dt.Columns.Add("Color", typeof(Color)); dt.Columns.Add("Alpha", typeof(byte)); dt.Columns.Add("Red", typeof(byte)); dt.Columns.Add("Green", typeof(byte)); dt.Columns.Add("Blue", typeof(byte)); dt.Columns.Add("色调", typeof(float)); dt.Columns.Add("饱和度", typeof(float)); dt.Columns.Add("亮度", typeof(float)); dt.Columns.Add(" 颜色 ", typeof(string)); return dt; } static void Main() { Application.Run(new KnownColorTester()); } } }
上述程序的主要改动在于第 23 行,使用 System.Data.DataView 类的 Sort 属性,让颜色值按 ARGB 的大小顺序进行排序,使得相同和相近的颜色排列在一起。第 24 行,使用 System.Data.DataView 类的 Count 属性,在程序的标题栏显示共有多少个不同名称的已知的系统颜色。在第 31 行隐藏了第三栏,因为它和第二栏的值是完全一样的。
在 Windows Server 2003 操作系统的 .NET Framework 4 环境中编译和运行:
C:\CS\ConsoleColorTester> csc /t:winexe KnownColorTester.cs Microsoft(R) Visual C# 2010 编译器 4.0.30319.1 版 版权所有(C) Microsoft Corporation。保留所有权利。 C:\CS\ConsoleColorTester> KnownColorTester
从上面这些图中可以看出,共有 174 个不同名称的已知的系统颜色。当然,这 174 个不同名称中有些名称代表同一种颜色。例如:
这六个名称都表示黑色。
这 174 个不同的名称中,带 Yellow 的有以下五个:
但是没有一个是 DarkYellow,所以就使得我们的 ConsoleColorTester.cs 程序将 ConsoleColor.DarkYellow 不正确地识别为 ARGB 值全为零的颜色了。
这个 ConsoleColor.DarkYellow 看起来和 KnownColor.Olive 有点相似,你们以为呢?
祭出 .NET Reflector 这个神器:
在 mscorlib.dll 中的 System 命名空间中找到 Console 类的 BackgroundColor 和 ForegroundColor 属性:
可以看到,这两个属性的情况很类似,都与 Microsoft.Win32.Win32Native 类中的 Color 枚举有关:
最终,还是无从得知 ConsoleColor.DarkYellow 的 ARGB 值到底是多少。各位读者朋友有没有什么办法呢?希望我在篇文章能够起到抛砖引玉的作用。
前面的 ConsoleColorSample.cs 程序在 Ubuntu 10.10 操作系统的 Mono 2.8.2 环境中编译和运行:
下面是 ConsoleColorTester.cs 在 Ubuntu 10.10 操作系统的 Mono 2.8.2 环境中编译和运行的情况:
从上图可以看出,饱和度一栏和 Windows 操作系统下有所不同,很小的值被直接舍入为零了。还有 DarkYellow 的 Alpha 值虽然也是零,但显示出来的颜色实际上是不透明的。
参考资料