从Graphics.DrawString()中居中文本输出

73

我正在使用.NETCF (Windows Mobile) Graphics类和DrawString()方法将单个字符呈现到屏幕上。

问题是,无论我为字符串渲染的位置的Y坐标设置什么,它似乎总是比那个位置低,而且文本大小越大,Y偏移就越大。

例如,在文本大小为12时,偏移量大约为4,但在32时,偏移量约为10。

我希望字符垂直地占据绘制它的矩形的大部分,并在水平方向上居中。以下是我的基本代码。this引用了它所绘制的用户控件。

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);

是的,我知道我可以使用标签控件并使用它来设置居中,但我实际上需要使用Graphics类手动完成此操作。

6个回答

95

我也赞成使用StringFormat对象。你可以简单地使用它来指定"center, center",文本就会在提供的矩形或点中心绘制:

StringFormat format = new StringFormat();
format.LineAlignment = StringAlignment.Center;
format.Alignment = StringAlignment.Center;

然而,在CF中有一个问题。如果对两个值都使用Center,则会关闭TextWrapping。不知道为什么会这样,看起来这是CF的一个错误。


72

要调整文本的对齐方式,请使用以下代码:

StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString("My String", this.Font, Brushes.Black, ClientRectangle, sf);
请注意,此处的文本已在给定范围内对齐。在此示例中,该范围为ClientRectangle。

1
Chris的解决方案很棒。顺便说一句,如果你想在水平中心设置位置,但垂直在任何位置:e.Graphics.DrawString("My String", this.Font, Brushes.Black, e.CellBounds.Width/2, y, sf); // y是垂直位置 - camino
我使用了 graphics.VisibleClipBounds.Width / 2,因为我找不到 .CellBounds 属性,而且它运行得相当不错。 - Olorunfemi Davis

24

通过我收到的建议的结合,我得出了这个:

    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();
        }
    }

到目前为止,这个工作非常完美。

唯一需要改变的是将FindBestFitFont()调用移动到OnResize()事件中,这样我就不需要每次绘制字母时都调用它。它只在控件大小改变时需要调用。我只是为了完整性而将其包含在函数中。


不要忘记处理需要处理的内容。 - nrofis

13

绘制居中文本:

TextRenderer.DrawText(g, "my text", Font, Bounds, ForeColor, BackColor, 
  TextFormatFlags.HorizontalCenter | 
  TextFormatFlags.VerticalCenter |
  TextFormatFlags.GlyphOverhangPadding);

确定填充区域的最佳字体大小有些困难。我找到一个工作方案是试错法:从大字体开始,然后重复测量字符串,缩小字体直到适合为止。
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();
  }
}

您可以将其用作:

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)

1
谢谢您展示了另一种绘制图形的方法,这对我解决居中对齐问题非常有帮助。结果发现TextRenderer.DrawText比简单的e.graphics.drawstring更精确。非常感谢,我投了赞成票 :) - user3061078

5

这里是一些代码。假设您正在对表单或用户控件进行操作。

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));

从你的帖子来看,似乎是ClientRectangle部分(也就是你没有使用它)让你感到困难。


3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接