I have an application which uses the mouse to free-draw a rectangle on a picbox image. However the rectangle only shows up behind the picbox, rather than on top of it. Is there a property i can set which can fix this? (show rect on top of picbox image rather than behind it). Here is the code:
System.Drawing.Graphics picboxGraphics;
bool mDown = false;
int mouseX;
int mouseY;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mDown = true;
mouseX = e.X;
mouseY = e.Y;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (mDown == true)
{
this.Refresh();
Pen drawPen = new Pen(Color.Red, 5);
int width = e.X - mouseX, height = e.Y - mouseY;
Rectangle rect = new Rectangle(mouseX, mouseY, width * Math.Sign(width), height * Math.Sign(height));
picboxGraphics = this.CreateGraphics();
picboxGraphics.DrawRectangle(drawPen, rect);
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
mDown = false;
}
You creating graphics from form which is behind the picbox
you can create graphics from picbox's image and draw someting. But if you want layer system you can draw your thins on an transparent image and combine them. with this vay you can make an undo or delete layer system.
There are several problems in your code: dispose, global variable and wrong control to create graphics from. Make it this way:
using(var graphics = (sender as Control).CreateGraphics())
graphics.DrawRectangle(drawPen, rect);
But honestly, you have to organize it differently (assuming you are going to mimik Paint):
create new object (when mouse is down)
in Paint event draw object (if any)
during mouse move event, update edited object properties and call Invalidate
when you minimize/restore your form, the object will be still there (while in your example, it will get lost).
You can support List of objects to have storage for many rectangles and add history support (as TC Alper Tokcan answer suggesting) to at least Undo last object.
Related
So I am trying to recreate the Snipping Tool that comes with Windows.
However I've manage to get it to draw a rectangle from the top left corner and then when I move my cursor to the bottom right.
However if I try to move my cursor from let's say the middle of the screen to the top left it doesnt draw the rectangle.
Why is this?
Here is a GIF showing what happends
https://i.imgur.com/0Y7xXnS.gifv
//These variables control the mouse position
int selectX;
int selectY;
int selectWidth;
int selectHeight;
public Pen selectPen;
//This variable control when you start the right click
bool start;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Hide the Form
this.Hide();
//Create the Bitmap (This is an a black bitmap holding just the size.) (Bitmap is mutable)
Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
//Create a graphics object that's ready for alteration. `printscreen` is now a graphics Object
Graphics graphics = Graphics.FromImage(printscreen);
//Alter the graphics object which again is the printscreen
graphics.CopyFromScreen(0, 0, 0, 0, printscreen.Size);
//Create a temporary memory stream for the image
using (MemoryStream s = new MemoryStream())
{
//save graphic variable into memory
printscreen.Save(s, ImageFormat.Bmp);
//Set the size of the picturebox
pictureBox1.Size = new Size(Width, Height);
//set the value of the picturebox.Image to an Image.FromStream and load the temp stream
pictureBox1.Image = Image.FromStream(s);
}
//Show Form
this.Show();
//Cross Cursor
Cursor = Cursors.Cross;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
selectX = e.X;
selectY = e.Y;
selectPen = new Pen(Color.DimGray, 1);
selectPen.DashStyle = DashStyle.Solid;
pictureBox1.Refresh();
start = true;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (start)
{
selectWidth = e.X - selectX;
selectHeight = e.Y - selectY;
pictureBox1.Refresh();
pictureBox1.CreateGraphics().DrawRectangle(selectPen,
selectX, selectY, selectWidth, selectHeight);
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Stream sound = Resources.SnapshotSound;
SoundPlayer player = new SoundPlayer(sound);
player.PlaySync();
Application.Exit();
}
}
So what you end up with is basically a rectangle with a negative width and height.
You'll probably want to use the Math.Abs to make sure you get a proper width and height in the cases the difference would become negative.
With that, you will also probably want to draw from different positions based on where your cursor is in relation to the selected point. Something like
selectWidth = e.X - selectX;
selectHeight = e.Y - selectY;
// If width is less than 0, draw from pointer, otherwise from select X
var drawFromX = selectWidth < 0 ? e.X : selectX;
// If height is less than 0, draw from pointer, otherwise from select Y
var drawFromY = selectHeight < 0 ? e.Y : selectY;
pictureBox1.Refresh();
pictureBox1.CreateGraphics().DrawRectangle(selectPen,
drawFromX,
drawFromY,
Math.Abs(selectWidth), // Make sure the rectangle width is positive
Math.Abs(selectHeight)); // Make sure the rectangle height is positive
This question already has answers here:
Draw a rectangle on mouse click
(4 answers)
Closed 7 years ago.
So I've been trying lately to make a paint application to practice C#.
My problem for the past 2 days is in the creation of rectangles.
I made a panel so all the drawing are going there. The user selects the shape he wants to draw using a menu and he can start drawing with the mouse.
I encounter 2 problems which are the following:
1) Even though my starting point was inside the panel, I moved the mouse and went outside the panel and the rectangle was drawn outside the panel as shown in the picture below.
2) After I create this rectangle and I try to draw another, the previous one is deleted. So in a way I can't draw 2 rectangles at once.
Here's a part of my source code.
Graphics mygraphics;
Pen lPen = new Pen(Color.Black); //Left Pen
Pen rPen = new Pen(Color.White); //Right pen
Point sp = new Point(0, 0);
private bool isRectangle;
private bool isLeft, isRight; //isLeft -- Left Click, isRight -- Right Click
private void drawPanel_MouseMove(object sender, MouseEventArgs e)
{
if (isRectangle == true)
{
if (e.Button == MouseButtons.Left)
{
isLeft = true;
Point p = e.Location;
int x = Math.Min(sp.X, p.X);
int y = Math.Min(sp.Y, p.Y);
int w = Math.Abs(p.X - sp.X);
int h = Math.Abs(p.Y - sp.Y);
mRect = new Rectangle(x, y, w, h);
this.Invalidate();
}
}
}
private void drawPanel_MouseDown(object sender, MouseEventArgs e)
{
sp = e.Location;
}
private void drawPanel_MouseUp(object sender, MouseEventArgs e)
{
isLeft = false;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(lPen, mRect);
}
What I want to accomplish is my rectangles not getting deleted after I try to draw another one and to draw them inside the panel.
What do you guys suggest?
Here are the minimal changes I suggest:
List<Rectangle> rectangles = new List<Rectangle>();
Rectangle mRect = Rectangle.Empty;
private void drawPanel_MouseUp(object sender, MouseEventArgs e)
{
isLeft = false;
rectangles.Add(mRect);
mRect = Rectangle.Empty;
drawPanel.Invalidate();
}
private void drawPanel_Paint(object sender, PaintEventArgs e)
{
foreach (Rectangle rect in rectangles) e.Graphics.DrawRectangle(lPen, rect );
e.Graphics.DrawRectangle(Pens.Orange, mRect); // or whatever..
}
Note that the Paint event now is the one of the Panel. Do make sure you hook it up with the Panel!!
Also note how I draw the current rectangle mRect in a different color than the list of the other rectangles; this is of course optional..
I have embedded the code below in a project in which a want to paint with a brush OVER a picture. The problem is that if a write this code for a main panel , everything goes fine, but as I want to use it for an existing image, I can not see the brush. I suppose that the brush paints but it is a matter of foreground / background.
//the first line goes to the main form - under the initialize component
graphics = DisplayPicturebox.CreateGraphics();
bool draw = false;
Graphics graphics;
private void DisplayPicturebox_MouseDown(object sender, MouseEventArgs e)
{
draw = true;
}
private void DisplayPicturebox_MouseUp(object sender, MouseEventArgs e)
{
draw = false;
}
private void DisplayPicturebox_MouseMove(object sender, MouseEventArgs e)
{
if (draw)
{
//create a brush:
SolidBrush MysolidBrush = new SolidBrush(Color.Red);
graphics.FillEllipse(MysolidBrush, e.X, e.Y,
Convert.ToInt32(toolStripTextBox1.Text),
Convert.ToInt32(toolStripTextBox1.Text));
}
}
A few important things to note here:
Graphics need to be processed in a pipeline or saved in some way so that repaints don't eliminate the changes the user had made.
Keeping a graphics context open indefinitely is a bad idea. You should open the context log enough to draw what you need to either to a buffer or to the screen, then close that context.
If you want to use an offscreen buffer you need to keep in mind the coordinate system you are working in ("Screen" versus "control", versus "buffer"). If you get these confused you may not see what you expect
Those concepts in mind, you might consider the following changes to your code:
// at the beginning of your class declare an offscreen buffer
private System.Drawing.Bitmap buffer;
// .. later create it in the constructor
public Form1() {
InitializeComponent();
// use the w/h of the picture box here
buffer = new System.Drawing.Bitmap(picBox.Width, picBox.Height);
}
// in the designer add a paint event for the picture box:
private picBox_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
e.Graphics.DrawImageUnscaled(buffer);
}
// finally, your slightly modified painting routine...
private picBox__MouseMove(object sender, MouseEventArgs e)
{
if (draw)
{
using (var context = System.Drawing.Graphics.FromImage(buffer)) {
//create a brush:
SolidBrush MysolidBrush = new SolidBrush(Color.Red);
graphics.FillEllipse(MysolidBrush, e.X, e.Y,
Convert.ToInt32(toolStripTextBox1.Text),
Convert.ToInt32(toolStripTextBox1.Text));
}
}
}
This isn't meant to be perfect code but the general template should work and get you closer to what I think you are looking for. Hope that helps some!
Try this one, modify the mousemove event:
private void DisplayPicturebox_MouseMove(object sender, MouseEventArgs e)
{
if (draw)
{
graphics = DisplayPicturebox.CreateGraphics();
SolidBrush MysolidBrush = new SolidBrush(Color.Red);
float newX = (float)DisplayPicturebox.Image.Size.Width / (float)DisplayPicturebox.Size.Width;
float newY = (float)DisplayPicturebox.Image.Size.Height / (float)DisplayPicturebox.Size.Height;
graphics = Graphics.FromImage(DisplayPicturebox.Image);
graphics.FillEllipse(MysolidBrush, e.X * newX, e.Y * newY, Convert.ToInt32(toolStripTextBox1.Text), Convert.ToInt32(toolStripTextBox1.Text));
DisplayPicturebox.Refresh();
}
}
I have created a small application in C#, in which I draw a rectangle when the mouse is moved.
However, when the form is minimized or maximized, the drawing is erased. Also, when I draw a second time, the drawing of the first rectangle is erased.
How can I solve this problem? Here is the code that I currently have:
int X, Y;
Graphics G;
Rectangle Rec;
int UpX, UpY, DwX, DwY;
public Form1()
{
InitializeComponent();
//G = panel1.CreateGraphics();
//Rec = new Rectangle(X, Y, panel1.Width, panel1.Height);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
UpX = e.X;
UpY = e.Y;
//Rec = new Rectangle(e.X, e.Y, 0, 0);
//this.Invalidate();
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
DwX = e.X;
DwY = e.Y;
Rec = new Rectangle(UpX, UpY, DwX - UpX, DwY - UpY);
Graphics G = pictureBox1.CreateGraphics();
using (Pen pen = new Pen(Color.Red, 2))
{
G.DrawRectangle(pen, Rec);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// Draws the rectangle as the mouse moves
Rec = new Rectangle(UpX, UpY, e.X - UpX, e.Y - UpY);
Graphics G = pictureBox1.CreateGraphics();
using (Pen pen = new Pen(Color.Red, 2))
{
G.DrawRectangle(pen, Rec);
}
G.Save();
pictureBox1.SuspendLayout();
pictureBox1.Invalidate();
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
pictureBox1.Update();
}
The reason why your drawings are getting erased is because you're drawing into a Graphics object you obtained by calling the CreateGraphics method. In particular, this line of your code is incorrect:
Graphics G = pictureBox1.CreateGraphics();
As you've discovered, whenever the form is repainted (which happens when it is maximized, minimized, covered by another object on the screen, or in a number of other possible situations), everything that you've drawn into that temporary Graphics object is lost. The form completely repaints itself with its internal painting logic; it's completely forgotten about what you temporarily drew on top of it.
The correct way to draw persistent images in WinForms is to override the OnPaint method of the control that you want to draw onto (or, you could also handle the Paint event). So, if you wanted to paint onto your form, you would place your drawing code into the following method:
protected override void OnPaint(PaintEventArgs e)
{
// Call the base class first
base.OnPaint(e);
// Then insert your custom drawing code
Rec = new Rectangle(UpX, UpY, DwX - UpX, DwY - UpY);
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, Rec);
}
}
And to trigger a re-paint, you just call the Invalidate method in any of the mouse events (MouseDown, MouseMove, and MouseUp):
this.Invalidate();
Note, however, that there's absolutely no reason to call the Update method in the Paint event handler. All that calling the Update method does is force the control to repaint itself. But that is already happening when the Paint event gets raised!
I want to draw a rectangle. Thing what I want is that show the user to rectangle on the mouse event.
Like in the image. This is for C# .net Forms application.
Help me to achieve this. Any help is appreciated.
Thank You
Yohan
You can do that in three steps:
First check if mouse is pressed down
If it is then on mouse move event keep initializing the rectangle with new positions while mouse is being dragged
Then on paint event draw the rectangle. (It will be raised for almost every mouse event, depends mouse refresh rate and dpi)
You can do somthing like this (in your Form):
public class Form1
{
Rectangle mRect;
public Form1()
{
InitializeComponents();
//Improves prformance and reduces flickering
this.DoubleBuffered = true;
}
//Initiate rectangle with mouse down event
protected override void OnMouseDown(MouseEventArgs e)
{
mRect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
//check if mouse is down and being draged, then draw rectangle
protected override void OnMouseMove(MouseEventArgs e)
{
if( e.Button == MouseButtons.Left)
{
mRect = new Rectangle(mRect.Left, mRect.Top, e.X - mRect.Left, e.Y - mRect.Top);
this.Invalidate();
}
}
//draw the rectangle on paint event
protected override void OnPaint(PaintEventArgs e)
{
//Draw a rectangle with 2pixel wide line
using(Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, mRect);
}
}
}
later if you want to check if Buttons (shown in diagram) are in rectangle or not , you can do that by checking the Button's region and check if they lie in your drawn rectangle.
The solution by Shekhar_Pro draws a rectangle just in one direction (top to bottom, left to right) if you want to draw a rectangle regardless of the mouse position and the direction of the movement the solution is:
Point selPoint;
Rectangle mRect;
void OnMouseDown(object sender, MouseEventArgs e)
{
selPoint = e.Location;
// add it to AutoScrollPosition if your control is scrollable
}
void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point p = e.Location;
int x = Math.Min(selPoint.X, p.X)
int y = Math.Min(selPoint.Y, p.Y)
int w = Math.Abs(p.X - selPoint.X);
int h = Math.Abs(p.Y - selPoint.Y);
mRect = new Rectangle(x, y, w, h);
this.Invalidate();
}
}
void OnPaint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Blue, mRect);
}
Those blue rectangles look a lot like controls. Drawing a line on top of a control is hard to do in Winforms. You have to create a transparent window that overlays the design surface and draw the rectangle on that window. This is also the way the Winforms designer works. Sample code is here.