C# Winforms. Drawing text accurately centrally within a rectangle - c#

What I'm attempting to do is draw a number inside a circle so that it's positioned centrally both vertically and horizontally. I'm drawing both the circle and text string using the same rectangle structure and using center for both horizontal and vertical alignments. But, as you can see from the image where I drew a horizontal line across the white circle, the text is aligning its base line centrally and my eye says it's horizontally aligning slightly to the left.
I've tried fudging by adding string.height/2 to the top of the rectangle to shift it down 1/2 its height but it's still not correct. (I adjust the font size so the number will fit within the box according to the number of digits in the counter)
How can I do this properly please?
var rect = new Rectangle(bmpWidth - maxCircleDiameter, bmpHeight - maxCircleDiameter, maxCircleDiameter, maxCircleDiameter);
g.FillEllipse(Brushes.White, rect);
using (var format = NewClassFactory.GetFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.Character))
g.DrawString(toDisplay, newFont, Brushes.Black, rect, format);

You can use TextRenderer.DrawText to draw a text in a rectangle bound, using a color and font and specifying different text format flag:
var rect = new Rectangle(10, 10, 32, 32);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.White, rect);
TextRenderer.DrawText(e.Graphics, "100", this.Font, rect, Color.Black,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);

Related

Using graphics to draw rectangle at top left corner of WinForms control cuts off one pixel of top and left of rectangle

I have a WinForms project and I'm trying to draw a rectangle at (0,0), the top, left corner of the form. For some reason it's cutting off one pixel of height and width of the rectangle. Here's the code:
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.Red, 5), new Rectangle(0, 0, 50, 50));
}
Here is the result, blown up for clarity:
I understand I could correct for this by drawing the rectangle at (1,1), but that's not how it should work based on what I've seen from controls that are placed at (0,0) and (1,1). For instance, here's what a panel looks like at (1,1), and it clearly has a one-pixel gap:
So my question is: why does drawing a rectangle at (0,0) not behave like placing a control at (0,0)? Why does the rectangle get cut off by one pixel on the top and left?
The default value for Alignment property of Pen is PenAlignment.Center which means the drawn line will be centered over the line. So what you see is expected.
You may want to set the Alignment to PenAlignment.Inset:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(Color.Red, 5) { Alignment = PenAlignment.Inset },
new Rectangle(0, 0, 50, 50));
}
You may also want to read Pen.Alignment remarks:
Center is the default value for this property and specifies that the
width of the pen is centered on the outline of the curve or polygon. A
value of Inset for this property specifies that the width of the pen
is inside the outline of the curve or polygon. The other three values,
Right, Left, and Outset, will result in a pen that is centered.

Adding antialiasing

I am trying to using antialiasing but I don't why it isn't working:
{
Pen pen = new Pen(Color.Black, 3);
Pen r = new Pen(Color.YellowGreen, 3);
Graphics b = panel2.CreateGraphics();
b.DrawEllipse(pen, 6, 0, 90, 90);
b.SmoothingMode = SmoothingMode.AntiAlias;
b.DrawLine(r, new Point(50, 90), new Point(50, 0));
}
First it should be noted that the Graphics object does not contain any graphics; it is a tool that lets you draw onto a related bitmap, including a control's surface. Therefore changing any of its properties, like the SmoothingMode only influences graphics you draw from then on, not anything you have drawn before..
The circle certainly would have antialised pixels if you would draw it after setting the SmoothingMode from its default None to AntiAlias.
The Line is vertical, so it doesn't need antialiasing except at its ends, where there is some. But if you tilt it or move it to a non-integer position anti-aliasing will show!
Let's modify your code a little and look closely at the result:
Pen pen = new Pen(Color.Black, 3);
Pen r = new Pen(Color.YellowGreen, 3);
Graphics b = panel2.CreateGraphics();
b.DrawEllipse(pen, 6, 6, 90, 90);
b.SmoothingMode = SmoothingMode.AntiAlias;
b.DrawLine(r, new Point(50, 90), new Point(50, 0));
b.DrawLine(r, new Point(60, 90), new Point(70, 0));
b.DrawLine(r, new PointF(40.5f, 90), new PointF(40.5f, 0));
b.DrawEllipse(pen, 6, 6, 30, 30);
The smaller circle has many gray pixels and even the original green line has a lighter top end. The two new lines are fully anti-aliased now, one because it is tilted, the other because it sits 'between' pixels.
Btw: If it is turned on you will also see anti-alising when your Pen.Width is even or when it is a non-integer number. The reason for the latter should be obvious; the former comes from the PenAlignment property. Its default Center tries to center the pen, but not at the pixel boundary but at the center of the coordinate pixels. Therefore only an uneven width will completely fill the pixels and not cause anti-aliasing. For closed shapes you can change this behaviour by changing the Pen.Alignment to Inset:
This property determines how the Pen draws closed curves and
polygons. The PenAlignment enumeration specifies five values;
however, only two values—Center and Inset—will change the appearance
of a drawn line. Center is the default value for this property and
specifies that the width of the pen is centered on the outline of the
curve or polygon. A value of Inset for this property specifies that the
width of the pen is inside the outline of the curve or polygon. The
other three values, Right, Left, and Outset, will result in a pen that
is centered.
A Pen that has its alignment set to Inset will yield unreliable
results, sometimes drawing in the inset position and sometimes in the
centered position.Also, an inset pen cannot be used to draw compound
lines and cannot draw dashed lines with Triangle dash caps.
PS: The question was not about how to draw properly, so let me just note that you never ought to do it using control.CreateGraphics as this will always only result in non-persistent graphics. Instead you need to use the Paint event and its e.Graphics object..

