Draw a line on PictureBox from parent - c#

I have a PictureBox as UserControl. I added this User Control on the main form. Now I have to press a button and create a line on the user control. On my project, every time I press this button, I want to send to user control parameters of two PointF(x and y) and draw a new line, in addition to the existent one. I have so far the Paint event when picturebox is loaded.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Pen graphPen = new Pen(Color.Red, 2);
PointF pt1D = new PointF();
PointF pt2D = new PointF();
pt1D.X = 0;
pt1D.Y = 10;
pt2D.X = 10;
pt2D.Y = 10;
e.Graphics.DrawLine(graphPen, pt1D, pt2D);
}

Assuming that you want to draw the line on the click of the button, here's a modified version of your code:
List<PointF> points = new List<PointF>();
Pen graphPen = new Pen(Color.Red, 2);
private void btnDrawLines_Click(object sender, EventArgs e)
{
Graphics g = picBox.CreateGraphics();
PointF pt1D = new PointF();
PointF pt2D = new PointF();
pt1D.X = 0;
pt1D.Y = 10;
pt2D.X = 10;
pt2D.Y = 10;
g.DrawLine(graphPen, pt1D, pt2D);
points.Add(pt1D);
points.Add(pt2D);
}
private void picBox_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < points.Count; i+=2)
e.Graphics.DrawLine(graphPen, points[i], points[i + 1]);
}
Note that you can get a Graphics object through the PictureBox class's CreateGraphics() method which is the same as the e.Graphics object in the Paint event handler.

If you are adding lines to be drawn, the you probably want a little Line class:
public class Line {
public Point Point1 { get; set; }
public Point Point2 { get; set; }
public Line(Point point1, Point point2) {
this.Point1 = point1;
this.Point2 = point2;
}
}
And then you can just add these "lines" to a list:
private List<Line> _Lines = new List<Line>();
and add to them and tell the control to update it's drawing:
_Lines.Add(new Line(new Point(10, 10), new Point(42, 42)));
_Lines.Add(new Line(new Point(20, 40), new Point(20, 60)));
pictureBox1.Invalidate()
then in your drawing:
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
e.Graphics.Clear(Color.White);
foreach (Line l in _Lines) {
e.Graphics.DrawLine(Pens.Red, l.Point1, l.Point2);
}
}

Related

c# winforms proportional change of graphics scaling window

when I press the button, I draw a koch snowflake.but please tell me how I can make the snowflake proportionally change size when I change the window scale?
private void button1_Click(object sender, EventArgs e)
{
pen_white = new Pen(Color.White, 1);
holst = CreateGraphics();
holst.Clear(Color.CadetBlue);
var point1 = new PointF(200, 200);
var point2 = new PointF(500, 200);
var point3 = new PointF(350, 400);
holst.DrawLine(pen_white, point1, point2);
holst.DrawLine(pen_white, point2, point3);
holst.DrawLine(pen_white, point3, point1);
FractalKoh(point1, point2, point3, 7);
FractalKoh(point2, point3, point1, 7);
FractalKoh(point3, point1, point2, 7);
}
Windows Form has an event "Size Changed".
Inside the event handler method you can get width and height with following:
private void Form1_SizeChanged(object sender, EventArgs e)
{
int width= this.Width;
int height= this.Height;
double widthCoeficient = initialWidth / width;
double heightCoeficient = initialHeight / height;
}
In your form declare to global variables.
private int initialWidth;
private int initialHeight;
in form_load initialize this variable
initialHeight = this.Height;
initialWidth = this.Width;

C# - Handle mouse position with multiple PictureBoxes

