Painting text on Button - Difference in look - c#

I have my custom button where I have overridden the OnPaint() and draw the text in it only. On runtime the text looks differently - spacing between chars is lacking. Here is the image of design & runtime of the button :
The paint methods is as:
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
if (base.ContainsFocus)
{
// Draw inner dotted rectangle when button is on focus
Pen pen = new Pen(Color.Gray, 3);
Point p = base.Location;
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
Rectangle rectangle = new Rectangle(4, 4, Size.Width - 8,
Size.Height - 8);
ControlPaint.DrawFocusRectangle(pevent.Graphics, rectangle);
}
// Draw the string to screen
SizeF sf = pevent.Graphics.MeasureString(displayText, this.Font,
this.Width);
Point ThePoint = new Point();
ThePoint.X = (int)((this.Width / 2) - (sf.Width / 2));
ThePoint.Y = (int)((this.Height / 2) - (sf.Height / 2));
pevent.Graphics.DrawString(displayText, Font,
new SolidBrush(Color.FromArgb(255, 255, 254, 255)), ThePoint);
this.Text = "";
}
Any idea where am I going wrong and how to take care of the same?

You need to set the correct smoothing mode like this:
Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
Then, the result should look OK.

Devils Child's answer will affect the quality of lines and circles, etc.
But for text rendering, you can use:
e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;

Related

How can I resize the width and height of a UserControl according to a child Label?

I'm coding a clone messaging app. I have a user control called "Bubble" that I use as a message bubble. And its user control layout is like this:
It contains only lblMessage, lblTime and pictureBox. lblMessage's AutoSize property off and Anchor is top-left-right. My goal is to size or wrap this according to the content. I am able to resize vertically using this code.
void SetHeight()
{
//SizeF maxSize = new Size(500, int.MaxValue);
Graphics g = CreateGraphics();
SizeF size = g.MeasureString(lblMessage.Text, lblMessage.Font, lblMessage.Width);
lblMessage.Height = int.Parse(Math.Round(size.Height + 2, 0).ToString());
lblTime.Top= lblMessage.Bottom +10;
this.Height = lblTime.Bottom + 10;
}
My result is like this:
I can resize vertically, but not horizontally at the same time. The way I resize it vertically is to use width property of lblMessage as the limit size. I want to resize user control according to size of short text. I thought about creating a maxSize for MeasureString but I can't get it to work. I'm trying to find a function that continues from a bottom line when it reaches a certain width value. I look forward to your help.
I searched all stackowerflow questions in depth but none of them worked in my case.
**Edit after Jimi comments
I tried to apply his solution to my project. I don't think I did everything right, but with a little bit of comment, something came up that worked for me at least.
private void Bubble_Paint(object sender, PaintEventArgs e)
{
float minimumWidth = lblTime.Width + pictureBox1.Width + 35;
float maximumWidth = 500;
string measureString = lblMessage.Text;
Font stringFont = new Font("Helvetica", 12.0F);
CharacterRange[] characterRanges = { new CharacterRange(0, measureString.Length) };
float layautWidth = maximumWidth - minimumWidth;
RectangleF layoutRect = new RectangleF(10, 6, layautWidth, this.Height);
StringFormat stringFormat = new StringFormat();
stringFormat.SetMeasurableCharacterRanges(characterRanges);
Color myColor = Color.FromArgb(211, 212, 212);
SolidBrush myBrush = new SolidBrush(myColor);
Region[] stringRegions = e.Graphics.MeasureCharacterRanges(measureString, stringFont, layoutRect, stringFormat);
RectangleF measureRect1 = stringRegions[0].GetBounds(e.Graphics);
//When I gave Rectangle to the DrawString, some last letter was truncated, so I used plain text printing until I got to the bottom line.
if (measureRect1.Height < 30)
e.Graphics.DrawString(measureString, stringFont, myBrush, 10, 6, stringFormat);
else e.Graphics.DrawString(measureString, stringFont, myBrush, measureRect1, stringFormat);
e.Graphics.DrawRectangle(new Pen(Color.Transparent, 0), Rectangle.Round(measureRect1));
this.Width = Convert.ToInt32(measureRect1.Width + minimumWidth);
lblTime.Top = Convert.ToInt32(measureRect1.Height) + 10;
this.Height = lblTime.Bottom + 10;
}
Result in container preview:
user control that goes down one line from the specified limit
and for one line
dynamically result inside the application

