Introduction
A structured print document is one which combines a design time template with a run time collection of data items.
Classes that make up the structured print document
StructuredprintDocumentProvider
This is the main component that does the printing. It has three properties: DocumentName is the name of the document as it appears in the print spool, TextStyleProvider is the TextStyleProvider component that provides a set of standard styles to be used in the document and StructuredPrintDocument is the property that defines the document.
StructuredPrintDocument
This has a number of properties including Mode which determines whether the document is going to print in design time (layout) or run time mode and Pages the collection of StructuredprintPage objects that define the pages in this document.
StructuredPrintPage
This represents a single page of the template. This has a number of properties such as Landscape which determines whether or not to print the page in landscape mode, Sections which is the collection of StructuredprintDocumentArea objects that print on this page, AdditionalPageTests which are the checks that are to be performed to decide whether this page template has more pages to print and DataActionsBefore, DataActionsAfter and DataActionsOnNextPage that perform actions on the underlying collections before, after and on each new page of the page template respectively.
StructuredPrintDocumentArea
This does the work of printing the document. It has properties BoundaryRectangle which is the rectangular section of the page to print in, DataSourceName which tells the name of the data to be printed, WriterType that controls which IStructuredPrintDocumentAreaWriterBase derived class will perform the actual printing, as well as data actions and additional page tests.
Extensibility - The IStructuredPrintDocumentAreaWriterBase interface
Additional parameters needed for specific document area writers
In addition to the properties common to all the document area writer classes provided by the IStructuredPrintDocumentAreaWriterBase interface there may be extra parameters needed for a particular document area writer - for example, a picture writer could have a parameter to decide whether to stretch the picture to fit the clipping region and a table writer would need extra parameters to control the width and styles used in the columns.
This is implemented by the SetExtraParameter method of the interface.
Applying a style set to the document
To provide a common look and feel to the various elements of the printed document you can create a collection of document paragraph text styles to the document:

The properties of each paragraph text style are:
Font - The font to use to print text in this style.
BorderColour - The pen colour to draw the border of the section.
BorderWidth - The pen width to draw the border of the section.
TopBorder, BottomBorder, LeftSideBorder, RightSideBorder - which parts of the border to draw.
ForColour - The colour to use for the text.
Highlight - The colour to use for the background of the text.
TextGutter - The space between the edge of the document area and the text area.
VerticalAlignment, HorizontalAlignment - The alignment of data in the document area.
Design mode and runtime mode
In the design mode, the document prints each page only once and for each document section it prints the data element name rather than the actual data used:

In the run time mode each page template is populated with the data and printed as long as there are more pages needed. This means that the data actions and additional page tests are executed:

