基本情况
在 .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 不是已知的系统颜色。
已知的系统颜色(KnownColor)
我们将前面的 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 个不同名称中有些名称代表同一种颜色。例如:
- ControlText
- InfoText
- MenuText
- WindowFrame
- WindowText
- Black
这六个名称都表示黑色。
这 174 个不同的名称中,带 Yellow 的有以下五个:
- Yellow
- LightYellow
- LightGoldenrodYellow
- YellowGreen
- GreenYellow
但是没有一个是 DarkYellow,所以就使得我们的 ConsoleColorTester.cs 程序将 ConsoleColor.DarkYellow 不正确地识别为 ARGB 值全为零的颜色了。
这个 ConsoleColor.DarkYellow 看起来和 KnownColor.Olive 有点相似,你们以为呢?
使用 .NET Reflector 进一步查找
祭出 .NET Reflector 这个神器:
在 mscorlib.dll 中的 System 命名空间中找到 Console 类的 BackgroundColor 和 ForegroundColor 属性:
可以看到,这两个属性的情况很类似,都与 Microsoft.Win32.Win32Native 类中的 Color 枚举有关:
最终,还是无从得知 ConsoleColor.DarkYellow 的 ARGB 值到底是多少。各位读者朋友有没有什么办法呢?希望我在篇文章能够起到抛砖引玉的作用。
在 Linux 操作系统中的情况
前面的 ConsoleColorSample.cs 程序在 Ubuntu 10.10 操作系统的 Mono 2.8.2 环境中编译和运行:
下面是 ConsoleColorTester.cs 在 Ubuntu 10.10 操作系统的 Mono 2.8.2 环境中编译和运行的情况:
从上图可以看出,饱和度一栏和 Windows 操作系统下有所不同,很小的值被直接舍入为零了。还有 DarkYellow 的 Alpha 值虽然也是零,但显示出来的颜色实际上是不透明的。
参考资料