Align middle text string inside the button

I'm making some custom control for a small project. I have created a TP1CustomFlatButton like this:
It was easy for me if I added a label with text to bottom of my TP1CustomFlatButton. I didn't want to handle events for that label so I used event onPaint to draw the text. I followed the turtorial of Microsoft and I got the custom flat button like the picture I attached. What I'm trying to get is to make the text align center at the bottom of my TP1CustomFlatButton.
This is my code for TP1CustomFlatButton:
// constructor
public TP1CustomFlatButton()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.BackColor = Color.MediumSeaGreen;
this.ForeColor = Color.White;
this.Text = "TP1CustomButton";
}
protected override void OnPaint(PaintEventArgs pevent)
{
pevent.Graphics.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, this.Width, this.Height));
TextFormatFlags flags = TextFormatFlags.Bottom;
//render text
TextRenderer.DrawText(pevent.Graphics, this.Text, this.Font, new Point((int)(this.Width - Text.Length)/2,this.Height), this.ForeColor, flags);
//draw image
Image img = this.BackgroundImage;
//create rectangle to display image
Rectangle imgRec = new Rectangle(this.Width - 32 /3, this.Height - 32/ 3, 32, 32);
if(img!=null)
pevent.Graphics.DrawImage(img, imgRec);
}
I'm really confused with the coordinate X and Y. As you can see the code I tried to make the "SETTINGS" text string to align center at bottom of my TP1CustomFlatButton. I spent 5 hours to read more information about coordinate and location of controls in Windows Form. But now I'm really tired.
Hope someone can give me any advice or any solution for my custom control.
You need to use the MeasureString()method in order to calculate the middle.
also see the changes i have madein order to find the middle (calculated in drawPoint field).
see my example based on your code:
public TP1CustomFlatButton()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.BackColor = Color.MediumSeaGreen;
this.ForeColor = Color.White;
this.Text = "middle";
}
protected override void OnPaint(PaintEventArgs pevent)
{
pevent.Graphics.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, this.Width, this.Height));
TextFormatFlags flags = TextFormatFlags.Bottom;
//render text
String drawString = this.Text;
SizeF size = pevent.Graphics.MeasureString(drawString, this.Font);
Point drawPoint = new Point((int)this.Size.Width / 2 - (int)size.Width / 2,this.Height);
TextRenderer.DrawText(pevent.Graphics, this.Text, this.Font, drawPoint, this.ForeColor, flags);
//draw image
Image img = this.BackgroundImage;
//create rectangle to display image
Rectangle imgRec = new Rectangle(this.Width - 32 / 3, this.Height - 32 / 3, 32, 32);
if (img != null)
pevent.Graphics.DrawImage(img, imgRec);
}
Output:
Try Changing the TextFormatFlags as below:
TextFormatFlags flags = TextFormatFlags.Bottom | TextFormatFlags.VerticalCenter;
Also, checkout this link

Flip text vertically using Drawstring

