Drucken mehrseitiger Dokumente

    • VB.NET

    Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

      Drucken mehrseitiger Dokumente

      Heute wollen wir uns mit dem Drucken mehrseitiger Dokumente befassen, das Drucken einseitiger Dokumente ist in diesem Zusammenhang trivial, es wird nicht explizit behandelt.
      Als Empfehlung: Wählt einen PDF-Drucker als Standarddrucker aus, da wird bei der Programmenwicklung kein Papier verschwendet.
      Zuerst werden wir 3 Kreise verschiedener Durchmesser zu Papier bringen, jeden Kreis auf ein neues Blatt.
      Was wir brauchen, ist eine Form mit einem Button ein PrintDocument wowie einen PrintPreviewDialog, auf letzteren kann man auch verzichten und in einer Using-Schleife einen erstellen:

      VB.NET-Quellcode

      1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      2. Using dlg As New PrintPreviewDialog
      3. dlg.ShowDialog()
      4. End Using
      5. End Sub
      Starten wir diesen Code, werden wir erinnert, dass das Dokument keine Seiten enthält:

      Wir müssen dem Dialog mitteilen, welches Dokument zum Drucken verwendet werden soll, außerdem geben wir ihm einen Arbeitstitel, der beim PDF-Druck als Dateiname verwendet wird:

      VB.NET-Quellcode

      1. Using dlg As New PrintPreviewDialog
      2. Me.PrintDocument1.DocumentName = "Test" ' Arbeitstitel
      3. dlg.Document = Me.PrintDocument1
      4. dlg.ShowDialog()
      5. End Using
      Nun wird uns ein leeres Blatt angeboten:

      Um ein Blatt mit Inhalt zu befüllen, benötigen wir den PrintPage-Handler des zu druckenden Dokuments, wir erstellen ihn, indem wir im Designer auf PrintDocument1 doppelklicken:

      VB.NET-Quellcode

      1. Private Sub PrintDocument1_PrintPage(sender As System.Object, e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
      2. End Sub
      Auch dieser Code liefert nur ein leeres Dokument.
      Zum Test fügen wir eine einfache DrawString-Anweisung in die PrintDocument1_PrintPage-Prozedur ein:

      VB.NET-Quellcode

      1. e.Graphics.DrawString("Bla", New Font("arial", 30), Brushes.Black, New Point(30, 30))
      Und schon passiert etwas:

      Nun bereiten wir die zu zeichnenden Kreise vor, wir geben der Klasse folgende Member:

      VB.NET-Quellcode

      1. Private Diameter() As Integer = { 10, 15, 20 } ' Array der darzustellenden Durchmesser in Zentimeter
      2. Private index As Integer = 0 ' Startindex im Array
      Dieser wird jedesmal genullt, wenn wir den Dialog aufrufen:

      VB.NET-Quellcode

      1. index = 0 ' Startindex zurücksetzen
      2. Using dlg As New PrintPreviewDialog
      Zuerst geben wir die Seitenzahl aus, entsprechend des Indexes. Seitenzahlen beginnen mit 1, also inkrementieren wir den Index:

      VB.NET-Quellcode

      1. Private Sub PrintDocument1_PrintPage(sender As System.Object, e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
      2. Dim g = e.Graphics
      3. g.DrawString(String.Format("Überschrift Seite {0}", index + 1), New Font("arial", 20), Brushes.Black, New Point(30, 30))
      4. End Sub
      Mit Zoom=150% sieht das so aus, jedoch wird momentan nur eine Seite gedruckt:

      Wir wollen genau 3 Seiten drucken, dies steuern wir mit der Property HasMorePages von e (PrintPageEventArgs-Parameter der PrintPage-Prozedur)

      VB.NET-Quellcode

      1. e.HasMorePages = index < 2 ' True bei index >= 2
      2. index += 1 ' nächste Seite
      Und schon werden 3 Seiten gedruckt, hier die 3. Seite mit 150 %

      Nun wollen wir mal einen ersten Druckversuch wagen.
      Ups, hier stimmt was nicht:

      Im Preview-Dialog werden 3 Seiten angezeigt, gedruckt wird aber Seite 4. :S
      Ursache ist, dass zum eigentlichen Druck wieder die PrintDocument1_PrintPage-Prozedur verwendet wird, der Index zählt weiter, e.HasMorePages ist False, da der Index größer-gleich 2 ist.
      Also müssen wir den Index zurücksetzen, wenn ein neuer Druckauftrag gestartet wird:

      VB.NET-Quellcode

      1. Private Sub PrintDocument1_BeginPrint(sender As System.Object, e As System.Drawing.Printing.PrintEventArgs) Handles PrintDocument1.BeginPrint
      2. index = 0 ' Startindex initialisieren
      3. End Sub
      Hier wird im Parameter e.PrintAction auch das Ziel mitgeteilt: PrintToPreview oder PrintToPrinter.
      Nun werden 3 Seiten zur Vorschau angezeigt und es werden 3 Seiten gedruckt, wie es sein soll:

      Nun wollen wir die Kreise darstellen, das ist nun eigentlich trivial:

      VB.NET-Quellcode

      1. Private Sub PrintDocument1_PrintPage(sender As System.Object, e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
      2. Dim g = e.Graphics
      3. g.PageUnit = GraphicsUnit.Millimeter ' Alle Positionsangaben erfolgen nun in Millimetern
      4. g.DrawString(String.Format("Überschrift Seite {0}", index + 1), New Font("arial", 20), Brushes.Black, New Point(30, 30))
      5. Dim dia = Me.Diameter(index) * 10 ' mm => cm
      6. Dim rc = New Rectangle(0, 0, dia, dia)
      7. g.DrawEllipse(Pens.Black, rc)
      8. e.HasMorePages = index < 2 ' True bei index >= 2
      9. index += 1 ' nächste Seite
      10. If index > 2 Then
      11. index = 0
      12. End If
      13. Debug.WriteLine(index.ToString)
      14. End Sub

      Noch ein Ups: Da wir GraphicsUnit.Millimeter eingestellt haben, verschiebt sich auch die Position der Überschrift, wir korrigieren dies, indem wir die Position (10, 10) vorgeben.
      Die Kreise zentrisch in der GraphicsUnit.Millimeter darzustellen ist etwas schwieriger, da sich die GraphicsUnit, im Gegensatz zur MSDN, nicht auf das VisibleClipBound auswirkt.

      Graphics.VisibleClipBounds-Eigenschaft schrieb:

      Die Einheit für das sich ergebende Rechteck wird von der PageUnit-Eigenschaft festgelegt. Die Standardeinheit ist Pixel. Ein Graphics ist normalerweise mit einem Steuerelement verbunden, und der Ursprung des Rechtecks ist relativ zum Clientbereich dieses Steuerelements.
      -----
      So, dies soll zur Aufwärmung reichen,
      im nächsten Beitrag wollen wir ein langes Textdokument drucken. ;)
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

      2. Teil:
      Wir drucken ein langes Textdokument.
      Um uns zunächst einen Eindruck zu verschaffen, drucken wir mal einfach drauflos:
      PrintPreviewialog wie oben. Um keine Verwirrung zu stiften, nehme ich Button2 und PrintDocument2, so finden beide Aufgaben in einem Projekt Platz.
      Zunächst zur Zählung 2 Variablen, eine für die aktuelle Seite, eine für die Position im Text. Dieser wird bei mir in einer RichTextBox dargestellt, ich nehme einfach den Text von Post #1 aus dem Post-Editor.

      VB.NET-Quellcode

      1. Private pageNb As Integer = 0 ' aktuelle Seitennummer
      2. Private indexText As Integer = 0 ' Startposition der aktuellen Seite im Text
      Diese werden in der BeginPrint-Prozedur initialisiert

      VB.NET-Quellcode

      1. Private Sub PrintDocument2_BeginPrint(sender As System.Object, e As System.Drawing.Printing.PrintEventArgs) Handles PrintDocument2.BeginPrint
      2. Me.pageNb = 0 ' aktuelle Seitennummer
      3. Me.indexText = 0 ' Startposition der aktuellen Seite im Text
      4. End Sub
      und die PrintPage-Routine sieht im 1. Wurf so aus:

      VB.NET-Quellcode

      1. Private Sub PrintDocument2_PrintPage(sender As System.Object, e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument2.PrintPage
      2. Dim g = e.Graphics
      3. g.DrawString(String.Format("Seite {0}", Me.pageNb + 1), New Font("arial", 20), Brushes.Black, New Point(10, 10))
      4. Dim rc = g.VisibleClipBounds ' der Druckbereich
      5. Dim txt = Me.RichTextBox1.Text ' brauchen wir noch öfter
      6. Dim ft = New Font("Arial", 10) ' der Druck-Font
      7. g.DrawString(txt.Substring(Me.indexText), ft, Brushes.Black, rc)
      8. End Sub

      Wie zu erwarten, sieht das Resultat nicht sonderlich berauschend aus, aber es wird schon mal was gedruckt.
      Als erstes wollen wir der Seite ihre Ränder geben. Diese stehen in der Property Me.PrintDocument2.DefaultPageSettings.Margins.
      Der Druckbereich wird nun folgendermaßen berechnet, oben links einfach draufaddieren, die Breite muss um den linken und den rechten; die Höhe um den oberen und unteren Rand korrigiert werden:

      VB.NET-Quellcode

      1. ' Ruft die Seitenränder für diese Seite ab oder legt diese fest.
      2. Dim margin = Me.PrintDocument2.DefaultPageSettings.Margins
      3. rc.Offset(margin.Left, margin.Top) ' obere linke Ecke
      4. rc.Width -= (margin.Left + margin.Right) ' neue Breite
      5. rc.Height -= (margin.Top + margin.Bottom) ' neue Höhe

      Das Layout ist akzeptabel, wenn Ihr andere Druckbereiche haben wollt, macht Euch einen Dialog, bei dem Ihr die Ränder vorgeben könnt. Vorgabe sollte DefaultPageSettings.Margins des Druckdokuments sein.
      Wenn wir uns nun den Output ansehen, insbesondere den unteren Rand, sehen wir 1., dass nicht der gesamte Text gedruckt wurde und 2. dass unten sogar der untere Teil der letzten Zeile fehlt:

      Wir müssen also feststellen, wieviel des Textes überhaupt auf die Druckseite passt.
      Dazu hat uns Bill die Funktion MeasureString im Zusammenwirken mit der Aufgabe Bestimmung der Zeilenanzahl gegeben:

      VB.NET-Quellcode

      1. ' System.Drawing.StringFormat, das Formatierungsinformationen für die Zeichenfolge darstellt.
      2. Dim stringFormat As New StringFormat(StringFormatFlags.LineLimit) ' Bestimmung der Zeilenanzahl
      3. ' Anzahl der Zeichen in der Zeichenfolge, die auf einmal gedruckt wird
      4. ' Zielgröße, abhängig von Font und Rechteck
      5. Dim charactersFitted As Integer
      6. ' Anzahl der Textzeilen in der Zeichenfolge, wird hier nicht weiter verwendet
      7. Dim linesFilled As Integer
      8. ' Bestimmung der Anzahl der druckbaren Zeichen
      9. e.Graphics.MeasureString(txt.Substring(Me.indexText), ft, rc.Size, stringFormat, charactersFitted, linesFilled)
      Der Rest ergibt sich dann von selbst, mit der entsprechenden Überladung des DrawString-Befehls wird eine ganzzahlige Zeilenanzahl ausgegeben.

      VB.NET-Quellcode

      1. ' Druck des Textes, wegen StringFormatFlags.LineLimit werden keine (höhen-)halben Zeilen gedruckt
      2. e.Graphics.DrawString(txt.Substring(Me.indexText), ft, Brushes.Black, rc, stringFormat)
      3. ' Festlegen der Text-Startposition der nächsten Seite
      4. Me.indexText += charactersFitted
      5. ' bestimmen, ob es überhaupt weitere Seiten gibt
      6. e.HasMorePages = Me.indexText < txt.Length
      7. ' die nächste Seitennummer
      8. Me.pageNb += 1
      Und nun sind wir schon (fast) fertig:

      Es werden 2 Seiten gedruckt, der Text wird vollständig ausgegeben.
      Nun wollen wir noch sehen, was passiert, wenn wir einen anderen Font nehmen.
      Hier könnten wir einen FontDialog nehmen, wir begnügen uns gier mit einem NumericUpDown-Control, mit dem wir die Fontgröße vorgeben können:

      VB.NET-Quellcode

      1. Dim ft = New Font("Arial", NumericUpDown1.Value) ' der Druck-Font
      Mit einer Fontgröße von 8 und 12 ergeben sich folgende Previews:
      bzw.
      die gedruckten Dokumente sehen selbstverständlich genau so aus.
      Hier noch mal die komplette
      PrintPage-Routine

      VB.NET-Quellcode

      1. Private Sub PrintDocument2_PrintPage(sender As System.Object, e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument2.PrintPage
      2. Dim g = e.Graphics
      3. g.DrawString(String.Format("Seite {0}", Me.pageNb + 1), New Font("arial", 20), Brushes.Black, New Point(10, 10))
      4. Dim rc = g.VisibleClipBounds ' der Druckbereich
      5. Dim txt = Me.RichTextBox1.Text ' brauchen wir noch öfter
      6. Dim ft = New Font("Arial", NumericUpDown1.Value) ' der Druck-Font
      7. ' Ruft die Seitenränder für diese Seite ab oder legt diese fest.
      8. Dim margin = Me.PrintDocument2.DefaultPageSettings.Margins
      9. rc.Offset(margin.Left, margin.Top) ' obere linke Ecke
      10. rc.Width -= (margin.Left + margin.Right) ' neue Breite
      11. rc.Height -= (margin.Top + margin.Bottom) ' neue Höhe
      12. g.DrawString(txt.Substring(Me.indexText), ft, Brushes.Black, rc)
      13. ' System.Drawing.StringFormat, das Formatierungsinformationen für die Zeichenfolge darstellt.
      14. Dim stringFormat As New StringFormat(StringFormatFlags.LineLimit) ' Bestimmung der Zeilenanzahl
      15. ' Anzahl der Zeichen in der Zeichenfolge, die auf einmal gedruckt wird
      16. ' Zielgröße, abhängig von Font und Rechteck
      17. Dim charactersFitted As Integer
      18. ' Anzahl der Textzeilen in der Zeichenfolge, wird hier nicht weiter verwendet
      19. Dim linesFilled As Integer
      20. ' Bestimmung der Anzahl der druckbaren Zeichen
      21. e.Graphics.MeasureString(txt.Substring(Me.indexText), ft, rc.Size, stringFormat, charactersFitted, linesFilled)
      22. ' Druck des Textes, wegen StringFormatFlags.LineLimit werden keine (höhen-)halben Zeilen gedruckt
      23. e.Graphics.DrawString(txt.Substring(Me.indexText), ft, Brushes.Black, rc, stringFormat)
      24. ' Festlegen der Text-Startposition der nächsten Seite
      25. Me.indexText += charactersFitted
      26. ' bestimmen, ob es überhaupt weitere Seiten gibt
      27. e.HasMorePages = Me.indexText < txt.Length
      28. ' die nächste Seitennummer
      29. Me.pageNb += 1
      30. End Sub
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Mehrfach wurde im Forum explizit der Druckbereich angesprochen, deswegen poste ich hier noch mal ein Beispiel
      • mit einem PrintDialog zur Druckerauswahl und Vorgabe der Anzahl der Kopien,
      • einem PageSetupDialog zur Festlegung der Seitenausrichtung und der Seitenränder
      • sowie einem PrintPreviewDialog zur Ausgabe der Druckvorschau.
      In dieser wird der modifizierte Druckbereich mit einem Rechteck umrandet und es wird ein Text in dieses Rechteck geschrieben, der Druck wird nach der 1. Seite abgebrochen.
      Eine Form mit 2 Button sowie einem PrintDocument:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Public Class Form1
      2. ' vorbereiteter Text
      3. Private PrintText As String
      4. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
      5. ' den Drucktext aufbauen
      6. Dim txt = "Franz jagt im komplett verwahrlosten Taxi quer durch Bayern. "
      7. For i = 1 To 100
      8. Me.PrintText &= txt
      9. Next
      10. End Sub
      11. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      12. ' Drucker und Anzahl der Kopien festlegen
      13. Using dlg As New PrintDialog
      14. dlg.Document = Me.PrintDocument1
      15. dlg.ShowDialog()
      16. End Using
      17. End Sub
      18. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
      19. ' Seitenränder festlegen
      20. Using dlg As New PageSetupDialog
      21. dlg.Document = Me.PrintDocument1
      22. If dlg.ShowDialog() <> Windows.Forms.DialogResult.OK Then
      23. Return
      24. End If
      25. End Using
      26. ' Druckvorschau
      27. Using dlg As New PrintPreviewDialog
      28. dlg.Document = Me.PrintDocument1
      29. dlg.ShowDialog()
      30. End Using
      31. End Sub
      32. Private Sub PrintDocument1_PrintPage(sender As Object, e As Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
      33. Dim g = e.Graphics
      34. Dim rc = g.VisibleClipBounds ' der Druckbereich
      35. ' Seitenränder für diese Seite entsprechend dem PagesetupDialog festlegen
      36. Dim margin = Me.PrintDocument1.DefaultPageSettings.Margins
      37. rc.Offset(margin.Left, margin.Top) ' obere linke Ecke
      38. rc.Width -= (margin.Left + margin.Right) ' neue Breite
      39. rc.Height -= (margin.Top + margin.Bottom) ' neue Höhe
      40. ' das Um-Rechteck zeichnen
      41. g.DrawRectangle(Pens.Black, rc.Left, rc.Top, rc.Width, rc.Height)
      42. ' den Texz ausgeben
      43. Dim ft = New Font("Arial", 12) ' der Druck-Font
      44. g.DrawString(Me.PrintText, ft, Brushes.Black, rc)
      45. End Sub
      46. End Class

      Standard-Bildränder (10 mm):


      Oberer Bildrand auf 100 mm vergrößert:

      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

      Hier möchte ich noch mal auf die Auswahl und die Vorgabe von Hoch- oder Querformat eingehen.
      Per Default ist beim Drucken das Papier auf Hochkant eingestellt.
      Wenn der User entscheiden soll, ob das Dokument hoch oder quer gedruckt wird, muss vor dem PrintPreviewDialog ein PrintDialog vorgeschaltet werden.

      VB.NET-Quellcode

      1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      2. Using dlg = New PrintDialog ' Druckerauswahl, Druckereigenschaften, Papierausrichtung
      3. dlg.Document = Me.PrintDocument1
      4. If dlg.ShowDialog <> Windows.Forms.DialogResult.OK Then
      5. Return
      6. End If
      7. End Using
      8. Using dlg = New PrintPreviewDialog ' Vorschau und Druck
      9. dlg.Document = Me.PrintDocument1
      10. If dlg.ShowDialog <> Windows.Forms.DialogResult.OK Then
      11. Return
      12. End If
      13. End Using
      14. End Sub

      Abhängig vom ausgewählten Drucker wird bei Eigenschaften ein spezifischer Dialog angezeigt, in dem die Ausrichtung des Papiers vorgegeben werden kann, hier am Beispiel des Foxit PhantomPDF Printer:

      Steht fest, dass das Dokument im Querformat gedruckt werden soll, kann dies dem Druckdokument auch direkt vorgegeben werden
      oder es wird die vom User im PrintDialog vorgenommene Auswahl überschrieben
      oder es wird der Wert einer externen Checkbox eingetragen:

      VB.NET-Quellcode

      1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      2. Me.PrintDocument1.DefaultPageSettings.Landscape = True ' True: Quer, False: Hochkant
      3. Using dlg = New PrintPreviewDialog ' Vorschau und Druck
      4. dlg.Document = Me.PrintDocument1
      5. If dlg.ShowDialog <> Windows.Forms.DialogResult.OK Then
      6. Return
      7. End If
      8. End Using
      9. End Sub

      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

      In diesem Thread wurde nach den verfügbaren Druckern und der Farbtauglichkeit des gewählten Druckers gefragt.
      Um das nicht immer wieder einzeln auszuführen, hier die Abfragen:
      Abfrage der installierten Drucker:

      VB.NET-Quellcode

      1. For Each printer In PrinterSettings.InstalledPrinters ' Shared Property
      2. Me.ListBox1.Items.Add(printer.ToString())
      3. Next
      Abfrage der Farbtauglichkeit:

      VB.NET-Quellcode

      1. Using dlg = New PrintDialog
      2. If dlg.PrinterSettings.SupportsColor Then
      3. MessageBox.Show("Farbe")
      4. Else
      5. MessageBox.Show("Schwarz-Weiß")
      6. End If
      7. End Using
      Zu weiteren Properties seht Euch bitte die PrinterSettings an: docs.microsoft.com/de-de/dotne…s?view=netframework-4.7.2
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Druck aus unterschiedlichen Druckerschächten

      Ich bin gerade beim Aufräumen meiner Lesezeichen und fand da den Beitrag Druck aus unterschiedlichen Druckerschächten
      von @guromu , der hier genau zum Thema passt, den ich deswegen hier vollständig und kommentiert präsentiere.
      Seite 1 wird aus dem Schacht der ComboBox cbPage1 "befüttert", alle weiteren Seiten kommen aus dem Schacht der ComboBox cbPageElse.
      Bei der Zuweisung wird getestet, ob mehrere Schächte vorhanden sind und ob Items ausgewählt wurden.
      Ansonsten wird der Defaul-Schacht bzw. der zuletzt ausgewählte Schacht verwendet.
      Zum Testen wird natürlich ein Drucker mit mehreren Schächten benötigt, über den ich leider nicht verfüge.
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Imports System.Drawing.Printing
      2. ' Form mit Button, PrintDocument und den ComboBoxen cbPage1 und cbPageElse
      3. Public Class Form1
      4. Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
      5. ' Add list of paper sources found on the printer to the combo box.
      6. ' The DisplayMember property is used to identify the property that will provide the display string.
      7. Me.cbPage1.DisplayMember = "SourceName"
      8. Me.cbPageElse.DisplayMember = "SourceName"
      9. For i = 0 To Me.PrintDocument1.PrinterSettings.PaperSources.Count - 1
      10. Dim pkSource = Me.PrintDocument1.PrinterSettings.PaperSources.Item(i)
      11. ' identisches Befüllen der ComboBoxen
      12. Me.cbPage1.Items.Add(pkSource)
      13. Me.cbPageElse.Items.Add(pkSource)
      14. Next
      15. End Sub
      16. Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
      17. ' Aufruf des Preview-Dialogs
      18. Using dlg = New PrintPreviewDialog()
      19. dlg.WindowState = FormWindowState.Maximized
      20. dlg.Document = Me.PrintDocument1
      21. ' 3 Seiten anzeigen
      22. dlg.PrintPreviewControl.Columns = 3
      23. dlg.ShowDialog()
      24. End Using
      25. End Sub
      26. Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As PrintPageEventArgs) Handles PrintDocument1.PrintPage
      27. ' Seitennummer ausgeben und hochzählen
      28. e.Graphics.DrawString("Seite " & Me.PageNumber.ToString(), Me.Font, Brushes.Black, 30, 30)
      29. Me.PageNumber += 1
      30. e.HasMorePages = Me.PageNumber <= 3
      31. End Sub
      32. Dim PageNumber As Integer
      33. Private Sub PrintDocument1_BeginPrint(ByVal sender As Object, ByVal e As PrintEventArgs) Handles PrintDocument1.BeginPrint
      34. ' Beginn mit Seitennummer 1
      35. Me.PageNumber = 1
      36. End Sub
      37. Private Sub PrintDocument1_QueryPageSettings(sender As Object, e As QueryPageSettingsEventArgs) Handles PrintDocument1.QueryPageSettings
      38. ' überhaupt mehrere Schächte vorhanden
      39. If Me.cbPage1.Items.Count > 0 Then
      40. If PageNumber = 1 AndAlso Me.cbPage1.SelectedIndex >= 0 Then
      41. ' Seite 1 und Schacht für Seite 1 ausgewählt
      42. e.PageSettings.PaperSource = Me.PrintDocument1.PrinterSettings.PaperSources.Item(Me.cbPage1.SelectedIndex)
      43. ElseIf Me.cbPageElse.SelectedIndex >= 0 Then
      44. ' Folgeseiten und Schacht für Folgeseiten ausgewählt
      45. e.PageSettings.PaperSource = Me.PrintDocument1.PrinterSettings.PaperSources.Item(Me.cbPageElse.SelectedIndex)
      46. End If
      47. End If
      48. End Sub
      49. End Class
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Drucken einer Tabelle

      Gelegentlich wurde ich danach gefragt, wie eine Tabelle gedruckt wird.
      Gefunden habe ich den ursprünglichen Code auf dieser Seite, die leider nicht mehr verfügbar ist:
      vb-magazin.de/forums/forums/p/5464/21352.aspx
      Die Herangehensweise ist, für jede Spalte die maximal erforderliche Breite zu bestimmen. Dazu bedienen wir uns Graphics.MeasureString(), jede einzelne Zelle wird veremessen.
      Das Maximum für jede Spalte wird zwischengespeichert. Die Summe der Spaltenbreiten ist die Breite der Darstellung zuzüglich der Strichbreite.
      Der Druck wird über Flags gesteuert:
      • zentrierte Ausgabe - die Tabelle wird horizontal in die Mitte der Seite gedruckt,
      • es werden Gitternetzlinien ausgegeben,
      • die Spaltenbreite wird der maximalen Textbreite der Spalte angepasst,
      • der Hintergrund der Zellen wird zeilenweise alternierend geändert,
      • die Rahmenbreite um den Text.
      Diese Flags und Werte können natürlich über CheckBoxen und NumericUpDown vorgegeben werden.
      Der Druck selbst erfolgt dann zeilenweise über die Tabelle.
      Die ausgegebenen Zeilen werden gezählt, und wenn die Seite voll und die Tabelle noch nicht fertig gedruckt ist, wird mit e.HasMorePages = True die nächste Seite vorbereitet.
      In diesem Fall ist es wichtig, sich die zuletzt gedruckte Zeile zu merken, damit der Druck vollständig ist.

      Viel Vergnügen


      PrintTable.zip
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      PDF-Druck mit Vorgabe des Dateinamens

      @Haudruferzappeltnoch hatte das Problem, das mir auch schon eine Weile aufgestoßen ist:
      Der Microsoft Print to PDF-Drucker nimmt die Property .PrinterSettings.PrintFileName nicht an.
      @Micha aus DZ am Lober hat das Problem in seinem 1.( :!: ) Beitrag gelöst: Dokumentname PDFPrinter
      Hier der Code mit SaveFileDialog und Microsoft Print to PDF als Standard-Drucker:

      C#-Quellcode

      1. private void BtnPrintPdf_Click(object sender, EventArgs e)
      2. {
      3. using (SaveFileDialog dlg = new SaveFileDialog())
      4. {
      5. dlg.Filter = "PDF|*.pdf";
      6. if (dlg.ShowDialog() != DialogResult.OK)
      7. {
      8. return;
      9. }
      10. this.printDocument1.PrinterSettings.PrintToFile = true; // dies hier
      11. this.printDocument1.PrinterSettings.PrintFileName = dlg.FileName;
      12. }
      13. using (PrintPreviewDialog dlg = new PrintPreviewDialog())
      14. {
      15. dlg.Document = this.printDocument1;
      16. dlg.ShowDialog();
      17. }
      18. }
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      @Peter329 hatte das Problem, dass nur eine Auswahl an Seiten gedruckt werden soll: Printer Dialog Parameter Übergabe
      Wird der PrintDialog im Designer parametriert, dann sieht das so aus:

      Bei Aufruf aus dem Code heraus dann so:

      VB.NET-Quellcode

      1. Private Sub PrintAny()
      2. Using pd As New PrintDialog()
      3. pd.Document = Me.PrintDocument1
      4. pd.AllowSelection = True
      5. pd.AllowCurrentPage = True
      6. pd.AllowSomePages = True
      7. pd.PrinterSettings.PrintRange = Printing.PrintRange.SomePages
      8. pd.PrinterSettings.FromPage = 3
      9. pd.PrinterSettings.ToPage = 7
      10. pd.ShowDialog()
      11. End Using
      12. End Sub
      Der aufgerufene Dialog sieht dann so aus:
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!