I've done some more work on printing forms in C# since my last question, I've got this code now:
public void printToolStripMenuItem_Click(object sender, EventArgs e)
{
Rectangle bounds = this.Bounds;
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
PrintDocument doc = new PrintDocument();
doc.PrintPage += this.Doc_PrintPage;
PrintDialog dlgSettings = new PrintDialog();
dlgSettings.Document = doc;
if (dlgSettings.ShowDialog() == DialogResult.OK)
{
doc.Print();
}
}
private void Doc_PrintPage(object sender, PrintPageEventArgs e)
{
float x = e.MarginBounds.Left;
float y = e.MarginBounds.Top;
e.Graphics.DrawImage(bitmap);
}
Where printToolStripMenuItem_Click is the print button. I know I'm close because I saw the print dialogue before I edited the code to fit my needs. Right now, I'm getting an error that says "bitmap" in "e.Graphics.DrawImage(bitmap);" doesn't exist in context.
What can I change to make this print the image? I'm trying to print the image of the screen before I try creating the print document, because this seems easier, and just incase it works. I'm lazy sometimes :P
Note: This is all code inside my form2.cs file, the form I need to print.
Thanks :)
You're declaring bitmap in printToolStripMenuItem_Click but using it in Doc_PrintPage. You need to pass it some way. The easiest way would be to make it an instance variable (i.e. declare it in the class instead of the method, then assign it in printToolStripMenuItem_Click).
public class SomeForm
{
private Bitmap bitmap;
public void printToolStripMenuItem_Click(object sender, EventArgs e)
{
//...
bitmap = new Bitmap(bounds.Width, bounds.Height);
//...
}
}
You also are missing a parameter in the e.Graphics.DrawImage call. You need to specify where to draw the image. For instance, if you want it in the upper left corner do:
e.Graphics.DrawImage(bitmap, new Point(0,0));
You should use an anonymous method to create an event handler inside your function.
This way, it will still be able to read your local variables, through the magic of closures.
doc.PrintPage += (s, e) => {
float x = e.MarginBounds.Left;
float y = e.MarginBounds.Top;
e.Graphics.DrawImage(bitmap);
};
Related
i want to print my form on the whole page but instead the picture looks like this :
http://i.stack.imgur.com/JSXh2.jpg
it looks very small that's why i need the form to be printed on the whole full page
here is my code :
private void button5_Click(object sender, EventArgs e)
{
CaptureScreen();
printDocument1.Print();
}
private Bitmap _memoryImage;
private void CaptureScreen()
{
// put into using construct because Graphics objects do not
// get automatically disposed when leaving method scope
using (var myGraphics = CreateGraphics())
{
var s = Size;
_memoryImage = new Bitmap(s.Width, s.Height, myGraphics);
using (var memoryGraphics = Graphics.FromImage(_memoryImage))
{
memoryGraphics.CopyFromScreen(Location.X, Location.Y, 0, 0, s);
}
}
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
var wScale = e.MarginBounds.Width / (float)_memoryImage.Width;
var hScale = e.MarginBounds.Height / (float)_memoryImage.Height;
// choose the smaller of the two scales
var scale = wScale < hScale ? wScale : hScale;
// apply scaling to the image
e.Graphics.ScaleTransform(scale, scale);
// print to default printer's page
e.Graphics.DrawImage(_memoryImage, 0, 0);
}
your help would be appreciated
try this to make a bitmap of your form:
Bitmap bm = new Bitmap(f1.Width, f1.Height);
this.DrawToBitmap(bm, new Rectangle(f1.Location, f1.Size));
maybe that's the problem! Report back if it works!
If you want to print it in landscape format than you have to rotate your bitmap using:
public Bitmap rotateInternalFunction(Bitmap bitmap)
{
bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
return bitmap;
}
I have a little bit code that allows me (windows form) to load a image, draw a rectangle on it and save it. Yet I want to realise a "Undo" function. If I write a rectangle I draw into the bitmap and save the modified bitmap. After I draw another rectangle I save the bitmap too (in a List). Yet I created a button that deletes the last bitmap I saved and set the bitmap (the new last) as Image in the pictureBox. That works, but If I do another rectangle and click Undo, nothing happens. I am confused and have no Idea, where the problem is. Here 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.Windows.Forms;
namespace RecAngle
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
}
Rectangle mRect;
Bitmap bm;
Image file;
Boolean opened = false;
SaveFileDialog sfd = new SaveFileDialog();
OpenFileDialog ofd = new OpenFileDialog();
Boolean draw = false;
List<Bitmap> bitMapList = new List<Bitmap>();
Boolean undo = false;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mRect = new Rectangle(e.X, e.Y, 0, 0);
pictureBox1.Invalidate();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mRect = new Rectangle(mRect.Left, mRect.Top, e.X - mRect.Left, e.Y - mRect.Top);
pictureBox1.Invalidate();
draw = true;
}
}
private void button1_Click(object sender, EventArgs e)
{
DialogResult dr = ofd.ShowDialog();
if (dr == DialogResult.OK)
{
file = Image.FromFile(ofd.FileName);
bm = new Bitmap(ofd.FileName);
pictureBox1.Image = bm;
bitMapList.Add(bm);
opened = true;
}
}
private void button2_Click(object sender, EventArgs e)
{
DialogResult dr = sfd.ShowDialog();
if (opened)
{
try
{
bm.Save(sfd.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch (Exception x)
{
Console.WriteLine(x);
}
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
try
{
if (draw)
{
if (undo)
{
bm = bitMapList[bitMapList.Count - 1];
}
using (Graphics a = Graphics.FromImage(bm))
{
Pen pen = new Pen(Color.Red, 2);
a.DrawRectangle(pen, mRect);
pictureBox1.Invalidate();
bitMapList.Add( new Bitmap(bm));
pictureBox1.Image = bitMapList[bitMapList.Count - 1];
}
}
}
catch (Exception x)
{
Console.WriteLine(x);
}
}
private void button3_Click(object sender, EventArgs e)
{
if (bitMapList.Count != 0)
{
bitMapList.RemoveAt(bitMapList.Count - 1);
pictureBox1.Image = bitMapList[bitMapList.Count - 1];
undo = true;
}
}
}
}
Do I save something wrong? I think it is something with the "Remove" but I really dont see the error.
Thank you for help
Your code is very confusing. Why are you calling Invalidate all the time? Why are you using List where Stack would be more appropriate? Why are you assigning bm when doing undo in the MouseUp method, rather then directly in the Undo button click? Why are you keeping the Graphics instance alive for so long, when you only need it to paint one rectangle?
And finally, why aren't you setting undo back to false?
Also, you're very confused about where what bitmap is at a time. In the Undo method, you're checking bitMapList.Count != 0, and then you remove the last item. However, if bitMapList.Count is 1, the next line will cause an IndexOutOfRangeException.
You have to break through this confusion. Think about what you're trying to do, and think about how to do it cleanly. This is a mess. What bitmaps do you want in the history? Do you really need a separate bitmap instance for the "actual" bitmap, even though you've just put the very same bitmap in the history? Why aren't you simply treating the latest bitmap in the history as the "current" one? Then you'd simply Pop the last item from stack and everything would work with no issue, and cleanly. Of course, you'd have to do the painting on a new instance of the bitmap, but that's no overhead, since new Bitmap(bm); does exactly the same thing anyway. The only thing that would change would be the order - you'd create the new bitmap, paint the rectangle and Push it to the stack. As simple as that.
Code should be easy to understand. That's one of the primary goals for code maintenance. That's why you want to separate your logic into different methods etc., it has to be easy to read and understand, and having everything in a few methods with small areas of responsibility helps. If you're already in a confusing mess with a code this short, imagine how hard it's going to be to maintain an application that's more complicated than this.
All that said, the quick fix for your immediate issue is changing the undo code in pictureBox1_MouseUp like so:
if (undo)
{
bm = new Bitmap(bitMapList[bitMapList.Count - 1]);
undo = false;
}
However, I strongly encourage you to rewrite the code instead, and make it more clear. You're not even naming your controls, that doesn't help readability at all. Why not have btnUndo instead of button3?
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 want to draw a text within an image in C# by using the method drawstring.But how can i obtain the co-ordinates of the clicked position and how can i relate this to the dimension of the image.
Please Help.
Working example:
private Bitmap _bmp = new Bitmap(250, 250);
public Form1()
{
InitializeComponent();
panel1.MouseDown += new MouseEventHandler(panel1_MouseDown);
panel1.Paint += new PaintEventHandler(panel1_Paint);
using (Graphics g = Graphics.FromImage(_bmp))
g.Clear(SystemColors.Window);
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_bmp, new Point(0, 0));
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
using (Graphics g = Graphics.FromImage(_bmp))
{
g.DrawString("Mouse Clicked Here!", panel1.Font, Brushes.Black, e.Location);
}
panel1.Invalidate();
}
You might want to use TextRenderer instead of DrawString. DrawString has issues.
TextRenderer.DrawText(g, "Mouse Clicked Here!", panel1.Font, e.Location, Color.Black);
If I was doing this, I'd follow this guy's tutorial;
http://www.emanueleferonato.com/2006/09/02/click-image-and-get-coordinates-with-javascript/
and in his function, generate an ajax post-back with the co-ordinates in the querystring. Then do whatever you need to do with them. You would be better off using client side scripting for this sort of thing though.
I have a PictureBox on my Windows Forms application.
I load a picture in it and I have enabled the Paint event in my code. It draws a rectangle.
Like this:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics gr = e.Graphics;
Pen p = new Pen(Color.Red);
p.Width = 5.0f;
gr.DrawRectangle(p, 1, 2, 30, 40);
}
And I click the "save" button:
private void button2_Click(object sender, EventArgs e)
{
pictureBox1.Image.Save(#"C:\Documents and Settings\tr1g3800\Desktop\WALKING\30P\100000test.jpg",ImageFormat.Jpeg);
}
But the saved file never contains the rectangle that I drew.
Does anyone have any idea?
Thanks.Your anwers all helped.
This worked
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.ImageLocation=#"C:\Documents and Settings\tr1g3800\Desktop\WALKING\30P\100000.jpg" ;
}
private void button2_Click(object sender, EventArgs e)
{
pictureBox1.Image.Save(#"C:\Documents and Settings\tr1g3800\Desktop\WALKING\30P\100000test.jpg",ImageFormat.Jpeg);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
Bitmap bmp = new Bitmap(pictureBox1.Image);
Graphics gr = Graphics.FromImage(bmp);
Pen p = new Pen(Color.Red);
p.Width = 5.0f;
gr.DrawRectangle(p, 1, 2, 30, 40);
pictureBox1.Image = bmp;
}
You probably shouldn't draw directly on the PictureBox.
You need to use a Bitmap instead. Try putting the bitmap in the PictureBox.Image and then call Save().
Check this for more details
Here is my solution with additional support to various file types:
public void ExportToBmp(string path)
{
using(var bitmap = new Bitmap(pictureBox.Width, pictureBox.Height))
{
pictureBox.DrawToBitmap(bitmap, pictureBox.ClientRectangle);
ImageFormat imageFormat = null;
var extension = Path.GetExtension(path);
switch (extension)
{
case ".bmp":
imageFormat = ImageFormat.Bmp;
break;
case ".png":
imageFormat = ImageFormat.Png;
break;
case ".jpeg":
case ".jpg":
imageFormat = ImageFormat.Jpeg;
break;
case ".gif":
imageFormat = ImageFormat.Gif;
break;
default:
throw new NotSupportedException("File extension is not supported");
}
bitmap.Save(path, imageFormat);
}
}
Here is a small example that clarified a few things for me (I was struggling with this a bit too).
pBox is a PictureBox on Form1, make it at least 50x50
appPath was derived from System.Reflection but use any path you like
There are two buttons, one for drawing, one for saving, their click events are in the code below.
Things I learned:
(1) "pBox.Image =" doesn't do anything but initialize the pBox image, it DOES NOT have to be a filename as EVERY example I found used (had problem saving to that same file because it was share locked). Also, if your goal is to see things on the entire control's surface, you'll probably like setting the size at initialize time to the size you need. I used the pBox's size in this example but normally I use the bitmap size (because I typically begin with a real picture file).
(2) I always had problems either seeing my draws show up on the control or seeing my changes saved in the output file (or both). In my prior attempts I would duplicate the draws both on the control and on the bitmap. Of course that isn't necessary but the edited bitmap DOES need to be reloaded into the control.image... and THAT was the piece of this puzzle I was missing.
(A) Create a bitmap from the control.image and draw on the bitmap
(B) Load the bitmap into the control.Image (so you can see the changes caused by the draw)
(C) Save the control.Image
(2-option) You have a global (or passed) bitmap (probably from a real file)
(A) Draw on the bitmap
(B) Load the bitmap into the control.Image (so you can see the changes)
(C) Save the bitmap
public Form1()
{
InitializeComponent();
pBox.Image = new Bitmap(pBox.Width, pBox.Height);
}
private void DrawStuff1_Click(object sender, EventArgs e)
{
Bitmap bmp = new Bitmap(pBox.Image);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(Brushes.Red, 5, 5, 25, 25); //hard-coded size to reduce clutter
pBox.Image = bmp; //this makes your changes visible
}
private void Save_Click(object sender, EventArgs e)
{
pBox.Image.Save(appPath + "SavedImage.bmp");
}
You need paint to image of picture, not to the Graphics control on Paint event.
EDIT:
using( Graphics g = Graphics.FromImage( pictureBox1.Image ) ) {
// there you will be do, what you do in Paint event
}
// ... somewhere else ...
pictureBox1.Save( _required_parameters_ );