Why isn't the PrintPreview exactly the same as the paper print? - c#

Okay, before you spam me with StringFormat.Alignment = StringAlignment.Center ... hear my whole issue:
When I draw text with the following code, the string is centered in the PrintPreview, but NOT CENTERED on the actual paper when it prints. The whole page is off to the right just a little, thus some stuff shows as printing on the print preview, but falls off the paper (not just outside the margin range, but OFF the paper) when printed.
private void button1_Click(object sender, EventArgs e)
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
PrintPreviewDialog ppd = new PrintPreviewDialog();
((Form)ppd).WindowState = FormWindowState.Maximized;
ppd.Document = pd;
ppd.ShowDialog();
}
void pd_PrintPage(object sender, PrintPageEventArgs e)
{
for (int y = 100; y < 400; y += 25)
{
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawRectangle(Pens.Black, new Rectangle(5, y, 840, 25));
}
e.HasMorePages = false;
}
Any thoughts as to why it's off? This should be trivial, but it isn't.
EDIT: I've found that it's not just text... It's printing EVERYTHING off just a little. I've updated the code above to provide a better example of the issue. Just drop this in a form with a button on it.
EDIT 2: With the answer given, I've modified the code and this now works. I'm providing the final code for those that may want to see it. I have to recognize whether i'm seeing this in the PrintPreview dialog or on paper, so I have a IsPreview flag to handle this.
public partial class Form1 : Form
{
bool IsPreview = true;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
IsPreview = true;
PrintDocument pd = new PrintDocument();
pd.EndPrint += new PrintEventHandler(pd_EndPrint);
pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
PrintPreviewDialog ppd = new PrintPreviewDialog();
((Form)ppd).WindowState = FormWindowState.Maximized;
ppd.Document = pd;
ppd.ShowDialog();
}
void pd_EndPrint(object sender, PrintEventArgs e)
{
IsPreview = false;
}
void pd_PrintPage(object sender, PrintPageEventArgs e)
{
Rectangle b3 = e.PageBounds;
if (IsPreview)
{
e.Graphics.TranslateTransform(e.PageSettings.HardMarginX, e.PageSettings.HardMarginY);
}
b3.Width -= (int)e.PageSettings.HardMarginX * 2;
b3.Height -= (int)e.PageSettings.HardMarginY * 3;
int y = b3.Y;
int x=0;
while ((y + 25) < b3.Bottom)
{
x++;
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
Rectangle R = new Rectangle(b3.X, y, b3.Width, 25);
e.Graphics.DrawRectangle(Pens.Black, R);
e.Graphics.DrawString(x.ToString(), this.Font, Brushes.Black, b3.X + 5, y + 5);
y += 25;
}
// draw the last little bit
e.Graphics.DrawRectangle(Pens.Black, new Rectangle(b3.X, y, b3.Width, b3.Height - y));
e.HasMorePages = false;
}
}

Being off towards the right tends to be explainable by the value of the PageSettings.HardMarginX property. A value produced by the printer driver. Printer drivers are however typically not very good at guessing what the actual paper route through the printer might look like. That's mechanical, pinch rollers, tray alignment and whatnot. Software and Mechanical engineers don't have lunch together often enough.
But a software engineer can almost always fix a mechanical engineer's problem. You'll need an Options dialog to allow the user to fix the mechanical engineer's problem. Use the value in e.Graphics.TranslateTransform call.

The reason the preview doesn't match up to the paper is because you haven't selected a printer yet. Every printer is a bit different for things like unprintable areas, forced margins, etc. It's been a while since I've done winforms printing, but I do recall that the passed graphics object has a way to account for that.

Related

Resize Panel To Print Full Page

