I made a function that draws a rectangle with rounded corners using examples I got researching here, now I want to call this function with a button click, but I'm not sure how to provide the arguments to the function, can someone help in how to I call this function?
public void DrawCond(Graphics g, Pen p, float width, float height, float x, float y)
{
// Auxiliary variables
float radius;
if (width>=3.55)
{
radius = 1F;
}
else if (width>2.25)
{
radius = 0.8F;
}
else if (width>1.61)
{
radius = 0.65F;
}
else
{
radius = 0.5F;
}
GraphicsPath gp = new GraphicsPath();
//Draw lines
gp.AddLine(x + radius, y, x + width - 2 * radius, y); //bottom horizontal line
gp.AddLine(x + radius, y + height, x + width - 2 * radius, y + height); //top horizontal line
gp.AddLine(x + width, y + radius, x + width, y + height - 2 * radius); //inner vertical line
gp.AddLine(x + radius, y + radius, x + radius, y + height - 2 * radius); //outer vertical line
//Draw arcs
gp.AddArc(x, y + radius, radius, radius, 90, 90); //bottom left corner
gp.AddArc(x + width - radius, y + radius, radius, radius, 0, 90); //bottom right corner
gp.AddArc(x, y + height, radius, radius, 180, 90); //top left corner
gp.AddArc(x + width - radius, y + height, radius, radius, 270, 90); //top right corner
g.DrawPath(p, gp);
gp.Dispose();
}
You can create Graphics from panel you want to draw to like var g = panel.CreateGraphics() and pass it to your function.
Also you can create Pen by using one of its constructors. See MSDN for reference about Pen.
Related
I'm running into the problem that my rounded corners method returns non-uniform corners.
Method:
GraphicsPath GetRoundedPath(Rectangle r, int radius, bool topRight = true, bool topLeft = true, bool bottomLeft = true, bool bottomRight = true)
{
GraphicsPath path = new GraphicsPath();
if (topLeft)
path.AddArc(r.X, r.Y, radius, radius, 180, 90);
else path.AddLine(r.X, r.Y, r.Right, r.Y);
if (topRight)
path.AddArc(r.Right - radius, r.Y, radius, radius, 270, 90);
else path.AddLine(r.Right, r.Y, r.Right, r.Height);
if (bottomRight)
path.AddArc(r.Right - radius, r.Bottom - radius, radius, radius, 0, 90);
else path.AddLine(r.Right, r.Bottom, r.Right, r.Height);
if (bottomLeft)
path.AddArc(r.X, r.Bottom - radius, radius, radius, 90, 90);
else path.AddLine(r.X, r.Bottom, r.X, r.Y);
return path;
}
When invoke:
Panel p = new Panel();
p.BackColor = Color.Red;
p.Size = new Size(200, 200);
Controls.Add(p);
p.Region = new Region(GetRoundedPath(new Rectangle(p.ClientRectangle, 30, true, true, true, true));
p.Location = new Point(200, 200);
Return:
It is noticeable that the upper left corner is drawn normally, the upper right and lower left are crooked, and the lower left is even crooked.
After, I noticed that exactly one pixel was not visible on the right and bottom, then I opened the Paint(program) and painted it myself and got even corners:
What I tried:
Passed less Rectangle to GetRoundedPath
GetRoundedPath(new Rectangle(p.ClientRectangle.X, p.ClientRectangle.Y, p.ClientRectangle.Width - 1, p.ClientRectangle.Height - 1), 30, true, true, true, true)
The control just became a pixel smaller in width and height.
Don't to change Control.Region, to use Graphics.FillPath instead
p.Paint += (s, e) =>
{
e.Graphics.FillPath(Brushes.Red, GetRoundedPath(p.ClientRectangle, 30, true, true, true, true));
};
Nothing has changed, I also tried to use it in conjunction with the first
Increase radius for individual corners
int oppositeRadius = (int)Math.Round(radius * 1.5)
...
The corners became stretched and looked more like slanted lines
UPD: I found similar post - Strange GraphicsPath.AddArc() behaviour but it work only with Region and all corners, when I have problem with GraphicsPath
It looks like off by 1 pixel for center of arc error. Maybe try something like:
GraphicsPath GetRoundedPath(Rectangle r, int radius, bool topRight = true, bool topLeft = true, bool bottomLeft = true, bool bottomRight = true)
{
GraphicsPath path = new GraphicsPath();
if (topLeft)
path.AddArc(r.X, r.Y, radius, radius, 180, 90);
else path.AddLine(r.X, r.Y, r.Right, r.Y);
if (topRight)
path.AddArc(r.Right - radius - 1, r.Y, radius, radius, 270, 90);
else path.AddLine(r.Right, r.Y, r.Right, r.Height);
if (bottomRight)
path.AddArc(r.Right - radius - 1, r.Bottom - radius - 1, radius, radius, 0, 90);
else path.AddLine(r.Right, r.Bottom, r.Right, r.Height);
if (bottomLeft)
path.AddArc(r.X, r.Bottom - radius - 1, radius, radius, 90, 90);
else path.AddLine(r.X, r.Bottom, r.X, r.Y);
return path;
}
i am trying to develop a win form for chatting purpose. I am developing Chat bubbles using pure inbuilt functions of .Net Framework, No fancy UI, No third party libraries.
Now let's have a look on how every thing is being done.
My following function is responsible for generating a Panel dynamically for each chat message received, the Pain event is used to draw rounded rectangle and color is transparent. A picture box is used to show static avatar.
private void SetRemoteMessage(string msg)
{
PictureBox pb = new PictureBox();
pb.Bounds = new Rectangle(0, 0, 72, 72);
pb.Image = Base64ToImage(avatar_his);
Panel p = new Panel();
Label lb = new Label();
lb.BackColor = Color.Transparent;
lb.ForeColor = Color.Blue;
lb.Text = msg;
lb.Font = new Font("Arial", 14, FontStyle.Bold, GraphicsUnit.Point);
p.Bounds = new Rectangle(rX, rY, (Width / 2) - 25, pb.Height);
p.BackColor = Color.Transparent;
lb.Size = new Size(p.Width - pb.Width, p.Height);
lb.Paint += _control_Paint;
lb.Location = new Point(pb.Width + 5, 0);
p.Controls.Add(pb);
p.Controls.Add(lb);
SetPanel(p);
rY += p.Height + 20;
mY += p.Height + 20;
}
following is the Paint event binded to the parent "Container" Panel so that a simple rounded rectangle is shown for each bubble
private void _control_Paint(object sender, PaintEventArgs e)
{
Control c = (Control)sender;
if(!c.Name.Equals("lb"))
{
Graphics v = e.Graphics;
DrawRoundRect(v, Pens.Blue, e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1, 10);
}
else
{
using (Font font1 = new Font("Arial", 12, FontStyle.Bold, GraphicsUnit.Point))
{
e.Graphics.DrawString(c.Text, font1, Brushes.Blue, c.Bounds);
}
}
base.OnPaint(e);
}
and the following function to actually generate rounded rectangles
private void DrawRoundRect(Graphics g, Pen p, float X, float Y, float width, float height, float radius)
{
GraphicsPath gp = new GraphicsPath();
gp.AddLine(X + radius, Y, X + width - (radius * 2), Y);
gp.AddArc(X + width - (radius * 2), Y, radius * 2, radius * 2, 270, 90);
gp.AddLine(X + width, Y + radius, X + width, Y + height - (radius * 2));
gp.AddArc(X + width - (radius * 2), Y + height - (radius * 2), radius * 2, radius * 2, 0, 90);
gp.AddLine(X + width - (radius * 2), Y + height, X + radius, Y + height);
gp.AddArc(X, Y + height - (radius * 2), radius * 2, radius * 2, 90, 90);
gp.AddLine(X, Y + height - (radius * 2), X, Y + radius);
gp.AddArc(X, Y, radius * 2, radius * 2, 180, 90);
gp.CloseFigure();
g.DrawPath(p, gp);
}
The chat message is not wrapped automatically so to wrap it, the Pain method is used as it can be seen in Paint event . .the output is good as expected instead of one thing
The Problem can be seen easily the Panel height is not increasing according when a large message is given to display in bubble.
What i tried already is
Measuring string length with Graphics class, but i was not able to implement it with success
Enabling Scrollbars, Yes this approach worked but i am not interested to use this behavior
Counting string length and increasing height, This works but not efficient, specially when form is resized all calculations of measurement then become invalid.
public void DrawRoundRect(Graphics g, Pen p, float X, float Y, float width, float height, float radius)
{
GraphicsPath gp = new GraphicsPath();
gp.AddLine(X + radius, Y, X + width - (radius * 2), Y);
gp.AddArc(X + width - (radius * 2), Y, radius * 2, radius * 2, 270, 90);
gp.AddLine(X + width, Y + radius, X + width, Y + height - (radius * 2));
gp.AddArc(X + width - (radius * 2), Y + height - (radius * 2), radius * 2, radius * 2, 0, 90);
gp.AddLine(X + width - (radius * 2), Y + height, X + radius, Y + height);
gp.AddArc(X, Y + height - (radius * 2), radius * 2, radius * 2, 90, 90);
gp.AddLine(X, Y + height - (radius * 2), X, Y + radius);
gp.AddArc(X, Y, radius * 2, radius * 2, 180, 90);
gp.CloseFigure();
g.DrawPath(p, gp);
}
private void _pnlLogIn_Paint(object sender, PaintEventArgs e)
{
Graphics v = e.Graphics;
DrawRoundRect(v, Pens.Blue, e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1, 10);
//Without rounded corners
//e.Graphics.DrawRectangle(Pens.Blue, e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
base.OnPaint(e);
}
Hello, What is wrong with my code. My panel has already a round edge but the border of the rectangular panel remains. What code should i add or revise? thank you..
Image
Set your panel region to Graphics path.
In your code posted, do it after you call DrawPath in DrawRoundRect method.
Something like this:
_pnlLogIn.Region = new System.Drawing.Region(gp);
After I do above code change, I see my window as shown below.
I tried with bit bigger radius (40)..
I have implemented a rounded rectangle extension method, defined here.
public static Graphics DrawRRectangle(this Graphics g, Pen p, int x, int y, int width, int height, int feathering)
{
g.DrawLine(p, x, y + feathering, x, y + height - feathering);
g.DrawBezier(p, new Point(x, y + height - feathering),
new Point(x, y + height - feathering / 2), new Point(x + feathering / 2, y + height),
new Point(x + feathering, y + height));
g.DrawLine(p, x + feathering, y + height , x + width - feathering, y + height);
g.DrawBezier(p, new Point(x + width - feathering, y + height),
new Point(x + width - feathering / 2, y + height), new Point(x + width, y + height - feathering / 2),
new Point(x + width, y + height - feathering));
g.DrawLine(p, x + width, y + height - feathering, x + width, y + feathering);
g.DrawBezier(p, new Point(x + width, y + feathering),
new Point(x + width, y + feathering / 2), new Point(x + width - feathering / 2, y),
new Point(x + width - feathering, y));
g.DrawLine(p, x + width - feathering, y, x + feathering, y);
g.DrawBezier(p, new Point(x + feathering, y),
new Point(x + feathering / 2, y), new Point(x, y + feathering / 2),
new Point(x, y + feathering));
return g;
}
However when I use this method like so
g.DrawRRectangle(p, 100, 100, 1000, 1000, 100);,
I do not get the outcome I wanted, each of the corners are either misaligned of their pixels do not match up As seen in the images below.
Any suggestions anybody could offer would be helpful, I am unsure if this is a problem with the equations used to generate my curves however this is the first time I am dabbling with graphics, so it could just be my thinking. Thanks.
Whilst I can't comment on your implementation, you're going to run into problems further down the road with this. Your implementation will give the appearance of drawing a rounded rectangle, but say for example in future you want to fill the shape, you won't be able to because GDI/GDI+ won't see the drawn shapes as a single consecutive shape.
In this respect you should use a GraphicsPath.
See here for a complete solution for drawing rounded rectangles using a GraphicsPath.
I have a UserControl which has a button on it. On the UserControl OnPaint event I draw a rounded corner border (or a simple rectangle if the radius is zero) and then I fill the entire control. After these manipulations my Button (btnClose) disappears.
How do I make my button visible again?
protected override void OnPaint(PaintEventArgs pe)
{
using (System.Drawing.Pen p = new Pen(new SolidBrush(this.BorderColor)))
{
if (borderRadius > 0)
{
DrawRoundRect(pe.Graphics, p, 0, 0, this.Width - 1, this.Height - 1, borderRadius, this.FillColor);
}
else
{
this.BackColor = this.FillColor;
pe.Graphics.DrawRectangle(p, 0, 0, this.Width - 1, this.Height - 1);
}
btnClose.Location = new Point(this.Width - btnClose.Width - BTN_MARGIN_DELTA, BTN_MARGIN_DELTA);
}
base.OnPaint(pe);
}
Just in case, the DrawRoundRect function:
void DrawRoundRect(Graphics g, Pen p, float X, float Y, float width, float height, float radius, Color _fillColor)
{
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddLine(X + radius, Y, X + width - (radius * 2), Y);
gp.AddArc(X + width - (radius * 2), Y, radius * 2, radius * 2, 270, 90);
gp.AddLine(X + width, Y + radius, X + width, Y + height - (radius * 2));
gp.AddArc(X + width - (radius * 2), Y + height - (radius * 2), radius * 2, radius * 2, 0, 90);
gp.AddLine(X + width - (radius * 2), Y + height, X + radius, Y + height);
gp.AddArc(X, Y + height - (radius * 2), radius * 2, radius * 2, 90, 90);
gp.AddLine(X, Y + height - (radius * 2), X, Y + radius);
gp.AddArc(X, Y, radius * 2, radius * 2, 180, 90);
gp.CloseFigure();
using (SolidBrush brush = new SolidBrush(_fillColor))
{
g.FillPath(brush, gp);
g.DrawPath(p, gp);
}
}
}
Try moving the location code to the resize method:
protected override void OnResize(EventArgs e) {
btnClose.Location = new Point(this.Width - btnClose.Width - BTN_MARGIN_DELTA, BTN_MARGIN_DELTA);
}
Moving controls in a paint event could cause recursive calls to the paint event. Only "paint" in a paint event.
I set FillColor = Color.Gray, BorderColor = Color.Black, borderRadius = 5, BTN_MARGIN_DELTA = 2 and it seems to work without any problem. Here is a screenshot:
I think the problem isn't these lines of code.
Well, my mistake. It was a function that deletes all controls from UserControl. So I filter the controls on removal.
void ClearControls()
{
for (int i = 0; i < Items.Count; i++)
{
foreach (Control cc in Controls)
{
if (cc.Name.Contains(LINK_LABEL_FAMILY) || (cc.Name.Contains(LABEL_FAMILY)))
{
Controls.RemoveByKey(cc.Name);
}
}
}
}