I created in a Window Form 4 PictureBoxes (pictureBox1, pictureBox2, pictureBox3 and pictureBox4). I also created a function to draw a rectangle on a pictureBox like this:
To create the rectangle:
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
var x = e.X;
var y = e.Y;
var width = 10;
var height = 10;
FwRect = new[]
{
new PointF(x, y), new PointF(x, y + height), new PointF(x + width, y + height),
new PointF(x + width, y)
};
FwRectan = new Rectangle((int)x, (int)y, (int)width, (int)height);
Refresh();
}
Then I added this event for each pictureBox:
this.pictureBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
this.pictureBox2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
this.pictureBox3.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
this.pictureBox4.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
To draw the rectangle:
private void PictureBox_Paint(object sender, PaintEventArgs e)
{
using (var pen = new Pen(Color.Black, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, FwRectan);
}
}
Eventually, if I move the mouse inside the pictureBox1 and draw a rectangle, it also draw a rectangle for each pictureBox. How can I draw a rectangle only on the pictureBox that the mouse is located at?
Thank you very much!
You have 4 PictureBox, so you need 4 Rectangle to draw current mouse movement:
Rectangle[] _rectangle = new Rectangle[4];
then in both common PictureBox_MouseMove and PictureBox_Paint events you need to identify which value to use, index of picturebox. It can be done by using Tag property or by putting all pictureboxes into array so that their index there will match:
PictureBox _control = new PictureBox[] { pictureBox1, pictureBox2, pictureBox3, pictureBox4 };
The event handles will looks like this
void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
var x = e.X;
var y = e.Y;
var width = 10;
var height = 10;
FwRect = new[]
{
new PointF(x, y), new PointF(x, y + height), new PointF(x + width, y + height),
new PointF(x + width, y)
};
var index = _control.IndexOf(sender);
_rectangle[index] = new Rectangle((int)x, (int)y, (int)width, (int)height);
_rectangle[index].Invalidate();
}
void PictureBox_Paint(object sender, PaintEventArgs e)
{
var index = _control.IndexOf(sender);
using (var pen = new Pen(Color.Black, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, _rectangle[index]);
}
}
Edit:
Actually above solution will remember rectangle for each picturebox. Might not be what you want. The simple fix would be to clear other rectangles in mousemove. Though the more proper solution would be to remember sender from mousemove and only paint matching sender in paint.
Here's an example showing how to store the Rectangle in the .Tag property as mentioned by Sinatr in his post. This example also clears the Rectangle when the mouse leaves so you only ever have one Rectangle being drawn in the current PictureBox:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.pictureBox1.MouseMove += this.PictureBox_MouseMove;
this.pictureBox2.MouseMove += this.PictureBox_MouseMove;
this.pictureBox3.MouseMove += this.PictureBox_MouseMove;
this.pictureBox4.MouseMove += this.PictureBox_MouseMove;
this.pictureBox1.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox2.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox3.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox4.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox1.Paint += this.PictureBox_Paint;
this.pictureBox2.Paint += this.PictureBox_Paint;
this.pictureBox3.Paint += this.PictureBox_Paint;
this.pictureBox4.Paint += this.PictureBox_Paint;
}
private int bxWidth = 10;
private int bxHeight = 10;
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
PictureBox pb = (PictureBox)sender;
pb.Tag = new Rectangle(e.X, e.Y, bxWidth, bxHeight);
pb.Invalidate();
}
private void PictureBox_Paint(object sender, PaintEventArgs e)
{
PictureBox pb = (PictureBox)sender;
if (pb.Tag != null && pb.Tag is Rectangle)
{
Rectangle rc = (Rectangle)pb.Tag;
using (var pen = new Pen(Color.Black, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, rc);
}
}
}
private void pictureBox_MouseLeave(object sender, EventArgs e)
{
PictureBox pb = (PictureBox)sender;
pb.Tag = null; // if you want the box to disappear when the mouse leaves?
pb.Invalidate();
}
}
Here's what it looks like running:

Resize rectangle with form Size in real time?

