What's New
Q & A
Tip Jar
C# Helper...
Follow VBHelper on Twitter
MSDN Visual Basic Community
TitlePrint a long series of paragraphs in different fonts, breaking across pages in VB .NET
DescriptionThis example shows how to print a long series of paragraphs in different fonts, breaking across pages in VB .NET. It shows measure text to see where it needs to brak across pages, and how to tell VB not to draw partial words or lines.
Keywordsprint, preview, text, long text, print preview, VB .NET
CategoriesGraphics, VB.NET
The program stores information about its paragraphs in ParagraphInfo structures that hold a paragraph's font size and text. The form's Load event handler initializes the information stored in the m_Paragraphs collection.
' Information about the paragraphs to print.
Private Structure ParagraphInfo
    Public FontSize As Integer
    Public Text As String
    Public Sub New(ByVal font_size As Integer, ByVal txt As _
        FontSize = font_size
        Text = txt
    End Sub
End Structure

' The paragraphs.
Private m_Paragraphs As Collection
Private m_ParagraphsToPrint As Collection
Private m_PagesPrinted As Integer

' Load the paragraph info.
Private Sub Form1_Load(ByVal sender As Object, ByVal e As _
    System.EventArgs) Handles MyBase.Load
    m_Paragraphs = New Collection
    m_Paragraphs.Add(New ParagraphInfo(45, "23"))
    m_Paragraphs.Add(New ParagraphInfo(27, "Printing"))
    m_Paragraphs.Add(New ParagraphInfo(16, "Visual Basic " & _
        ".NET provides ..."))
End Sub
When the program needs to print or provide a print preview, it creates a PrintDocument object as usual. It uses all four of the object's event handlers: BeginPrint, QueryPageSettings, PrintPage, and EndPrint.

The BeginPrint event handler makes a copy of the ParagraphInfo data stored in m_Paragraphs.

' Get ready to print pages.
Private Sub Print_BeginPrint(ByVal sender As Object, ByVal _
    e As System.Drawing.Printing.PrintEventArgs)
    ' We have not yet printed any pages.
    m_PagesPrinted = 0

    ' Make a copy of the text to print.
    m_ParagraphsToPrint = New Collection
    For Each para_info As ParagraphInfo In m_Paragraphs
        m_ParagraphsToPrint.Add(New _
            ParagraphInfo(para_info.FontSize, _
    Next para_info
End Sub
The PrintDocument object raises its QueryPageSettings event before it prints each page. This program adds a 1 inch (100 printer units per inch) gutter to the left side of odd numbered pages and the right side of even numbered pages.
' Set the margins for the following page.
Private Sub Print_QueryPageSettings(ByVal sender As Object, _
    ByVal e As _
    ' Use a 1 inch gutter (printer units are 100 per inch).
    Const gutter As Integer = 100

    ' See if the next page will be the first, odd, or even.
    If m_PagesPrinted = 0 Then
        ' The next page is the first.
        ' Increase the left margin.
        e.PageSettings.Margins.Left += gutter
    ElseIf (m_PagesPrinted Mod 2) = 0 Then
        ' The next page will be odd.
        ' Shift the margins right.
        e.PageSettings.Margins.Left += gutter
        e.PageSettings.Margins.Right -= gutter
        ' The next page will be even.
        ' Shift the margins left.
        e.PageSettings.Margins.Left -= gutter
        e.PageSettings.Margins.Right += gutter
    End If
End Sub
The PrintPage event handler prints a page. It prints the page number in the upper corner opposite the gutter and outside of the margins.

Next the program sets its StringFormat properties. It sets Alignment to Near so the text is left-justified. It sets FormatFlags to LineLimit so a line at the bottom of the page that won't fit vertically isn't half drawn (otherwise VB might draw the top halfs of its letters). Finally, it sets Trimming to Word so VB only breaks lines between words not in the middle of words.

Then while the m_ParagraphsToPrint collection contains ParagraphInfo data, the program removes the first entry and prints it. It makes the necessary font and then uses the Graphics object's MeasureString method to see how big the string will be printed and to see how many characters will fit in the remaining available space on the page. If any characters will fit, the program draws them and increases ymin, the largest Y value that has been used.

If the program didn't fit the whole paragraph on the page, it saves the rest in a ParagraphStructure to be printed the next time the event handler runs for the next page.

When it has printed as much as it can, the event handler sets e.HasMorePages to True if the m_ParagraphsToPrint collection is not empty.

' Print the next page.
Private Sub Print_PrintPage(ByVal sender As Object, ByVal e _
    As System.Drawing.Printing.PrintPageEventArgs)
    ' Increment the page number.
    m_PagesPrinted += 1

    ' Draw the margins (for debugging).
    'e.Graphics.DrawRectangle(Pens.Red, e.MarginBounds)

    ' Print the page number right justified 
    ' in the upper corner opposite the gutter
    ' and outside of the margin.
    Dim the_font As Font
    Dim x As Integer
    Dim string_format As New StringFormat
    the_font = New Font("Times New Roman", _
        20, FontStyle.Regular, GraphicsUnit.Point)

    ' See if this is an odd or even page.
    If (m_PagesPrinted Mod 2) = 0 Then
        ' This is an even page. 
        ' The page number is on the left.
        x = (e.MarginBounds.Left + e.PageBounds.Left) \ 2
        string_format.Alignment = StringAlignment.Near
        ' This is an odd page.
        ' The page number is on the right.
        x = (e.MarginBounds.Right + e.PageBounds.Right) \ 2
        string_format.Alignment = StringAlignment.Far
    End If
    e.Graphics.DrawString(m_PagesPrinted.ToString, _
        the_font, Brushes.Black, x, _
        (e.MarginBounds.Top + e.PageBounds.Top) \ 2, _

    ' Draw the rest of the text left justified,
    ' wrap at words, and don't draw partial lines.
    string_format.Alignment = StringAlignment.Near
    string_format.FormatFlags = StringFormatFlags.LineLimit
    string_format.Trimming = StringTrimming.Word

    ' Draw some text.
    Dim paragraph_info As ParagraphInfo
    Dim ymin As Integer = e.MarginBounds.Top
    Dim layout_rect As RectangleF
    Dim text_size As SizeF
    Dim characters_fitted As Integer
    Dim lines_filled As Integer
    Do While m_ParagraphsToPrint.Count > 0
        ' Print the next paragraph.
        paragraph_info = DirectCast(m_ParagraphsToPrint(1), _

        ' Get the font.
        the_font = New Font("Times New Roman", _
            paragraph_info.FontSize, _
            FontStyle.Regular, GraphicsUnit.Point)

        ' Get the area available for this paragraph.
        layout_rect = New RectangleF( _
            e.MarginBounds.Left, ymin, _
            e.MarginBounds.Width, _
            e.MarginBounds.Bottom - ymin)

        ' If the layout rectangle's height < 1, make it 1.
        If layout_rect.Height < 1 Then layout_rect.Height = _

        ' See how big the text will be and 
        ' how many characters will fit.
        text_size = e.Graphics.MeasureString( _
            paragraph_info.Text, the_font, _
            New SizeF(layout_rect.Width, _
                layout_rect.Height), _
            string_format, characters_fitted, lines_filled)

        ' See if any characters will fit.
        If characters_fitted > 0 Then
            ' Draw the text.
            e.Graphics.DrawString(paragraph_info.Text, _
                the_font, Brushes.Black, _
                layout_rect, string_format)

            ' Draw a rectangle around the text (for
            ' debugging).
            'e.Graphics.DrawRectangle(Pens.Green, _
            '    layout_rect.Left, _
            '    layout_rect.Top, _
            '    text_size.Width, _
            '    text_size.Height)

            ' Increase the location where we can start.
            ' Add a little interparagraph spacing.
            ymin += CInt(text_size.Height + _
                e.Graphics.MeasureString("M", _
                    the_font).Height / 2)
        End If

        ' See if some of the paragraph didn't fit on the
        ' page.
        If characters_fitted < Len(paragraph_info.Text) Then
            ' Some of the paragraph didn't fit.
            ' Prepare to print the rest on the next page.
            paragraph_info.Text = paragraph_info.Text. _
            m_ParagraphsToPrint.Add(paragraph_info, _

            ' That's all that will fit on this page.
            Exit Do
        End If

    ' If we have more paragraphs, we have more pages.
    e.HasMorePages = (m_ParagraphsToPrint.Count > 0)
End Sub
Finally the EndPrint event handler sets the m_ParagraphsToPrint collection to Nothing. If the program had allocated more elaborate data structures, opened database connections, and so forth to generate the printout, the program would clean up here.
' Clean up.
Private Sub Print_EndPrint(ByVal sender As Object, ByVal e _
    As System.Drawing.Printing.PrintEventArgs)
    m_ParagraphsToPrint = Nothing
End Sub
Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.