Print DataSet or DataTable contents from VB.NET

This page contains the source code for my PrintHandler class that you can add to your project to print the contents of a Dataset in a report format. The PrintHandler:

  • Prints and/or Previews all rows of the specified table in your dataset.

  • Allows you to display the standard Window's Page Setup Dialog.

  • Does not print DataSet rows flagged as deleted.

  • Only prints rows passing the filter criteria if the DataSet is filtered.

  • Wraps columns to the next page if there are more columns than can fit on a page.

  • Let's you set the report's title (the default is the specified table's name).

  • Let's you set the number of columns to print (the default is all columns).

  • Exposes a line threshold property which, if exceeded, prompts for confirmation before printing a large DataSet.

  • Allows you to control printed column width by resizing the corresponding columns in the DataGrid bound to the DataSet.

  • Lets you specify column captions (column names of the specified table are used by default).

  • Catches and re-throws all exceptions so they can be caught by the calling module.

Overview

PrintHandler is designed to print a DataSet that's bound to a DataGrid. Typical usage is to display data in a DataGrid, format the DataGrid's columns and present a way for the user to call the PrintHandler's methods.

To format a DataGrid you add a GridColumnStyle for each column to a GridTableStyle then associate the GridTableStyle with the DataGrid. PrintHandler uses the GridColumnStyle's HeaderText value as the caption for a column. If the HeaderText value is not set the actual column name is used.

Users control the width of printed columns by resizing the corresponding columns in the DataGrid. Columns whose width is zero (e.g. not visible in the DataGrid) are not printed. Inital column widths can be set via the GridColumnStyle objects.

Here's an example of a DataGrid with 3 columns. Column 1 is the Customer_Name which PrintHandler will caption Customer due to the HeaderText value. Column 2 will not be printed because its width is zero. The actual column name of Customer_Address will be used for column 3 since the HeaderText is not set.

    '
    ' Create a GridTableStyle and set its MappingName to the name of the DataTable.
    '
    Dim aGridTableStyle As New DataGridTableStyle
    aGridTableStyle.MappingName = "Customers"
    '
    ' Create GridColumnStyle objects for the grid columns. 
    '
    Dim aCol1 As New DataGridTextBoxColumn
    Dim aCol2 As New DataGridTextBoxColumn
    Dim aCol2 As New DataGridTextBoxColumn

    aCol1.MappingName = "Customer_Name"  
    aCol1.HeaderText = "Customer"        
    aCol1.Width = 75                     
    aCol1.Alignment = HorizontalAlignment.Left
    aGridTableStyle.GridColumnStyles.Add(aCol1)

    aCol2.MappingName = "Customer_Number"
    aCol2.Width = 0
    aGridTableStyle.GridColumnStyles.Add(aCol2)

    aCol3.MappingName = "Customer_Address"
    aCol3.Width = 100
    aCol3.Alignment = HorizontalAlignment.Left
    aGridTableStyle.GridColumnStyles.Add(aCol3)
    '
    ' Add the GridColumnStyles to the GridTableStyle.
    '
    DataGrid.TableStyles.Add(aGridTableStyle)

To call the PrintHandler to Print or Preview the data:

PrintHandler receives the DataSet to print as a parameter. However, HeaderText and column width values are associated with GridColumnStyles and not the DataSet itself so this information is lost. To preserve it, the Caption property of the DataSet's columns is set and an ExtendedProperty is created for each column to hold its width.

    Dim aCol As Integer
    Dim aTblIndex As Integer = 0
    Dim aNumColumns As Integer
    Dim aColumnStyles As GridColumnStylesCollection
    
    Try
        '
        ' The dataset may have columns not displayed in the grid. Looping through 
        ' GridColumnStyles errors when the number of GridColumnStyles is exceeded. 
        ' The error is caught and the number of displayed columns is adjusted. The 
        ' GridColumnStyles collection doesn't expose a Count property nor does the 
        ' DataGrid expose the number of displayed columns.
        '
        aColumnStyles = DataGrid.TableStyles(aTblIndex).GridColumnStyles
        aNumColumns = myDataSet.Tables(aTblIndex).Columns.Count - 1

        With myDataSet.Tables(aTblIndex)
            For aCol = 0 To aNumColumns
                .Columns(aCol).Caption = aColumnStyles.Item(aCol).HeaderText
                .Columns.Item(aCol).ExtendedProperties.Clear()

                If theColumnStyles.Item(aCol).Width = 0 Then
                    .Columns.Item(aCol).ExtendedProperties.Add("PrintWidth", -1)
                Else
                    .Columns.Item(aCol).ExtendedProperties.Add( _
                        "PrintWidth", aColumnStyles.Item(aCol).Width)
                End If
            Next
        End With

    Catch Ex As System.ArgumentOutOfRangeException
        aNumColumns = aCol- 1

    Catch Ex As Exception
        Throw New Exception("Error setting column captions.", Ex)

    End Try

    '
    ' Call the PrintHandler.
    '
    Dim aPrintObj As New PrintHandler

    With aPrintObj
        .LineThreshold = 500
        .NumberOfColumns = aNumColumns
        .DataSetToPrint = myDataSet
        .ReportTitle = "Customer List"
        .DataSetToPrint = myDataSet
        .TableIndex = aTblIndex 

        If blnPreview Then
            .PrintPreview()
        Else
            .Print()
        End If
    End With

    aPrintObj = Nothing

To display the Print Setup Dialog:

    Dim aPrintObj As New PrintHandler
    aPrintObj.PageSetupDialog(True)
    aPrintObj = Nothing

Details

Printing in VB.NET is based on a PrintDocument from the System.Drawing.Printing namespace which sends output to a printer similar to VB6's Printer Object. A variable is defined as a PrintDocument and declared WithEvents so you can react to its events.

To display Window's Page Setup Dialog PrintHandler uses code similar to:

    Private WithEvents myDocumentToPrint As PrintDocument

    Dim aPS As New PageSetupDialog
    aPS.Document = myDocumentToPrint
    '
    ' On the 1-rst call to the print dialog initialize the document's 
    ' properties. On subsequent calls use what was previously set.
    '
    If Not myPageSetUp Then
        With aPS.Document.DefaultPageSettings
            .Margins.Top = 50
            .Margins.Left = 50
            .Margins.Right = 50
            .Margins.Bottom = 50
            .Landscape = True
        End With
    End If

The PrintHandler's Preview method verifies the DataSet has data then calls the preceeding PageSetupDialog code to initialize the PrintDocument if necessary. An instance of a PrintPreviewDialog class is created and passed the PrintDocument. Finally the PrintPreviewDialog's ShowDialog method is called:

    Dim aPrevDialog As New PrintPreviewDialog

    aPrevDialog.Document = myDocumentToPrint
    aPrevDialog.ShowDialog()

Similarly, the PrintHandler's Print method verifies the DataSet has data. If it has more rows than the LineThreshold value a print confirmation is issued. Once the decision to print is made the above PageSetupDialog code is conditionally called. Lastly, printing is performed by calling the PrintDocument's Print method:

    myDocumentToPrint.Print()

Actual printing logic resides in the PrintDocument's PrintPage Event. PrintPage is a Callback Event called by Windows repeatedly whenever the PrintDocument's Print or Preview methods are called. PrintPage is called until the HasMorePages property of its PrintPageEventArgs parameter is set False.

See the source code for the gory details of this event. Basically, this code creates a generic report header line using the report title that was passed in. The width of the PrintDocument page is determined and the header line is centered within it.

A DataRow is retrieved from the DataSet and the columns are enumerated in a loop. If the column width stored in the PrintWidth ExtendedProperty is greater than zero the column's caption is printed. As stated earlier, if the caption property was not set the actual column name is used. Additionally, if the PrintWidth ExtendedProperty was not set, the width of the resultant column caption is used.

Once the heading of the report has been printed a nested loop is entered to print the dataset's data. The outside loop processes the rows while the inner loop prints the values of the columns in the row. Checks are performed to skip rows marked as deleted and to see if the number of lines (rows) printed exceed the number of lines that can fit on a page.

If the data spans pages the PrintDocument's PrintPageEventArgs' HasMorePages property is set. When all DataRows have been printed the HasMorePages propery is set False spooling the output to the printer.

Again, see the source code for all the details.




About TheScarms
About TheScarms


Sample code
version info

If you use this code, please mention "www.TheScarms.com"

Email this page


© Copyright 2024 TheScarms
Goto top of page