I have some code that writes some text to a defined region.
graphics.DrawString(text, goodFont, Brushes.Black, textarea, stringFormat);
There are some cases where I would like to flip the text on the horizontal so that it goes from:
To
I have tried to measure the string width and take the inverse of that:
float w = graphics.MeasureString(text, goodFont).Width;
graphics.DrawString(text, goodFont, Brushes.Black, -w, 0, stringFormat);
but then my issue is that the text extends outside the boundary of the box I wish to draw it in (textarea).
I would like to flip the text on the horizontal while maintaining my box boundary. Can anybody point me in the right direction for how to accomplish my task?
Thanks in advance!
EDIT: I am trying to avoid having to create a bitmap and then do the transformation.
You can use graphics transformation. The easier I see is to use the this Matrix Constructor (Rectangle, Point[]) like this:
Point[] transformPoints =
{
// upper-left:
new Point(textarea.Right - 1, textarea.Top),
// upper-right:
new Point(textarea.Left + 1, textarea.Top),
// lower-left:
new Point(textarea.Right - 1, textarea.Bottom),
};
var oldMatrix = graphics.Transform;
var matrix = new Matrix(textarea, transformPoints);
try
{
graphics.Transform = matrix;
graphics.DrawString(text, goodFont, Brushes.Black, textarea, stringFormat);
}
finally
{
graphics.Transform = oldMatrix;
matrix.Dispose();
}
P.S. Although #serhiyb posted similar answer a few seconds before mine, I think this is easier to understand - you define the transformation by simply specifying a source rectangle and how to transform its upper-left, upper-right and lower-left points.
You can use the Matrix Constructor to transform the graphics and later draw the graphics using the DrawString method.
Try this:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
string text = "This is a Test";
g.DrawString(text, Font, Brushes.Black, 0, 0);
g.MultiplyTransform(new Matrix(-1, 0, 0, 1, 68, 50));
g.DrawString(text, Font, Brushes.Black, 0, 0);
g.ResetTransform();
}
Output:
You can use Transformation Matrix for that
Something like:
float w = graphics.MeasureString(text, goodFont).Width;
graphics.MultiplyTransform(new Matrix(-1, 0, 0, 1, w, 0));
/*
Matrix:
-1 0
0 1
newX -> -x
newY -> y
and dx offset = w (since we need to move image to right because of new negative x)
*/
graphics.DrawString(text, goodFont, Brushes.Black, textarea, stringFormat);
graphics.ResetTransform();
You may need to play with Matrix/area parameters as I'm coding it blindly but I hope you got the idea.

How Can i Distribute String in rectangle

