关于C#中创建自定义形状控件的说明

在C#应用程序编程中有许多界面控件供我们使用,这些控件的形状都是规则的矩形,但是为了美观友好的UI体验,有时我们需要创建一些不规则的自定义形状的控件。
同样在C#中,类型Region是描述控件显示区域的类,类型GraphicsPath则表示一系列相互连接的直线和曲线,通过构造出闭合的不规则的GraphicsPath曲线来创建对应形状区域的Region对象,并设置控件的显示区域为此Region对象,就可以使控件按照GraphicsPath对象描述的形状来显示。
下面示例创建一个中间镂空的圆环控件(当然我们也可以添加其他任何形状的边界到GraphicsPath对象中,从而创建形状更加复杂的自定义控件):

一、创建一个原始的空白窗体Form:
csharp-region-control-1

二、在窗体Form的构造函数中添加如下代码:

// 创建用户控件
UserControl userControl = new UserControl();
// 设置用户控件大小
userControl.Width = 200;
userControl.Height = 200;
// 设置用户控件在父控件中居中显示
userControl.Location = new Point((this.ClientSize.Width - userControl.Width) / 2, (this.ClientSize.Height - userControl.Height) / 2);
// 设置用户控件背景色为红色
userControl.BackColor = Color.Red;
// 创建GraphicsPath对象
GraphicsPath graphicsPath = new GraphicsPath();
// 定义外部圆形区域
Rectangle outBounds = new Rectangle(0, 0, 200, 200);
// 定义内部圆形区域
Rectangle inBounds = new Rectangle(50, 50, 100, 100);
// 添加内外圆形区域到GraphicsPath对象中
graphicsPath.AddEllipse(outBounds);
graphicsPath.AddEllipse(inBounds);
// 设置用户控件Region对象
userControl.Region = new Region(graphicsPath);
// 将用户控件添加到窗体中
this.Controls.Add(userControl);

三、运行窗体Form,其中红色的圆环控件就是我们创建的自定义形状的控件:
csharp-region-control-2

更多信息请参阅:GraphicsPath 类 (System.Drawing.Drawing2D)Region 类 (System.Drawing)

关于C#中获取屏幕图像的说明

获取显示器屏幕当前显示的画面在许多场景中都会用到,比例截图。在C#中我们可以通过Graphics对象获取屏幕图像:

// 获取主屏幕的显示范围
Rectangle bounds = Screen.PrimaryScreen.Bounds;
// 创建大小与主屏幕一直的空图像
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
// 通过图像创建Graphics对象
Graphics graphics = Graphics.FromImage(bitmap);
// 将屏幕图片完整绘制到图像上
graphics.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
// 释放Graphics对象
graphics.Dispose();
// 返回屏幕图像
return bitmap;

获取到屏幕图像后,我们就可以根据需求对该图像进行后续处理了。

关于C#中换算像素和毫米的说明

在C#中是以像素作为尺寸单位的,像素是一种相对的尺寸概念,与毫米的转换跟当前显示器的分辨率有关,在不同分辨率下转换的系数也不同。
借助C#中的GDI可以实现像素与毫米的换算:

一、根据Win32 API定义函数获取显示器设备信息:

/// <summary>
/// 获取设备信息
/// </summary>
/// <param name="hdc">要查询设备的句柄</param>
/// <param name="index">设备信息所在的索引值</param>
/// <returns>返回对应索引值上的设备信息</returns>
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, int index);

二、根据显示器设备信息计算像素与毫米的换算比率:

// 定义获取Graphics对象所需的控件
Panel panel = new Panel();
// 根据控件所在句柄获取Graphics对象
Graphics graphics = Graphics.FromHwnd(panel.Handle);
// 获取设备句柄
IntPtr hdc = graphics.GetHdc();
// 获取屏幕宽度(毫米)HORZSIZE
int width = GetDeviceCaps(hdc, 4);
// 获取屏幕高度(毫米)VERTSIZE
int height = GetDeviceCaps(hdc, 6);
// 获取屏幕宽度(像素)HORZRES
int xPixels = GetDeviceCaps(hdc, 8);
// 获取屏幕高度(像素)VERTRES
int yPixels = GetDeviceCaps(hdc, 10);
// 释放设备句柄
graphics.ReleaseHdc(hdc);
// 释放Graphics对象
graphics.Dispose();
// 计算X轴方向像素与毫米的比率
double xRate = (double)xPixels / (double)width;
// 计算Y轴方向像素与毫米的比率
double yRate = (double)yPixels / (double)height;