I have a panel on a tabcontrolpanel that takes up roughly 35% of the screen. I am printing the panel, and it prints the exact size it is on the screen. Is it possible in C# and winforms to scale the image to print on a 8.5x11 peice of paper (even if I have to print it landscape)?
This is my current code --
private void btnPrint_Click(object sender, EventArgs e)
{
PrintDocument doc = new PrintDocument();
doc.PrintPage += new SPrintPageEventHandler(doc_PrintPage);
doc.Print();
}
private void doc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Bitmap image = new Bitmap(panelToPrint.Width, panelToPrint.Height, panelToPrint.CreateGraphics());
panelToPrint.DrawToBitmap(image, new Rectangle(0, 0, panelToPrint.Width, panelToPrint.Height));
RectangleF bounds = e.PageSettings.PrintableArea;
float factor = ((float)image.Height / (float)image.Width);
e.Graphics.DrawImage(image, bounds.Left, bounds.Top, bounds.Width, factor * bounds.Width);
}
Edit
I tried to modify my syntax to by default print in landscape mode, but I am still getting the same output as before, just the image printing roughly on the top 15% of the page
private void btnPrint_Click(object sender, EventArgs e)
{
System.Drawing.Printing.PrintDocument doc = new System.Drawing.Printing.PrintDocument();
doc.DefaultPageSettings.Landscape = true;
PrintDialog pdi = new PrintDialog();
pdi.Document = doc;
if (pdi.ShowDialog() == DialogResult.OK)
{
doc.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(doc_PrintPage);
doc.Print();
}
else
{
MessageBox.Show("User cancelled the print job");
}
}
private void doc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Bitmap image = new Bitmap(panelToPrint.Width, panelToPrint.Height, panelToPrint.CreateGraphics());
panelToPrint.DrawToBitmap(image, new Rectangle(0, 0, panelToPrint.Width, panelToPrint.Height));
RectangleF bounds = e.PageSettings.PrintableArea;
float factor = ((float)image.Height / (float)image.Width);
e.Graphics.DrawImage(image, bounds.Left, bounds.Top, bounds.Width, factor * bounds.Width);
}
control.DrawToBitmap does exactly that. It prints the control (as you see it on the screen) to a bitmap. If you want to print it in a different size you have to resize the panel before printing.
A better approach is to draw your data that is contained into the panel directly to the page using graphics methods like DrawString etc.

How To print a long string into multiple pages in C#

