最后更新日期:2004年10月8日 22:20:24
阅读要求:了解 C# 调用 Win32API 的基本原理和操作方式
在开发 WinForm 控件时,通常需要测量文本绘出时的实际尺寸。 .NET FCL 中的 GDI+ 类——System.Drawing.Graphics 提供了用于上述需要的 MeasureString 方法,该方法返回了一个 SizeF 结构的浮点数表示的结果,从表面上看起来似乎很精确,但在实际使用中发现有时此方法并不能精确测量出文本的实际宽度。 也曾反编译 System.Drawing.Dll,但没看出什么名堂来。 如果使用 GDI 却能很好地测量出文本的实际绘出宽度,下面提供了调用 GDI Win32API 来测量的 C# 实现源代码,以供参考。
注: 代码中的 WindowsAPI 是一个自定义类,包装了大部分 Win32API 调用的 C# 定义。
/// <summary> /// 在指定的矩形区域内,按照指定的格式,测量文本的实际绘出尺寸。 /// </summary> /// <param name="graphics">绘图对象</param> /// <param name="text">被测量的文本</param> /// <param name="font">测量所用字体</param> /// <param name="rc">以矩形表示的要绘制区域</param> /// <param name="drawFlags">文本格式</param> /// <returns>尺寸</returns> public static Size GetTextSize(Graphics graphics, string text, Font font, Rectangle rc, DrawTextFormatFlags drawFlags) { // 一个记录设备上下文句柄的变量 IntPtr hdc = IntPtr.Zero; if ( graphics != null ) { // 获取与提供的 Graphics 关联的设备上下文句柄 hdc = graphics.GetHdc(); } else { // 如果未提供 Graphics,使用屏幕作为设备上下文 hdc = WindowsAPI.GetDC(IntPtr.Zero); }
// 测量所用字体的句柄 IntPtr fontHandle = font.ToHfont();
// 将测量所用字体添加到设备上下文 // 并记录原来所使用的字体 IntPtr oldHfont = WindowsAPI.SelectObject(hdc, fontHandle);
// RECT 用于 Win32API 调用,.NET FCL 中的 Retangle 不适用于 Win32API 调用。 // 其定义如下: // // [StructLayout(LayoutKind.Sequential)] // 这是必须的。 // public struct RECT // { // public int left; // public int top; // public int right; // public int bottom; // }
// 创建一个 GDI Win32API 调用所需的 RECT 实例 RECT rect = new RECT(); rect.left = rc.Left; rect.right = rc.Right; rect.top = rc.Top; rect.bottom = rc.Bottom;
// 文本绘制格式标志的枚举定义: // public enum DrawTextFormatFlags // { // DT_TOP = 0x00000000, // DT_LEFT = 0x00000000, // DT_CENTER = 0x00000001, // DT_RIGHT = 0x00000002, // DT_VCENTER = 0x00000004, // DT_BOTTOM = 0x00000008, // DT_WORDBREAK = 0x00000010, // DT_SINGLELINE = 0x00000020, // DT_EXPANDTABS = 0x00000040, // DT_TABSTOP = 0x00000080, // DT_NOCLIP = 0x00000100, // DT_EXTERNALLEADING = 0x00000200, // DT_CALCRECT = 0x00000400, // DT_NOPREFIX = 0x00000800, // DT_INTERNAL = 0x00001000, // DT_EDITCONTROL = 0x00002000, // DT_PATH_ELLIPSIS = 0x00004000, // DT_END_ELLIPSIS = 0x00008000, // DT_MODIFYSTRING = 0x00010000, // DT_RTLREADING = 0x00020000, // DT_WORD_ELLIPSIS = 0x00040000 // }
// 调用 GDI Win32API 以测量文本的实际绘出时尺寸。 WindowsAPI.DrawText(hdc, text, text.Length, ref rect, (int)(drawFlags | DrawTextFormatFlags.DT_CALCRECT));
// 重设为原来的字体,这是 GDI 必须的。 WindowsAPI.SelectObject(hdc, oldHfont);
// 删除创建的测量用字体的句柄 WindowsAPI.DeleteObject(fontHandle);
// 释放已获取的设备上下文句柄 if ( graphics != null ) graphics.ReleaseHdc(hdc); else WindowsAPI.ReleaseDC(IntPtr.Zero, hdc); // 返回实测结果 return new Size(rect.right - rect.left, rect.bottom - rect.top); }
以下是三个有用的重载方法:
public static Size GetTextSize(string text, Font font, Rectangle rc, DrawTextFormatFlags drawFlags) { return GetTextSize(null, text, font, rc, drawFlags); }
public static Size GetTextSize(Graphics graphics, string text, Font font) { // 设置一个文本绘制格式,以单行左对齐方式来测量。 DrawTextFormatFlags drawFlags = DrawTextFormatFlags.DT_SINGLELINE | DrawTextFormatFlags.DT_LEFT | DrawTextFormatFlags.DT_CALCRECT; // 这个标志表示要测量
Rectangle rect = Rectangle.Empty;
return GetTextSize(graphics, text, font, rect, drawFlags) ; }
public static Size GetTextSize(string text, Font font) { return GetTextSize(null, text, font); } 
|