How to get the exact text margins used by TextRenderer

不羁岁月 提交于 2019-12-03 01:09:43

The value you need for computing left and right margins is TEXTMETRIC.tmHeight, which is possible to obtain using Win32 API.

However, I found that tmHeight is just a line height of a font in pixels, so these three approaches will give you the same value (you can use whichever you like in your code):

int apiHeight = GetTextMetrics(graphics, font).tmHeight;
int gdiHeight = TextRenderer.MeasureString(...).Height;
int gdipHeight = (int)Math.Ceiling(font.GetHeight(graphics));

To obtain left and right margins, we use the same code as TextRenderer does under the hood:

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);

Now you can easily check that

(leftMargin + rightMargin) == overallPadding

Just to note:

I needed to solve this problem in order to implement "Search Highlight" feature in a ListView-based control that uses GDI text rendering:

Works like a charm :)

jhjuisaha

This answer is an excerpt from here - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

If you have ever wanted a Label or TextBox in Windows Forms that performs a little more like on the web, then you've probably figured out that there's no intuitive way to make a Label or TextBox automatically adjust its height to fit the text it contains. While it may not be intuitive, it's definitely not impossible.

In this example, I'll use a TextBox (you could just as easily use a Label) that is docked to the top of a form.To use this, add aTextBox called MyTextBox to the form, and set Dock to DockStyle.Top. Wire up the Resize event of the TextBox to this event handler.

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; 
}

If you want to resize the a Label or TextBox that is not docked (for example, one that is in a FlowLayoutPanel or other Panel, or just placed on the form), then you can handle the Form's Resize even instead, and just modify the Control's properties directly.

This might seem (very) crude, but this is the only native implementation I can think of: DrawText draws to an IDeviceContext, which is implemented by Graphics. Now, we can take advantage of that with the following code:

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

With the new Bitmap you can go pixel by pixel and count them by some condition. Again, this method is very crude and wasteful, but at least it's native....

This is not tested but based on the following sources:

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

I've done something similar a few years ago, to highlight search results (search pattern appears in bold etc.). My implementation was in DevExpress, so the code might not be relevant. If you think it's of use I can copy it, just need to find that implementation.

In System.Windows.Forms, the class to use would be Graphics. It has a MeasureCharacterRanges() method which accepts a StringFormat (start with GenericTypographic and go from there). It is much more appropriate than TextRenderer for displaying a complete string by chaining parts with different styles, fonts or brushes.

You've gone way further than me with the actual padding measuring. DevExpress's controls gave you the text bounding rectangle to start with so that was done for me.

Here's an article by Pierre Arnaud that came up for me in Google, which touches on this area. Unfortunately the GDI+ "Gory details" link there is broken.

Cheers,
Jonno

The fix is to calculate what MeasureText is going to add:

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);

We calculate the width of one space and the width of two spaces. Both have the extra width added, so we can extrapolate the extra width that is being added. The added width apparently is always the same, so subtracting extra from every width returned by MeasureText gives the expected results.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!