I'm trying to print a very long string using code below but it prints the whole text only in one page. Is there any easy way to print it correctly?
string text="the text has like 1000 words";
System.Drawing.Printing.PrintDocument p = new System.Drawing.Printing.PrintDocument();
p.PrintPage += delegate (object sender1, System.Drawing.Printing.PrintPageEventArgs e1)
{
e1.Graphics.DrawString(text, new System.Drawing.Font("Times New Roman", 12), new System.Drawing.SolidBrush(System.Drawing.Color.Black), new System.Drawing.RectangleF(50, 50, p.DefaultPageSettings.PrintableArea.Width - 50, p.DefaultPageSettings.PrintableArea.Height - 50));
};
try
{
p.Print();
}
catch (Exception ex)
{
throw new Exception("Exception Occured While Printing", ex);
}
I tried to solve it with this article but the method stuck in some loop that I don't know how to fix.
You have to keep in mind that PrintDocument will raise the event PrintPage as long as you tell it there are more pages. It is your task to keep track of what is printed, what needs to printed next and if you need another page or not.
There are several ways to accomplish that. I have chosen in this case to take the content of your text, check how much of it fits on the current page, DrawString that bit and then update my processing string, called remainingtext to be able to repeat for the next Page. The decision if a next page is needed is controlled by setting HasMorepages of the PrintEvent arguments instance to true or false when we're done.
Here is the code:
PrintDocument p = new PrintDocument();
var font = new Font("Times New Roman", 12);
var margins = p.DefaultPageSettings.Margins;
var layoutArea = new RectangleF(
margins.Left,
margins.Top,
p.DefaultPageSettings.PrintableArea.Width - (margins.Left + margins.Right ),
p.DefaultPageSettings.PrintableArea.Height - (margins.Top + margins.Bottom));
var layoutSize = layoutArea.Size;
layoutSize.Height = layoutSize.Height - font.GetHeight(); // keep lastline visible
var brush = new SolidBrush(Color.Black);
// what still needs to be printed
var remainingText = text;
p.PrintPage += delegate (object sender1, PrintPageEventArgs e1)
{
int charsFitted, linesFilled;
// measure how many characters will fit of the remaining text
var realsize = e1.Graphics.MeasureString(
remainingText,
font,
layoutSize,
StringFormat.GenericDefault,
out charsFitted, // this will return what we need
out linesFilled);
// take from the remainingText what we're going to print on this page
var fitsOnPage = remainingText.Substring(0, charsFitted);
// keep what is not printed on this page
remainingText = remainingText.Substring(charsFitted).Trim();
// print what fits on the page
e1.Graphics.DrawString(
fitsOnPage,
font,
brush,
layoutArea);
// if there is still text left, tell the PrintDocument it needs to call
// PrintPage again.
e1.HasMorePages = remainingText.Length > 0;
};
p.Print();
When I hookup an PrintPreviewControl this is the result:
So thanks to rene I figured it out how to print it correctly. I edit his code and the difference here is that the user chooses the paper size and rectangel drawing happens with choosen paper margin size. This is a ready printing method in case you want to use it...
private void Print(string thetext){
try
{
System.Drawing.Printing.PrintDocument p = new System.Drawing.Printing.PrintDocument();
var font = new Font("Times New Roman", 12);
var brush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
// what still needs to be printed
var remainingText = theText;
p.PrintPage += delegate (object sender1, System.Drawing.Printing.PrintPageEventArgs e1)
{
int charsFitted, linesFilled;
// measure how many characters will fit of the remaining text
var realsize = e1.Graphics.MeasureString(
remainingText,
font,
e1.MarginBounds.Size,
System.Drawing.StringFormat.GenericDefault,
out charsFitted, // this will return what we need
out linesFilled);
// take from the remainingText what we're going to print on this page
var fitsOnPage = remainingText.Substring(0, charsFitted);
// keep what is not printed on this page
remainingText = remainingText.Substring(charsFitted).Trim();
// print what fits on the page
e1.Graphics.DrawString(
fitsOnPage,
font,
brush,
e1.MarginBounds);
// if there is still text left, tell the PrintDocument it needs to call
// PrintPage again.
e1.HasMorePages = remainingText.Length > 0;
};
System.Windows.Forms.PrintDialog pd = new System.Windows.Forms.PrintDialog();
pd.Document = p;
DialogResult result = pd.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK)
{
p.Print();
}
}catch(Exception e2)
{
System.Windows.MessageBox.Show(e2.Message, "Unable to print",MessageBoxButton.OK);
}
}
Code snippet for "when user clicked 'btnPrint' button, print 'rtbEditor' rich textbox's text.":
private void btnPrint_Click(object sender, EventArgs e) {
var d = new PrintDialog();
d.Document = new PrintDocument();
d.Document.PrintPage += Dd_PrintPage;
remainingText = rtbEditor.Text;
var res = d.ShowDialog();
try { if (res == DialogResult.OK) d.Document.Print(); }
catch { }
}
string remainingText;
private void Dd_PrintPage(object sender, PrintPageEventArgs e){
//this code was for 1 page
//e.Graphics.DrawString(rtbEditor.Text,new Font("Comic Sans MS", 12f),Brushes.Black,new PointF(10,10));
//this is for multiple pages
PrintDocument p = ((PrintDocument)sender);
var font = new Font("Times New Roman", 12);
var margins = p.DefaultPageSettings.Margins;
var layoutArea = new RectangleF(
margins.Left,
margins.Top,
p.DefaultPageSettings.PrintableArea.Width - (margins.Left + margins.Right),
p.DefaultPageSettings.PrintableArea.Height - (margins.Top + margins.Bottom));
var layoutSize = layoutArea.Size;
layoutSize.Height = layoutSize.Height - font.GetHeight(); // keep lastline visible
var brush = new SolidBrush(Color.Black);
int charsFitted, linesFilled;
// measure how many characters will fit of the remaining text
var realsize = e.Graphics.MeasureString(
remainingText,
font,
layoutSize,
StringFormat.GenericDefault,
out charsFitted, // this will return what we need
out linesFilled);
// take from the remainingText what we're going to print on this page
var fitsOnPage = remainingText.Substring(0, charsFitted);
// keep what is not printed on this page
remainingText = remainingText.Substring(charsFitted).Trim();
// print what fits on the page
e.Graphics.DrawString(
fitsOnPage,
font,
brush,
layoutArea);
// if there is still text left, tell the PrintDocument it needs to call
// PrintPage again.
e.HasMorePages = remainingText.Length > 0;
}

