如何使用面板上的图形 class 绘制多色文本?
How can I draw multi-colored text using graphics class on panel?
我想在面板上绘制以下文字:
这是一个多色文本。
我发现这个 article 关于绘制彩色文本。
我用文字替换了字符,但没有用。
(我用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);
}
}
}
}
}
有什么问题?
这是(某种程度上)classic。
MeasureCharacterRanges 执行的非常精确的测量与 Graphics.DrawString
执行的实际字符串绘制之间存在差异。
Region.GetBounds()
返回的RectagleF
按原样考虑文本的度量。
另一方面,Graphics.DrawString
在计算给定范围内的文本布置时执行某种网格拟合。
这里就不多说了,范围很广,不过我已经写过一些了:
.
如果您有兴趣,可以在此上下文中找到有关 Graphics
对象行为的一些详细信息。
总而言之,文本被正确测量,但是 Graphics.DrawString
执行的调整导致文本不完全适合测量范围:绘制的文本稍微 溢出.
您可以使用几个 StringFormat
标志来纠正这个 问题 :
添加 [StringFormat].Trimming = StringTrimming.None
应用此设置后,您可以立即看出问题所在:最后一个字符(或几个字符)被换行,弄乱了绘图。
要更正它,请将 StringFormatFlags.NoWrap
添加到 StringFormatFlags.NoClip
显然,这将解决问题。显然是因为现在整个字符串都绘制在一行上。
我建议你另一种方法,使用 TextRenderer.DrawText 来呈现字符串。
请注意 TextRenderer
实际上是 WinForms
控件(嗯,不是所有控件)用来将文本呈现到屏幕的 class。
这是使用以下方法的结果:
示例代码,使用您的原始代码并进行一些修改:
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)];
}
我想在面板上绘制以下文字:
这是一个多色文本。
我发现这个 article 关于绘制彩色文本。
我用文字替换了字符,但没有用。
(我用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);
}
}
}
}
}
有什么问题?
这是(某种程度上)classic。
MeasureCharacterRanges 执行的非常精确的测量与 Graphics.DrawString
执行的实际字符串绘制之间存在差异。
Region.GetBounds()
返回的RectagleF
按原样考虑文本的度量。
另一方面,Graphics.DrawString
在计算给定范围内的文本布置时执行某种网格拟合。
这里就不多说了,范围很广,不过我已经写过一些了:
如果您有兴趣,可以在此上下文中找到有关 Graphics
对象行为的一些详细信息。
总而言之,文本被正确测量,但是 Graphics.DrawString
执行的调整导致文本不完全适合测量范围:绘制的文本稍微 溢出.
您可以使用几个 StringFormat
标志来纠正这个 问题 :
添加 [StringFormat].Trimming = StringTrimming.None
应用此设置后,您可以立即看出问题所在:最后一个字符(或几个字符)被换行,弄乱了绘图。
要更正它,请将 StringFormatFlags.NoWrap
添加到 StringFormatFlags.NoClip
显然,这将解决问题。显然是因为现在整个字符串都绘制在一行上。
我建议你另一种方法,使用 TextRenderer.DrawText 来呈现字符串。
请注意 TextRenderer
实际上是 WinForms
控件(嗯,不是所有控件)用来将文本呈现到屏幕的 class。
这是使用以下方法的结果:
示例代码,使用您的原始代码并进行一些修改:
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)];
}