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
整个多格式文本的结尾。
答案 0 :(得分:5)
计算左右边距所需的值是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-based control中实现“搜索突出显示”功能:
像魅力一样工作:)
答案 1 :(得分:2)
这个答案摘自此处 - 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 。最佳。将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,而且只需直接修改Control的属性。
答案 2 :(得分:1)
这似乎(非常)粗糙,但这是我能想到的唯一本机实现:
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
答案 3 :(得分:1)
几年前我做过类似的事情,以突出显示搜索结果(搜索模式以粗体显示等)。我的实现是在DevExpress中,因此代码可能不相关。如果您认为它有用,我可以复制它,只需找到该实现。
在System.Windows.Forms
中,要使用的班级为Graphics
。它有MeasureCharacterRanges()方法接受StringFormat
(从GenericTypographic开始并从那里开始)。它比TextRenderer更合适,通过链接具有不同样式,字体或画笔的部分来显示完整的字符串。
通过实际的填充测量,你已经比我更进一步了。 DevExpress的控件为您提供了文本边界矩形,以便为我完成。
Here's皮埃尔·阿诺德(Pierre Arnaud)在谷歌找到的一篇文章,涉及这一领域。不幸的是,GDI +“Gory details”链接已经破了。
干杯,
Jonno
答案 4 :(得分:0)
修复方法是计算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);
我们计算一个空间的宽度和两个空格的宽度。两者都添加了额外的宽度,因此我们可以推断出正在添加的额外宽度。添加的宽度显然始终相同,因此从extra
返回的每个宽度中减去MeasureText
会得到预期的结果。