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.
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
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.
|