views:

4173

answers:

8

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 centered 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 centered 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 centering with that, but I actually do need to do this manually with the Graphics class.

+1  A: 

You can use an instance of the string formatter object passed in to the drawstring method to centre the text.

see http://msdn.microsoft.com/en-us/library/21kdfbzs.aspx and http://msdn.microsoft.com/en-us/library/system.drawing.stringformat.aspx

John
A: 

Here's some code. This assumes you are doing this on a form, or a UserControl.

Graphics g = this.CreateGraphics();
SizeF size = g.MeasureString("string to measure");

int nLeft = Convert.ToInt32((this.ClientRectangle.Width / 2) - (size.Width / 2));
int nTop = Convert.ToInt32((this.ClientRectangle.Height / 2) - (size.Height / 2));

From your post, it sounds like the ClientRectangle part (as in, you're not using it) is what's giving you difficulty.

DannySmurf
A: 

Here's some code. This assumes you are doing this on a form, or a UserControl.

To clarify here, I tried setting the location of the text to Point(0,0), or the upper left corner. And it still renders it with an offset proportional to the font size.

Also, MeasureString() is giving weird results. For the letter "A" with FontSize of 45, it returns Width=8.0, Height=14.0 Does that make any sense?

Adam Haile
A: 

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)
dbkk
A: 

Regarding the TextRenderer idea...looks good, unfortunately it seems that it is not available in the Compact Framework. Sorry, should've specified that.

Adam Haile
+1  A: 

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.

Adam Haile
+3  A: 

I'd like to add another vote for the StringFormat object. You can use this simply to specify "centre, centre" 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.

Quibblesome
+1  A: 

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.

Chris Hughes