using C#, I wrote the below code to print out images in an array (size = totalToPrint) of Picturebox pictureBoxArr[] each in size of 100x100 pixel. I want to print them vertically with the 20-pixel distance between them heightDistanceBetweenImages the problem is that it only prints on 1 page (say letter size) no matter how many images ( then it only prints 8 images and dismisses the rest). How can I solve this problem, and print it on multiple pages?
int totalToPrint;
int xFirstAncorPoint = 100;
int yFirstAncorPoint = 100;
int ImagSize = 100; // Squre of 100x100 pixel
int heightDistanceBetweenImages = 20;
PrintDialog pd = new PrintDialog();
PrintDocument pDoc = new PrintDocument();
pDoc.PrintPage += PrintPicture;
pd.Document = pDoc;
if (pd.ShowDialog() == DialogResult.OK)
{
pDoc.Print();
}
}
public void PrintPicture(Object sender, PrintPageEventArgs e)
{
Bitmap bmp1 = new Bitmap(ImagSize , totalToPrint * (ImagSize + heightDistanceBetweenImages));
for (int i = 0; i < totalToPrint; i++)
{
pictureBoxArr[i].DrawToBitmap(bmp1, new Rectangle(0, i * (heightDistanceBetweenImages + ImagSize), pictureBoxArr[0].Width, pictureBoxArr[0].Height));
}
e.Graphics.DrawImage(bmp1, xFirstAncorPoint, yFirstAncorPoint);
bmp1.Dispose();
}
You are about half way there. PrintDocument would be your future reference.
The PrintPage gives you more than just a drawing space; it also has your page bounds, page margins, etc. It also has HasMorePages property that you set if you need to print more pages. This property defaults to false, so you were only printing 1 page. Also if anything is outside the bounds of the page, it would not print that. With a little change here and there, you would end up with something like this.
// using queue to manage images to print
Queue<Image> printImages = new Queue<Image>();
int totalToPrint;
int xFirstAncorPoint = 100;
int yFirstAncorPoint = 100;
int ImagSize = 100; // Squre of 100x100 pixel
int heightDistanceBetweenImages = 20;
private void btnPrintTest_Click(object sender, EventArgs e) {
PrintDialog pd = new PrintDialog();
PrintDocument pDoc = new PrintDocument();
pDoc.PrintPage += PrintPicture;
pd.Document = pDoc;
if (pd.ShowDialog() == DialogResult.OK) {
// add image references to printImages queue.
for (int i = 0; i < pictureBoxArr.Length; i++) {
printImages.Enqueue(pictureBoxArr[i].Image);
}
pDoc.Print();
}
}
private void PrintPicture(object sender, PrintPageEventArgs e) {
int boundsHeight = e.MarginBounds.Height; // Get height of bounds that we are expected to print in.
int currentHeight = yFirstAncorPoint;
while (currentHeight <= boundsHeight && printImages.Count > 0) {
var nextImg = printImages.Peek();
int nextElementHeight = nextImg.Height + heightDistanceBetweenImages;
if (nextElementHeight + currentHeight <= boundsHeight) {
e.Graphics.DrawImage(nextImg, new PointF(xFirstAncorPoint, currentHeight + heightDistanceBetweenImages));
printImages.Dequeue();
}
currentHeight += nextElementHeight;
}
// how we specify if we may have more pages to print
e.HasMorePages = printImages.Count > 0;
}
Hopefully this gets you on the right path and with some minor tweaks for your code, you will have what you need.
Related
Using instructions, I found on stackoverflow I can print across multiple pages. But if I print the document multiple times without leaving the app, the pages are combined.
Result: First time 4 separate pages after 4 prints only 1 single page.
Here is my code (the form just has a single button). I think I need to reset something somewhere but can't figure it out. Appreciate your help!
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
namespace PrintIt
{
public partial class Form1 : Form
{
int N, Y, PageNr;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
printDocument1.DocumentName = "Test Print";
printDialog1.Document = printDocument1;
printDocument1.PrintPage += new PrintPageEventHandler(printit);
// Initialize the dialog's PrinterSettings property to hold user
// defined printer settings.
pageSetupDialog1.PageSettings =
new System.Drawing.Printing.PageSettings();
//Show the dialog storing the result.
DialogResult result = pageSetupDialog1.ShowDialog();
if (result == DialogResult.OK)
{
printDocument1.DefaultPageSettings.PaperSize = pageSetupDialog1.PageSettings.PaperSize;
printDocument1.DefaultPageSettings.Landscape = pageSetupDialog1.PageSettings.Landscape;
string message = "Would you like to view the print preview first?";
string caption = "Print Preview";
MessageBoxButtons buttons = MessageBoxButtons.YesNo;
Y = 0;
N = 0;
PageNr = 1;
result = MessageBox.Show(message, caption, buttons); // Displays the MessageBox.
if (result == System.Windows.Forms.DialogResult.Yes)
{
printPreviewDialog1.Document = printDocument1;
((ToolStripButton)((ToolStrip)printPreviewDialog1.Controls[1]).Items[0]).Enabled = false;//disable the direct print from printpreview.as when we click that Print button PrintPage event fires again.
printPreviewDialog1.ShowDialog();
}
if (printDialog1.ShowDialog() == DialogResult.OK)
{
printDocument1.Print();
}
}
}
private void printit(System.Object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Font LFont = new Font("Arial", 12);
float fontHeight = LFont.GetHeight();
int startY = 40;
int pageHeight = printDocument1.DefaultPageSettings.PaperSize.Height;
e.Graphics.DrawString("Page:" + PageNr, LFont, Brushes.Black, PageNr * 100, 10);
for (int i = Y; i < 200; i++)
{
e.Graphics.DrawString("Line: " + i, LFont, Brushes.Black, PageNr * 100, startY + N);
N += (int)fontHeight;
if (startY + N >= pageHeight - 100)
{
e.HasMorePages = true;
N = 0;
Y = i;
PageNr += 1;
return;
}
}
e.HasMorePages = false;
}
}
}
Finally figured it out. At first, I added the necessary print objects from the toolbox onto the form design. While this works it does not handle multiple pages correctly.
I now removed those objects from the form design and added them in code when the button is pressed:
PrintPreviewDialog printPreviewDialog1 = new PrintPreviewDialog();
PrintDocument printDocument1 = new PrintDocument();
PageSetupDialog pageSetupDialog1 = new PageSetupDialog();
PrintDialog printDialog1 = new PrintDialog();
Works like a charm!
I'm very new to visual C# I want to display an array of images in a picture box
Here's my code:
string[] list = Directory.GetFiles(#"C:\\pictures", "*.jpg");
Image[] images = new Image[5];
for (int index = 0; index < 5; index++)
{
//HERE IS WHERE IM STUCKED WITH
picturebox[index] = Image.FromFile(list[index]);
}
Edit-1 : This answer has a scope limited to Win-Forms C#.
You need certain assemblies added in your application before using this code.
using System.IO;
using System.Windows.Forms;
Edit ended;
Original Answer
You have to draw all image to one image for displaying them in single picturebox
That is bit complex you can use mutiple pictureboxes
In following code they are created dynamically according to need:
// For confirm visibility of all images set
this.AutoScroll = true;
string[] list = Directory.GetFiles(#"C:\pictures", "*.jpg");
PictureBox[] picturebox= new PictureBox[list.Length];
int y = 0;
for (int index = 0; index < picturebox.Length; index++)
{
this.Controls.Add(picturebox[index]);
// Following three lines set the images(picture boxes) locations
if(index % 3 == 0)
y = y + 150; // 3 images per rows, first image will be at (20,150)
picturebox[index].Location=new Point(index * 120 + 20, y);
picturebox[index ].Size = new Size(100,120);
picturebox[index].Image = Image.FromFile(list[index]);
}
The answer provided throws an object reference exception. Otherwise thanks for the example!
for (int index = 0; index < picturebox.Length; index++)
{
this.Controls.Add(picturebox[index]);
// Following three lines set the images(picture boxes) locations
should be
for (int index = 0; index < picturebox.Length; index++)
{
picturebox[index] = new PictureBox();
this.Controls.Add(picturebox[index]);
// Following three lines set the images(picture boxes) locations
Use picturebox[index].Image = Image.FromFile(list[index]);
//this code help you to work with picturebox in arraye
public partial class Form_Begin : Form
{
PictureBox[] pictureBoxs = new PictureBox[50];
public Form_Begin()
{
InitializeComponent();
pictureBoxs[0] = pictureBox1;
pictureBoxs[1] = pictureBox2;
pictureBoxs[2] = pictureBox3;
pictureBoxs[3] = pictureBox4;}
List<PictureBox> pictureBoxes = new List<PictureBox>();
private void buttonX1_Click(object sender, EventArgs e)
{
for (int i = 0; i <2; i++)
{
pictureBoxs[i].Image =your_name_project.Properties.Resources.Image_1; // Load Image_1 from Resources on property of picturebox
}
for (int i = 2; i < 4; i++)
{
pictureBoxs[i].Image =your_name_project.Properties.Resources.Image_2; // Load Image_12 from Resources on property of picturebox
}
private void picbutton_Click(object sender, EventArgs e)
{
OpenFileDialog open = new OpenFileDialog();
PictureBox[] picture = new PictureBox[5];
int x = 0;
int y = 15;
for (int index = length; index < picture.Length; index++)
{
picture[index] = new PictureBox();
picture[index].Size = new Size(100, 50);
open.Title = "OPen Image";
open.Filter = "JPG Files (*.jpg)|*.jpg|JPEG Files (*.jpeg)|*.jpeg|PNG Files (*.png)|*.png|GIF Files (*.gif)|*.gif";
DialogResult result = open.ShowDialog();
if (result == DialogResult.OK)
{
picture[index].BackgroundImage = new Bitmap(open.FileName);
picture[index].SizeMode = PictureBoxSizeMode.AutoSize;
listBox1.Controls.Add(picture[index]);
if ((x % 3 == 0) && (index != 0))
{
y = y + 150; // 3 images per rows, first image will be at (20,150)
x = 0;
}
picture[index].Location = new Point(x * 210 + 20, y);
picture[index].Size = new Size(200, 150);
x++;
}
}
}
I am making a program that can write in Bengali (a virtual keyboard) or in English. Everything was perfect until I started programming the printing. The user should be able to select any text and change the font and color. Because every character could be different, I need to print character by character. Here is my code:
private void printToolStripMenuItem_Click(object sender, EventArgs e)
{
PrintDialog print = new PrintDialog();
doc = new System.Drawing.Printing.PrintDocument();
print.Document = doc;
doc.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(printDoc);
if (print.ShowDialog() == DialogResult.OK)
{
doc.Print();
}
}
private void printDoc(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
System.IO.StringReader reader = new System.IO.StringReader(richTextBox1.Text);
float linesPerPage = 0;
float yPosition = 0;
int count = 0;
float leftMargin = e.MarginBounds.Left;
float rightMargin = e.MarginBounds.Right;
float topMargin = e.MarginBounds.Top;
string line = null;
Font printFont = this.richTextBox1.Font;
SolidBrush printBrush = new SolidBrush(Color.Black);
int charpos = 0;
int xPosition = (int)leftMargin;
linesPerPage = e.MarginBounds.Height / printFont.GetHeight(e.Graphics);
while (count < linesPerPage && ((line = reader.ReadLine()) != null))
{
xPosition = (int)leftMargin;
yPosition = topMargin + (count * printFont.GetHeight(e.Graphics));
count++;
for (int i = 0; i < line.Length; i++)
{
richTextBox1.Select(charpos, 1);
if ((xPosition + ((int)e.Graphics.MeasureString(richTextBox1.SelectedText, richTextBox1.SelectionFont).Width)) > rightMargin)
{
count++;
if (!(count < linesPerPage))
{
break;
}
xPosition = (int)leftMargin;
yPosition = topMargin + (count * printFont.GetHeight(e.Graphics));
}
printBrush = new SolidBrush(richTextBox1.SelectionColor);
e.Graphics.DrawString(richTextBox1.SelectedText, richTextBox1.SelectionFont, printBrush, new PointF(xPosition, yPosition));
xPosition += ((int)e.Graphics.MeasureString(richTextBox1.SelectedText, richTextBox1.SelectionFont).Width);
charpos++;
}
}
if (line != null)
{
e.HasMorePages = true;
}
else
{
e.HasMorePages = false;
printBrush.Dispose();
}
}
However, when I get a print preview, it shows a space between each of the the characters:
I think this is because e.Graphics.MeasureString() is not giving me the tightest bounding box possible, or it's not giving me the exact width of the character as specified by the font. I'm quite new to C# Anyone know of a way to get the exact width of a character? It's been stumping me for a long time.
According to this MSDN article:
The (Graphics Class) MeasureString method is designed for use with individual string and includes a small amount of extra space before and after the string to allow for overhanging glyphs
You can instead use TextRenderer.MeasureString() to get the precise font width.
GOT IT. Finally. You need to use e.Graphics.MeasureString() after all. Just another overload of it. Use this:
e.Graphics.MeasureString(richTextBox1.SelectedText, richTextBox1.SelectionFont, new PointF(xPosition, yPosition), StringFormat.GenericTypographic).Width;
To remove the space you need to pass StringFormat.GenericTypographic. Hope it helps. (you can use my code for printing text with different color and text if you replace e.Graphics.MeasureString(string, Font) with the above).
I am attempting to print something using a C# Winforms application. I can't seem to understand how multiple pages works. Let's say I have the following code in my constructor:
private string _stringToPrint;
_stringToPrint = "";
for (int i = 0; i < 120; i++)
{
_stringToPrint = _stringToPrint + "Line " + i.ToString() + Environment.NewLine;
}
Then I have this code on my button click event:
private void MnuFilePrintClick(object sender, EventArgs e)
{
var pd = new PrintDocument();
pd.PrintPage += pd_PrintPage;
var z = new PrintPreviewDialog { Document = pd };
z.ShowDialog(this);
}
void pd_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
var font = new Font("Arial", 10f, FontStyle.Regular);
g.DrawString(_stringToPrint, font, Brushes.Black, new PointF(10f, 10f));
}
Right now, when I run this code, it is giving me one page and after like 70 lines, it just runs off the paper. How would I print this string so that it prints enough for one page, then runs over to the second page, etc..?
You could have a counter and set the amount of lines you want per page like so:
private string[] _stringToPrint = new string[100]; // 100 is the amount of lines
private int counter = 0;
private int amtleft = _stringToPrint.Length;
private int amtperpage = 40; // The amount of lines per page
for (int i = 0; i < 120; i++)
{
_stringToPrint[i] ="Line " + i.ToString();
}
Then in pd_PrintPage:
void pd_PrintPage(object sender, PrintPageEventArgs e)
{
int currentamt = (amtleft > 40)?40:amtleft;
Graphics g = e.Graphics;
var font = new Font("Arial", 10f, FontStyle.Regular);
for(int x = counter; x < (currentamt+counter); x++)
{
g.DrawString(_stringToPrint[x], font, Brushes.Black, new PointF(10f, (float)x*10));
// x*10 is just so the lines are printed downwards and not on top of each other
// For example Line 2 would be printed below Line 1 etc
}
counter+=currentamt;
amtleft-=currentamt;
if(amtleft<0)
e.HasMorePages = true;
else
e.HasMorePages = false;
// If e.HasMorePages is set to true and the 'PrintPage' has finished, it will print another page, else it wont
}
I have had bad experiences with e.HasMorePages so this may not work.
Let me know if this works and I hope it helps!
I'd like to modify this PictureBox Array Project.
i want to put a reset button than will clear all the PictureBox Array it created
more likely the form will be empty again as like from the beginning.
this is some of it's code;
// Function to add PictureBox Controls
private void AddControls(int cNumber)
{
imgArray = new System.Windows.Forms.PictureBox[cNumber]; // assign number array
for (int i = 0; i < cNumber; i++)
{
imgArray[i] = new System.Windows.Forms.PictureBox(); // Initialize one variable
}
// When call this function you determine number of controls
}
private void ImagesInFolder()
{
FileInfo FInfo;
// Fill the array (imgName) with all images in any folder
imgName = Directory.GetFiles(Application.StartupPath + #"\Images");
// How many Picture files in this folder
NumOfFiles = imgName.Length;
imgExtension = new string[NumOfFiles];
for (int i = 0; i < NumOfFiles; i++)
{
FInfo = new FileInfo(imgName[i]);
imgExtension[i] = FInfo.Extension; // We need to know the Extension
//
}
}
private void ShowFolderImages()
{
int Xpos = 8;
int Ypos = 8;
Image img;
Image.GetThumbnailImageAbort myCallback =
new Image.GetThumbnailImageAbort(ThumbnailCallback);
MyProgress.Visible = true;
MyProgress.Minimum = 1;
MyProgress.Maximum = NumOfFiles;
MyProgress.Value = 1;
MyProgress.Step = 1;
string[] Ext = new string [] {".GIF", ".JPG", ".BMP", ".PNG"};
AddControls(NumOfFiles);
for (int i = 0; i < NumOfFiles; i++)
{
switch (imgExtension[i].ToUpper())
{
case ".JPG":
case ".BMP":
case ".GIF":
case ".PNG":
img = Image.FromFile(imgName[i]); // or img = new Bitmap(imgName[i]);
imgArray[i].Image = img.GetThumbnailImage(64, 64, myCallback, IntPtr.Zero);
img = null;
if (Xpos > 432) // six images in a line
{
Xpos = 8; // leave eight pixels at Left
Ypos = Ypos + 72; // height of image + 8
}
imgArray[i].Left = Xpos;
imgArray[i].Top = Ypos;
imgArray[i].Width = 64;
imgArray[i].Height = 64;
imgArray[i].Visible = true;
// Fill the (Tag) with name and full path of image
imgArray[i].Tag = imgName[i];
imgArray[i].Click += new System.EventHandler(ClickImage);
this.BackPanel.Controls.Add(imgArray[i]);
Xpos = Xpos + 72; // width of image + 8
Application.DoEvents();
MyProgress.PerformStep();
break;
}
}
MyProgress.Visible = false;
}
private bool ThumbnailCallback()
{
return false;
}
private void btnLoad_Click(object sender, System.EventArgs e)
{
ImagesInFolder(); // Load images
ShowFolderImages(); // Show images on PictureBox array
this.Text = "Click wanted image";
}
how can i do that?
sorry i don't have any code for the reset button yet.
i don't know what to do, i am new to c#.
You can just set the image to null:
private void Clear()
{
foreach (var pictureBox in imgArray)
{
pictureBox.Image = null;
pictureBox.Invalidate();
}
}
I will follow this steps to be sure everything will be fred :
private void btnReset_Click(object sender, System.EventArgs e)
{
for(int x = this.BackPanel.Controls.Count - 1; x >= 0; x--)
{
if(this.BackPanel.Controls[x].GetType() == typeof(PictureBox))
this.BackPanel.Controls.Remove(x);
}
for(int x = 0; x < imgArray.Length; x++)
{
imgArray[x].Image = null;
imgArray[x] = null;
}
}
Assuming there are no other child controls in this.Backpanel (the container control that is actually displaying your images), this will probably work:
private void ClearImages() {
this.BackPanel.Controls.Clear();
imgArray = null;
}
Good Luck!
If you are drawing on a pictureBox and you want to clear it:
Graphics g = Graphics.FromImage(this.pictureBox1.Image);
g.Clear(this.pictureBox1.BackColor);
After that you can draw again on the control.
I hope this can help to someone.