środa, 25 listopada 2009

Drukowanie

Drukowanie jest naturalną czynnością. W przypadku C# dysponujemy wieloma gotowymi metodami i klasami ułatwiającymi to zadanie. Artykuł rozpocznę od najbardziej elementarnych czynności które należy podjąć w celu wydrukowania strony, aż po bardziej zaawansowane mechanizmy. 



 

Każda operacja drukowania rozpoczyna się od utworzenia obiektu klasy PrintDocument. Cały proces drukowania rozpoczyna się w momencie wywołania metody Print klasy PrintDocument. Wywołanie tej metody pociąga za sobą wywołanie zdarzeń: BeginPrint, PrintPage, EndPrint. Jak z samych nazw można się domyślić kolejne zdarzenia oznaczają: rozpoczęcie procesu drukowania, drukowanie strony, zakończenie drukowania. Szczególnie ciekawe jest zdarzenie PrintPage. Jest ono wywoływane podczas drukowania każdej nowej strony. Zdarzenie to, na podstawie właściwości HasMorePages przesyła do drukarki informację, czy do drukowania pozostały jeszcze jakieś strony. Oprócz tego zdarzenie PrintPage zawiera dane które zamierzamy wydrukować.

Stwórzmy dla przykładu najprostszą aplikację pozwalającą na wydrukowanie, krótkiej ankiety osobowej:




Na formatkę rzucamy 4 labele i textboxy oraz jeden button (nazwy kontrolek pozostawiłem standardowe tj. label1, textBox1…). Po naciśnięciu na klawisz Drukuj, chcielibyśmy aby nasza drukarka zabrała się do pracy:) (po drodze oczywiście dodamy okienko umożliwiające wybranie która drukarka ma wydrukować dla nas stronę).
Oczywiście nasz interfejs należy oprogramować:



    1         private PrintDocument printDocument;
    2         private PrintDialog printDialog; //Okno dialogowe wyboru drukarki
    3         public Form1()
    4         {
    5             InitializeComponent();
    6         }
    7
    8         private void button1_Click(object sender, EventArgs e)
    9         {
   10             printDocument = new PrintDocument();
   11             printDialog = new PrintDialog();
   12             printDialog.Document = printDocument;
   13             printDocument.PrintPage += new PrintPageEventHandler(printDocument_PrintPage);
   14             printDocument.Print();
   15         }
   16
   17         void printDocument_PrintPage(object sender, PrintPageEventArgs e)
   18         {
   19             Graphics g = e.Graphics;
   20             string textToPrint = string.Format("Imie: {0}\nNazwisko: {1}\nRok urodzenia: {2}\nMiasto: {3}", textBox1.Text, textBox2.Text, textBox3.Text, textBox4.Text);
   21             using (Font font = new Font("Arial", 10))
   22             {
   23                 g.DrawString(textToPrint, font, Brushes.Black, 50, 50);
   24             }
   25         }

Dzięki obiektowi Graphics, niejako "malujemy" to co chcemy zobaczyć na wydrukowanej stronie. 


Całym procesem drukowania zarządza klasa PrintDocument. Dzięki niej możemy zarządzać wszystkimi aspektami drukowania. Spójrzmy na diagram przedstawiający podstawowe właściwości klas zarządzających procesem drukowania:


Oczywiście praktycznie wszystkie te wartości można ustawić w okienku PrintDialog, jednak warto wiedzieć, że można je ustawić także z kodu.

Aby utrwalić zdobytą wiedzę, proponuję stworzyć prostą aplikację, tworzącą raport zarobków pracowników firmy. Dane pobierzemy z pliku txt (można równie dobrze pobrać je z bazy danych). Struktura pliku jest następująca:

Jak widać mamy w naszym pliku kolumny takie jak: imię, nazwisko, wiek oraz zarobki. Interfejs naszej aplikacji może wyglądać tak:

Kod:


    1 using System;
    2 using System.Collections.Generic;
    3 using System.ComponentModel;
    4 using System.Data;
    5 using System.Drawing;
    6 using System.Linq;
    7 using System.Text;
    8 using System.Windows.Forms;
    9
   10 using System.IO;
   11 using System.Drawing.Printing;
   12
   13 namespace WindowsFormsApplication7
   14 {
   15     public partial class Form1 : Form
   16     {
   17         private StreamReader sr;
   18         private float lineHeight;
   19         private PrintDocument pd;
   20
   21         public Form1()
   22         {
   23             InitializeComponent();
   24             pd = new PrintDocument();
   25         }
   26
   27         private void button1_Click(object sender, EventArgs e)
   28         {
   29             using (OpenFileDialog openFile = new OpenFileDialog())
   30             {
   31                 openFile.Filter = "Text Files |*.txt";
   32                 if (openFile.ShowDialog() == DialogResult.OK)
   33                 {
   34                     richTextBox1.LoadFile(openFile.FileName, RichTextBoxStreamType.PlainText);
   35                     sr = new StreamReader(openFile.FileName, Encoding.UTF8);
   36                     textBox1.Text = openFile.FileName;
   37                     button3.Enabled = true;
   38                 }
   39             }
   40
   41         }
   42
   43         private void button3_Click(object sender, EventArgs e)
   44         {
   45             pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
   46             pd.Print();
   47         }
   48
   49         void pd_PrintPage(object sender, PrintPageEventArgs e)
   50         {
   51             Graphics g = e.Graphics;
   52             int lineWrite = 2;
   53             string actualLine;
   54             string[] printLine;
   55             float y = 0;
   56             using (Font f = new Font("Arial", 10))
   57             using (Font f2 = new Font("Arial",10, FontStyle.Bold))
   58             {
   59                 lineHeight = f.GetHeight(g);
   60                 int linesPerPage = (int)((e.MarginBounds.Bottom - e.MarginBounds.Top) / lineHeight);
   61                 g.DrawString("Raport zarobków", f2, Brushes.Black, new PointF(0, y));
   62                 y = 2 * lineHeight;
   63                 while ((actualLine = sr.ReadLine()) != null)
   64                 {
   65                     printLine = actualLine.Split(';');
   66                     string firstName = printLine[0];
   67                     string lastName = printLine[1];
   68                     int age = int.Parse(printLine[2]);
   69                     decimal wage = decimal.Parse(printLine[3]);
   70                     g.DrawString(firstName, f, Brushes.Black, new PointF(0, y));
   71                     g.DrawString(lastName, f, Brushes.Black, new PointF(100, y));
   72                     g.DrawString(age.ToString(), f, Brushes.Black, new PointF(200, y));
   73                     g.DrawString(wage.ToString(), f, Brushes.Black, new PointF(300, y));
   74                     y += lineHeight;
   75                     lineWrite++;
   76                     if (lineWrite > linesPerPage)
   77                     {
   78                         e.HasMorePages = true;
   79                         break;
   80                     }
   81                 }
   82             }
   83         }
   84     }
   85 }

 Dla przykładu zamieszczam całą aplikację (wraz z kodami źródłowymi) gotową do użycia: pobierz. Odnośnie samego drukowania nie jest ono takie trudne jak się wydaje. Istnieje wiele bibliotek wspomagających ten proces (jedną z popularniejszych jest z pewnością Crystal Reports).

2 komentarze: