首页 > 编程 > .NET > 正文

NET 中,使用 GDI 来精确测量文本的实际绘出尺寸(C#)

2024-07-21 02:18:59
字体:
来源:转载
供稿:网友
阅读要求:了解 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);
}


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表