Search code examples
c#graphicscompact-framework

Center text output from Graphics.DrawString()


I'm using the .NETCF (Windows Mobile) Graphics class and the DrawString() method to render a single character to the screen.

The problem is that I can't seem to get it centred properly. No matter what I set for the Y coordinate of the location of the string render, it always comes out lower than that and the larger the text size the greater the Y offset.

For example, at text size 12, the offset is about 4, but at 32 the offset is about 10.

I want the character to vertically take up most of the rectangle it's being drawn in and be centred horizontally. Here's my basic code. this is referencing the user control it's being drawn in.

Graphics g = this.CreateGraphics();

float padx = ((float)this.Size.Width) * (0.05F);
float pady = ((float)this.Size.Height) * (0.05F);

float width = ((float)this.Size.Width) - 2 * padx;
float height = ((float)this.Size.Height) - 2 * pady;

float emSize = height;

g.DrawString(letter, new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular),
            new SolidBrush(Color.Black), padx, pady);

Yes, I know there is the label control that I could use instead and set the centring with that, but I actually do need to do this manually with the Graphics class.


Solution

  • 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.