Center text output from Graphics.DrawString()
To draw a centered text:
TextRenderer.DrawText(g, "my text", Font, Bounds, ForeColor, BackColor,
TextFormatFlags.HorizontalCenter |
TextFormatFlags.VerticalCenter |
TextFormatFlags.GlyphOverhangPadding);
Determining optimal font size to fill an area is a bit more difficult. One working soultion I found is trial-and-error: start with a big font, then repeatedly measure the string and shrink the font until it fits.
Font FindBestFitFont(Graphics g, String text, Font font,
Size proposedSize, TextFormatFlags flags)
{
// Compute actual size, shrink if needed
while (true)
{
Size size = TextRenderer.MeasureText(g, text, font, proposedSize, flags);
// It fits, back out
if ( size.Height <= proposedSize.Height &&
size.Width <= proposedSize.Width) { return font; }
// Try a smaller font (90% of old size)
Font oldFont = font;
font = new Font(font.FontFamily, (float)(font.Size * .9));
oldFont.Dispose();
}
}
You'd use this as:
Font bestFitFont = FindBestFitFont(g, text, someBigFont, sizeToFitIn, flags);
// Then do your drawing using the bestFitFont
// Don't forget to dispose the font (if/when needed)
I'd like to add another vote for the StringFormat object. You can use this simply to specify "center, center" and the text will be drawn centrally in the rectangle or points provided:
StringFormat format = new StringFormat();
format.LineAlignment = StringAlignment.Center;
format.Alignment = StringAlignment.Center;
However there is one issue with this in CF. If you use Center for both values then it turns TextWrapping off. No idea why this happens, it appears to be a bug with the CF.
To align a text use the following:
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString("My String", this.Font, Brushes.Black, ClientRectangle, sf);
Please note that the text here is aligned in the given bounds. In this sample this is the ClientRectangle.
Through a combination of the suggestions I got, I came up with this:
private void DrawLetter()
{
Graphics g = this.CreateGraphics();
float width = ((float)this.ClientRectangle.Width);
float height = ((float)this.ClientRectangle.Width);
float emSize = height;
Font font = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular);
font = FindBestFitFont(g, letter.ToString(), font, this.ClientRectangle.Size);
SizeF size = g.MeasureString(letter.ToString(), font);
g.DrawString(letter, font, new SolidBrush(Color.Black), (width-size.Width)/2, 0);
}
private Font FindBestFitFont(Graphics g, String text, Font font, Size proposedSize)
{
// Compute actual size, shrink if needed
while (true)
{
SizeF size = g.MeasureString(text, font);
// It fits, back out
if (size.Height <= proposedSize.Height &&
size.Width <= proposedSize.Width) { return font; }
// Try a smaller font (90% of old size)
Font oldFont = font;
font = new Font(font.Name, (float)(font.Size * .9), font.Style);
oldFont.Dispose();
}
}
So far, this works flawlessly.
The only thing I would change is to move the FindBestFitFont() call to the OnResize() event so that I'm not calling it every time I draw a letter. It only needs to be called when the control size changes. I just included it in the function for completeness.