三、像素数换算为毫米数:

// 定义要换算的毫米数
double millimeter = 10;
// 计算X轴方向像素数
int xPixel = (int)Math.Round(xRate * millimeter);
// 计算Y轴方向像素数
int yPixel = (int)Math.Round(yRate * millimeter);

四、毫米数换算为像素数:

// 定义要换算的像素数
int pixel = 10;
// 计算X轴方向毫米数
double xMillimeter = pixel / xRate;
// 计算Y轴方向毫米数
double yMillimeter = pixel / yRate;

更多信息请参阅:GetDeviceCaps function (Windows)

关于C#中读写Config文件配置数据的说明

在编写C#程序时,我们可以为每个应用程序创建一个默认的配置文件,一般命名为App.config,其在编译后将会生成一个以应用程序名称命名的并且后缀名为config的文件。我们可以将需要的配置信息记录在该文件的configuration/appSettings节点下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="key1" value="value1"/>
    <add key="key2" value="value2"/>
  </appSettings>
</configuration>

该文件实际是一个以configuration为根节点的XML文件,其除了可以用appSettings节点来配置一些常规的数据外,还可以使用其他很多节点来配置应用程序的相关运行数据和环境,如assemblyBinding(程序集绑定)、connectionStrings(连接字符串)等。对该配置文件的读写除了可以采用常规的读写XML文件方法外,我们还可以通过C#中的静态类ConfigurationManager(在System.Configuration.dll中)来管理配置数据。

一、读取配置数据:
使用ConfigurationManager类的AppSettings属性(或者ConnectionStrings属性)来读取配置文件数据:

// 使用key属性读取
string value1 = ConfigurationManager.AppSettings["key1"];
// 使用索引读取
string value2 = ConfigurationManager.AppSettings[1];

二、写入配置数据:
使用ConfigurationManager类的Configuration对象来写入配置文件数据:

// 获取可修改的配置对象
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// 获取AppSettings的配置集合
KeyValueConfigurationCollection settings = configuration.AppSettings.Settings;
// 移除配置项
settings.Remove("key1");
settings.Remove("key2");
// 写入配置项
settings.Add("key", "value");
// 保存修改过的配置信息
configuration.Save(ConfigurationSaveMode.Modified);
// 刷新配置节点,使程序在下次检索时重新从磁盘中读取。
ConfigurationManager.RefreshSection("appSettings");

更多信息请参阅:ConfigurationManager 类 (System.Configuration)

关于C#中日期和时间字符串格式化的说明

一、标准日期和时间格式字符串:
标准日期和时间格式字符串使用单个格式说明符来定义日期和时间值的文本表示形式。包含一个以上字符(包括空白)的任何日期和时间格式字符串都会被解释为自定义日期和时间格式字符串。可通过两种方式使用标准或自定义格式字符串:定义由格式设置操作生成的字符串;定义可通过分析操作转换为DateTime或DateTimeOffset值的日期和时间值的文本表示形式。
标准日期和时间格式字符串可以与DateTime和DateTimeOffset值一起使用。
以下列出支持的标准日期和时间格式说明符并显示由每个格式说明符产生的示例输出:

“d”
说明:短日期模式。
示例:
2009-06-15T13:45:30 -> 6/15/2009 (en-US)
2009-06-15T13:45:30 -> 15/06/2009 (fr-FR)
2009-06-15T13:45:30 -> 2009/06/15 (ja-JP)

“D”
说明:长日期模式。
示例:
2009-06-15T13:45:30 -> Monday, June 15, 2009 (en-US)
2009-06-15T13:45:30 -> Montag, 15.Juni 2009 (de-DE)

“f”
说明:完整日期/时间模式(短时间)。
示例:
2009-06-15T13:45:30 -> Monday, June 15, 2009 1:45 PM (en-US)
2009-06-15T13:45:30 -> den 15 juni 2009 13:45 (sv-SE)

