Draw on Panel, save as Bitmap - c#

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);
}
}
}

Related

How to copy from screen 9 different Images using C#?

I'm doing a school project using C# in which I designed a Form in C# that I can draw on and now I need to copy what I draw into a 9 different Images and save them. So far, I tried using the copy from screen function like that:
Size s = this.Size;
Bitmap memoryImage = new Bitmap(s.Width, s.Height, this.CreateGraphics());
Graphics.FromImage(memoryImage).CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, s);
memoryImage.Save(#"C:\Users\omerm\Desktop\projectFinals\ProjectFiles\C-Sharp\Drawing\WinFormsApp1\Digits\1.jpg");
My problem is that the image I get from this code includes parts of my screen that aren't part of my Form. And I also need to copy 9 different images who are fractions of this one instead of just the one and make sure those 9 are around the same shape(squares). does anyone know how to do that?
I'm terrible at explaining this so think that I need to take the original image and cut into 9 like in a tic tac toe board and save each one of them as a different image in the shape of squares instead of the original.
full code:
namespace WinFormsApp1
{
public partial class Form1 : Form
{
Pen Pen = new Pen(Color.Black, 2);
int pX, pY;
Graphics g;
public object MemoryImage { get; private set; }
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
pX = e.X;
pY = e.Y;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Graphics g = pictureBox1.CreateGraphics();
g.DrawLine(Pen, pX, pY, e.X, e.Y);
pX = e.X;
pY = e.Y;
}
}
private void button1_Click(object sender, EventArgs e)
{
Size s = this.Size;
Bitmap memoryImage = new Bitmap(s.Width, s.Height, this.CreateGraphics());
Graphics.FromImage(memoryImage).CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, s);
memoryImage.Save(#"C:\Users\omerm\Desktop\projectFinals\ProjectFiles\C-Sharp\Drawing\WinFormsApp1\Digits\1.jpg");
}
private void ChooseColor(object sender, EventArgs e)
{
Pen.Color = ((PictureBox)sender).BackColor;
}
}
}
Tried looking online but what I look for is really specific.
Here's an example of persisting the data in a structure and drawing it in the Paint() event. Additionally, I've demonstrated how to tell the PictureBox to draw itself instead of copying the screen:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private List<Point> curSquiggle;
private List<List<Point>> Squiggles = new List<List<Point>>();
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
curSquiggle = new List<Point>();
curSquiggle.Add(e.Location);
Squiggles.Add(curSquiggle);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
curSquiggle.Add(e.Location);
pictureBox1.Invalidate();
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach(List<Point> squiggle in Squiggles)
{
e.Graphics.DrawLines(Pens.Black, squiggle.ToArray());
}
}
private void button1_Click_1(object sender, EventArgs e)
{
Bitmap bmp = new Bitmap(pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height);
pictureBox1.DrawToBitmap(bmp, pictureBox1.ClientRectangle);
// Do something with the bmp, like display it in another form:
Form frm = new Form();
frm.AutoSize = true;
PictureBox pb = new PictureBox();
pb.SizeMode = PictureBoxSizeMode.AutoSize;
pb.Image = bmp;
pb.Location = new Point(0, 0);
frm.Controls.Add(pb);
frm.Show();
}
}
Example run:
Here's a different version of the save code which splits the main picture into 9 pieces:
private void button1_Click_1(object sender, EventArgs e)
{
Bitmap bmp = new Bitmap(pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height);
pictureBox1.DrawToBitmap(bmp, pictureBox1.ClientRectangle);
int cols = 3;
int rows = 3;
int cellWidth = bmp.Width / cols;
int cellHeight = bmp.Height / rows;
Rectangle destRect = new Rectangle(0, 0, cellWidth, cellHeight);
List<Bitmap> cells = new List<Bitmap>();
for(int r=0; r<rows; r++)
{
for(int c=0; c<cols; c++)
{
Bitmap cell = new Bitmap(cellWidth, cellHeight);
Rectangle srcRect = new Rectangle(c * cellWidth, r * cellHeight, cellWidth, cellHeight);
using (Graphics g = Graphics.FromImage(cell))
{
g.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel);
}
cells.Add(cell);
}
}
foreach(Bitmap cell in cells)
{
Form frm = new Form();
frm.AutoSize = true;
frm.AutoSizeMode = AutoSizeMode.GrowAndShrink;
PictureBox pb = new PictureBox();
pb.SizeMode = PictureBoxSizeMode.AutoSize;
pb.Image = cell;
pb.Location = new Point(0, 0);
frm.Controls.Add(pb);
frm.Show();
}
}
Splitting into nine pieces:

