Strange DrawRectangle and DrawLine - c#

I want to draw several figures on the PictureBox. I expected that the following code will draw a rectangle with it's full size diagonal, but it doesn't. Line do not connected with bottom-right corner of rectangle. I'm really curious what may be wrong?
private void onPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen p = new Pen(System.Drawing.Color.Black, 2);
g.DrawRectangle(p, 50, 10, 400, 400);
g.DrawLine(p, 50, 10, 400, 400);
}

In DrawRectangle, the last two arguments are width and height. In DrawLine, the last two arguments are final x and final y.
So just add the starting x and starting y to the width and height to get your diagonal line:
g.DrawRectangle(p, 50, 10, 400, 400);
g.DrawLine(p, 50, 10, 450, 410);

Also, if you declare a Rectangle then you can change the values without needing to change the drawing code. Something like:
private void onPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle rc = new Rectangle(50, 10, 400, 400);
using (Pen p = new Pen(System.Drawing.Color.Black, 2))
{
g.DrawRectangle(p, rc);
g.DrawLine(p, rc.Left, rc.Top, rc.Right, rc.Bottom);
}
}

Related

C# how to draw line with one pixel width

i try to draw line on form:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g;
g = e.Graphics;
g.PageUnit = GraphicsUnit.Pixel;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
Pen myPen = new Pen(Color.Red);
myPen.Width = 1;
g.DrawLine(myPen, 100, 50, 200, 50);
g.DrawLine(myPen, 150, 0, 150, 100);
}
it looks like this: but after magnification it looks like this: - line width is 3 pixels ...
my question is: how to draw line width one pixel width ?
ok - i edited it but now it looks like this (in magnification): (wihtout pageunit looks the same)
What you are looking for is using the following before all your drawing calls:
g.SmoothingMode = SmoothingMode.None;
although the example in your question can not be reproduced.

Drawing in two pictureBoxes

I created two pictureBoxes on my windows form application. I want to draw in both of them, using FillEllipse(), but for some reason I can only draw in the first pictureBox. I belive the problem is that I don't understand properly how events work. Here is the code:
public Form1()
{
InitializeComponent();
pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
pictureBox2.Paint += new PaintEventHandler(this.pictureBox2_Paint);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics gr = e.Graphics;
Point p1 = pictureBox1.Location;
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 40, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 80, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 80, p1.Y + 40, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 80, p1.Y + 80, 20, 20));
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
Graphics br = e.Graphics;
Point p2 = pictureBox2.Location;
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 40, p2.Y + 40, 20, 20));
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 40, p2.Y + 80, 20, 20));
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 80, p2.Y + 40, 20, 20));
br.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p2.X + 80, p2.Y + 80, 20, 20));
}
You are drawing in pictureBox2. Your problem is that you are drawing outside of the viewport, because you are drawing at pictureBox2's location on the form, but within the picturebox. pictureBox2.Location gives to position of the box on the form. If this box is at position x=240, y=240, you are drawing INSIDE the box at those positions. If your box is only w=50, h=50, you will not see what you are drawing because it's waaaay to the right and bottom
So, this is really inefficient you're drawing everything twice resulting in twice the load. What you need to do is draw once into a bitmap and load the bitmap into the two picture boxes.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-create-a-bitmap-at-run-time
However, if working with bitmaps won't allow you to achieve whatever it is you're trying to do, write a custom control. While this wont improve performance all that much it will at least stop code duplication.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/creating-a-wf-control-design-time-features
Bitmap img= new Bitmap(100, 100);
Point p1 = pictureBox1.Location;
Graphics gr = Graphics.FromImage(img);
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 40, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 40, p1.Y + 80, 20, 20));
gr.FillEllipse(new SolidBrush(Color.Red), new Rectangle(p1.X + 80, p1.Y + 40, 20, 20));
picturebox1.Image = img;
picturebox2.Image = img;
The PaintEventArgs e.Graphics gives you a control's DeviceContext (consider it a reference to the graphics surface of a control) and a set of tools that can be used to perform graphics operations on that context.
When using e.Graphics, all drawings occur inside the underlying surface the graphics object references (a PictureBox ClipRectangle in this case). All coordinates are related to this area.
Thus, you just need to specify the position and size of a drawing using the control's Client Area as the only reference. The drawing area dimensions are also reported by the e.Graphics.ClipBounds (expressed in PageUnits) and the control ClientRectangle property (expressed in Pixels).
The ClientRectangle is the area of a control which excludes non-client elements such as Borders, Menus, ScrollBars, TitleBar etc.; it's "inner-body".
The client area definition can, however, change in relation to a control's internal structure (imagine a ListView or a ComboBox control, for example) .
The Paint() event of a control is raised each time a control needs to repaint itself.
It's always raised after a control is first created.
After that, it could be triggered when, for example, a control is for some reasons "obscured" by another control/window or when the Form that contains it is minimized.
It can be raised "manually" invoking the control's Invalidate() method.
This is probably the prefered way to force a control to repaint itself, because the Invalidate() method allows to specify a defined portion (a Rectangle or a Region) of a control that needs repainting, limiting the painting to this area.
You could modify your code this way:
public Form1()
{
InitializeComponent();
pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
pictureBox2.Paint += new PaintEventHandler(this.pictureBox2_Paint);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 80, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 80, 20, 20));
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(40, 80, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 40, 20, 20));
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(80, 80, 20, 20));
}
SmoothingMode = SmoothingMode.AntiAlias is used to "prettify" the graphics results.
It which will generate smoother borders.
The Drawing Brush used is a stock object (system-provided) which doesn't need to be Disposed(). If you create a Brush using one of the brush related classes, the object you create must be disposed.
using (SolidBrush brush = new SolidBrush(Color.Red))
{
e.Graphics.FillEllipse(brush, new Rectangle(40, 40, 20, 20));
}