“F”
说明:完整日期/时间模式(长时间)。
示例:
2009-06-15T13:45:30 -> Monday, June 15, 2009 1:45:30 PM (zh-CN)
2009-06-15T13:45:30 -> den 15 juni 2009 13:45:30 (sv-SE)

“g”
说明:常规日期/时间模式(短时间)。
示例:
2009-06-15T13:45:30 -> 6/15/2009 1:45 PM (en-US)
2009-06-15T13:45:30 -> 15/06/2009 13:45 (es-ES)
2009-06-15T13:45:30 -> 2009/6/15 13:45 (zh-CN)

“G”
说明:常规日期/时间模式(长时间)。
示例:
2009-06-15T13:45:30 -> 6/15/2009 1:45:30 PM (en-US)
2009-06-15T13:45:30 -> 15/06/2009 13:45:30 (es-ES)
2009-06-15T13:45:30 -> 2009/6/15 13:45:30 (zh-CN)

“M”、”m”
说明:月/日模式。
示例:
2009-06-15T13:45:30 -> June 15 (en-US)
2009-06-15T13:45:30 -> 15.juni (da-DK)
2009-06-15T13:45:30 -> 15 Juni (id-ID)

“O”、”o”
说明:往返日期/时间模式。
示例:
DateTime值:
2009-06-15T13:45:30 (DateTimeKind.Local) -> 2009-06-15T13:45:30.0000000-07:00
2009-06-15T13:45:30 (DateTimeKind.Utc) -> 2009-06-15T13:45:30.0000000Z
2009-06-15T13:45:30 (DateTimeKind.Unspecified) -> 2009-06-15T13:45:30.0000000
DateTimeOffset值:
2009-06-15T13:45:30-07:00 -> 2009-06-15T13:45:30.0000000-07:00

“R”、”r”
说明:RFC1123模式。
示例:
2009-06-15T13:45:30 -> Mon, 15 Jun 2009 20:45:30 GMT

“s”
说明:可排序日期/时间模式。
示例:
2009-06-15T13:45:30 (DateTimeKind.Local) -> 2009-06-15T13:45:30
2009-06-15T13:45:30 (DateTimeKind.Utc) -> 2009-06-15T13:45:30

“t”
说明:短时间模式。
示例:
2009-06-15T13:45:30 -> 1:45 PM (en-US)
2009-06-15T13:45:30 -> 13:45 (hr-HR)

“T”
说明:长时间模式。
示例:
2009-06-15T13:45:30 -> 1:45:30 PM (en-US)
2009-06-15T13:45:30 -> 13:45:30 (hr-HR)

“u”
说明:通用可排序日期/时间模式。
示例:
2009-06-15T13:45:30 -> 2009-06-15 20:45:30Z

“U”
说明:通用完整日期/时间模式。
示例:
2009-06-15T13:45:30 -> Monday, June 15, 2009 8:45:30 PM (en-US)
2009-06-15T13:45:30 -> den 15 juni 2009 20:45:30 (sv-SE)

“Y”、”y”
说明:年月模式。
示例:
2009-06-15T13:45:30 -> June, 2009 (en-US)
2009-06-15T13:45:30 -> juni 2009 (da-DK)
2009-06-15T13:45:30 -> Juni 2009 (id-ID)

任何其他单个字符
说明:未知说明符,引发运行时FormatException。

二、自定义日期和时间格式字符串:
日期和时间格式字符串定义由格式设置操作生成的DateTime或DateTimeOffset值的文本表示形式。它还可定义分析操作中需要的日期和时间值的表示形式,以便成功将字符串转换为日期和时间。自定义格式字符串由一个或多个自定义日期和时间格式说明符组成。任何不是标准日期和时间格式字符串的字符串都会解释为自定义日期和时间格式字符串。
自定义日期和时间格式字符串可以与DateTime和DateTimeOffset值一起使用。
以下列出支持的自定义日期和时间格式说明符并显示由每个格式说明符产生的示例输出:

“d”
说明:一个月中的某一天(1到31)。
示例:
2009-06-01T13:45:30 -> 1
2009-06-15T13:45:30 -> 15

“dd”
说明:一个月中的某一天(01到31)。
示例:
2009-06-01T13:45:30 -> 01
2009-06-15T13:45:30 -> 15

“ddd”
说明:一周中某天的缩写名称。
示例:
2009-06-15T13:45:30 -> Mon (en-US)
2009-06-15T13:45:30 -> lun.(fr-FR)

