如何获取TextRenderer使用的确切文本边距

System.Windows.Forms.TextRenderer.DrawText方法根据flags参数的值呈现带或不带左右填充的格式化文本:

  • TextFormatFlags.NoPadding – 将文本紧密地放入边界框中,
  • TextFormatFlags.GlyphOverhangPadding – 添加一些左右边距,
  • TextFormatFlags.LeftAndRightPadding – 增加更大的利润。

现在,我的问题是如何获得DrawText为给定设备上下文,字符串,字体等文本添加的填充(左和右)的确切数量

我使用.NET Reflector挖掘.NET 4,发现TextRenderer计算“悬垂填充”,它是字体高度的1/6,然后将此值乘以使用这些系数计算左右边距:

  • 对于TextFormatFlags.GlyphOverhangPadding ,左边1.0,右边1.5,
  • 左侧2.0,右侧2.5,用于TextFormatFlags.LeftAndRightPadding

生成的值将向上舍入并传递给DrawTextExADrawTextExW本机API函数。 重新创建此过程很困难,因为字体的高度不是来自System.Drawing.Font而是来自System.Windows.Forms.Internal.WindowsFont ,这些类为同一字体返回不同的值。 并且涉及System.Windows.Forms.Internal命名空间中的许多其他内部BCL类。 反编译所有这些并在我的应用程序中重用它们的代码不是一个选项,因为这将是一个严重的.NET实现依赖。 这就是为什么我需要知道WinForms中是否有一些公共API,或者至少我可以使用哪些Windows函数来获取左右边距的值。


注意:我尝试使用和不使用填充的TextRenderer.MeasureText并比较结果,但这只给出了左边距和右边距的总和,我需要单独使用它们。


注意2:如果你想知道我为什么需要这个:我想绘制一个包含多种字体/颜色的字符串。 这涉及到使用NoPadding选项为每个统一格式化的子字符串调用DrawText一次(这样文本不会传播)但我还想在整个多格式文本的开头和结尾处手动添加正常的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)); 

要获得左边距和右边距,我们使用与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); 

现在你可以轻松检查一下

 (leftMargin + rightMargin) == overallPadding 

请注意:

我需要解决这个问题,以便在使用GDI文本呈现的基于ListView的控件中实现“搜索突出显示”function:

在此处输入图像描述

奇迹般有效 :)

这个答案摘自此处 – http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

如果您曾经希望Windows窗体中的Label或TextBox在Web上运行得更像,那么您可能已经发现没有直观的方法可以使Label或TextBox自动调整其高度以适合它包含的文本。 虽然它可能不直观,但绝对不是不可能的。

在这个例子中,我将使用一个停靠在窗体顶部的TextBox(你可以很容易地使用Label)。要使用它,将一个名为MyTextBox的文本框添加到窗体中,并将​​Dock设置为DockStyle.Top。 将TextBox的Resize事件连接到此事件处理程序。

 private void MyTextBox_Resize( object sender, EventArgs e ) { // Grab a reference to the TextBox TextBox tb = sender as TextBox; // Figure out how much space is used for borders, etc. int chromeHeight = tb.Height - tb.ClientSize.Height; // Create a proposed size that is very tall, but exact in width. Size proposedSize = new Size( tb.ClientSize.Width, int.MaxValue ); // Measure the text using the TextRenderer Size textSize = TextRenderer.MeasureText( tb.Text, tb.Font, proposedSize, TextFormatFlags.TextBoxControl | TextFormatFlags.WordBreak ); // Adjust the height to include the text height, chrome height, // and vertical margin tb.Height = chromeHeight + textSize.Height + tb.Margin.Vertical; } 

如果要调整未停靠的Label或TextBox的大小(例如,在FlowLayoutPanel或其他Panel中,或者只是放在窗体上),那么您甚至可以处理Form的Resize,只需修改直接控制属性。

这似乎(非常)粗糙,但这是我能想到的唯一本机实现: DrawText绘制到IDeviceContext ,由Graphics实现。 现在,我们可以使用以下代码来利用它:

 Bitmap bmp = new Bitmap(....); Graphics graphic = Graphics.FromImage(bmp); textRenderer.DrawText(graphic,....); graphic.Dispose(); 

使用新的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

几年前我做了类似的事情,以突出搜索结果(搜索模式以粗体显示等)。 我的实现是在DevExpress中,因此代码可能不相关。 如果您认为它有用,我可以复制它,只需要找到该实现。

System.Windows.Forms ,要使用的类是Graphics 。 它有一个MeasureCharacterRanges()方法,它接受一个StringFormat (从GenericTypographic开始并从那里开始)。 它比TextRenderer更适合通过链接具有不同样式,字体或画笔的部分来显示完整的字符串。

通过实际的填充测量,你比我更进一步。 DevExpress的控件为您提供了文本边界矩形,以便为我完成。

这是皮埃尔·阿诺德(Pierre Arnaud)在谷歌找到的一篇文章,涉及这个领域。 不幸的是,GDI +“Gory详细信息”链接已经破了。

干杯,
Jonno

修复是计算要添加的MeasureText

 var formatFlags FormatFlags = TextFormatFlags.NoPadding | TextFormatFlags.SingleLine; int largeWidth = TextRenderer.MeasureText( " ", font, new Size(int.MaxValue, int.MaxValue), formatFlags ).Width; int smallWidth = TextRenderer.MeasureText( " ", font, new Size(int.MaxValue, int.MaxValue), formatFlags ).Width; int extra = smallWidth - (largeWidth - smallWidth); 

我们计算一个空间的宽度和两个空格的宽度。 两者都增加了额外的宽度,因此我们可以推断出正在添加的额外宽度。 添加的宽度显然总是相同的,因此从MeasureText返回的每个宽度中减去extra宽度会得到预期的结果。