Как нарисовать разноцветный текст с помощью графического класса на панели? - PullRequest
0 голосов
/ 28 октября 2018

Я хочу нарисовать следующий текст на панели:

sample text

Это разноцветный текст.

Я нашел это статья о рисовании разноцветного текста.

Я заменил символы словами, но это не работает.

output-screenshot

(я использую 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);
                    }
                }
            }
        }
    }

В чем проблема?

1 Ответ

0 голосов
/ 28 октября 2018

Это (в некотором смысле) классика.
Существует несоответствие между довольно точным измерением, выполненным MeasureCharacterRanges , и фактическим отображением строки, выполненным Graphics.DrawString.

RectagleF, возвращаемое Region.GetBounds(), учитывает меру текста как есть.
Graphics.DrawString, с другой стороны, выполняет своего рода сетку при вычислении расположения текста внутри заданногограницы.

Я не буду здесь это объяснять, это довольно обширный вопрос, но я уже написал кое-что об этом:
Рисование длинной строки в результатах растрового изображенияв выпусках чертежей .
Если вам интересно, вы можете найти некоторые подробности о поведении объекта Graphics в этом контексте.

В сумме это, текстизмерено правильно, но корректировки, выполняемые Graphics.DrawString, приводят к тому, что текст не полностью помещается в измеренные границы: нарисованный текст слегка переполняется .

Вы можете исправить это проблема с помощьюпара StringFormat флагов:
Добавить [StringFormat].Trimming = StringTrimming.None

С этим параметром вы можете сразу увидеть, в чем проблема: последний символ (или несколько символов)) переносятся на новую строку, портя чертеж.

Чтобы исправить это, добавьте StringFormatFlags.NoWrap к StringFormatFlags.NoClip
Это, по-видимому, решитпроблема.Очевидно, потому что теперь вся строка рисуется в одной строке.

Я предлагаю вам другой метод, использующий TextRenderer.DrawText для визуализации строк.
Обратите внимание, что TextRenderer на самом деле это класс, используемый WinForms элементами управления (ну, не всеми из них) для визуализации текста на экране.

Это результат использования следующего метода:

MeasureCharacterRanges

Пример кода с использованием исходного кода с некоторыми изменениями:

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)];
}
...