Here is the following rectangle below:
When I resize the form, I need this rectangle to match the size of the form.
When changing the width of the rectangle, do not interfere with its visibility within the form.
I'm using the following:
Note:
I did the rectangle manually, but if you have rectangle ready, better yet!
public Form1()
{
InitializeComponent();
this.Paint += Form1_Paint;
this.rectangles = new Dictionary<string, Rectangle>();
this.sizeScreen = this.Size;
this.sizeRectangles = new Size(8, 8);
this.brush = new SolidBrush(Color.Red);
FillLeft();
FillRight();
FillUp();
FillDown();
}
private Size sizeScreen;
private Size sizeRectangles;
private SolidBrush brush;
private Dictionary<string, Rectangle> rectangles;
private void FillLeft()
{
Rectangle rectangle = new Rectangle()
{
Height = this.sizeScreen.Height,
Width = this.sizeRectangles.Width,
X = 0,
Y = this.sizeRectangles.Height
};
this.rectangles.Add("left", rectangle);
}
private void FillRight()
{
Rectangle rectangle = new Rectangle()
{
Height = this.sizeScreen.Height,
Width = this.sizeRectangles.Width,
X = this.sizeScreen.Width - (this.sizeRectangles.Width * 5),
Y = this.sizeRectangles.Height
};
this.rectangles.Add("right", rectangle);
}
private void FillUp()
{
Rectangle rectangle = new Rectangle()
{
Height = this.sizeRectangles.Height,
Width = this.sizeScreen.Width,
X = 0,
Y = this.sizeRectangles.Height
};
this.rectangles.Add("up", rectangle);
}
private void FillDown()
{
Rectangle rectangle = new Rectangle()
{
Height = this.sizeRectangles.Height,
Width = this.sizeScreen.Width,
X = 0,
Y = this.sizeScreen.Height - (this.sizeRectangles.Height * 11)
};
this.rectangles.Add("down", rectangle);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < this.rectangles.Count; i++)
{
e.Graphics.FillRectangles(this.brush, this.rectangles.Values.ToArray());
}
}
I want to set the rectangle on the form when it is resized
This is the way I'm creating the rectangle, but it does not stay right on the screen, to resize it I do not know
I think this would simplify what you are trying to do:
const int PenWidth = 10;
private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle r = this.ClientRectangle;
Pen pen = new Pen(Color.Red, PenWidth);
e.Graphics.DrawRectangle(pen, r);
}
You could even add a margin:
const int PenWidth = 10;
const int PenMargin = 10;
private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle r = this.ClientRectangle;
r.Inflate(-PenMargin, -PenMargin);
Pen pen = new Pen(Color.Red, PenWidth);
e.Graphics.DrawRectangle(pen, r);
}
To prevent traces (suggested by Wyck):
private void Form1_Resize(object sender, EventArgs e)
{
Invalidate();
}
Handle the Resize event and call Invalidate in the handler. Create a Pen of the desired color and width and set its Alignment to Inset. Handle the Paint event and in the handler call DrawRectangle passing in the ClientRectangle of the form.
Here is an example.
const float borderWidth = 8.0f;
Pen borderPen = new Pen(Color.Red, borderWidth) { Alignment = System.Drawing.Drawing2D.PenAlignment.Inset };
public Form2()
{
InitializeComponent();
this.Paint += Form2_Paint;
this.Resize += Form2_Resize;
}
private void Form2_Resize(object sender, EventArgs e)
{
Invalidate();
}
private void Form2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(borderPen, this.ClientRectangle);
}
Apply the following fixes to the code:
Set ResizeRedraw property of the form to true. It sets the underlying style for the form so by each resize it sends the paint message and you don't need to handle Resize event.
Use DrawRectangle and draw using wide pen. So you don't need to fill multiple rectangles.
Set the PenAlignment to Inset. So you don't need to calculate the location of rectangle.
Do dispose the pen when you don't need it.
Example
public Form1()
{
InitializeComponent();
ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (var pen = new Pen(Color.Red, PenWidth))
{
pen.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
e.Graphics.DrawRectangle(pen, ClientRectangle);
}
}
i have used this on my current project. when ever you resize the form, it will automatically resize all the object inside your form
i have class named clsResize and i call this on the form load.
1st you have to initialize the class inside the form then create 2 new method.
see example below
public partial class frmNewForm : Form
{
clsResize _form_resize;
public string selectedProd;
public frmNewForm()
{
InitializeComponent();
_form_resize = new clsResize(this);
this.Load += _Load;
this.Resize += _Resize;
}
private void _Load(object sender, EventArgs e)
{
_form_resize._get_initial_size();
}
private void _Resize(object sender, EventArgs e)
{
_form_resize._resize();
}
}
and here is the class that i used.
public class clsResize
{
List<System.Drawing.Rectangle> _arr_control_storage = new List<System.Drawing.Rectangle>();
private bool showRowHeader = false;
public clsResize(Form _form_)
{
form = _form_; //the calling form
_formSize = _form_.ClientSize; //Save initial form size
_fontsize = _form_.Font.Size; //Font size
}
private float _fontsize { get; set; }
private System.Drawing.SizeF _formSize {get;set; }
private Form form { get; set; }
public void _get_initial_size() //get initial size//
{
var _controls = _get_all_controls(form);//call the enumerator
foreach (Control control in _controls) //Loop through the controls
{
_arr_control_storage.Add(control.Bounds); //saves control bounds/dimension
//If you have datagridview
if (control.GetType() == typeof(DataGridView))
_dgv_Column_Adjust(((DataGridView)control), showRowHeader);
}
}
public void _resize() //Set the resize
{
double _form_ratio_width = (double)form.ClientSize.Width /(double)_formSize.Width; //ratio could be greater or less than 1
double _form_ratio_height = (double)form.ClientSize.Height / (double)_formSize.Height; // this one too
var _controls = _get_all_controls(form); //reenumerate the control collection
int _pos = -1;//do not change this value unless you know what you are doing
try
{
foreach (Control control in _controls)
{
// do some math calc
_pos += 1;//increment by 1;
System.Drawing.Size _controlSize = new System.Drawing.Size((int)(_arr_control_storage[_pos].Width * _form_ratio_width),
(int)(_arr_control_storage[_pos].Height * _form_ratio_height)); //use for sizing
System.Drawing.Point _controlposition = new System.Drawing.Point((int)
(_arr_control_storage[_pos].X * _form_ratio_width), (int)(_arr_control_storage[_pos].Y * _form_ratio_height));//use for location
//set bounds
control.Bounds = new System.Drawing.Rectangle(_controlposition, _controlSize); //Put together
//Assuming you have a datagridview inside a form()
//if you want to show the row header, replace the false statement of
//showRowHeader on top/public declaration to true;
if (control.GetType() == typeof(DataGridView))
_dgv_Column_Adjust(((DataGridView)control), showRowHeader);
//Font AutoSize
control.Font = new System.Drawing.Font(form.Font.FontFamily,
(float)(((Convert.ToDouble(_fontsize) * _form_ratio_width) / 2) +
((Convert.ToDouble(_fontsize) * _form_ratio_height) / 2)));
}
}
catch(Exception e)
{
MessageBox.Show(e.Message);
return;
}
}
private void _dgv_Column_Adjust(DataGridView dgv, bool _showRowHeader) //if you have Datagridview
//and want to resize the column base on its dimension.
{
int intRowHeader = 0;
const int Hscrollbarwidth = 5;
if (_showRowHeader)
intRowHeader = dgv.RowHeadersWidth;
else
dgv.RowHeadersVisible = false;
for (int i = 0; i < dgv.ColumnCount; i++)
{
if (dgv.Dock == DockStyle.Fill) //in case the datagridview is docked
dgv.Columns[i].Width = ((dgv.Width - intRowHeader) / dgv.ColumnCount);
else
dgv.Columns[i].Width = ((dgv.Width - intRowHeader - Hscrollbarwidth) / dgv.ColumnCount);
}
}
private static IEnumerable<Control> _get_all_controls(Control c)
{
return c.Controls.Cast<Control>().SelectMany(item =>
_get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
control.Name != string.Empty);
}
}

