C#GDI +换行文本(没有TextRenderer)

时间:2012-11-09 12:50:07

标签: c# gdi textwrapping

说我有字符串“Lorem ipsum dolor sit amet,consectetur adipiscing elit.Cunsellus arcu massa,tempus non tincidunt ut,tempus sit amet odio.Mauris in dui sed enim vulputate dictum。” < / p>

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

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

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

到目前为止,我得到了以下内容:

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

但上面的代码不起作用。有些线路仍然太长。

3 个答案:

答案 0 :(得分:2)

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

如果你得到不好的结果,那几乎总是因为你没有正确指定TextFormatFlags。它必须完全匹配将在DrawText()方法调用中使用的标志。这并不总是很容易找到,特别是当文本是通过在框架内绘制代码绘制的。使用Reference Source查找。

答案 1 :(得分: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();
    }

这应该会帮助你。

答案 2 :(得分:1)

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

“简单”的方法是扫描你的字符串,寻找你乐意插入换行符的位置(例如在空格处 - 但你也可以考虑使用逗号和破折号等标点符号作为主要位置)。

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

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

另请注意,您使用的StringFormat在处理此类文本片段时可能会出现问题,因为它可以设置为在您测量的文本的开头/结尾包含/排除空格,因此可以拧紧向上计算宽度,这样当最后一个字符“擦过”格式化区域的边缘时,它会使您过早地进行自动换行或稍晚进行自动换行。以类似的方式,你必须注意行尾的空格和多个空白字符序列。

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