0

假设我有字符串“Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus arcu massa, tempus non tincidunt ut, tempus sit amet odio. Mauris in dui sed enim vulputate dictum.”

我希望该字符串由特定长度(以像素为单位)包裹并且不破坏单词。以 80 像素为例。

我有用于绘制字符串的 Font 变量和 Graphics 变量(通常称为“g”),因此我可以在需要时测量字符串的长度。

我发现的所有示例仅按字符长度包装文本,但我需要以像素为单位进行 GDI+ 绘图。我不想使用 TextRenderer 控件,因为它似乎有错误。有时它会测量它自己的文本高度错误。这很罕见,但它会发生。

到目前为止,我得到了以下信息:

public static string WrapTextByPixels(string text, ref Graphics g, Font font, float maxWidth)
        {
            string[] originalLines = text.Split(new[] { " " }, StringSplitOptions.None);

            var wrapBuilder = new StringBuilder();

            float currentLineWidth = 0;

            foreach (var item in originalLines)
            {
                float itemWidth = g.MeasureString(item, font).Width;

                currentLineWidth += itemWidth;
                if (currentLineWidth > maxWidth ||
                    itemWidth > maxWidth) // When a single word is longer than the maxWidth then just add it
                {
                    wrapBuilder.Append(Environment.NewLine);
                    currentLineWidth = 0;
                }
                wrapBuilder.Append(item + " ");
            }

            return wrapBuilder.ToString();
        }

但是上面的代码不起作用。有些行还是太长了。

4

3 回答 3

2

您不能在此处绕过 TextRenderer,您必须使用其 MeasureText() 方法,以便计算的布局与稍后在 DrawText() 方法渲染文本时渲染的内容相匹配。其他计算字符串长度的方法,如 Graphics.MeasureString() 只会产生更多错误,Graphics 类的文本布局非常不同。非常糟糕,TextRenderer 被添加到 .NET 2.0 中的原因

如果你得到不好的结果,那几乎总是因为你没有正确指定 TextFormatFlags。它必须与将在 DrawText() 方法调用中使用的标志完全匹配。这并不总是很容易发现,尤其是当文本是通过在框架内绘制代码来绘制时。使用参考源找出答案。

于 2012-11-09T13:46:41.147 回答
1

这是我曾经发现的一种扩展方法,它不是我自己的,我想给予信任,但我不记得我从哪里得到它:

public static string WrapText(this string text, double pixels, string fontFamily, float emSize)
    {
        string[] originalLines = text.Split(new[] { " " }, StringSplitOptions.None);

        var wrapBuilder = new StringBuilder();

        double actualWidth = 0;

        foreach (var item in originalLines)
        {
            var formatted = new FormattedText(
                item,
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface(fontFamily),
                emSize,
                Brushes.Black);

            actualWidth += formatted.Width;
            if (actualWidth > pixels)
            {
                wrapBuilder.Append(Environment.NewLine);
                actualWidth = 0;
            }
            wrapBuilder.Append(item + " ");
        }

        return wrapBuilder.ToString();
    }

这应该可以帮助你。

于 2012-11-09T12:52:30.557 回答
1

我并不完全相信 GDI+ 文本格式化程序已损坏,但在某些情况下它无法满足您的需求,您需要自己进行自动换行。

“简单”的方法是扫描您的字符串以查找您乐于插入换行符的位置(例如在空格处 - 但您可能还希望将逗号和破折号等标点符号视为潜在位置)。

当您逐字构建新字符串时,使用Graphics.MeasureString来确定行的新宽度。当超出所需宽度时,您需要在当前单词之前插入换行符。

请注意,您将遇到无法在可用宽度内中断的文本(一个非常长的单词,其中没有空格,或者非常窄的格式宽度),因此您可能需要一个在这些情况下中断单词的后备机制(要么停在适合的最后一个字符,要么发疯并添加连字符系统)。

另请注意,在StringFormat处理此类文本片段时,您使用的 可能会导致问题,因为它可以设置为在您正在测量的文本的开头/结尾处包含/排除空格,因此这可能会搞砸宽度计算,从而导致当最后一个字符“掠过”格式化区域的边缘时,您将换行有点太早或太晚。以类似的方式,您必须注意行尾的空格和多个空格字符序列。

对于调试,一个好的方法是将格式化宽度基于窗口的宽度,然后为每次重绘重新格式化文本。然后,您可以逐渐将寡妇拖出并向后拖,以检查格式化程序是否以理想的宽度自动换行,并处理狭窄的格式化区域等。

于 2012-11-09T13:35:11.187 回答