I want to distribute string in rectangle.
Except each character set position
Rectangle displayRectangle = new Rectangle (new Point(40, 40), new Size (80, 80));
StringFormat format1 = new StringFormat(StringFormatFlags.NoClip);
format1.LineAlignment = StringAlignment.Center;
e.Graphics.DrawRectangle(Pens.Black, displayRectangle);
e.Graphics.DrawString("Showing Format1", this.Font,
Brushes.Black, (RectangleF)displayRectangle, format1);
But, StringFormat Alignment doesn't have distribute alignment. So I want to know a way how to distribute string in rectangle.
For the moment, I'm going to assume you can/will use the Win32 API (e.g., via. P/Invoke). .NET may have a wrapper for the function I'm going to suggest (but then again, it may not -- I'm really not sure). If it does, it'll be up to you to find and use it. Most of what I'm suggesting is more about the basic approach than the function anyway.
You can use GetTextExtentExPointI, which will compute the size of a rectangle necessary to hold a set characters you specify and (importantly) the horizontal position of each character in that rectangle.
So, what you want to do is use this to compute the size of a rectangle and position of each character in that rectangle, with it assuming normal kerning of the characters. Then, you'll divide the width of that rectangle into the width you actually want. This will give you a factor by which each position must increase to get that character to the position you want. You'll then multiply the position it returned for each character by that factor to get your desired position.
Just for example, let's assume it gave you positions of 0, 17, 35 and 44 for the characters with normal spacing. Let's also assume your target rectangle is 1.8 times as wide as the rectangle it computed for normal spacing. You'll take each of those positions and multiply by 1.8 to get the position you want to use for that character, giving 0, 31, 63, and 79 for the "corrected" positions.
Then you'll (obviously enough) go through your string and draw each character at the computed position.
Here's how to do it if you just want to literally distribute the characters evenly across the middle of the display rectangle:
private void Form1_Paint(object sender, PaintEventArgs e)
{
string text = "this is distribute";
Rectangle displayRectangle = new Rectangle(new Point(40, 40), new Size(400, 80));
e.Graphics.DrawRectangle(Pens.Black, displayRectangle);
int step = displayRectangle.Width / text.Length;
SizeF szF = e.Graphics.MeasureString(text, this.Font); // just to get the HEIGHT
int y = (displayRectangle.Y + displayRectangle.Height / 2) - (int)szF.Height / 2;
for (int i = 0; i < text.Length; i++)
{
e.Graphics.DrawString(text.Substring(i, 1), this.Font, Brushes.Black, displayRectangle.X + (i * step), y);
}
}
Here's my stab at #Jerry Coffin's algorithm using .Net managed methods:
private void Form1_Paint(object sender, PaintEventArgs e)
{
string text = "this is distribute";
Rectangle displayRectangle = new Rectangle(new Point(40, 40), new Size(400, 80));
e.Graphics.DrawRectangle(Pens.Black, displayRectangle);
StringFormat format1 = new StringFormat(StringFormatFlags.NoClip);
format1.LineAlignment = StringAlignment.Center;
format1.Alignment = StringAlignment.Near;
// SetMeasurableCharacterRanges() can only handle 32 regions max at a time!
// The below workaround simply measures each character separately:
RectangleF rcF = (RectangleF)displayRectangle;
List<Region> regions = new List<System.Drawing.Region>();
for (int i = 0; i < text.Length; i++)
{
format1.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(i, 1)});
regions.AddRange(e.Graphics.MeasureCharacterRanges(text, this.Font, rcF, format1));
}
RectangleF minBounds = regions[0].GetBounds(e.Graphics);
RectangleF maxBounds = regions[regions.Count - 1].GetBounds(e.Graphics);
float ratio = (float)displayRectangle.Width / (float)((maxBounds.X + maxBounds.Width) - minBounds.X);
for(int i = 0; i < regions.Count; i++)
{
Region region = regions[i];
RectangleF boundsF = region.GetBounds(e.Graphics);
PointF ptF = new PointF(displayRectangle.X + (int)((boundsF.Left - minBounds.X) * ratio), (int)boundsF.Top);
e.Graphics.DrawString(text.Substring(i, 1), this.Font, Brushes.Black, ptF);
}
}

Thick Line Drawing problem in C#.NET

I wanted to draw thick lines using Graphics.Lines() method. But it looks like the API has some bugs. If you try to render the user control with the following code, you would get weird looking image. I was wondering if there is some smoothing mode or something similar that could take care of this line drawing glitch.
private void UserControl1_Paint(object sender, PaintEventArgs e)
{
int n = 100;
Point[] points = new Point[n];
double x = 2;
int y = 50;
for (int i = 0; i < n; i++)
{
Point p = new Point();
p.X = 200 + (int)(i * x);
p.Y = 200 + (int)(Math.Sin(i * 0.2) * y);
points[i] = p;
}
Pen pen = new Pen(new SolidBrush(Color.Blue));
//Pen pen = new Pen(new LinearGradientBrush(new Point(0, 0), new Point(0, 100), Color.Black, Color.Red));
pen.Width = 200;
e.Graphics.DrawLines(pen, points);
}
You see the effect of GDI+ trying to draw end-caps on the line. That's not going to come to a good end with such a thick pen. About what you'd imagine from daVinci painting the Mona Lisa with a broom. Fix:
Pen pen = new Pen(new SolidBrush(Color.Blue));
pen.EndCap = System.Drawing.Drawing2D.LineCap.Square;
pen.StartCap = System.Drawing.Drawing2D.LineCap.Square;
Or draw a polygon instead so that GDI+ has a better idea what is front and back:
e.Graphics.DrawPolygon(pen, points);
Well, it doesn't look like a devil anymore. Keep the line width proportional to the details in the line.
Here is the result of your code drawing using a pen of width 200 (pixels):
And here it is at a width of 2:
The pen width property is usually pixels, but it is based on the Graphics object's PageUnit property (itself a GraphicsUnit property). Check to make sure you've set these values to what you want.

Categories