Im in the process of making a snipping tool. I've got it so that my program can be used to draw a rectangle with the mouse and have that image saved. Now what I'm trying to do is have the image generated be transfered to a picture box that show's the user what they have just captured before they decide to save it or anything else.
Is there a way in which I can do this?
So far my screen capture code saves the captured image to the clipboard with the following code found in my ScreenCapture class:
public static bool saveToClipboard = true;
public static void CaptureImage(bool showCursor, Size curSize, Point curPos, Point SourcePoint, Point DestinationPoint, Rectangle SelectionRectangle, string FilePath, string extension)
{
using (Bitmap bitmap = new Bitmap(SelectionRectangle.Width, SelectionRectangle.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(SourcePoint, DestinationPoint, SelectionRectangle.Size);
if (showCursor)
{
Rectangle cursorBounds = new Rectangle(curPos, curSize);
Cursors.Default.Draw(g, cursorBounds);
}
}
if (saveToClipboard)
{
Image img = (Image)bitmap;
Clipboard.SetImage(img);
}
}
}
Has anyone ever done something like this before? Also, is it possible to have the picture box auto resize so that the screen capture size is used and not the picture boxes?
update
Further to some of the comments below, I've been trying to save the image I store in my above class and pass it to the picture box. But nothing is displayed when I do it. The code I've been using is thus:
Held on the form1.cs:
public void SetImage(Image img)
{
imagePreview.Image = img;
}
And in the screen capture class:
if (saveToClipboard)
{
Image img = (Image)bitmap;
ControlPanel cp = new ControlPanel();
cp.SetImage(img);
Clipboard.SetImage(img);
}
ControlPanel cp = new ControlPanel();
cp.SetImage(img);
this won't work because you need to access the parent form in use, not create a new instance of it.
Look at the answer to this question on creating a simple custom event, but add an Image to the ProgressEventArgs that they create. Then on your main form, subsribe to the event and update the picturebox from there.
Related
I want to finish my paint program for my Daughter and am having issues with the panel keeping drawings rendered on the panel when the application is minimized. I would also like for the program to automatically save to a file what she drew on the panel when she clicks on the "CLEAR" and/or "CLOSE" button.
If you want to keep your drawings you could instead of drawing in the graphics of the panel draw inside a Image of that panel nonetheless you can use Panel.DrawToImage() it should be like this
Panel.DrawToBitmap(Bitmap, Panel.ClientRectangle);
Bitmap bmp; //this will be the image where you would draw to
Graphics g; // the graphics
public ALANA_PAINT()
{
//do your stuff
bmp = new Bitmap(Width,Height)// Initialize the bitmap
Panel.BackgroundImage = bmp;
g = Graphics.FromImage(bmp);
}
//your normal drawing methods
public void Save()
{
bmp.Save(path,imageFormat);
}
For the close function the forms have a FormsClosing event that you could call you save from there
You need to not put the drawing code in pnl1_MouseMove against a new Graphics you get from pnl1.CreateGraphics();. You need to do one of two things.
1) Keep a list of all the "shapes" that need to be drawn and redraw, then subscribe to the event pn1.Paint += pn1_Paint and have that paint method go through the list of all the shapes you have recorded and re draw them
or
2) Don't paint to the Graphics object for the panel, call CreateGraphics on a Bitmap you have in the class then have that bitmap drawn as the background of pn1.
The benefit of method 2 is it will allow most of your code to be the same, you are now just drawing against a bitmap instead of directly against the panel.
you are going to need a method like this one for the 2nd part of your question :
public void SaveBitmap(string location)
{
Bitmap bmp = new Bitmap((int)myPanel.Width, (int)myPanel.Height);
DrawToBitmap(bmp, new Rectangle(0, 0, myPanel.Width, myPanel.Height));
using (FileStream saveStream = new FileStream(location + ".bmp", FileMode.OpenOrCreate))
{
bmp.Save(saveStream, ImageFormat.Bmp);
}
}
If you want to handle it when she closes the window, use the Closing event in the Window.
I have a timer tick event set to 250ms:
private void timer4_Tick(object sender, EventArgs e)
{
if (pictureboximagestosavecount == 72)
{
timer4.Enabled = false;
}
else
{
Bitmap bmp = new Bitmap(this.Width, this.Height);
Rectangle rect = new Rectangle(this.Location.X, this.Location.Y, this.Width, this.Height);
pictureboximagestosavecount++;
savePictureBox(pictureBox1, #"c:\temp\pboximages\" + pictureboximagestosavecount.ToString("D6") + "pbimg.gif");
this.DrawToBitmap(bmp, rect);
bmp.Save(#"c:\temp\pboximages\" + pictureboximagestosavecount.ToString("D6") + "form.gif");
}
}
First I'm saving the pictureBox as gif using the method savePictureBox.
Second I save the form1:
bmp.Save(#"c:\temp\pboximages\" + pictureboximagestosavecount.ToString("D6") + "form.gif");
After timer4 is stopped I have abutton click event where I create animated gif from the saved files:
private void animatedgifbutton_Click(object sender, EventArgs e)
{
DirectoryInfo di1;
FileInfo[] fi1;
di1 = new DirectoryInfo(#"c:\temp\pboximages\");
fi1 = di1.GetFiles("*form.gif");
List<string> newImages = new List<string>();
for (int i = 0; i < fi1.Length; i++)
{
newImages.Add(fi1[i].FullName);
}
animatedgif.MakeGIF(newImages, #"c:\temp\pboximages\animated1.gif", 6, true);
}
When I'm doing *.form.gif and I see in the List newImages only the form gifs files the animatedgif.MakeGIF throw error since it need to get List of gifs files but I guess that when I save the form it's saving it as bitmap and not real gif.
How can I take a screenshot of form1 and save it as real gif ?
When I save the pictureBox1 to the hard disk it is GIF and there are no problems.
The problem is with saving the form.
EDIT:
I also saw now that the way I'm saving a screenshot of the form1 is not good it's not saving the whole form area only part of it. I gues using this bmp and rect is not good.
This is what I get for example when I'm saving a screenshot of the form:
In other words I need in timer4 tick event to save the form screen the whole form to the hard disk as a gif file real gif file not bmp.
It doesn't save as a proper GIF because you don't tell it to. The extension is not enough, you need to add the ImageFormat to the Save call!
And it doesn't save the whole area of the Form because you don't use the right Rectangle to control it.
Both issues go away very simply if you use the modified savePictueBox function:
void saveControl(Control Ctl, string fileName)
{
Rectangle Rect = Ctl.ClientRectangle;
// if (Ctl is Form) Rect = Ctl.Bounds; // (*)
using (Bitmap bmp = new Bitmap(Rect.Width, Rect.Height))
{
Ctl.DrawToBitmap(bmp, Rect );
bmp.Save(fileName, System.Drawing.Imaging.ImageFormat.Gif);
}
}
Note that this uses the ClientRectangle, that is, it leaves out the Form border. If you want if to include the Border you can uncomment the line (*)
Now you can call it as
saveControl(pictureBox1, yourFileName);
and as
saveControl(this, yourOtherFileName);
Two notes on Animated Gifs:
Since you are recording the Gifs in real-time don't try to create the animation at the same time!
I found this post interesting. fireydude's answer does work, once you have included all those references from the WPF world..Not sure how to get the best quality and unfortunately there is no control over the timing, so far..
I have a List<> object, "imagelist", that contains the paths of many images such as .png's. Now, with the following code:
private void paint_picture(PictureBox picture, string pathofpic)
{
Graphics g = picture.CreateGraphics();
Bitmap drawnpic = null;
if (imagelist.ContainsKey(picture.Name))
{
drawnpic = new Bitmap(pathofpic);
g.DrawImage(drawnpic, 0, 0, picture.Size.Width, picture.Size.Height);
imagelist[picture.Name] = pathofpic;
}
drawnpic.Dispose();
g.Dispose();
}
I call this every time the card's image is changed, but I can't seem to make the image persist on the picturebox, when I drag the picturebox across the form (for example, over other pictureboxes). The click and drag code is just moving the picturebox with the mouse, not really relevant.
I've tried invalidating the form when I de-select the image, but it doesn't do anything.
Is there something I'm missing? Screenshot below, I dragged one image around the form and it overwrote the other images it moved across:
That’s how painting works – you have to handle its Paint event and keep painting the same thing each time it needs repainting.
What you can do is draw on top of your original image:
Bitmap b = new Bitmap(picture.Image);
using (Graphics g = Graphics.FromImage(b)) {
using (Bitmap drawnpic = new Bitmap(pathofpic)) {
g.DrawImage(drawnpic, 0, 0, b.Width, b.Height);
}
}
picture.Image = b;
Then you’d save the original image somewhere and probably use it instead of picture.Image in the new Bitmap line.
And PascalCase for method names, please. ;)
I have troubles understanding how the graphics objects are drawn. Suppose to have this function:
private void DrawLineOnOverlay()
{
using (var g = pictureBox.CreateGraphics())
{
g.DrawLine(OverlayPen, cursorStartx, cursorStarty, cursorEndx, cursorEndy);
}
}
to draw simple lines in a pictureBox control where you have already done this:
pictureBox.Image = BitmapToBeLoaded; // Load an 8-bit indexed Bitmap
My understanding is that both the image loaded and the pixel drawn using the graphics object are parte of the very same image: pictureBox.Image
but this:
Bitmap graphic = pictureBox.Image;
if (graphic != null )
{
graphic = new Bitmap (pictureBox.Image);
graphic.Save( "C:\\packed.png", ImageFormat.Png);
}
does not work: the image saved does not show the lines drawn in red over the image. Why this? What is wrong?
If you want to be able to save your drawings then you need to draw them on a surface and then save the surface. Usually, drawing on PictureBox canvas would not let you save the image because the Image class has nothing to do with drawings. Image is just an abstract class on top of Bitmap which is able to load a GDI+ supported image file and then present it in PictureBox. Drawings are done on a GDI+ drawing surface which is Graphics object.
You can create a surface:
Bitmap surface = new Bitmap(640, 480);
Graphics g = Graphics.FromImage(surface);
using (var OverlayPen = new Pen(Color.Red))
{
g.DrawLine(OverlayPen, cursorStartx, cursorStarty, cursorEndx, cursorEndy);
}
If you want to show drawings, you can set Surface as PictureBox's image. And remember to use using pattern when you are creating graphical objects like pens or brushes because if you don't they're going to stay on memory all the way to the end of the context and they may cause overflow at some points.
To save then:
surface.Save( "C:\\packed.png", ImageFormat.Png);
I have a picturebox with a png in it. Yet even when I set the BackColor to Transparent, it is not transparent. Any ideas what might be wrong? :)
Thanks!
I have also faced the problem regarding transparent pictures.
you have to Draw it through code.
See my question A PictureBox Problem
EDIT:
In paint event (Control containing Background Image)
write this
//If added your image in project's resources get from there OR your Image location
Image img = yourNamespace.Properties.Resources.yourPicture;
e.Graphics.DrawImage(img,50,50,100,100);
Your PNG file should also have the transparent background. This is can be done while creating the image(png) files.
You really have to draw it through code. Place a pictureBox on your form, set sizeMode and docking as you like. Then you may fire the following function on the pictureBox's PAINT event:
public void LogoDrawTransparent(PaintEventArgs e)
{
// Create a Bitmap object from an image file.
Image myImg;
Bitmap myBitmap;
try
{
myImg = cls_convertImagesByte.GetImageFromByte(newImg);
myBitmap = new Bitmap(myImg); // #"C:\Temp\imgSwacaa.jpg");
// Get the color of a background pixel.
Color backColor = myBitmap.GetPixel(0, 0); // GetPixel(1, 1);
Color backColorGray = Color.Gray;
Color backColorGrayLight = Color.LightGray;
Color backColorWhiteSmoke = Color.WhiteSmoke;
Color backColorWhite = Color.White;
Color backColorWheat = Color.Wheat;
// Make backColor transparent for myBitmap.
myBitmap.MakeTransparent(backColor);
// OPTIONALLY, you may make any other "suspicious" back color transparent (usually gray, light gray or whitesmoke)
myBitmap.MakeTransparent(backColorGray);
myBitmap.MakeTransparent(backColorGrayLight);
myBitmap.MakeTransparent(backColorWhiteSmoke);
// Draw myBitmap to the screen.
e.Graphics.DrawImage(myBitmap, 0, 0, pictureBox1.Width, pictureBox1.Height); //myBitmap.Width, myBitmap.Height);
}
catch
{
try { pictureBox1.Image = cls_convertImagesByte.GetImageFromByte(newImg); }
catch { } //must do something
}
}
This is my class that is referenced in the above function:
class cls_convertImagesByte
{
public static Image GetImageFromByte(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
public static byte[] GetByteArrayFromImage(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
}
Thanks. chagbert.
From what I learned I can't do it within a windows form, as it doesn't have layers for the images. So guess will have to make it as WPF. :)
How did you create the background? Is that set by setting the Form.BackgroundImage?
If that background (the paper like image) is a container control, the transparency should just work.
However, If you are placing two PictureBox objects on top of eachother this doesn't work. The transparent area takes on the color of its parent object. If you have two PictureBox objects they both will have the Form as their parent. If this is your situation, it can be solved by setting the transparent image's Parent property to be the background image.
private void Form1_Load(object sender, EventArgs e)
{
transparentPictureBox.Parent = backgroundPictureBox;
}
When changing the Parent property, the Location of the tranparentPictureBox will become relative to its new parent. You'd have to subtract the x and y coordinates of the background image from the transparent image. See my answer on A PictureBox Question for an example with a screen shot.
AFAIK, you can not set the Parent in the Designer, only in code. Therefore, the Designer will still not show a transparent image, but at runtime it should.
The same problem occurs if you place a transparent Label on top of a PictureBox object.