Painting a TextBox

I'm in need of a way to make TextBox appear like a parallelogram but i can't figure out how to do so. I currently have this code:
private void IOBox_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point cursor = PointToClient(Cursor.Position);
Point[] points = { cursor, new Point(cursor.X + 50, cursor.Y), new Point(cursor.X + 30, cursor.Y - 20),
new Point(cursor.X - 20, cursor.Y - 20) };
Pen pen = new Pen(SystemColors.MenuHighlight, 2);
g.DrawLines(pen, points);
}
But apparently it's not working. Either i misplaced/misused it or i'm not doing something right.
This is the method that i use to add it.
int IOCounter = 0;
private void inputOutput_Click(object sender, EventArgs e)
{
IOBox box = new IOBox();
box.Name = "IOBox" + IOCounter;
IOCounter++;
box.Location = PointToClient(Cursor.Position);
this.Controls.Add(box);
}
Any idea how i can fix it? IOBox is a UserControl made by me which contains a TextBox. Is that rightful to do?
If its possible, you should make your application using WPF. WPF is designed to do exactly what you are trying to do.
However, it can be done in WinForms, though not easily. You will need to make a new class that inherits the TextBox WinForm control. Here is an example that makes a TextBox look like a circle:
public class MyTextBox : TextBox
{
public MyTextBox() : base()
{
SetStyle(ControlStyles.UserPaint, true);
Multiline = true;
Width = 130;
Height = 119;
}
public override sealed bool Multiline
{
get { return base.Multiline; }
set { base.Multiline = value; }
}
protected override void OnPaintBackground(PaintEventArgs e)
{
var buttonPath = new System.Drawing.Drawing2D.GraphicsPath();
var newRectangle = ClientRectangle;
newRectangle.Inflate(-10, -10);
e.Graphics.DrawEllipse(System.Drawing.Pens.Black, newRectangle);
newRectangle.Inflate(1, 1);
buttonPath.AddEllipse(newRectangle);
Region = new System.Drawing.Region(buttonPath);
base.OnPaintBackground(e);
}
}
Keep in mind that you will still have to do other things, such as clipping the text, etc. But this should get you started.

I'm trying to show the current image in the pictureBox and stretch it but it's not showing anything why?