Keep Picturebox Image While Painting on top of it

In my form, I have a picturebox. I wanted to be able to draw arrows on top of an image. I managed to get halfway there. On the Form load event, I assign an image to the picturebox. I'm able to create arrows with the code below. The problem is every time I create the arrow the picture I assigned on my form load event gets erased. Why does my image get erased? How do I maintain my image that I assigned on form load while drawing arrows on top of it?
private bool isMoving = false;
private Point mouseDownPosition = Point.Empty;
private Point mouseMovePosition = Point.Empty;
private List<Tuple<Point, Point>> lines = new List<Tuple<Point, Point>>();
Pen _Pen;
private void Test_Load(object sender, EventArgs e)
{
pictureBox1.Image = Properties.Resources.background;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (isMoving)
{
if (pictureBox1.Image == null)
{
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.White);
}
pictureBox1.Image = bmp;
}
using (Graphics g = Graphics.FromImage(pictureBox1.Image))
{
g.Clear(pictureBox1.BackColor);
AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
_Pen = new Pen(Color.IndianRed, 3);
_Pen.CustomEndCap = bigArrow;
g.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
_Pen.Dispose();
}
}
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
isMoving = true;
mouseDownPosition = e.Location;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMoving)
{
mouseMovePosition = e.Location;
pictureBox1.Invalidate();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (isMoving)
{
lines.Add(Tuple.Create(mouseDownPosition, mouseMovePosition));
}
isMoving = false;
}
I think the problem is with this line here g.Clear(pictureBox1.BackColor);
Yes, problem is with this line here g.Clear(pictureBox1.BackColor); You erase entire the control area before you draw the line.
You should draw to e.Graphics directly:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (isMoving)
{
if (pictureBox1.Image == null) e.Graphics.Clear(Color.White);
// Add this line for high quality drawing:
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
_Pen = new Pen(Color.IndianRed, 3);
_Pen.CustomEndCap = bigArrow;
e.Graphics.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
_Pen.Dispose();
}
}

Saving bitmap in a paint program