How can i change in Graphics DrawString the text font size?

I'm using this method to draw the text on form1:
private bool DrawText(bool draw, string texttodraw)
{
Graphics g = this.CreateGraphics();
SizeF size = g.MeasureString(texttodraw, SystemFonts.DefaultFont,14);
g.DrawString(texttodraw, Font, Brushes.Red, pictureBox1.Location.X + (pictureBox1.Width / 2) - (size.Width / 2),
pictureBox1.Location.Y - 30);
return draw;
}
I tried to set Width to 14 on the SizeF size lline but it didn't change the dize and the only thing it did is moving a bit the text from it's location .
How can i change the font size of the text, and also to keep the perspective(if this is the right word to use) of the text location ?
This is how it look like when not using the Width 14 at all the text is in the center above the pictureBox1. I want that when i change the text size it will be kept to be in the center like it is now.
The text is in Red and it's in hebrew in this case.
Try using a bigger font:
using (Font bigFont = new Font(SystemFonts.DefaultFont.FontFamily, 14, FontStyle.Regular)) {
SizeF size = g.MeasureString(texttodraw, bigFont, 14);
g.DrawString(texttodraw, bigFont, Brushes.Red, pictureBox1.Location.X + (pictureBox1.Width / 2) - (size.Width / 2),
pictureBox1.Location.Y - 30);
}
Do avoid using CreateGraphics, it's only a temporary drawing that will get erased by overlapping windows or minimizing the form. It will also cause flicker. Use the graphics object from the paint event and invalidate the control to update the painting.
Also, do favor using TextRenderer.DrawText and TextRenderer.MeasureText for your text renderings. DrawString should primarily be used for printing to paper.
I think the best way is to use StringFormat object to center-align the text either horizontally or vertically or both using the 5th overload of Graphics.DrawString() funciton:
You need to provide a Rectangle objet and alignment is done with respect to this object.
StringFormat sf=new StringFormat();
sf.LineAlignment = StringAlignment.Center;//center-align vertically
sf.Alignment = StringAlignment.Center; //center-align horizontally
private void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Font drawFont = new Font("Arial Black", 9);
e.Graphics.DrawString(nic.Text, drawFont, Brushes.Maroon, 174, 12);

Bitmap Text Background and HorizontalAlignment

I am overlaying text to a bitmap as shown below, I need to be able to set a background colour behind the text and also set the HorizontalAlignment (i.e left/right/centre), can anyone advise me how this can be done. Also note the text size can vary.
Thanks.
Bitmap frameBitmap = new Bitmap(streamFrameWidth, streamFrameHeight,
streamFrameWidth * 3,
System.Drawing.Imaging.PixelFormat.Format24bppRgb, pFrame);
using (Graphics g = Graphics.FromImage(frameBitmap))
{
// Create font and brush.
Font drawFont = new Font("Arial", 12, FontStyle.Bold);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Create point for upper-left corner of drawing.
PointF drawPoint = new PointF(10.0F, 40.0F);
//HorizontalAlignment.
// draw the text
g.DrawString(overlayText, drawFont, drawBrush, drawPoint);
}
You can control the alignment of the drawn text by using the StringFormat parameter of the DrawString method.
Example
MSDN
You probably need TextRenderer.MeasureText.
It returns the size of the text to be displayed. Combining the size of the text with the size of the Bitmap, you can work out the appropriate location of the text based upon the required HorizontalAlignment.
Once you know the bounds (size and location) of the text, you can simply paint a color to those bounds to implement a background colour before drawing the text on top.

Drawing a Rectangular Border around a Text Drawn by GraphicsPath

I'm using the GraphicPath.Addstring method to add a string to the graphic path and Graphics.drawpath to draw the string. I want to calculate the width and height of the text drawn so that I can draw a rectangle around the drawn text. I have tried using Graphics.MeasureString and TextRenderer.MeasureText to calculate the width and Graphicpath.addrectangle and Graphics.Drawpath to draw the rectangle, but I'm not able to get the correct dimensions with different fonts.
Please help me solve this.
Add your string to the path then call
GraphicsPath.GetBounds();
to determine the region. This will need to be inflated depending on the pen size used to render the string.
Maybe look into Graphics.MeasureCharacterRanges
hope this example helps you
var gp = new GraphicsPath();
var g = baseControl.CreateGraphics();
var textSize = g.MeasureString(text, basefont);
gp.AddRectangle(new Rectangle(new Point(0, 0), textSize);
gp.AddString("Your String", basefont.FontFamily, basefont.Style, basefont.Size - 0.25f);
Little correction :
gp.AddRectangle(new Rectangle(new Point(0, 0), textSize.ToSize());

Categories

Resources