“dddd”
说明:一周中某天的完整名称。
示例:
2009-06-15T13:45:30 -> Monday (en-US)
2009-06-15T13:45:30 -> lundi (fr-FR)

“f”
说明:日期和时间值的十分之几秒。
示例:
2009-06-15T13:45:30.6170000 -> 6
2009-06-15T13:45:30.05 -> 0

“ff”
说明:日期和时间值的百分之几秒。
示例:
2009-06-15T13:45:30.6170000 -> 61
2009-06-15T13:45:30.0500000 -> 00

“fff”
说明:日期和时间值的千分之几秒。
示例:
6/15/2009 13:45:30.617 -> 617
6/15/2009 13:45:30.0005 -> 000

“ffff”
说明:日期和时间值的万分之几秒。
示例:
2009-06-15T13:45:30.6175000 -> 6175
2009-06-15T13:45:30.0000500 -> 0000

“fffff”
说明:日期和时间值的十万分之几秒。
示例:
2009-06-15T13:45:30.6175400 -> 61754
6/15/2009 13:45:30.000005 -> 00000

“ffffff”
说明:日期和时间值的百万分之几秒。
示例:
2009-06-15T13:45:30.6175420 -> 617542
2009-06-15T13:45:30.0000005 -> 000000

“fffffff”
说明:日期和时间值的千万分之几秒。
示例:
2009-06-15T13:45:30.6175425 -> 6175425
2009-06-15T13:45:30.0001150 -> 0001150

“F”
说明:如果非零,则为日期和时间值的十分之几秒。
示例:
2009-06-15T13:45:30.6170000 -> 6
2009-06-15T13:45:30.0500000 ->(无输出)

“FF”
说明:如果非零,则为日期和时间值的百分之几秒。
示例:
2009-06-15T13:45:30.6170000 -> 61
2009-06-15T13:45:30.0050000 ->(无输出)

“FFF”
说明:如果非零,则为日期和时间值的千分之几秒。
示例:
2009-06-15T13:45:30.6170000 -> 617
2009-06-15T13:45:30.0005000 ->(无输出)

“FFFF”
说明:如果非零,则为日期和时间值的万分之几秒。
示例:
2009-06-15T13:45:30.5275000 -> 5275
2009-06-15T13:45:30.0000500 ->(无输出)

“FFFFF”
说明:如果非零,则为日期和时间值的十万分之几秒。
示例:
2009-06-15T13:45:30.6175400 -> 61754
2009-06-15T13:45:30.0000050 ->(无输出)

“FFFFFF”
说明:如果非零,则为日期和时间值的百万分之几秒。
示例:
2009-06-15T13:45:30.6175420 -> 617542
2009-06-15T13:45:30.0000005 ->(无输出)

“FFFFFFF”
说明:如果非零,则为日期和时间值的千万分之几秒。
示例:
2009-06-15T13:45:30.6175425 -> 6175425
2009-06-15T13:45:30.0001150 -> 000115

“g”、”gg”
说明:时期或纪元。
示例:
2009-06-15T13:45:30.6170000 -> A.D.

“h”
说明:采用12小时制的小时(从1到12)。
示例:
2009-06-15T01:45:30 -> 1
2009-06-15T13:45:30 -> 1

“hh”
说明:说明:采用12小时制的小时(从01到12)。
示例:
2009-06-15T01:45:30 -> 01
2009-06-15T13:45:30 -> 01

“H”
说明:采用24小时制的小时(从0到23)。
示例:
2009-06-15T01:45:30 -> 1
2009-06-15T13:45:30 -> 13

“HH”
说明:采用24小时制的小时(从00到23)。
示例:
2009-06-15T01:45:30 -> 01
2009-06-15T13:45:30 -> 13

“K”
说明:时区信息。
示例:
DateTime值:
2009-06-15T13:45:30, Kind Unspecified ->
2009-06-15T13:45:30, Kind Utc -> Z
2009-06-15T13:45:30, Kind Local -> -07:00(取决于本地计算机的设置)
DateTimeOffset值:
2009-06-15T01:45:30-07:00 -> -07:00
2009-06-15T08:45:30+00:00 -> +00:00

