I want to draw the following text on panel:
It is a multi-colored text.
I found this article about drawing multicolored text.
I replaced characters with words but it doesn't work.
(I use FillPath/DrawPath to draw text)
my code:
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);
}
}
}
}
}
What is the problem?
This is (in a way) a classic.
There is a discrepancy between the quite precise measure performed by MeasureCharacterRanges and the actual string drawing performed by
Graphics.DrawString
.The
RectagleF
returned byRegion.GetBounds()
considers the measure of the Text as it is.Graphics.DrawString
, on the other hand, performs a sort of grid-fitting when calculating a Text disposition inside the given bounds.I won't explain it here, it's quite a broad matter, but I've written something about it already:
Drawing a Long String on to a Bitmap results in Drawing Issues.
If you're interested, you can find some details on the
Graphics
object behaviour in this context.The sum of it is, the Text is measured correctly, but the adjustments that
Graphics.DrawString
performs, cause the Text to not fit completely in the measured bounds: the drawn Text slightly overflows.You could correct this problem using a couple of
StringFormat
flags:Add
[StringFormat].Trimming = StringTrimming.None
With this setting applied, you can immediately see what the problem is: the last char (or few chars) are wrapped to a new line, messing up the drawing.
To correct it, add
StringFormatFlags.NoWrap
toStringFormatFlags.NoClip
This will, apparently, solve the problem. Apparently because now the whole string is drawn on a single line.
I propose you another method, using TextRenderer.DrawText to render the strings.
Note that
TextRenderer
is actually the class used by theWinForms
controls (well, not all of them) to render Text to the screen.This is the result using the method that follows:
Sample code, using your original code with some modifications: