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:
Related
I just need to show scrollbars on forms if my shape height is greater than the form height. That way, when the user scrolls down, it can show the end of the shape.
This is my code:
public partial class Form1: Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
}
private void Form1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawLine(new Pen(Color.Black, 2), 100, 50, 100, 1000);
//if line height > form height then show scroll bars
}
}
You need to enable the AutoScroll property of the form, use the AutoScrollPosition coordinates in drawings, and set the AutoScrollMinSize property to contain your shapes:
In the constructor add:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
AutoScroll = true;
}
}
and the painting routine:
private void Form1_Paint(object sender, PaintEventArgs e)
{
using (Matrix m = new Matrix(1, 0, 0, 1, AutoScrollPosition.X, AutoScrollPosition.Y))
{
var sY = VerticalScroll.Value;
var sH = ClientRectangle.Y;
var w = ClientRectangle.Width - 2 - (VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0);
var h = ClientRectangle.Height;
var paintRect = new Rectangle(0, sY, w, h); //This will be your painting rectangle.
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.Transform = m;
g.Clear(BackColor);
using (Pen pn = new Pen(Color.Black, 2))
e.Graphics.DrawLine(pn, 100, 50, 100, 1000);
sH += 1050; //Your line.y + line.height
//Likewise, you can increase the w to show the HorizontalScroll if you need that.
AutoScrollMinSize = new Size(w, sH);
}
}
Good Luck.
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);
}
}
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.
Im using WFA and C#, and im trying to draw an elipse on top of a picturebox containing an image. here is my code but currently its drawing behind of the PB.
private void Form1_Paint(object sender, PaintEventArgs e)
{
PictureBox pb=new PictureBox();
pb.Location=new Point(10,25);
pb.BackgroundImage = Image.FromFile("fire2_clipped_rev_1.png");
pb.Size = Image.FromFile("fire2_clipped_rev_1.png").Size;
Truck.TruckF(pb.Location, pb.CreateGraphics());
pb.Invalidate();
Controls.Add(pb);
}
static Image truckf = Image.FromFile("fire2_clipped_rev_1.png");
public static void TruckF(Point location, Graphics e)
{
Wheels(truckf.Size,location,e);
}
private static void Wheels(Size simage,Point location,Graphics e)
{
e.FillEllipse(Brushes.Black, location.X / 6F, location.Y / 1.43F, 20, 20);
}
You need to change to handle the Load event on your form. Also the PictureBox is a control and you need to draw an image and hand it to the PictureBox to get this to work. Try this:
private PictureBox pb;
private void Truck_Load(object sender, EventArgs e)
{
pb = new PictureBox();
pb.Location = new Point(10, 25);
Image bg = Image.FromFile("fire2_clipped_rev_1.png");
pb.BackgroundImage = bg;
pb.Size = bg.Size;
Bitmap img = new Bitmap(pb.Size.Width, pb.Size.Height);
pb.Image = img;
using (var g = Graphics.FromImage(img))
{
Truck.TruckF(pb.Location, g);
}
Controls.Add(pb);
}
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);
}
}