Is there a way to get the position of a drawn string in a control? I'm writing a control that acts as a GroupBox and I when I draw the string that acts as the title, the border line strikes through it.
In order to fix this problem, I was just going to fill and rectangle where the title is with a back color so the title doesn't appear to be struck through. My problem is that the position of the title is dictated by the StringFormat I pass through the Graphics.DrawString() method. I don't have to explicitly declare the location of the string, but I do have to explicitly declare the location of the rectangle and I don't know where the location of the string is.
How should I go about this?
I believe you can use MeasureCharacterRanges to accurately measure string position
here is a little sample:
The method for doing measurement:
public static RectangleF MeasureStringBounds(Graphics graphics, string text, Font font,RectangleF bounding, StringFormat format)
{
var ranges =new[] {new CharacterRange(0, text.Length)};
format.SetMeasurableCharacterRanges(ranges);
var regions = graphics.MeasureCharacterRanges(text, font, bounding, format);
var accurateBoundings = regions[0].GetBounds(graphics);
return accurateBoundings;
}
Usage:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var str = "hello";
var format = new StringFormat {Alignment = StringAlignment.Center};
e.Graphics.DrawString(str, Font, new SolidBrush(Color.Black), new Rectangle(0, 0, Width, Height), format);
//measuring part
var region = MeasureStringBounds(e.Graphics, str, Font, new RectangleF(0, 0, Width, Height), format);
//Draw measured region
e.Graphics.DrawRectangle(new Pen(Color.Red), region.X, region.Y, region.Width, region.Height);
}
Related
In a WinForms app, I am trying to measure the size of some text I want to draw with no padding. Here's the closest I've gotten...
protected override void OnPaint(PaintEventArgs e) {
DrawIt(e.Graphics);
}
private void DrawIt(Graphics graphics) {
var text = "123";
var font = new Font("Arial", 32);
var proposedSize = new Size(int.MaxValue, int.MaxValue);
var measuredSize = TextRenderer.MeasureText(graphics, text, font, proposedSize, TextFormatFlags.NoPadding);
var rect = new Rectangle(100, 100, measuredSize.Width, measuredSize.Height);
graphics.DrawRectangle(Pens.Blue, rect);
TextRenderer.DrawText(graphics, text, font, rect, Color.Black, TextFormatFlags.NoPadding);
}
... but as you can see from the results ...
... there is still a considerable amount of padding, particularly on the top and bottom. Is there any way to measure the actual bounds of the drawn characters (with something really awful like printing to an image and then looking for painted pixels)?
Thanks in advance.
(I've marked this answer as "the" answer just so people know it was answered, but #TaW actually provided the solution -- see his link above.)
#TaW - That was the trick. I'm still struggling to get the text to go where I want it to, but I'm over the hump. Here's the code I ended out with...
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
DrawIt(e.Graphics);
}
private void DrawIt(Graphics graphics) {
var text = "123";
var font = new Font("Arial", 40);
// Build a path containing the text in the desired font, and get its bounds.
GraphicsPath path = new GraphicsPath();
path.AddString(text, font.FontFamily, (int)font.Style, font.SizeInPoints, new Point(0, 0), StringFormat.GenericDefault);
var bounds = path.GetBounds();
// Move it where I want it.
var xlate = new Matrix();
xlate.Translate(100, 100);
path.Transform(xlate);
// Draw the path (and a bounding rectangle).
graphics.DrawPath(Pens.Black, path);
bounds = path.GetBounds();
graphics.DrawRectangle(Pens.Blue, bounds.Left, bounds.Top, bounds.Width, bounds.Height);
}
... and here is the result (notice the nice, tight bounding box) ...
Have you tried
Graphics.MeasureString("myString", myFont, int.MaxValue, StringFormat.GenericTypographic)
I have a sample string for which i calculates the size and draws the string in the form through below code,
public partial class Form1 : Form
{
Rectangle textRectangle;
string text = "testing the size 1 testing the size 1 testing the size 1 testing the size 1";
public Form1()
{
InitializeComponent();
Size textSize = this.CreateGraphics().MeasureString(text, this.Font, 100).ToSize();
textRectangle = new Rectangle(0, 0, textSize.Width, textSize.Height);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (SolidBrush brush = new SolidBrush(Color.Blue))
{
e.Graphics.DrawString(text, this.Font, brush, textRectangle);
}
}
}
My issue is the textRectangle calculated from the simple string is not sufficient to draw in form. Please refer to attached image, that some of the given string is not drawn.
Could anyone please update me, why the size calculated from MeasureString() is not enough to draw using the DrawString() method?
The above mentioned issue is resolved when using MeasureTrailingSpaces formatflags . Use the below code,
StringFormat stringFormat = new StringFormat(StringFormatFlags.MeasureTrailingSpaces);
Size textSize = this.CreateGraphics().MeasureString(text, this.Font, 100, stringFormat).ToSize();
textRectangle = new Rectangle(0, 0, textSize.Width, textSize.Height);
While calculating size, the spaces are not considered, so to consider spaces in string, MeasureTrailingSpaces have to be used.
In my application, I generate a Bitmap with a variable string.
Here is my function:
public void Image(String text, String font, int size)
{
Font font = new Font(font, size);
float res = ((font.SizeInPoints * text.Length) / 72) * 96;
using (Bitmap img = new Bitmap((int)res, font.Height))
{
Graphics g = Graphics.FromImage(img);
SolidBrush drawBrush = new SolidBrush(Color.Black);
g.DrawString(text, font, drawBrush, 1, 0);
String directory = AppDomain.CurrentDomain.BaseDirectory + "Content\\Images\\Signature\\";
string outputFileName = directory + "sign.png";
img.Save(outputFileName, ImageFormat.Png);
}
}
I would like the width of the image to match perfectly the width of the string printed in that bitmap.
As you can see, I tried to calculate the width with point size of the font.
The problem is that each letter printed has a different width so I can not get the size before creating the Bitmap.
Plus, I don't even know how to retrieve the actual size of the printed string...
Does anyone have an idea?
Use the Graphics.MeasureString function. It takes a string and a font, and returns the size of the rendered text as a SizeF. There are also additional overloads that can take formatting information, and one that takes a SizeF representing the maximum width for wrapping.
Details can be found here: https://msdn.microsoft.com/en-us/library/6xe5hazb(v=vs.110).aspx
// Set up string.
string measureString = "Measure String";
Font stringFont = new Font("Arial", 16);
// Set maximum layout size.
SizeF layoutSize = new SizeF(100.0F, 200.0F);
// Set string format.
StringFormat newStringFormat = new StringFormat();
newStringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
// Measure string.
SizeF stringSize = new SizeF();
stringSize = e.Graphics.MeasureString(measureString, stringFont, layoutSize, newStringFormat);
// Draw rectangle representing size of string.
e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 0.0F, 0.0F, stringSize.Width, stringSize.Height);
// Draw string to screen.
e.Graphics.DrawString(measureString, stringFont, Brushes.Black, new PointF(0,
I'm trying to achieve framed texts (using Windows Forms), e.g.:
Height is always the same, because my strings are less than 20 chars. What about width? Is there any way to get it automatically?
Use Graphics.MeasureString()
From MSDN: http://msdn.microsoft.com/en-us/library/6xe5hazb.aspx
private void MeasureStringMin(PaintEventArgs e)
{
// Set up string.
string measureString = "Measure String";
Font stringFont = new Font("Arial", 16);
// Measure string.
SizeF stringSize = new SizeF();
stringSize = e.Graphics.MeasureString(measureString, stringFont);
// Draw rectangle representing size of string.
e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 0.0F, 0.0F, stringSize.Width, stringSize.Height);
// Draw string to screen.
e.Graphics.DrawString(measureString, stringFont, Brushes.Black, new PointF(0, 0));
}
If you don't feel like dealing with the Paint eventhandler, you could try the TextRenderer class. It has a static method that is identical to the "MeasureString" method in the above answer. In this class it is called MeasureText however.
I want to display a lengthy rotated string in the background of one of my rows in a DataGridView. However, this:
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
if (e.RowIndex == 0)
{
...
//Draw the string
Graphics g = dataGridView1.CreateGraphics();
g.Clip = new Region(e.RowBounds);
g.RotateTransform(-45);
g.DrawString(printMe, font, brush, e.RowBounds, format);
}
}
does not work because text is clipped before it's rotated.
I've also tried painting on a Bitmap first, but there seems to be a problem painting transparent Bitmaps - the text comes out pure black.
Any ideas?
I figured it out. The problem was that Bitmaps apparently don't have transparency, even when you use PixelFormat.Format32bppArgb. Drawing the string caused it to draw over a black background, which is why it was so dark.
The solution was to copy the row from the screen onto a bitmap, draw onto the bitmap, and copy that back to the screen.
g.CopyFromScreen(absolutePosition, Point.Empty, args.RowBounds.Size);
//Draw the rotated string here
args.Graphics.DrawImageUnscaledAndClipped(buffer, args.RowBounds);
Here is the full code listing for reference:
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs args)
{
if(args.RowIndex == 0)
{
Font font = new Font("Verdana", 11);
Brush brush = new SolidBrush(Color.FromArgb(70, Color.DarkGreen));
StringFormat format = new StringFormat
{
FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip,
Trimming = StringTrimming.None,
};
//Setup the string to be printed
string printMe = String.Join(" ", Enumerable.Repeat("RUNNING", 10).ToArray());
printMe = String.Join(Environment.NewLine, Enumerable.Repeat(printMe, 50).ToArray());
//Draw string onto a bitmap
Bitmap buffer = new Bitmap(args.RowBounds.Width, args.RowBounds.Height);
Graphics g = Graphics.FromImage(buffer);
Point absolutePosition = dataGridView1.PointToScreen(args.RowBounds.Location);
g.CopyFromScreen(absolutePosition, Point.Empty, args.RowBounds.Size);
g.RotateTransform(-45, MatrixOrder.Append);
g.TranslateTransform(-50, 0, MatrixOrder.Append); //So we don't see the corner of the rotated rectangle
g.DrawString(printMe, font, brush, args.RowBounds, format);
//Draw the bitmap onto the table
args.Graphics.DrawImageUnscaledAndClipped(buffer, args.RowBounds);
}
}