Hi I would do the selected area to cut an image on picturebox control.
I have the following code:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Rectangle rect;
private Pen p;
public Form1()
{
InitializeComponent();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (this.p == null)
this.p = new Pen(Color.FromArgb(100, 200, 200, 200), 5);
if (this.rect.Width > 0)
e.Graphics.DrawRectangle(this.p, this.rect);
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (e.X < this.rect.X)
{
this.rect.Width = this.rect.X - e.X;
this.rect.X = e.X;
}
else
{
this.rect.Width = e.X - this.rect.X;
}
if (e.Y < this.rect.Y)
{
this.rect.Height = this.rect.Y - e.Y;
this.rect.Y = e.Y;
}
else
{
this.rect.Height = e.Y - this.rect.Y;
}
this.Invalidate(this.rect);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
this.rect.X = e.X;
this.rect.Y = e.Y;
}
}
}
It returns an error here:
Application.Run(new Form1());
Why?
thanks for all replies ;p
You shouldn't dispose of the Graphics object that is passed as part of the PaintEventArgs. That is probably what is causing your issue.
Try using this optimized code, if you still get error post it here (edit your original question) and we'll see.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (this.p == nulll)
this.p = new Pen(Color.FromArgb(100, 200, 200, 200), 5);
if (this.rect.Width > 0)
e.Graphics.DrawRectangle(this.p, this.rect);
}
what is the error?
you are leaking Pen's. For every paint message you create a new pen and throw the old without disposing.
Off the top of my head I can't remember if you should dispose of the graphics object you get from the event args
Related
How do I check if a mouse clicked a rectangle?
Graphics gfx;
Rectangle hitbox;
hitbox = new hitbox(50,50,10,10);
//TIMER AT THE BOTTOM
gfx.Draw(System.Drawing.Pens.Black,hitbox);
Just a sample quick and dirty, if your "gfx" is a "e.Graphics..." from a Form:
public partial class Form1 : Form
{
private readonly Rectangle hitbox = new Rectangle(50, 50, 10, 10);
private readonly Pen pen = new Pen(Brushes.Black);
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(pen, hitbox);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if ((e.X > hitbox.X) && (e.X < hitbox.X + hitbox.Width) &&
(e.Y > hitbox.Y) && (e.Y < hitbox.Y + hitbox.Height))
{
Text = "HIT";
}
else
{
Text = "NO";
}
}
}
Rectangle has several handy but often overlooked functions. In this case using the Rectangle.Contains(Point) function is the best solution:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (hitbox.Contains(e.Location)) .. // clicked inside
}
To determine if you clicked on the outline you will want to decide on a width, since the user can't easily hit a single pixel.
For this you can use either GraphicsPath.IsOutlineVisible(Point)..
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
gp.AddRectanle(hitbox);
using (Pen pen = new Pen(Color.Black, 2f))
if (gp.IsOutlineVisible(e.location), pen) .. // clicked on outline
}
..or stick to rectangles..:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Rectangle inner = hitbox;
Rectangle outer = hitbox;
inner.Inflate(-1, -1); // a two pixel
outer.Inflate(1, 1); // ..outline
if (outer.Contains(e.Location) && !innerContains(e.Location)) .. // clicked on outline
}
I am trying to set the starting point when the LMB is down and draw a line from the starting point to the current mouse position when the LMB is up, much like how MSPaint does it.
My problem is that I can't seem to get the line to appear on the picturebox when LMB is up. Can someone enlighten me please?
Edit:Sorry guys I realised the problem was elsewhere, but I learned a bunch of stuff in the process, thanks for all the input.
public partial class FormPaint : Form
{
Point? startPoint = Point.Empty;
Point? endPoint = Point.Empty;
bool isMouseDown = new Boolean();
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (Control.MouseButtons == MouseButtons.Left)
{
startPoint = e.Location;
isMouseDown = true;
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
brush = new SolidBrush(color);
using (Graphics g = Graphics.FromImage(pictureBox1.Image))
{
g.DrawLine(new Pen(brush), startPoint.Value, endPoint.Value);
pictureBox1.Invalidate();
}
isMouseDown = false;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
endPoint = e.Location;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (brush = new SolidBrush(color))
{
e.Graphics.DrawLine(new Pen(brush, 5), startPoint.Value, endPoint.Value);
}
}
}
When you call Invalidate it forces the picture box to repaint. The problem is though that it discards everything you painted before. Then it calls the Paint on the picture box.
I would suggest to save the drawing data into a list and perform the painting inside the Paint event of the picture box using that saved data.
Also read How do I draw a circle and line in the picturebox?
Complete sample with line draw preview, enjoy.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinForm
{
public partial class frmMain : Form
{
/// <summary>
/// form constructor
/// </summary>
public frmMain()
{
InitializeComponent();
}
private PictureBox imgCanvas;
private bool isMouseDown;
private Point startPoint;
private Point currentPoint;
/// <summary>
/// form load
/// </summary>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
imgCanvas = new PictureBox
{
Location = new Point(8, 8),
Size = new Size(this.ClientSize.Width - 16, this.ClientSize.Height - 16),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom,
BorderStyle = BorderStyle.Fixed3D,
};
imgCanvas.MouseDown += imgCanvas_MouseDown;
imgCanvas.MouseUp += imgCanvas_MouseUp;
imgCanvas.MouseMove += imgCanvas_MouseMove;
imgCanvas.Paint += imgCanvas_Paint;
this.Controls.Add(imgCanvas);
}
void imgCanvas_Paint(object sender, PaintEventArgs e)
{
if (isMouseDown)
{
e.Graphics.DrawLine(Pens.Red, startPoint, currentPoint);
}
}
void imgCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown)
{
currentPoint = e.Location;
(sender as PictureBox).Refresh();
}
}
void imgCanvas_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
isMouseDown = true;
startPoint = e.Location;
}
}
void imgCanvas_MouseUp(object sender, MouseEventArgs e)
{
if (isMouseDown)
{
if (e.Button == MouseButtons.Left)
{
isMouseDown = false;
PictureBox pb = sender as PictureBox;
// create image
if (pb.Image == null)
{
pb.Image = new Bitmap(pb.ClientSize.Width, pb.ClientSize.Height);
}
// draw
using (Graphics g = Graphics.FromImage(pb.Image))
{
g.DrawLine(Pens.Green, startPoint, e.Location);
pb.Refresh();
}
}
}
}
}
}
Result:
I'm trying to make a simple application where the user can draw on the Panel and save it to their computer as a bitmap. When I proceed to the save part, however, all I get is an empty (white) bitmap.
I've been browsing many other solutions and I am pretty sure I am saving the bitmap the correct way, so I am starting to wonder if my drawing process is incorrect. What exactly is wrong here?
public partial class Form1 : Form
{
SolidBrush brush;
Pen pen;
Point[] points = new Point[3];
Graphics display;
Bitmap bmap;
public Form1()
{
InitializeComponent();
display = panel1.CreateGraphics();
bmap = new Bitmap(panel1.Width, panel1.Height);
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
brush = new SolidBrush(Color.Black);
pen = new Pen(Color.Black);
display.FillEllipse(brush, e.X, e.Y, 10, 10);
panel1.DrawToBitmap(bmap, new Rectangle(0, 0, panel1.Width, panel1.Height));
//this.Invalidate();
}
private void clearToolStripMenuItem_Click(object sender, EventArgs e)
{
Graphics display = panel1.CreateGraphics();
display.Clear(panel1.BackColor);
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
bmap.Save(#"C:\Temp\Test.bmp");
}
}
EDIT
With this revision, I just get a black bmp and I don't even see elipses being created anymore on my screen. Although I did notice that if I put invalidate and Draw to bitmap back in the mousedown event, then the save button will save the last ellipse, while there is still nothing appearing on my screen.
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
mousedown = true;
x = e.X;
y = e.Y;
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
//Graphics g = e.Graphics;
if(mousedown==true)
{
brush = new SolidBrush(Color.Black);
pen = new Pen(Color.Black);
Graphics.FromImage(bmap).FillEllipse(brush, x, y, 10, 10);
panel1.Invalidate();
//panel1.DrawToBitmap(bmap, new Rectangle(0, 0, panel1.Width, panel1.Height));
//panel1.Invalidate();
}
}
As Hans did most of the work in his comment, here is how your code should probably look:
public partial class Form1 : Form {
Bitmap bmap;
public Form1() {
InitializeComponent();
bmap = new Bitmap(panel1.ClientWidth, panel1.ClientHeight);
panel1.MouseDown += panel1_MouseDown;
panel1.Paint += panel1_Paint;
}
void panel1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawImage(bmap, Point.Empty);
}
void panel1_MouseDown(object sender, MouseEventArgs e) {
using (Graphics g = Graphics.FromImage(bmap)) {
g.FillEllipse(Brushes.Black, e.X, e.Y, 10, 10);
}
panel1.Invalidate();
}
private void clearToolStripMenuItem_Click(object sender, EventArgs e) {
using (Graphics g = Graphics.FromImage(bmap)) {
g.Clear(Color.White);
}
panel1.Invalidate();
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e) {
bmap.Save(#"c:\temp\bmap.bmp");
}
}
CreateGraphics is just a temporary canvas, so you rarely, if ever, use that for drawing purposes, especially since you are trying to save an image.
This will works fine. I tested it and worked well
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace drawing
{
public partial class Form2 : Form
{
Graphics g;
bool startPaint = false;
int? initX = null;
int? initY = null;
bool drawSquare = false;
bool drawRectangle = false;
bool drawCircle = false;
public Form2()
{
InitializeComponent();
bmp = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Height);
}
Bitmap bmp;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
}
void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (startPaint)
{
using ( g = Graphics.FromImage(bmp))
{
// g.FillEllipse(Brushes.Black, new Rectangle(e.X, e.Y , 5, 5));
Pen p = new Pen(btn_PenColor.BackColor, float.Parse(cmb_PenSize.Text));
g.DrawLine(p, new Point(initX ?? e.X, initY ?? e.Y), new Point(e.X, e.Y));
initX = e.X;
initY = e.Y;
//g.DrawImage(bmp, new Rectangle(e.X - 4, e.Y - 4, 8, 8));
}
panel1.Invalidate();
}
}
private void pnl_Draw_MouseDown(object sender, MouseEventArgs e)
{
startPaint = true;
if (drawSquare)
{
//Use Solid Brush for filling the graphic shapes
SolidBrush sb = new SolidBrush(btn_PenColor.BackColor);
//setting the width and height same for creating square.
//Getting the width and Heigt value from Textbox(txt_ShapeSize)
g.FillRectangle(sb, e.X, e.Y, int.Parse(txt_ShapeSize.Text), int.Parse(txt_ShapeSize.Text));
//setting startPaint and drawSquare value to false for creating one graphic on one click.
startPaint = false;
drawSquare = false;
}
if (drawRectangle)
{
SolidBrush sb = new SolidBrush(btn_PenColor.BackColor);
//setting the width twice of the height
g.FillRectangle(sb, e.X, e.Y, 2 * int.Parse(txt_ShapeSize.Text), int.Parse(txt_ShapeSize.Text));
startPaint = false;
drawRectangle = false;
}
if (drawCircle)
{
SolidBrush sb = new SolidBrush(btn_PenColor.BackColor);
g.FillEllipse(sb, e.X, e.Y, int.Parse(txt_ShapeSize.Text), int.Parse(txt_ShapeSize.Text));
startPaint = false;
drawCircle = false;
}
}
private void pnl_Draw_MouseUp(object sender, MouseEventArgs e)
{
startPaint = false;
initX = null;
initY = null;
}
void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(bmp, Point.Empty);
}
private void button1_Click(object sender, EventArgs e)
{
bmp.Save("D://filename.jpg", ImageFormat.Png);
}
}
}
I have loaded an image in my panel.I want to erase the parts of that image by using the mouse(dragging on the panel).Here is my code to load my image:
private void drawP_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(myImage, new Point(0, 0));
}
How can I do it?
Thanks in advance.
Updated:
sorry not to say earlier,I have set another image(image2) as background of the panel and I want it to be seen after erasing myImage(image loaded with the code above).
Hi I'm going to assume that you want this feature to work like the eraser on paint.
there are 3 events you are going to need
1.mousedown - to call the first erase and open up the mousemove event method.
2.mouseup - to stop the mousemove event method
3.mousemove - just to call the erase method
Code: //part pseudo as im not in visual studio right now :(
//global vars
bool enable = false;
void erase(Point mousepoint)
{
Point f = (mousepoint.X - yourpanel.left?, mousepoint.Y - yourpanel.top?);
//gets mouse position on accual picture;
yourImageGraphics.fillreactangle( f.X - 10, f.Y+10, 20,20 ,Color.White)
// int X , int Y, width , height, color
}
void mousedown(?)
{
enable=true;
erase(Cursor.Position //but you get this from e?);
}
void mouseup(?);
{
enable=false;
}
void mousemove(?)
{
if (enable)
erase(e.Position?);
}
Also it looks like you are going to have to make a graphics object for your panel :(
I hope this helps because question was a bit vague.
Here I created simple example. Of course it can be done better, but I just wondering how to do it... so sharing my results.
public partial class mainForm : Form
{
private Bitmap image;
private Rectangle imgRect;
public mainForm()
{
InitializeComponent();
BackColor = Color.Chartreuse;
image = new Bitmap(#"C:\image.jpg");
imgRect = new Rectangle(0,0,image.Width, image.Height);
}
private void main_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(image, 0, 0);
}
private void main_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Left && e.X < image.Width && e.Y < image.Height)
{
image.SetPixel(e.X, e.Y, Color.Magenta);//change pixel color;
image.MakeTransparent(Color.Magenta);//Make image transparent
Invalidate(imgRect);
}
}
}
...and lets test
Ha! scared that I deleted his eye :)
A TextureBrush on a pen can be used for erasing.
Working example (image1 and image2 are the same size images):
Bitmap bmp1;
TextureBrush tb;
Point _LastPoint;
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
bmp1 = new Bitmap(#"c:\image1.png");
tb = new TextureBrush(new Bitmap(#"c:\image2.png"));
}
private void Form1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if (!_LastPoint.IsEmpty) {
using (Graphics g = Graphics.FromImage(bmp1))
using (Pen p = new Pen(tb, 15)) {
p.StartCap = LineCap.Round;
p.EndCap = LineCap.Round;
g.DrawLine(p, _LastPoint, e.Location);
}
}
_LastPoint = e.Location;
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e) {
_LastPoint = Point.Empty;
}
private void Form1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawImage(bmp1, new Point(0, 0));
}
I am working on kind of drawing program but I have a problem with flickering while moving a mouse cursor while drawing a rubberband line. I hope you can help me to remove that flickering of line, here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace GraphicsTest
{
public partial class Form1 : Form
{
int xFirst, yFirst;
Bitmap bm = new Bitmap(1000, 1000);
Graphics bmG;
Graphics xG;
Pen pen = new Pen(Color.Black, 1);
bool draw = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
bmG = Graphics.FromImage(bm);
xG = this.CreateGraphics();
bmG.Clear(Color.White);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
xFirst = e.X;
yFirst = e.Y;
draw = true;
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
draw = false;
xG.DrawImage(bm, 0, 0);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (draw)
{
xG.DrawImage(bm, 0, 0);
xG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
xG.DrawImage(bm, 0, 0);
}
}
}
First don't use CreateGraphics() unless you absolutely have to. Bind an event handler to OnPaint and call Invalidate() when you want to refresh the surface.
If you don't want it to flicker you'll need to double buffer your drawing surface. The easiest way to do this is to set your form's DoubleBuffered property to True.
I would highly recommend if you plan on extending this to do your drawing to the PictureBox control. PictureBox is double-buffered by default and allows you to control your drawing region much more simply.
In code:
public partial class Form1 : Form
{
int xFirst, yFirst;
Bitmap bm = new Bitmap(1000, 1000);
Graphics bmG;
Pen pen = new Pen(Color.Black, 1);
bool draw = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
bmG = Graphics.FromImage(bm);
bmG.Clear(Color.White);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
xFirst = e.X;
yFirst = e.Y;
draw = true;
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
draw = false;
Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (draw)
{
Invalidate();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (draw) {
e.Graphics.DrawImage(bm, 0, 0);
e.Graphics.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
} else {
e.Graphics.DrawImage(bm, 0, 0);
}
}
}
Edit:
Another issue, you are creating a private Pen member. Pens (and Brushes, as well as many GDI+ objects) represent handles to unmanaged objects that need to be disposed otherwise your program will leak. Either wrap them in using statements (the preferred and exception-safe way) or explicitly dispose of them in the form's Dispose method.
Alternatively in System.Drawing you can access some pre-built Pens and Brushes that don't need to be (and shouldn't be) disposed. Use them like:
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (draw) {
e.Graphics.DrawImage(bm, 0, 0);
e.Graphics.DrawLine(Pens.Black, xFirst, yFirst, e.X, e.Y);
} else {
e.Graphics.DrawImage(bm, 0, 0);
}
}
The reason it is flickering is that you are drawing the background (which is immediately displayed on screen, wiping away the line) and then superimpose the line. Thus the line keeps disappearing and appearing, giving a flickering display.
The best solution to this is called Double Buffering. What you do is draw the whole image to an "offscreen" bitmap, and only show it on the screen when it is completed. Because you only ever display the completed image, there is no flickering effect. You should just be able to set this.DoubleBuffered = true to get WinForms to do all the hard work for you.
NB: You shouldn't really be drawing outside of your paint handler - ideally, you should Invalidate() the area that needs redrawing, and then your paint handler will redraw just that area (with any overlaid lines etc as needed).
Fixed and working code.
public partial class Form1 : Form
{
int x1, y1, x2, y2;
bool drag = false;
Bitmap bm = new Bitmap(1000, 1000);
Graphics bmg;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
bmg = Graphics.FromImage(bm);
}
private void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
drag = true;
x1 = e.X;
y1 = e.Y;
}
private void pictureBox_MouseUp(object sender, MouseEventArgs e)
{
drag = false;
bmg.DrawLine(Pens.Black, x1, y1, e.X, e.Y);
pictureBox.Invalidate();
}
private void pictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (drag)
{
x2 = e.X;
y2 = e.Y;
pictureBox.Invalidate();
}
}
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
if (drag) {
e.Graphics.DrawImage(bm, 0, 0);
e.Graphics.DrawLine(Pens.Black, x1, y1, x2, y2);
}
else {
e.Graphics.DrawImage(bm, 0, 0);
}
}
}
I Use this to manage the double buffering into a panel:
myPanel.GetType().GetMethod("SetStyle",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).Invoke(myPanel,
new object[]
{
System.Windows.Forms.ControlStyles.UserPaint |
System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
System.Windows.Forms.ControlStyles.DoubleBuffer, true
});