c# How can I move a picturebox by a vScrollbar?

I have a pictureBox in c# and I have to move it by a vScrollBar.
It should be like this: (pseudo-code!)
if (scrollbar.ScrollUp)
{
int i = 0;
i += +1 per scroll
pictureBox.Location = new Point(0, i);
}
if (scrollbar.ScrollDown)
{
int k = 0;
k += -1 per scroll
pictureBox.Location = new Point(0, k);
}
I hope someone can understand my problem. Thanks
The example for the MSDN ScrollBar.Scroll event actually shows how to scroll a PictureBox.
Code from MSDN:
private void HandleScroll(Object sender, ScrollEventArgs e)
{
//Create a graphics object and draw a portion of the image in the PictureBox.
Graphics g = pictureBox1.CreateGraphics();
int xWidth = pictureBox1.Width;
int yHeight = pictureBox1.Height;
int x;
int y;
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
x = e.NewValue;
y = vScrollBar1.Value;
}
else //e.ScrollOrientation == ScrollOrientation.VerticalScroll
{
y = e.NewValue;
x = hScrollBar1.Value;
}
g.DrawImage(pictureBox1.Image,
new Rectangle(0, 0, xWidth, yHeight), //where to draw the image
new Rectangle(x, y, xWidth, yHeight), //the portion of the image to draw
GraphicsUnit.Pixel);
pictureBox1.Update();
}
Where HandleScroll is the event handler for the ScrollBar's Scroll event.
scrollBar1.Scroll += HandleScroll;
This is assuming you are trying to scroll the PictureBox. If you really just want to move it around, you could try the following:
private void HandleScroll(Object sender, ScrollEventArgs e)
{
var diff = scrollBar1.Value - e.NewValue;
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
pictureBox1.Location = new Point(diff, pictureBox1.Location.Y);
}
else //e.ScrollOrientation == ScrollOrientation.VerticalScroll
{
pictureBox1.Location = new Point(pictureBox1.Location.X, diff);
}
}
Warning, this code has not been tested.

