System.Windows.Forms.TextRenderer.DrawText方法使用或不使用左右填充来呈现带格式的文本,具体取决于flags参数的值:
TextFormatFlags.NoPadding - 将文本紧密地放入边框中,TextFormatFlags.GlyphOverhangPadding - 添加一些左右边距,TextFormatFlags.LeftAndRightPadding - 增加更大的利润.现在,我的问题是如何获得DrawText给定设备上下文,字符串,字体等文本添加的填充(左和右)的确切数量?
我使用.NET Reflector挖掘.NET 4,发现TextRenderer计算"悬垂填充",它是字体高度的1/6,然后将此值乘以使用这些系数计算左右边距:
TextFormatFlags.GlyphOverhangPadding,TextFormatFlags.LeftAndRightPadding.生成的值将向上舍入并传递给DrawTextExA或DrawTextExW本机API函数.重新创建此过程很困难,因为字体的高度不是来自System.Drawing.Font而是来自,System.Windows.Forms.Internal.WindowsFont并且这些类为同一字体返回不同的值.并且System.Windows.Forms.Internal涉及命名空间中的许多其他内部BCL类.反编译所有这些并在我的应用程序中重用它们的代码不是一个选项,因为那将是一个严重的.NET实现依赖.这就是为什么我需要知道WinForms中是否有一些公共API,或者至少我可以使用哪些Windows函数来获取左右边距的值.
注意:我已尝试TextRenderer.MeasureText使用和不使用填充并比较结果,但这只给了我左右边距的总和,我需要它们分开.
注意2:如果你想知道我为什么需要这个:我想绘制一个包含多种字体/颜色的字符串.这涉及DrawText为每个带有NoPadding选项的统一格式化子字符串调用一次(以便文本不会传播)但我还想GlyphOverhangPadding在整个多格式文本的开头和结尾处手动添加正常.
计算左右边距所需的值是TEXTMETRIC.tmHeight,可以使用Win32 API获得.
但是,我发现tmHeight只是字体的行高(以像素为单位),因此这三种方法将为您提供相同的值(您可以在代码中使用您喜欢的任何一种):
int apiHeight = GetTextMetrics(graphics, font).tmHeight;
int gdiHeight = TextRenderer.MeasureString(...).Height;
int gdipHeight = (int)Math.Ceiling(font.GetHeight(graphics));
Run Code Online (Sandbox Code Playgroud)
要获得左边距和右边距,我们使用与TextRenderer相同的代码:
private const float ItalicPaddingFactor = 0.5f;
...
float overhangPadding = (gdiHeight / 6.0f);
//NOTE: proper margins for TextFormatFlags.LeftAndRightPadding flag
//int leftMargin = (int)Math.Ceiling(overhangPadding);
//int rightMargin = (int)Math.Ceiling(overhangPadding * (2 + ItalicPaddingFactor));
//NOTE: proper margins for TextFormatFlags.GlyphOverhangPadding flag
int leftMargin = (int)Math.Ceiling(overhangPadding);
int rightMargin = (int)Math.Ceiling(overhangPadding * (1 + ItalicPaddingFactor));
Size sizeOverhangPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.GlyphOverhangPadding);
Size sizeNoPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.NoPadding);
int overallPadding = (sizeOverhangPadding.Width - sizeNoPadding.Width);
Run Code Online (Sandbox Code Playgroud)
现在你可以轻松检查
(leftMargin + rightMargin) == overallPadding
Run Code Online (Sandbox Code Playgroud)
请注意:
我需要解决这个问题,以便在使用GDI文本呈现的基于ListView的控件中实现"搜索突出显示"功能:

奇迹般有效 :)
这可能看起来(非常)粗糙,但这是我能想到的唯一本机实现:
DrawText绘制到IDeviceContext,它是由 实现的Graphics。现在,我们可以通过以下代码来利用这一点:
Bitmap bmp = new Bitmap(....);
Graphics graphic = Graphics.FromImage(bmp);
textRenderer.DrawText(graphic,....);
graphic.Dispose();
Run Code Online (Sandbox Code Playgroud)
使用新功能,Bitmap您可以逐像素进行计算并按某种条件对它们进行计数。再说一次,这个方法非常粗糙和浪费,但至少它是原生的......
这未经测试,但基于以下来源:
http://msdn.microsoft.com/en-us/library/4ftkekek.aspx
http://msdn.microsoft.com/en-us/library/system.drawing.idevicecontext.aspx
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx
http://www.pcreview.co.uk/forums/graphics-bitmap-t1399954.html