1

我想在面板上绘制以下文本:

示例文本

它是一个多色文本。

我发现这篇关于绘制多色文本的文章。

我用单词替换了字符,但它不起作用。

输出截图

(我使用 FillPath/DrawPath 来绘制文本)

我的代码:

private void Form1_Paint(object sender, PaintEventArgs e)
    {
        const string txt = "C# Helper! Draw some text with each letter in a random color.";

        // Make the font.
        using (Font the_font = new Font("Times New Roman", 40,
            FontStyle.Bold | FontStyle.Italic))
        {
            // Make a StringFormat object to use for text layout.
            using (StringFormat string_format = new StringFormat())
            {
                // Center the text.
                string_format.Alignment = StringAlignment.Center;
                string_format.LineAlignment = StringAlignment.Center;
                string_format.FormatFlags = StringFormatFlags.NoClip;

                // Make CharacterRanges to indicate which
                // ranges we want to measure.

                MatchCollection mc = Regex.Matches(txt, @"[^\s]+");
                CharacterRange[] ranges = new CharacterRange[mc.Count];
                int g = 0;
                foreach (Match m in mc)
                {
                    ranges[g] = new CharacterRange(m.Index, m.Length);
                    g++;
                }
                string_format.SetMeasurableCharacterRanges(ranges);

                // Measure the text to see where each character range goes.
                Region[] regions =
                    e.Graphics.MeasureCharacterRanges(
                        txt, the_font, this.ClientRectangle,
                        string_format);

                // Draw the characters one at a time.
                for (int i = 0; i < ranges.Length; i++)
                {
                    // See where this character would be drawn.
                    RectangleF rectf = regions[i].GetBounds(e.Graphics);
                    Rectangle rect = new Rectangle(
                        (int)rectf.X, (int)rectf.Y,
                        (int)rectf.Width, (int)rectf.Height);

                    // Make a brush with a random color.
                    using (Brush the_brush = new SolidBrush(RandomColor()))
                    {
                        // Draw the character.
                        string txts = txt.Substring(ranges[i].First, ranges[i].Length);
                        e.Graphics.DrawString(txts,
                            the_font, the_brush, rectf, string_format);
                    }
                }
            }
        }
    }

问题是什么?

4

1 回答 1

1

这(在某种程度上)是经典。MeasureCharacterRanges
执行的非常精确的测量与.Graphics.DrawString

RectagleF返回的 by将Region.GetBounds()Text 的度量视为原样。
Graphics.DrawString另一方面,在计算给定边界内的文本配置时,会执行一种网格拟合。

我不会在这里解释它,这是一个相当广泛的问题,但我已经写了一些关于它的东西:
在位图上绘制长字符串会导致绘图问题
如果您有兴趣,可以Graphics在此上下文中找到有关对象行为的一些详细信息。

总之,文本被正确测量,但是Graphics.DrawString执行的调整导致文本不完全适合测量的范围:绘制的文本稍微溢出

您可以使用几个标志来纠正这个问题: 添加StringFormat
[StringFormat].Trimming = StringTrimming.None

应用此设置后,您可以立即看到问题所在:最后一个字符(或几个字符)被换行,导致绘图混乱。

要更正它,添加StringFormatFlags.NoWrapStringFormatFlags.NoClip
This 显然会解决问题。显然是因为现在整个字符串都绘制在一条线上。

我建议您使用另一种方法,使用TextRenderer.DrawText来呈现字符串。
请注意,这TextRenderer实际上是WinForms控件(不是全部)用来将文本呈现到屏幕的类。

这是使用以下方法的结果:

测量字符范围

示例代码,使用您的原始代码并进行一些修改:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    Control control = sender as Control;
    const string txt = "C# Helper! Draw some text with each word in a random color.";

    TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter |
                            TextFormatFlags.NoPadding | TextFormatFlags.NoClipping;

    using (StringFormat format = new StringFormat())
    {
        format.Alignment = StringAlignment.Center;
        format.LineAlignment = StringAlignment.Center;

        MatchCollection mc = Regex.Matches(txt, @"[^\s]+");
        CharacterRange[] ranges = mc.Cast<Match>().Select(m => new CharacterRange(m.Index, m.Length)).ToArray();
        format.SetMeasurableCharacterRanges(ranges);

        using (Font font = new Font("Times New Roman", 40, FontStyle.Regular, GraphicsUnit.Point))
        {
            Region[] regions = e.Graphics.MeasureCharacterRanges(txt, font, control.ClientRectangle, format);

            for (int i = 0; i < ranges.Length; i++)
            {
                Rectangle WordBounds = Rectangle.Round(regions[i].GetBounds(e.Graphics));
                string word = txt.Substring(ranges[i].First, ranges[i].Length);

                TextRenderer.DrawText(e.Graphics, word, font, WordBounds, RandomColor(), flags);
            }
        }
    }
}


private Random rand = new Random();
private Color[] colors =
{
    Color.Red,
    Color.Green,
    Color.Blue,
    Color.Lime,
    Color.Orange,
    Color.Fuchsia,
};

private Color RandomColor()
{
    return colors[rand.Next(0, colors.Length)];
}
于 2018-10-28T05:32:10.150 回答