Draw circle in new position without removing previous circle?

In fact , I want to draw circle in new position each time double-click and without remove before circle ,It should be noted that, I used PictureBox.
public Point postionCursor { get; set; }
List<Point> points = new List<Point>();
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
postionCursor = this.PointToClient(new Point(Cursor.Position.X - 25, Cursor.Position.Y - 25));
points.Add(postionCursor);
pictureBox1.Invalidate();
pictureBox1.Paint += new PaintEventHandler(pic_Paint);
}
private void pic_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
foreach (Point pt in points)
{
Pen p = new Pen(Color.Tomato, 2);
SolidBrush myb = new SolidBrush(Color.White);
g.DrawEllipse(p, postionCursor.X, postionCursor.Y, 20, 20);
g.FillEllipse(myb, postionCursor.X, postionCursor.Y, 20, 20);
p.Dispose();
}
}
You're not using the pt variable in the foreach loop.
foreach (Point pt in points)
{
using(Pen p = new Pen(Color.Tomato, 2))
using(SolidBrush myb = new SolidBrush(Color.White))
{
g.FillEllipse(myb, pt.X, pt.Y, 20, 20);
g.DrawEllipse(p, pt.X, pt.Y, 20, 20);
}
}
In your code, you were just overwriting the circle in the same location for every Point in the points list.
Also, as Reza mentioned in the comments, you don't need to attach the PaintEventHandler event hanlder every time the PictureBox is clicked, you just need to do it once.
So I got to thinking, and then Visual Studio-ing, that perhaps we don't even need the foreach loop. I still maintain a List so we know where the user has clicked, but there's no need to loop through it and redraw everything every time.
I realize this doesn't handle the case where the underlying list is modified, but nor does the original sample. Here's my entire Form1 class:
public partial class Form1 : Form
{
private const int CircleDiameter = 20;
private const int PenWidth = 2;
private readonly List<Point> _points = new List<Point>();
public Form1()
{
InitializeComponent();
pictureBox1.Paint += (sender, args) =>
{
_points.ForEach(p => DrawPoint(p, args.Graphics));
};
}
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
var cursorLocation = pictureBox1.PointToClient(Cursor.Position);
_points.Add(cursorLocation);
var circleArea = new Rectangle(
cursorLocation.X - CircleDiameter/2 - PenWidth,
cursorLocation.Y - CircleDiameter/2 - PenWidth,
CircleDiameter + PenWidth*2,
CircleDiameter + PenWidth*2);
pictureBox1.Invalidate(circleArea);
}
private static void DrawPoint(Point point, Graphics graphics)
{
point.X -= CircleDiameter / 2;
point.Y -= CircleDiameter / 2;
using (var pen = new Pen(Color.Tomato, PenWidth))
using (var brush = new SolidBrush(Color.White))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawEllipse(pen, point.X, point.Y, CircleDiameter, CircleDiameter);
graphics.FillEllipse(brush, point.X, point.Y, CircleDiameter, CircleDiameter);
}
}
}
Update 1:
So I updated the code to use the Paint event which has the foreach loop. However, I don't Invalidate (and Paint) every time a circle is added - there's no need for that. Just adding a circle by drawing means the control only invalidates and re-paints the region where the new circle was added.
Try setting a breakpoint on the DrawAllPoints method. You'll see it only happens during full invalidation operations such as minimizing and restoring.
Update 2:
After further chat, I agree the Invalidate method is superior. Code updated to use Invalidate with a rectangle to invalidate.
And now it's looking very much like the OP :)

Categories

Resources