“m”
说明:分钟(0到59)。
示例:
2009-06-15T01:09:30 -> 9
2009-06-15T13:29:30 -> 29

“mm”
说明:分钟(00到59)。
示例:
2009-06-15T01:09:30 -> 09
2009-06-15T01:45:30 -> 45

“M”
说明:月份(1到12)。
示例:
2009-06-15T13:45:30 -> 6

“MM”
说明:月份(1到12)。
示例:
2009-06-15T13:45:30 -> 06

“MMM”
说明:月份的缩写名称。
示例:
2009-06-15T13:45:30 -> Jun (en-US)
2009-06-15T13:45:30 -> juin (fr-FR)
2009-06-15T13:45:30 -> Jun (zu-ZA)

“MMMM”
说明:月份的完整名称。
示例:
2009-06-15T13:45:30 -> June (en-US)
2009-06-15T13:45:30 -> juni (da-DK)
2009-06-15T13:45:30 -> uJuni (zu-ZA)

“s”
说明:秒(0到59)。
示例:
2009-06-15T13:45:09 -> 9

“ss”
说明:秒(00到59)。
示例:
2009-06-15T13:45:09 -> 09

“t”
说明:AM/PM指示符的第一个字符。
示例:
2009-06-15T13:45:30 -> P (en-US)
2009-06-15T13:45:30 -> (fr-FR)

“tt”
说明:AM/PM指示符。
示例:
2009-06-15T13:45:30 -> PM (en-US)
2009-06-15T13:45:30 -> (fr-FR)

“y”
说明:年份(0到99)。
示例:
0001-01-01T00:00:00 -> 1
0900-01-01T00:00:00 -> 0
1900-01-01T00:00:00 -> 0
2009-06-15T13:45:30 -> 9
2019-06-15T13:45:30 -> 19

“yy”
说明:年份(00到99)。
示例:
0001-01-01T00:00:00 -> 01
0900-01-01T00:00:00 -> 00
1900-01-01T00:00:00 -> 00
2019-06-15T13:45:30 -> 19

“yyy”
说明:年份(最少三位数字)。
示例:
0001-01-01T00:00:00 -> 001
0900-01-01T00:00:00 -> 900
1900-01-01T00:00:00 -> 1900
2009-06-15T13:45:30 -> 2009

“yyyy”
说明:由四位数字表示的年份。
示例:
0001-01-01T00:00:00 -> 0001
0900-01-01T00:00:00 -> 0900
1900-01-01T00:00:00 -> 1900
2009-06-15T13:45:30 -> 2009

“yyyyy”
说明:由五位数字表示的年份。
示例:
0001-01-01T00:00:00 -> 00001
2009-06-15T13:45:30 -> 02009

“z”
说明:相对于UTC的小时偏移量,无前导零。
示例:
2009-06-15T13:45:30-07:00 -> -7

“zz”
说明:相对于UTC的小时偏移量,带有表示一位数值的前导零。
示例:
2009-06-15T13:45:30-07:00 -> -07

“zzz”
说明:相对于UTC的小时和分钟偏移量。
示例:
2009-06-15T13:45:30-07:00 -> -07:00

“:”
说明:时间分隔符。
示例:
2009-06-15T13:45:30 -> : (en-US)
2009-06-15T13:45:30 -> .(it-IT)
2009-06-15T13:45:30 -> : (ja-JP)

“/”
说明:日期分隔符。
示例:
2009-06-15T13:45:30 -> / (en-US)
2009-06-15T13:45:30 -> – (ar-DZ)
2009-06-15T13:45:30 -> .(tr-TR)

‘字符’或”字符串”
说明:文本字符串分隔符。
2009-06-15T13:45:30 (“arr:” h:m t) -> arr: 1:45 P
2009-06-15T13:45:30 (‘arr:’ h:m t) -> arr: 1:45 P

“%”
说明:将下面的字符定义为自定义格式说明符。
示例:
2009-06-15T13:45:30 (%h) -> 1

“\”
说明:转义字符。
示例:
2009-06-15T13:45:30 (h \h) -> 1 h

任何其他字符
说明:字符将复制到未更改的结果字符串。
示例:
2009-06-15T01:45:30 (arr hh:mm t) -> arr 01:45 A

更多信息请参阅:标准日期和时间格式字符串自定义日期和时间格式字符串