Visual Studio 2012 C# Print printer list etc

Posted on Wednesday, May 15, 2013



I have a need to print out some documents programmatically using .Net and C#.  

This document just goes over the a few specific features Selecting a printer to print to.  Printing more copies, setting the portrait or landscape, and printing ranges.




Set up a Virtual Printer


Before I starting writing this program I downloaded and installed a virtual printer.  In the past I have used a CUPS printer on my Mac but for this time around I looked around for something to run directly on my Windows 7 machine.  I found CutePDF at http://www.cutepdf.com/Products/CutePDF/writer.asp [1]
I installed this virtual server so I could test my printing without using up so much paper.







Create a basic Windows Form Application





From Visual Studio 2012 Professional click on New project (or from the menu select File -->  New --> Project





  1.  Select Windows Forms Application
  2.  I named mine PrinterListTestApp
  3.   Click OK








In the Toolbox search for "print"   the PrintDocument and PrintPreviewControl will be displayed








Drag the PrintDocument and PrintPreivewControl on the the Form.








Double click on the PrintDocument1





This will open the PrintPageEventArgs method for this document


Here is the code I wrote for the document creation


        private void printDocument1_PrintPage(object sender,
            Font font = new Font("Microsoft Sans Serif", 48F,
                               System.Drawing.FontStyle.Bold,               
                               System.Drawing.GraphicsUnit.Point,
                             ((byte)(0)));
            Graphics g = e.Graphics;

            g.DrawString("Print Me", font,
                     System.Drawing.Brushes.Black, 100, 50);
        }








I resized the form a bit and resized the PrintPreviewControl and resize it slightly





From the PrintPreviewControl properties set the Document Property to the printDocument1






Run the program by clicking start





The program shows the print preview for this document.







Add a "Print" button to the form




        private void button1_Click(object sender, EventArgs e) {
            this.printDocument1.Print();
        }


Here is the code to print the document.





Run the program and click Print

This will print to your default printer,  so how do we select which printer to print to?



First get the list of printer





Add a combo box to the form




namespace PrinterListTestApp {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();

            String defaultPrinter = (new System.Drawing.Printing.
                            PrinterSettings()).PrinterName;
            foreach (string printer in System.Drawing.Printing.
                            PrinterSettings.InstalledPrinters) {
                comboBox1.Items.Add(printer);
                if(printer.Equals(defaultPrinter)){
                    comboBox1.SelectedItem = printer;
                }
            }
        }



Here is the code I added to add the list of printers to the combo box.

System.Drawing.Printing.PrinterSettings.InstalledPrinters

Will return a list of the installed printers.



(new System.Drawing.Printing.PrinterSettings()).PrinterName

Will get the name of the current default printer.



Run the program




Here you can see it selected my default printer the "Cute PDF Writer"




If I pull down the list you can see all my printers



Now that there is a combo box the code needs to be updated to print to the selected printer.


        private void button1_Click(object sender, EventArgs e) {
            this.printDocument1.PrinterSettings.PrinterName
                            = comboBox1.SelectedItem.ToString();
            this.printDocument1.Print();
        }



Run the program again





Select a printer that is not the default printer and click print.

It should print to the printer you selected







Printing more copies






Add A NumericUpDown to the Form (and a label)





Set the Value property to 1 of the NumericUpDown






I also set its minimum to 1

A simple solution is to just add a loop


        private void button1_Click(object sender, EventArgs e) {
            for (int x = 0; x < numericUpDown1.Value; x++) {
                this.printDocument1.PrinterSettings.PrinterName
                         = comboBox1.SelectedItem.ToString();
                this.printDocument1.Print();
            }
        }



This worked out fine for me



Where you can just set the Copies property like so


        private void button1_Click(object sender, EventArgs e) {
            this.printDocument1.PrinterSettings.PrinterName
                     = comboBox1.SelectedItem.ToString();
            this.printDocument1.PrinterSettings.Copies
                            = (short)numericUpDown1.Value; ;
            this.printDocument1.Print();
        }


But this would not work for me, and I am not really sure why.  There is a note from Microsoft saying "Some printers might not support printing more than one copy at a time.
So I am at a loss for the time being I am stuck using loops

Printing Landscape or Portrait




Put a few radio buttons inside a GroupBox


Double click on the radio buttons to create their CheckChanged events




        private void radioButton1_CheckedChanged(object sender,
                                                 EventArgs e) {
            if (radioButton1.Checked) {
                this.printDocument1.DefaultPageSettings.Landscape
                                             = false;
                this.printPreviewControl1.InvalidatePreview();
                this.printPreviewControl1.AutoZoom = true;
            }
        }

        private void radioButton2_CheckedChanged(object sender,
                                                EventArgs e) {
            if (radioButton2.Checked) {
                this.printDocument1.DefaultPageSettings.Landscape
                                               = true;
                this.printPreviewControl1.InvalidatePreview();
                this.printPreviewControl1.AutoZoom = true;
            }
        }



Here is the code I came up with that will change the  PrintPreview from landscape to portrait and resize it to fit.


Run the program






That works well, but still does not print it out correctly.


        private void button1_Click(object sender, EventArgs e) {
            if (radioButton2.Checked) {
                this.printDocument1.DefaultPageSettings.Landscape
                                 = radioButton2.Checked;
            }

            for (int x = 0; x < numericUpDown1.Value; x++) {
                this.printDocument1.PrinterSettings.PrinterName
                         = comboBox1.SelectedItem.ToString();
                this.printDocument1.Print();
            }
        }


I updated the print code to the following so it will print in landscape or portrait, whichever is selected.







At this point the Generating Preview dialog that pops up real quickly whenever I change the page orientation is driving me a little batty.

It looks like it's easier said than done.  I did find this page http://anotherdeveloper.blogspot.com/2012/01/how-to-hide-net-printpreviewcontrols.html [3] that looked promising but as I am crunched for time at the moment I guess I have to skip fixing this for now.




Printing Ranges


How do you only print pages 2, 4, and 6 of a document?

First I need to update my code to print out 10 pages.


namespace PrinterListTestApp {
    public partial class Form1 : Form {
        private int currentPage = 1;

        public Form1() {
            InitializeComponent();

            String defaultPrinter = (new System.Drawing.Printing.
                           PrinterSettings()).PrinterName;
            foreach (string printer in System.Drawing.Printing.
                           PrinterSettings.InstalledPrinters) {
                comboBox1.Items.Add(printer);
                if(printer.Equals(defaultPrinter)){
                    comboBox1.SelectedItem = printer;
                }
            }
        }

        private void printDocument1_PrintPage(object sender,
                 System.Drawing.Printing.PrintPageEventArgs e) {
            Font font = new Font("Microsoft Sans Serif", 48F,
                   System.Drawing.FontStyle.Bold,
                   System.Drawing.GraphicsUnit.Point,
                 ((byte)(0)));
            Graphics g = e.Graphics;

            if (this.currentPage < 11) {
                g.DrawString("Page " + this.currentPage, font,
                        System.Drawing.Brushes.Black, 100, 50);
                this.currentPage++;
                e.HasMorePages = true;
            }
            if (this.currentPage > 10) {
                currentPage = 1;
                e.HasMorePages = false;
            }
        }







Add another NumericUpDown Spinner to display different Pages in the Preview.



Double click on the new spinner to open its Value Changed event


        private void numericUpDown2_ValueChanged(object sender,
                                   EventArgs e) {
            printPreviewControl1.StartPage
                              = (short) numericUpDown2.Value - 1;
        }


This code will update the preview page when the spinner number changes.

Run the program and test it




As you change the page numbers you change the page number being previewed.







Add a couple of NumericUpDown spinners to allow a range to be set.



        private void button1_Click(object sender, EventArgs e) {
            if (radioButton2.Checked) {
                this.printDocument1.DefaultPageSettings.Landscape
                              = radioButton2.Checked;
            }

            for (int x = 0; x < numericUpDown1.Value; x++) {
                this.printDocument1.PrinterSettings.PrinterName
                         = comboBox1.SelectedItem.ToString();
                this.printDocument1.PrinterSettings.PrintRange
                                  = PrintRange.SomePages;
                this.printDocument1.PrinterSettings.FromPage
                                  = (short) numericUpDown3.Value;
                this.printDocument1.PrinterSettings.ToPage
                                  = (short)numericUpDown4.Value;
                this.printDocument1.Print();
            }
        }



Here is the updated code.  The PrintRange must be set to SomePages.  Then the FromPage and ToPage must be set.




Run the program






Set a range that is a subset


And if I print it out… it prints them all out.  So that did not work.....




I think I finally get it


It took me a while but I think I finally get it.  Setting a PrintDocuments FromPage does not do anything magical it just stores a value that can be retrieved from the PrintPageEventArgs.

With that in mind here is an update to my code


        private void printDocument1_PrintPage(object sender,
                System.Drawing.Printing.PrintPageEventArgs e) {
            Font font = new Font("Microsoft Sans Serif", 48F,
                   System.Drawing.FontStyle.Bold,
                   System.Drawing.GraphicsUnit.Point,
                 ((byte)(0)));
            Graphics g = e.Graphics;

            //Set the currentPage to the FromPage
            if (this.currentPage == 1 &&
                   e.PageSettings.PrinterSettings.PrintRange ==
                                     PrintRange.SomePages) {
                this.currentPage =
                      e.PageSettings.PrinterSettings.FromPage;
            }

            if (this.currentPage < 11) {
                g.DrawString("Page " + this.currentPage, font,
                        System.Drawing.Brushes.Black, 100, 50);
                this.currentPage++;
                e.HasMorePages = (this.currentPage < 11)
                       && this.currentPage <
                      e.PageSettings.PrinterSettings.ToPage + 1;
            }
            if (!e.HasMorePages) {
                currentPage = 1;
            }
        }


Now it prints out the range of pages I am expecting.


The same idea would go for storing the number of copies to print out.  You could handle it all in the PrintPageEventArgs.



References

[1]        Cute PDF         http://www.cutepdf.com/Products/CutePDF/writer.asp
                Accessed 05/2013
[2]        PrinterSettings.Copies Property
                Accessed 05/2013
[3]        How-to Hide the .NET PrintPreviewControl's "Generating Previews" Dialog
                Accessed 05/2013

2 comments:

  1. Hello could you show how a simple form of
    print some text, for example in the form I create
    panel, this panel I enter any text you choose size
    text and send the print button (Print)

    ReplyDelete
    Replies
    1. Printing only a Panel is a bit of work, but I did find a tool PrintForm for printing an entire form and I made a Blog post on it at
      http://www.whiteboardcoder.com/2013/08/printing-with-printform.html

      Hopefully that is something you can use.

      Delete