【问题标题】:Why doesn't TextRenderer.MeasureText work properly?为什么 TextRenderer.MeasureText 不能正常工作?
【发布时间】:2012-01-11 18:58:58
【问题描述】:

在给定一定宽度的可用画布的情况下,我想测量文本的高度。我传入的文本很长,我知道会换行。为此,我称之为:

using System.Windows.Forms;
...
string text = "Really really long text that is sure to wrap...";
Font font = new Font("Arial", 14);
Size canvas = new Size(1100, 850);
Size size = TextRenderer.MeasureText(text, font, canvas);

无论我为画布传递什么,它总是为 size.Height 返回 14。

我错过了一些简单的东西吗?

【问题讨论】:

  • 也许this answer 提供不同的结果?
  • 嗯,我怀疑你会得到 14 的高度。除了忘记指定文本应该如何换行之外,你还有更严重的问题。
  • 在文本末尾添加一个新行。

标签: c# .net winforms .net-4.0 textrenderer


【解决方案1】:

请使用TextFormatFlags 测量参数,如下所示:

Size size = TextRenderer.MeasureText(text, font, canvas, TextFormatFlags.WordBreak);

【讨论】:

  • 这让我达到了 99%,但结果不是很准确,字符串的最后一行最终被稍微切断(比如底部的 33%)。
  • 不幸的是,我无法重现我身边的任何线切割。请张贴真实的字符串和真实的大小,这会给您带来问题。或者也请尝试将 TextFormatFlags.WordBreak 与 TextFormatFlags.ExternalLeading 结合起来。
  • 我试过了,但没有运气。还是断了。我不能说这是 API 的错。我正在测量文本并将其交给第 3 方控件 - 可能是它使用了不同的渲染技术。
【解决方案2】:

DimitryG 的解决方案似乎很有效,但只有在没有大到足以填满一整行的单词时才有效。如果存在这样的词,则宽度将大于建议的宽度。在这种情况下有标志TextFormatFlags.EndEllipsis,但是我没有设法以某种方式组合标志,所以输出是正确的(如果我使用TextFormatFlags.WordEllipsis | TextFormatFlags.WordBreak,宽度是正确的,但是当 Word Ellipsis 时高度没有更新发生,意思是大字会被修剪,但高度和不修剪一样)。我也尝试了标志TextFormatFlags.EndEllipsis,但没有结果。

所以在有人说清楚之前,我建议使用TextBox 进行自动换行,然后将TextBox 中的行数乘以Font 的高度。

代码:

int MeasureMultilineTextHeigh(string text, Font font, int proposedWidth)
{
    // Exception handling.

    TextBox textBox = new TextBox()
    {
        Multiline = true,
        BorderStyle = BorderStyle.None,
        Width = proposedWidth,
        Font = font,
        Text = text,
    };

    int lineCount = textBox.GetLineFromCharIndex(int.MaxValue) + 1;
    int fontHeight = TextRenderer.MeasureText("X", font).Height;

    return lineCount * fontHeight;
}

但是这种方法有一个问题:当且仅当TextBox 的Multiline 属性设置为true,每个Font 将有自己的左右填充。有关详细信息,请参阅 this stackoverflow.com question 和 this social.msdn.microsoft.com question。所以这意味着在某些情况下,返回的值可能比预期的要大。要解决这个问题,你可以使用SetPadding函数去除填充(你可以在第一个问题中找到方法作为答案),代码:

private const int EM_SETRECT = 0xB3;

[DllImport(@"User32.dll", EntryPoint = @"SendMessage", CharSet = CharSet.Auto)]
private static extern int SendMessageRefRect(IntPtr hWnd, uint msg, int wParam, ref RECT rect);

[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
    public readonly int Left;
    public readonly int Top;
    public readonly int Right;
    public readonly int Bottom;

    private RECT(int left, int top, int right, int bottom)
    {
        Left = left;
        Top = top;
        Right = right;
        Bottom = bottom;
    }

    public RECT(Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }
}

public void SetPadding(TextBox textBox, Padding padding)
{
    var rect = new Rectangle(padding.Left, padding.Top, textBox.ClientSize.Width - padding.Left - padding.Right, textBox.ClientSize.Height - padding.Top - padding.Bottom);
    RECT rc = new RECT(rect);
    SendMessageRefRect(textBox.Handle, EM_SETRECT, 0, ref rc);
}

int MeasureMultilineTextHeigh(string text, Font font, int proposedWidth)
{
    // Exception handling.

    TextBox textBox = new TextBox()
    {
        Multiline = true,
        BorderStyle = BorderStyle.None,
        Width = proposedWidth,
        Font = font,
    };
    SetPadding(textBox, Padding.Empty);
    textBox.Text = text;

    int lineCount = textBox.GetLineFromCharIndex(int.MaxValue) + 1;
    int fontHeight = TextRenderer.MeasureText("X", font).Height;

    return lineCount * fontHeight;
}

需要使用语句:

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;

我希望这会有所帮助。对不起我的英语,如果我犯了任何错误。

【讨论】:

  • “DimitryG 的解决方案似乎效果很好,但只有在没有足够大的单词可以填满一整行的情况下。” - 尝试使用TextFormatFlags.TextBoxControl | TextFormatFlags.WordBreak 将其格式化为多行文本框。长的第一个单词将换行到下一行。
猜你喜欢
  • 2016-07-16
  • 2019-01-04
  • 2020-09-03
  • 2016-10-10
  • 2016-10-24
  • 2017-02-27
  • 2017-07-08
  • 2014-11-23
  • 2021-03-07
相关资源
最近更新 更多