Getting data into the document
There are two ways of getting data into the document. The first chance is before the document is printed using the LoadDataItem method e.g.:
[Editor comment: Line breaks used to avoid scrolling.]
Me.StructuredPrintDocumentProvider1.StructuredPrintDocument.
LoadDataItem("Employee", "", _Employees)
Alternatively, if the document is printing and it comes to a data element for which there is no data held in the document it will raise a DocumentContentQuery event to ask for the data:
Private Sub StructuredPrintDocumentProvider1_DocumentContentQuery(_
ByVal sender As Object, ByVal e As System.EventArgs)
Handles StructuredPrintDocumentProvider1.DocumentContentQuery
With CType(e, DocumentContentQueryEventArgs)
If .PropertyName = "TodaysDate" Then
.Scope =
DocumentContentQueryEventArgs.DocumentDataScope.GlobalScope
.Value = Now.ToLongDateString
End If
End With
End Sub
Invariant sections: labels and images
There are also fixed elements within a structured document such as labels and pictures which have a constant content at design time. These are commonly referred to as labels and pictures.
Previewing the structured document
To preview the document you need to have a System.Windows.Forms.PrintPreviewDialog control on your application form and set its Document member to the StructuredPrintDocument.PrintDocument property. This will then take over the printing according to the pages and sections you have added to it:
[Editor comment: Line breaks used to avoid scrolling.]
Private Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Me.PrintPreviewDialog1.Document = _
Me.StructuredPrintDocumentProvider1.
StructuredPrintDocument.PrintDocument
Me.PrintPreviewDialog1.ShowDialog()
End Sub
You can preview the document in design mode or runtime mode by setting the StructuredPrintDocument.Mode member:
If Me.chkDesign.Checked Then
Me.StructuredPrintDocumentProvider1.StructuredPrintDocument.Mode = _
StructuredPrintDocument.PrintMode.DesignTime
Else
Me.StructuredPrintDocumentProvider1.StructuredPrintDocument.Mode = _
StructuredPrintDocument.PrintMode.RunTime
End If
In the design mode each page of the document is shown once and the data element name is shown rather than the actual data.
Worked example - An employee list
The employee list comes from a collection class that inherits from ArrayList:
Public Class Employee
#Region "Private members"
Private _Name As String
Private _Extension As String
Private _StartDate As Date
#End Region
#Region "Public interface"
Public ReadOnly Property Name() As String
Get
Return _Name
End Get
End Property
Public ReadOnly Property Extension() As String
Get
Return _Extension
End Get
End Property
Public ReadOnly Property StartDate() As Date
Get
Return _StartDate
End Get
End Property
Public Photo As System.Drawing.Bitmap
#End Region
#Region "Public constructor"
Public Sub New(ByVal NameIn As String, _
ByVal ExtensionIn As String, ByVal DateStartedIn As Date)
_Name = NameIn
_Extension = ExtensionIn
_StartDate = DateStartedIn
End Sub
#End Region
End Class
Public Class Employees
Inherits ArrayList
#Region "Overriden interface"
Public Shadows Function Add(_
ByVal Value As Employee) As Integer
Return MyBase.Add(Value)
End Function
Public Shadows Property Item(_
ByVal Index As Integer) As Employee
Get
Return MyBase.Item(Index)
End Get
Set(ByVal Value As Employee)
MyBase.Item(Index) = Value
End Set
End Property
#End Region
End Class
And the requirement is to print a phone list with the employee name and extension and then for each employee to print a summary sheet with all the properties. The phone list is to be printed as a table in landscape format and the employee detail sheet is to be printed in portrait mode. Therefore, we start by defining the two pages:
With StructuredPrintDocumentProvider1
.StructuredPrintDocument = New StructuredPrintDocument
.StructuredPrintDocument.DocumentName = "Employee Listing"
.StructuredPrintDocument.AddPage(_
New StructuredPrintPage("Employee List"))
.StructuredPrintDocument.AddPage(_
New StructuredPrintPage("Employee Details"))
End With
Then, we design the first page which has a heading label, a fixed picture, a dynamic label that gets the date and a grid which holds the employee phone list:
[Editor comment: Line breaks used to avoid scrolling.]
With StructuredPrintDocumentProvider1.StructuredPrintDocument.
Pages.Page("Employee List")
.Landscape = True
.AddPicture("Heading", Me.PictureBox1.Image, "HeadingPicture", _
New System.Drawing.Rectangle(650, 300, 150, 300), False)
.AddLabel("Heading", "Employee Phone List", "Subheading", _
New System.Drawing.Rectangle(150, 255, 800, 30))
.AddSection(New StructuredPrintDocumentArea("Employee List"))
With .Section("Employee List")
.WriterType = GetType(StructuredPrintDocumentAreaTableWriter)
.BoundaryRectangle = _
New System.Drawing.Rectangle(150, 300, 450, 300)
.DataSourceName = "Employee"
.AdditionalPageTests.Add(New AdditionalPageTest("Employee", _
AdditionalPageTest.AdditionalPageTestTypes.NotAtEndOfCollection))
End With
Dim EmployeeTelNoTableFormat As ColumnFormatCollection = _
ColumnFormatCollection.CreateDefaultColumnFormatCollection(_
GetType(Employee), New System.Drawing.Font(_
System.Drawing.FontFamily.GenericSerif, 12))
EmployeeTelNoTableFormat.Column("Name").Width = 300
EmployeeTelNoTableFormat.Column("Extension").Width = 150
EmployeeTelNoTableFormat.Column("StartDate").PrintColumn = False
EmployeeTelNoTableFormat.Column("Extension").HeadingTextStyle.Font = _
New System.Drawing.Font(EmployeeTelNoTableFormat.Column("Extension").
HeadingTextStyle.Font, Drawing.FontStyle.Italic Or _
Drawing.FontStyle.Bold)
.Section("Employee List").SetExtraParameter("ColumnFormats", _
EmployeeTelNoTableFormat)
.AddSection(New StructuredPrintDocumentArea("Todays Date"))
With .Section("Todays Date")
.WriterType = GetType(StructuredPrintDocumentAreaTextWriter)
.BoundaryRectangle = _
New System.Drawing.Rectangle(150, 600, 450, 30)
.DataSourceName = "TodaysDate"
.StyleName = "Body"
End With
End With
After the employee phone list is finished we want to go back to the employee collection to start the next set of pages:
[Editor comment: Line breaks used to avoid scrolling.]
StructuredPrintDocumentProvider1.StructuredPrintDocument.
Pages.Page(1).DataActionsBefore.Add(New DocumentDataAction("Employee", _
DocumentDataAction.DataSetMoveActions.MoveToFirstRecord))
Now we have another page description for each employee in the list:
[Editor comment: Line breaks used to avoid scrolling.]
With StructuredPrintDocumentProvider1.StructuredPrintDocument.Pages
.Page(1).AddPicture("Heading", Me.PictureBox1.Image, _
"HeadingPicture", _
New System.Drawing.Rectangle(150, 150, 90, 150), False)
.Page(1).Sections.Item(0).SetExtraParameter("Stretch", True)
.Page(1).AddLabel("EmployeeName", "Employee Name", "Body", _
New System.Drawing.Rectangle(150, 350, 250, 30))
.Page(1).AddSection(New StructuredPrintDocumentArea(_
"Employee Name", "Employee.Name", GetType(StructuredDocuments.
StructuredDocuments.StructuredPrintDocumentAreaTextWriter), _
New System.Drawing.Rectangle(300, 350, 250, 30)))
.Page(1).Section("Employee Name").StyleName = "Body"
.Page(1).AddLabel("EmployeeExtension", "Extension", "Body", _
New System.Drawing.Rectangle(150, 450, 250, 30))
.Page(1).AddSection(New StructuredPrintDocumentArea(_
"Employee Extension", "Employee.Extension", _
GetType(StructuredDocuments.StructuredDocuments.
StructuredPrintDocumentAreaTextWriter), _
New System.Drawing.Rectangle(300, 450, 250, 30)))
.Page(1).Section("Employee Extension").StyleName = "Body"
.Page(1).AddLabel("EmployeeStartDate", "Joined", "Body", _
New System.Drawing.Rectangle(150, 550, 250, 30))
.Page(1).AddSection(New StructuredPrintDocumentArea(_
"Employee Start Date", "Employee.StartDate",
GetType(StructuredDocuments.StructuredDocuments.
StructuredPrintDocumentAreaTextWriter), _
New System.Drawing.Rectangle(300, 550, 250, 30)))
.Page(1).Section("Employee Start Date").StyleName = "Body"
.Page("Employee Details").DataActionsOnNextPage.
Add(New DocumentDataAction("Employee",
DocumentDataAction.DataSetMoveActions.MoveToNextRecord))
.Page("Employee Details").AdditionalPageTests.
Add(New AdditionalPageTest("Employee",
AdditionalPageTest.AdditionalPageTestTypes.NotAtEndOfCollection))
End With
This results in the following document in design mode:

Phone list

Employee details
And the same document at run time:

Phone list

Employee details
Future improvements
Currently, the page design is created in code - ideally this should be done using an interactive designer.
History
- 10 Apr 2006 - Updated the code by adding "TypeConverter" and "Designer" classes to serialise the print document template to the designer part of the Windows Forms code.