閱讀要求:瞭解 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); } | |