In For1 i have this code:
private void timer1_Tick(object sender, EventArgs e)
{
try
{
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Load(file_array_satellite[file_indxs_satellite]);
file_indxs_satellite = file_indxs_satellite - 1;
if (file_indxs_satellite < 0)
{
file_indxs_satellite = file_array_satellite.Length - 1;
}
}
catch
{
timer1.Enabled = false;
}
}
private void satellitesToolStripMenuItem_Click(object sender, EventArgs e)
{
file_array_satellite = Directory.GetFiles(UrlsPath, "RainImage*.*");
if (file_array_satellite.Length > 0)
{
DateTime[] creationTimes8 = new DateTime[file_array_satellite.Length];
for (int i = 0; i < file_array_satellite.Length; i++)
creationTimes8[i] = new FileInfo(file_array_satellite[i]).CreationTime;
Array.Sort(creationTimes8, file_array_satellite);
file_indxs_satellite = 0;
file_indxs_satellite = file_array_satellite.Length - 1;
timer1.Enabled = true;
}
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
this.pictureBox1.Size = new Size(500, 500);
pictureBox1.Location = new Point(this.Bounds.Width / 2,
this.Bounds.Height / 2);
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.BringToFront();
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
this.pictureBox1.Size = new Size(100, 100);
pictureBox1.Location = new Point(12,
27);
}
In the original the picturebox1 size is 100x100 and each image i stretch to fit in the pictureBox.
When it's 100x100 everything is ok i see the animation of each image in the pictureBox.
Now i did an event that when i enter with the mouse to the pictureBox area it should move to the center of the form resize to 500x500 stretch the images and show the same animation.
And when i leave the pictureBox area it should return to it's original size and location.
When i enter with the mouse to the pictureBox1 area the pictureBox just vanish i don't see it anywhere once i leave the pictureBox area i see it 100x100 in it's original place and size.
Why when i enter with the mouse to the pictureBox1 area it's vanish i don't see it in the center of the form on size 500x500 ?
file_array_satellite is string[] and file_indxs_satellite is int.
RainImage*.* are the files names on the hard disk after downloaded them.
The idea is not to convert/change the files sizes on the hard disk each time i enter or leave so i wanted that once i enter the pictureBox1 area it will stretch the current image in the pictureBox and show it . It's working when it's 100x100 but not on 500x500.
When you mouse over the PictureBox and move it to the center of the form, you are moving it out from under the mouse cursor. This causes the MouseLeave event to immediately trigger, which places it back under your mouse cursor again, which causes the MouseEnter event to trigger again, etc.
You can do something like this:
bool suppressMouseLeave;
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
suppressMouseLeave = true;
this.pictureBox1.Size = new Size(500, 500);
pictureBox1.Location = new Point(this.Bounds.Width / 2,
this.Bounds.Height / 2);
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.BringToFront();
//point the cursor to the new Position so that it's still kept on the pictureBox1
//This is important because it makes your idea acceptable.
//Otherwise you have to move your mouse onto your pictureBox and leave the
//mouse from it then to restore the pictureBox
Cursor.Position = PointToScreen(new Point(pictureBox1.Left + 250, pictureBox1.Top + 250));
suppressMouseLeave = false;
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
if(suppressMouseLeave) return;
this.pictureBox1.Size = new Size(100, 100);
pictureBox1.Location = new Point(12, 27);
}
I would venture a guess that this.Bounds.Width and this.Bounds.Height are not what you expect them to be, so the PictureBox isn't vanishing, you are just setting it to some location that is offscreen/off your form. Run Visual Studio in Debug mode and put a breakpoint around that line and see what this.Bounds is equal to. This may give you a clue as to the proper location you need to set.
How about in "in place" zoom like this?
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
Rectangle rc = pictureBox1.Bounds;
rc.Inflate(200, 200);
pictureBox1.Bounds = rc;
pictureBox1.BringToFront();
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
Rectangle rc = pictureBox1.Bounds;
rc.Inflate(-200, -200);
pictureBox1.Bounds = rc;
}

PrintDocument / PrintPreviewDialog Nothing Draws or Prints