I am working on a MS paint like program that is programmed entirely in c#. It's very basic, but I have stumbled upon a problem. So I saw another SO post regarding MS paint mock ups. It was about how to save the end result as a .bmp file. I tried using the solutions and answers provided and it worked.
The file saved. However when it saved, it only saved the blank panel ( im making a forms app) . I have only seen one SO post that deals with this issue but I couldn't incorporate to allow the user to interact. The following is my code:
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 Paint
{
public partial class Form1 : Form
{
Graphics g;
Pen p = new Pen(Color.Black, 1);
Point sp = new Point(0, 0);
Point ep = new Point(0, 0);
int k = 0;
public Form1()
{
InitializeComponent();
}
private void pictureBox2_Click(object sender, EventArgs e)
{
p.Color = red.BackColor;
default1.BackColor = red.BackColor;
}
private void blue_Click(object sender, EventArgs e)
{
p.Color = blue.BackColor;
default1.BackColor = blue.BackColor;
}
private void green_Click(object sender, EventArgs e)
{
p.Color = green.BackColor;
default1.BackColor = green.BackColor;
}
private void panel2_MouseDown(object sender, MouseEventArgs e)
{
sp = e.Location;
if (e.Button == MouseButtons.Left)
k = 1;
}
private void panel2_MouseUp(object sender, MouseEventArgs e)
{
k = 0;
}
private void panel2_MouseMove(object sender, MouseEventArgs e)
{
if (k == 1)
{
ep = e.Location;
g = panel2.CreateGraphics();
g.DrawLine(p, sp, ep);
}
sp = ep;
}
private void panel2_MouseLeave(object sender, EventArgs e)
{
k = 0; }
private void panel2_Paint(object sender, PaintEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
SaveFileDialog dialog = new SaveFileDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
int width = Convert.ToInt32(panel2.Width);
int height = Convert.ToInt32(panel2.Height);
Bitmap bmp = new Bitmap(width, height);
panel2.DrawToBitmap(bmp, new Rectangle(0, 0, width, height));
bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
}
}
}
}
So my question is... How do I Succesfully save a .bmp image in my c# forms app , as in how do i not make it save blank. Thanks in advance :)
edit
So I have tried the first answer and also im trying the ideas suggested by the individual in the comments and some how, instead of just saving a blank canvas. the application literally just saves a black image. Here is the code I ended up with. Where did I go wrong?
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 Paint
{
public partial class Form1 : Form
{
Graphics g;
Graphics h;
Pen p = new Pen(Color.Black, 1);
Point sp = new Point(0, 0);
Point ep = new Point(0, 0);
int k = 0;
Bitmap bmp =null;
public Form1()
{
InitializeComponent();
}
private void pictureBox2_Click(object sender, EventArgs e)
{
p.Color = red.BackColor;
default1.BackColor = red.BackColor;
}
private void blue_Click(object sender, EventArgs e)
{
p.Color = blue.BackColor;
default1.BackColor = blue.BackColor;
}
private void green_Click(object sender, EventArgs e)
{
p.Color = green.BackColor;
default1.BackColor = green.BackColor;
}
private void panel2_MouseDown(object sender, MouseEventArgs e)
{
sp = e.Location;
if (e.Button == MouseButtons.Left)
k = 1;
}
private void panel2_MouseUp(object sender, MouseEventArgs e)
{
k = 0;
}
private void panel2_MouseMove(object sender, MouseEventArgs e)
{
if (k == 1)
{
ep = e.Location;
int width = Convert.ToInt32(panel2.Width);
int height = Convert.ToInt32(panel2.Height);
Bitmap bmp = new Bitmap(width, height);
g = Graphics.FromImage(bmp);
g.DrawLine(p, sp, ep);
h = panel2.CreateGraphics();
h.DrawLine(p, sp, ep);
}
sp = ep;
}
private void panel2_MouseLeave(object sender, EventArgs e)
{
k = 0; }
private void panel2_Paint(object sender, PaintEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
SaveFileDialog dialog = new SaveFileDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
/*
Bitmap bmp = Bitmap.FromHbitmap(panel2.CreateGraphics().GetHdc());
panel2.DrawToBitmap(bmp, new Rectangle(0, 0, width, height));*/
int width = panel2.Width;
int height = Convert.ToInt32(panel2.Height);
if (bmp == null)
bmp = new Bitmap(width, height);
bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
}
Here is a revised version, pretty much what I told you in the comments..:
public partial class Form2 : Form
{
Graphics g;
Graphics h;
Pen p = new Pen(Color.Black, 1);
Point sp = new Point(0, 0);
Point ep = new Point(0, 0);
int k = 0;
Bitmap bmp =null;
public Form2()
{
InitializeComponent();
bmp = new Bitmap(panel2.ClientSize.Width, panel2.ClientSize.Height);
g = Graphics.FromImage(bmp);
g.Clear(panel2.BackColor);
}
private void pictureBox2_Click(object sender, EventArgs e)
{
p.Color = red.BackColor;
default1.BackColor = red.BackColor;
}
private void color_Click(object sender, EventArgs e)
{
Control ctl = sender as Control;
p.Color = ctl.BackColor;
default1.BackColor = ctl.BackColor;
}
private void panel2_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ep = e.Location;
g.DrawLine(p, sp, ep);
h = panel2.CreateGraphics();
h.DrawLine(p, sp, ep);
}
sp = ep;
}
private void panel2_MouseDown(object sender, MouseEventArgs e)
{
sp = e.Location;
}
private void button1_Click(object sender, EventArgs e)
{
SaveFileDialog dialog = new SaveFileDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
A few notes:
I mapped all the clicks of the palette control into one.
I have eliminated the flag k by doing the button test in the move.
I have kept the cached Graphics g; this is usually not recommended, but as we keep drawing into one and the same bitmap is is ok.
I have remove all duplicate declaration of the bitmap bmp.
I don't know what the picturebox does, so I didn't touch the code.
Drawbacks of the soution:
Since you don''t keep track of all the moves you can't do a good undo.
Since all lines are drawn separately you can't get good results with broader and/or semi-transparent Pens because the overlapping endpoints will look bad.
For a better solution of simple doodeling see here and after you have studied it you can tackle the even better solution, which will allow you to use all sort of drawing tools here..
Use Graphics.GetHdc Method and save it like this:
Bitmap bitMap = Bitmap.FromHbitmap(g.GetHdc());
bitMap.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Bmp);

Partial screenshot with rectangle of selection c#

I'm trying to take a partial screenshot of the screen and put it in a picturebox. The user has to draw a rectangle and then i have to get the portion inside the rectangle.
protected override void OnMouseDown(MouseEventArgs e)
{
mRect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
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();
}
}
protected override void OnPaint(PaintEventArgs e)
{
using(Pen pen = new Pen(Color.Red, 1))
{
e.Graphics.DrawRectangle(pen, mRect);
}
}
With this code i can draw a recangle, but i don't know how to do the screenshot. Maybe with the fonction CopyToScreen?
If someone have an idea?
Thanks!
Here is some code I use - it grabs the whole desktop
public static Bitmap GetScreenAsBmp()
{
int min_x=0, max_x=0, min_y=0, max_y=0;
foreach (Screen s in Screen.AllScreens)
{
min_x = System.Math.Min(min_x, s.Bounds.Left);
max_x = System.Math.Max(max_x, s.Bounds.Right);
min_y = System.Math.Min(min_y, s.Bounds.Top);
max_y = System.Math.Max(max_y, s.Bounds.Bottom);
}
Bitmap bmp = new Bitmap(max_x - min_x, max_y - min_y);
Graphics dst_dc = Graphics.FromImage(bmp);
dst_dc.CopyFromScreen(min_x, min_y, 0, 0, new Size(max_x - min_x, max_y - min_y));
dst_dc.Dispose();
return bmp;
}
This is a complete solution for the problem.
It is triggered by a Button and then:
creates and styles a second Form which
is transparent without borders etc and fills the screen
has all events needed to draw a Rectangle
copies the screen after the rectangle is drawn, i.e. after the MouseUp
and copies the Bitmap into the Image of a PictureBox on the 1st Form.
Finally it closes the 2nd form. During the screencopy process both windows are hidden, so you can grab the screen itself with all its content.
Note that many video overlays can't be captured this way!
Note that this code always captures from the 1st screen!
For best usability the users should somehow be informed that he is expected to draw the frame after the Button is pressed and the window has disappeared!
To use it simply add a Button and a PictureBox to your Form..
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Form form2;
Point MD = Point.Empty;
Rectangle rect = Rectangle.Empty;
private void button2_Click(object sender, EventArgs e)
{
Hide();
form2 = new Form();
form2.BackColor = Color.Wheat;
form2.TransparencyKey = form2.BackColor;
form2.ControlBox = false;
form2.MaximizeBox = false;
form2.MinimizeBox = false;
form2.FormBorderStyle = FormBorderStyle.None;
form2.WindowState = FormWindowState.Maximized;
form2.MouseDown += form2_MouseDown;
form2.MouseMove += form2_MouseMove;
form2.Paint += form2_Paint;
form2.MouseUp += form2_MouseUp;
form2.Show();
}
void form2_MouseDown(object sender, MouseEventArgs e)
{
MD = e.Location;
}
void form2_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button !=MouseButtons.Left) return;
Point MM = e.Location;
rect = new Rectangle(Math.Min(MD.X, MM.X), Math.Min(MD.Y, MM.Y),
Math.Abs(MD.X - MM.X), Math.Abs(MD.Y - MM.Y));
form2.Invalidate();
}
void form2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Red, rect);
}
void form2_MouseUp(object sender, MouseEventArgs e)
{
form2.Hide();
Screen scr = Screen.AllScreens[0];
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
using (Graphics G = Graphics.FromImage(bmp))
{
G.CopyFromScreen(rect.Location, Point.Empty, rect.Size,
CopyPixelOperation.SourceCopy);
pictureBox1.Image = bmp;
}
form2.Close();
Show();
}
}

C# graphics flickering

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
});

Categories