C# - Drawing a Rounded Rectangle on a panel

I am currently trying to draw a gradient filled rounded rectangle onto a panel bar in a form.
From some research I found some code that allowed me to create a custom rectangle :
static class CustomRectangle
{
public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
public static void FillRoundedRectangle(this Graphics graphics, Brush brush, Rectangle bounds, int cornerRadius)
{
if (graphics == null)
throw new ArgumentNullException("graphics");
if (brush == null)
throw new ArgumentNullException("brush");
using (GraphicsPath path = RoundedRect(bounds, cornerRadius))
{
graphics.FillPath(brush, path);
}
}
Credit to this page
Next using this custom rectangle, I have tried using the paint method for the bar panel.
private void quickMenuBar_Paint(object sender, PaintEventArgs e)
{
LinearGradientBrush myBrush = new LinearGradientBrush(new Point(20, 20), new Point(120, 520), Color.DarkBlue, Color.RoyalBlue);
System.Drawing.Graphics formGraphics = this.CreateGraphics();
CustomRectangle.FillRoundedRectangle(formGraphics, myBrush, new System.Drawing.Rectangle(20, 20, 100, 500), 25);
myBrush.Dispose();
formGraphics.Dispose();
}
But after executing this code, it only prints a rounded rectangle directly onto the form and behind the bar panel.
I have other methods that fill a panel with a standard rectangle using PaintEventArgs e:
e.Graphics.FillRectangle(myBrush , otherBar.ClientRectangle);
So obviously I need to use PaintEventArgs e in my custom rectangle method, but I do not know how or where to.
If there are better ways than this way of drawing a rounded rectnagles, please share.
You generally should never be using CreateGraphics(). Simply remove this line:
System.Drawing.Graphics formGraphics = this.CreateGraphics();
And use e.Graphics where you would previously use formGraphics. The Paint event is basically asking you to "paint something for me, here's the graphics object to paint on".
Since you're already providing a Graphics object instance to your rounded rectangle method, no changes are required there.

Drawline with same pen width, but the line widths are different in result

I draw lines with the same pen, but the line widths are different in result. Why?
Bitmap b = new Bitmap(400, 400);
Graphics g = Graphics.FromImage(b);
g.PageUnit = GraphicsUnit.Point;
g.Clear(Color.White);
Pen pen = new Pen(Color.Red, 1.2f);
for (int i = 20; i < 200; i = i + 20)
{
g.DrawLine(pen, 10, i, 190, i);
}
g.Dispose();
b.Save("d:/temp/test.png", ImageFormat.Png);
b.Dispose()
Here is the result:
MSDN for GraphicsUnit
It's because you're working with Points and not Pixels and the variation in the width of the lines is the result of a rounding errors in the placement of the line and the width of the line in relation to how it gets rendered in pixels in the final product.
If you don't care about printing the image, it might be best to stick with Pixels.
Edit: If you want to continue using points, space things relative to your pen width:
Bitmap b = new Bitmap(400, 400);
Graphics g = Graphics.FromImage(b);
g.PageUnit = GraphicsUnit.Point;
g.Clear(Color.White);
Pen pen = new Pen(Color.Red, 1.2f);
for (float i = 20f * pen.Width; i < 200f * pen.Width; i = i + 20f * pen.Width)
{
g.DrawLine(pen, 10f, i, 190f, i);
}
g.Dispose();
b.Save("c:/temp/test.png", ImageFormat.Png);
b.Dispose();

Using trackbars to display and distort a cross section

The question asked to calculate the volume of a swimming pool and display its cross section in a picture box. Width of the pool is fixed at 5 meters and the length is fixed at 20 meters.
The program should have 2 trackbars - one to adjust the depth of the deep end and one to adjust the depth of the shallow end. The minimum depth of each end is one meter, choose suitable maximum and minimum track bar values at design time.
Volume = averageDepth * width * length
The trackbar for the deep end of the pool adjusts the depth perfectly.
The trackbar for the shallow end changes the cross section and does not diplay one of the lines when used.
Could someone please help me correct this and figure it out?
Heres the code:
public partial class Form1 : Form
{
private Graphics paper;
private int averageDepth;
private int answer;
public Form1()
{
InitializeComponent();
paper = pictureBox1.CreateGraphics();
deepEndTrackbar.Minimum = 120;
deepEndTrackbar.Maximum = 180;
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
shallowEndTrackbar.Minimum = 120;
shallowEndTrackbar.Maximum = 180;
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
}
private void button1_Click(object sender, EventArgs e)
{
Graphics paper;
paper = pictureBox1.CreateGraphics();
Pen pen = new Pen(Color.Black);
paper.DrawLine(pen, 40, 50, 200, 50);
paper.DrawLine(pen, 40, 50, 40, 120);
paper.DrawLine(pen, 200, 50, 200, 90);
paper.DrawLine(pen, 200, 90, 40, 120);
}
private void deepEndTrackbar_Scroll(object sender, EventArgs e)
{
Pen pen = new Pen(Color.Black);
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
paper.Clear(Color.White);
paper.DrawLine(pen, 40, 50, 200, 50);
paper.DrawLine(pen, 200, 50, 200, 90);
paper.DrawLine(pen, 40, 50, 40, deepEndTrackbar.Value);
paper.DrawLine(pen, 200, 90, 40, deepEndTrackbar.Value);
}
private void shallowEndTrackbar_Scroll(object sender, EventArgs e)
{
Pen pen = new Pen(Color.Black);
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
paper.Clear(Color.White);
paper.DrawLine(pen, 40, 50, 200, shallowEndTrackbar.Value);
paper.DrawLine(pen, 200, 50, 200, shallowEndTrackbar.Value);
}
The deepEndTrackbar_Scroll routine seems to work. But it doesn't really as it does not use the value of the shallowEndTrackbar. In fact both routines should draw the very same lines:
paper.DrawLine(pen, 40, 50, 200, 50);
paper.DrawLine(pen, 200, 50, 200, shallowEndTrackbar.Value);
paper.DrawLine(pen, 40, 50, 40, deepEndTrackbar.Value);
paper.DrawLine(pen, 200, shallowEndTrackbar.Value, 40, deepEndTrackbar.Value);
This is the bare minimum correction.
The next step would be to have this code only once. The direct solution would be to call the same event for both Trackbars.
However all these things are only working on the surface. Minimize the window, restore and look: All Lines are gone! You need to persist the drawing!
The right place for the code is the Paint event of the PictureBox. You could override it, but it is simpler to just code it: Doubleclick it in the properties tab and insert the code.
Prefix this line to the code:
paper = e.Graphics;
And replace all drawing in the two TrackBar scroll events for this:
pictureBox1.Invalidate();
Another error is with initializing the Trackbar. Judging from the initial display one value (90) is not in the allowed range (120-180).
You also don't need the lines
private Graphics paper;
and
paper = pictureBox1.CreateGraphics();
Edit: As requested here is a complete solution.
Note: I have added a LengthTrackbar and moved the drawing down by 100 pixels:
public partial class Form1 : Form
{
Pen pen = Pens.Black;
//private int averageDepth;
//private int answer;
public Form1 ()
{
InitializeComponent();
deepEndTrackbar.Minimum = 120;
deepEndTrackbar.Maximum = 180;
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
shallowEndTrackbar.Minimum = 120;
shallowEndTrackbar.Maximum = 180;
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
lengthTrackbar.Minimum = 120;
lengthTrackbar.Maximum = 180;
lengthLabel.Text = Convert.ToString(lengthTrackbar.Value);
}
private void button1_Click(object sender, EventArgs e)
{
// reset the trackbar values:
shallowEndTrackbar.Value = 120;
deepEndTrackbar.Value = 120;
lengthTrackbar.Value = 120;
pictureBox1.Invalidate();
}
private void deepEndTrackbar_Scroll(object sender, EventArgs e)
{
deepEndLabel.Text = Convert.ToString(deepEndTrackbar.Value);
pictureBox1.Invalidate();
}
private void shallowEndTrackbar_Scroll(object sender, EventArgs e)
{
shallowEndLabel.Text = Convert.ToString(shallowEndTrackbar.Value);
pictureBox1.Invalidate();
}
private void lengthTrackbar_Scroll(object sender, EventArgs e)
{
lengthLabel.Text = Convert.ToString(lengthTrackbar.Value);
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics paper = e.Graphics;
// reset the paper
paper.Clear(Color.White);
// draw the 2D front lines:
paper.DrawLine(pen, 40, 150, 200, 150);
paper.DrawLine(pen, 200, 150, 200, 100 + shallowEndTrackbar.Value);
paper.DrawLine(pen, 40, 150, 40, 100 + deepEndTrackbar.Value);
paper.DrawLine(pen, 200, 100 + shallowEndTrackbar.Value, 40,
100 + deepEndTrackbar.Value);
// perspective 2:3
int lx = lengthTrackbar.Value / 2;
int ly = lengthTrackbar.Value / 3;
// draw the outer 3D lines:
paper.DrawLine(pen, 40, 150, 40 + lx, 150 - ly);
paper.DrawLine(pen, 200, 150, 200 + lx, 150 - ly);
paper.DrawLine(pen, 200, 100 + shallowEndTrackbar.Value,
200 + lx, 100 - ly + shallowEndTrackbar.Value);
paper.DrawLine(pen, 40 + lx, 150 - ly, 40 + lx + 200 - 40, 150 - ly);
paper.DrawLine(pen, 200 + lx, 150 - ly,
200 + lx, 100 -ly + shallowEndTrackbar.Value);
}
}

Categories

Resources