I am playing around with the PrintDocument and PrintPreviewDialog classes to learn them, but I cannot seem to get the document to print anything when sent to the printer, and also nothing draws at all in the PrintPreviewDialog, not even a white blank page, it is all grey. Ive tried changing margins, draw locations, drawing giant rectangle, etc., nothing shows up. I am not very familiar with GDI+ so I am sure I am missing something, but I cannot find what.
Here is the relevant code I am using:
//Print
private void menuPrint_click(Object source, EventArgs e)
{
printDocumentPage = 0;
printDocument1.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(printDocument1_PrintPage);
printDocument1.BeginPrint += new System.Drawing.Printing.PrintEventHandler(printDocument1_BeginPrint);
printPreviewDialog1.Document = printDocument1;
printPreviewDialog1.ShowDialog();
}
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
// Save our print action so we know if we are printing
// a preview or a real document.
printAction = e.PrintAction;
// Set some preferences, our method should print a box with any
// combination of these properties being true/false.
printDocument1.OriginAtMargins = false;
if (printAction != PrintAction.PrintToPreview)
{
PrintDialog printDlg = new PrintDialog();
printDocument1.DocumentName = "Print Document Simple Text";
printDlg.Document = printDocument1;
// Show printer dialog
if (printDlg.ShowDialog() == DialogResult.OK)
{
printDocument1.PrinterSettings = printDlg.PrinterSettings;
}
else
{
e.Cancel = true;
}
}
}
void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Graphics g = e.Graphics;
RectangleF marginBounds = e.MarginBounds;
RectangleF printableArea = e.PageSettings.PrintableArea;
//if (printAction == PrintAction.PrintToPreview)
//g.TranslateTransform(printableArea.X, printableArea.Y);
int availableWidth = (int)Math.Floor(printDocument1.OriginAtMargins ? marginBounds.Width : (e.PageSettings.Landscape ? printableArea.Height : printableArea.Width));
int availableHeight = (int)Math.Floor(printDocument1.OriginAtMargins ? marginBounds.Height : (e.PageSettings.Landscape ? printableArea.Width : printableArea.Height));
//g.DrawRectangle(Pens.Red, 0, 0, availableWidth - 1, availableHeight - 1);
// Doesnt work either
//g.DrawRectangle(Pens.Red, 0, 0, 100, 100);
System.Drawing.Font myFont = new System.Drawing.Font("Courier New", 10, FontStyle.Underline, GraphicsUnit.Pixel);
float lineHeight = myFont.GetHeight(g);
float yLineTop = e.MarginBounds.Top;
if (printDocumentPage >= m_report.PageCount)
{
e.HasMorePages = false;
return;
}
otPage page = m_report.pageAt((int)printDocumentPage);
for (int i = 0; i < page.LineCount ; i++)
{
otLine line = page.lineAt(i);
g.DrawString(line.getPrintLine(true), myFont, Brushes.Black, e.MarginBounds, StringFormat.GenericTypographic);
yLineTop += lineHeight;
}
printDocumentPage++;
if (printDocumentPage < m_report.PageCount)
{
e.HasMorePages = true;
return;
}
e.HasMorePages = false;
}
The PrintPreviewDialog appears, and both event handlers are run, it loops through the lines correctly and draws each of them, but nothing is drawn on the dialog and when sent to the printer, it prints empty pages. The PrintPreviewDialog is all grey, no white pages or anything, and zooming at all levels does nothing.
EDIT: Getting rid of my current page/line handling and just trying to draw a single string, any string, or rectangle, something nothing works.
Here is a screenshot of the print preview screen. Changing the zoom/pages does nothing either:
EDIT2:
I downloaded this very simple example project from http://www.c-sharpcorner.com/uploadfile/mahesh/printpreviewcontrol-in-c-sharp/ which I assume works as intended. Straight out of the box this does the same thing. Only a grey screen shows (and the button on the form). Nothing prints in the printpreviewcontrol at all.
What could be the problem?
EDIT3:
I tested this on another development machine and it works fine. Why wouldnt it work on my computer? The other computer is 64 bit whereas this is 32, but was built with same settings. Could it be my .NET framework is messed up?
EDIT4:
Sorry for all the edits. Ive found that if I switch my default printer, it works perfectly. Something to do with the e.MarginBounds.

Categories