Printing multipage output
You can print well-formatted multipage output under the following conditions:
-
When each control fits on a print page or less. You often encounter such jobs when printing a form with fixed-length fields.
-
When the printed output includes one or more PrintDataGrid controls that are too long to print on a single page, particularly if the control height might vary, depending on the data. A good example of this type of output is a customer order receipt, which starts with the customer information, has an indeterminate number of order line items, and ends with total information.
Printing known-length multipage output
If you know the length of each component in a multipage document, you can create a separate print layout component for each page you print, and specify each layout page in a separateaddObject()method, as follows:
printJob.addObject(introPrintView, "ShowAll"); printJob.addObject(finDetailPrintView, "ShowAll"); printJob.addObject(hrDetailPrintView, "ShowAll"); printJob.addObject(summaryPrintView, "ShowAll");
Using the PrintDataGrid control for multipage grids
When a DataGrid control with many rows does not fit on a single screen in your application, you typically have scroll bars that let users view all the data. When you print the DataGrid control, the output is the same as the screen display. Therefore, if your DataGrid control has rows or columns that are not immediately visible, they do not print. If you replace the DataGrid control with a PrintDataGrid control that does not have a height specified (or has a large height), you print all the rows, but some rows could be partially printed on the bottom of one page and partially printed at the top of another, as you often see with HTML printout.
You can solve these problems by using the following features of the PrintDataGrid control. These features let you correctly print grids that contain multiple pages of data without splitting rows across pages:
- sizeToPage property
- Makes the printed data grid contain only full rows.
- nextPage() method
- Gets the next printable page of data.
- validNextPage property
- Istrueif printing the data requires an additional page.
Using the sizeToPage attribute to format pages
A PrintDataGrid page consists of the rows that are visible in the control’s current view. Suppose, for example, that a PrintDataGrid control has a height of 130 pixels. The total height of each row and header is 30 pixels, and the control’s data provider has 10 rows. In this situation, the printed PrintDataGrid page contains only three complete data rows, plus the header. ThesizeToPageproperty specifies whether to include a fourth and partial data row.
ThesizeToPageproperty, which istrueby default, causes the PrintDataGrid control to remove any partially visible or empty rows and to resize itself to include only complete rows in the current view. For the data grid described in the preceding paragraph, when this property istrue, the DataGrid shrinks to show three complete data rows, and no incomplete rows; if the attribute isfalse, the grid includes a partial row at the bottom.
The following properties provide information on page sizing that are affected by thesizeToPageproperty:
Property |
Description |
---|---|
currentPageHeight |
Contains the height of the grid, in pixels, that results if thesizeToPageproperty istrue. If thesizeToPageproperty istrue, thecurrentPageHeightproperty equals theheightproperty. |
originalHeight |
Contains the grid height that results if thesizeToPageproperty isfalse. If thesizeToPageproperty isfalse, theoriginalHeightproperty equals theheightproperty. |
In most applications, you leave thesizeToPageattribute at its default value (true), and use theheightproperty to determine the grid height.
ThesizeToPageproperty does not affect the way the page breaks when a single PrintDataGrid control page is longer than a print page. To print multipage data grids without splitting rows, you must divide the grid items into multiple views by using thenextPage()method, as described in the following section.
Using the nextPage() method and validNextPage property to print multiple pages
ThevalidNextPageproperty istrueif the PrintDataGrid control has data beyond the rows that fit on the current print page. You use it to determine whether you need to format and print an additional page.
ThenextPage()method lets you page through the data provider contents by setting the first row of the PrintDataGrid control to be the data provider row that follows the last row of the previous PrintDataGrid page. In other words, thenextPage()method increases the grid’sverticalScrollPositionproperty by the value of the grid’srowCountproperty.
The following code shows a loop that prints a grid using multiple pages, without having rows that span pages:
// Queue the first page. printJob.addObject(thePrintView); // While there are more pages, print them. while (thePrintView.myDataGrid.validNextPage) { //Put the next page of data in the view. thePrintView.myDataGrid.nextPage(); //Queue the additional page. printJob.addObject(thePrintView); }
The section Example: Printing with multipage PrintDataGrid controls shows how to use thenextPage()method to print a report with a multipage data grid.
Updating the PrintDataGrid layout
When you use a PrintDataGrid control to print a single data grid across multiple pages, you queue each page of the grid individually. If your application customizes each page beyond simply using thenextPage()method to page through the PrintDataGrid, you must call thevalidateNow()method to update the page layout before you print each page, as shown in Print output component.
Example: Printing with multipage PrintDataGrid controls
The following example prints a data grid in which you can specify the number of items in the data provider. You can, therefore, set the DataGrid control contents to print on one, two, or more pages, so that you can see the effects of different-sized data sets on the printed result.
The example also shows how you can put header information before the grid and footer information after the grid, as in a shipping list or receipt. It uses the technique of selectively showing and hiding the header and footer, depending on the page being printed. To keep the code as short as possible, the example uses simple placeholder information only.
The application consists of the following files:
-
The application file displays the form to the user, including TextArea and Button controls to set the number of lines and a Print button. The file includes the code to initialize the view, get the data, and handle the user’s print request. It uses the FormPrintView MXML component as a template for the printed output.
-
The FormPrintView.mxml file formats the printed output. It has two major elements:
-
The print output template includes the PrintDataGrid control and uses two MXML components to format the header and footer contents.
-
TheshowPage()function determines which sections of the template to include in a particular page of the output, based on the page type: first, middle, last, or single. On the first page of multipage output, theshowPage()function hides the footer; on the middle and last pages, it hides the header. On a single page, it shows both header and footer.
-
-
The FormPrintHeader.mxml and formPrintFooter.mxml files specify the contents of the start and the end of the output. To keep the application simple, the header has a single, static Label control. The footer displays a total of the numbers in the Quantity column. In a more complete application, the header page could have, for example, a shipping address, and the footer page could show more detail about the shipment totals.
The files include detailed comments explaining the purpose of the code.
Multipage print application file
The following code shows the multipage print application file:
<?xml version="1.0"?> <!-- printing\MultiPagePrint.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" initialize="initData();"> <fx:Script> <![CDATA[ import mx.printing.*; import mx.collections.ArrayCollection; // Import the MXML custom print view control. import myComponents.FormPrintView; // Declare variables and initialize simple variables. // The dgProvider ArrayCollection is the DataGrid data provider. // It must be bindable because you change its contents dynamically. [Bindable] public var dgProvider:ArrayCollection; public var footerHeight:Number = 20; public var prodIndex:Number; public var prodTotal:Number = 0; // Data initialization, called when the application initializes. public function initData():void { // Create the data provider for the DataGrid control. dgProvider = new ArrayCollection; } // Fill the dgProvider ArrayCollection with the specified items. public function setdgProvider(items:int):void { // First initialize the index and clear any existing data. prodIndex=1; dgProvider.removeAll(); // Fill the ArrayCollection, and calculate a product total. // For simplicity, it increases the Index field value by // 1, and the Qty field by 7 for each item. for (var z:int=0; z<items; z++) { var prod1:Object = {}; prod1.Qty = prodIndex * 7; prod1.Index = prodIndex++; prodTotal += prod1.Qty; dgProvider.addItem(prod1); } } // The function to print the output. public function doPrint():void { // Create a FlexPrintJob instance. var printJob:FlexPrintJob = new FlexPrintJob(); // Start the print job. if (printJob.start()) { // Create a FormPrintView control // as a child of the application. var thePrintView:FormPrintView = new FormPrintView(); addElement(thePrintView); // Set the print view properties. thePrintView.width=printJob.pageWidth; thePrintView.height=printJob.pageHeight; thePrintView.prodTotal = prodTotal; // Set the data provider of the FormPrintView // component's DataGrid to be the data provider of // the displayed DataGrid. thePrintView.myDataGrid.dataProvider = myDataGrid.dataProvider; // Create a single-page image. thePrintView.showPage("single"); // If the print image's DataGrid can hold all the // data provider's rows, add the page to the print job. if(!thePrintView.myDataGrid.validNextPage) { printJob.addObject(thePrintView); } // Otherwise, the job requires multiple pages. else { // Create the first page and add it to the print job. thePrintView.showPage("first"); printJob.addObject(thePrintView); thePrintView.pageNumber++; // Loop through the following code // until all pages are queued. while(true) { // Move the next page of data to the top of // the PrintDataGrid. thePrintView.myDataGrid.nextPage(); // Try creating a last page. thePrintView.showPage("last"); // If the page holds the remaining data, or if // the last page was completely filled by the last // grid data, queue it for printing. // Test if there is data for another // PrintDataGrid page. if(!thePrintView.myDataGrid.validNextPage) { // This is the last page; // queue it and exit the print loop. printJob.addObject(thePrintView); break; } else // This is not the last page. Queue a middle page. { thePrintView.showPage("middle"); printJob.addObject(thePrintView); thePrintView.pageNumber++; } } } // All pages are queued; remove the FormPrintView // control to free memory. removeElement(thePrintView); } // Send the job to the printer. printJob.send(); } ]]> </fx:Script> <!-- The form that appears on the user's system.--> <mx:Form id="myForm" width="80%"> <mx:FormHeading label="Product Information"/> <mx:DataGrid id="myDataGrid" dataProvider="{dgProvider}"> <mx:columns> <mx:DataGridColumn dataField="Index"/> <mx:DataGridColumn dataField="Qty"/> </mx:columns> </mx:DataGrid> <mx:Text width="100%" text="Specify the number of lines and click Fill Grid first. Then you can click Print."/> <mx:TextInput id="dataItems" text="35"/> <mx:HBox> <mx:Button id="setDP" label="Fill Grid" click="setdgProvider(int(dataItems.text));"/> <mx:Button id="printDG" label="Print" click="doPrint();"/> </mx:HBox> </mx:Form> </s:Application>
The executing SWF file for the previous example is shown below:
Print output component
The following lines show the FormPrintView.mxml custom component file:
<?xml version="1.0"?> <!-- printing\myComponents\FormPrintView.mxml --> <mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:MyComp="myComponents.*" backgroundColor="#FFFFFF" paddingTop="50" paddingBottom="50" paddingLeft="50"> <fx:Script> <![CDATA[ import mx.core.* // Declare and initialize the variables used in the component. // The application sets the actual prodTotal value. [Bindable] public var pageNumber:Number = 1; [Bindable] public var prodTotal:Number = 0; // Control the page contents by selectively hiding the header and // footer based on the page type. public function showPage(pageType:String):void { if(pageType == "first" || pageType == "middle") { // Hide the footer. footer.includeInLayout=false; footer.visible = false; } if(pageType == "middle" || pageType == "last") { // The header won't be used again; hide it. header.includeInLayout=false; header.visible = false; } if(pageType == "last") { // Show the footer. footer.includeInLayout=true; footer.visible = true; } //Update the DataGrid layout to reflect the results. validateNow(); } ]]> </fx:Script> <!-- The template for the printed page, with the contents for all pages. --> <mx:VBox width="80%" horizontalAlign="left"> <mx:Label text="Page {pageNumber}"/> </mx:VBox> <MyComp:FormPrintHeader id="header"/> <!-- The sizeToPage property is true by default, so the last page has only as many grid rows as are needed for the data. --> <mx:PrintDataGrid id="myDataGrid" width="60%" height="100%"> <!-- Specify the columns to ensure that their order is correct. --> <mx:columns> <mx:DataGridColumn dataField="Index" /> <mx:DataGridColumn dataField="Qty" /> </mx:columns> </mx:PrintDataGrid> <!-- Create a FormPrintFooter control and set its prodTotal variable. --> <MyComp:FormPrintFooter id="footer" pTotal="{prodTotal}"/> </mx:VBox>
Header and footer files
The following lines show the FormPrintHeader.mxml file.
<?xml version="1.0"?> <!-- printing\myComponents\FormPrintHeader.mxml --> <mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="60%" horizontalAlign="right" > <mx:Label text="This is a placeholder for first page contents"/> </mx:VBox>
The following lines show the FormPrintFooter.mxml file:
<?xml version="1.0"?> <!-- printing\myComponents\FormPrintFooter.mxml --> <mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="60%" horizontalAlign="right"> <!-- Declare and initialize the product total variable. --> <fx:Script> <![CDATA[ [Bindable] public var pTotal:Number = 0; ]]> </fx:Script> <mx:Label text="Product Total: {pTotal}"/> </mx:VBox>
Using the PrintAdvancedDataGrid control
The PrintAdvancedDataGrid and PrintOLAPDataGridcontrols provide the same functionality for the AdvancedDataGrid and OLAPDataGrid controls as the PrintDataGrid control does for the DataGrid control. For more information, see Using the PrintDataGrid control for multipage grids.
The following example uses the PrintAdvancedDataGrid control to print an instance of the AdvancedDataGrid control.
<?xml version="1.0"?> <!-- printing\ADGPrint.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Script> <![CDATA[ import mx.printing.*; import mx.collections.ArrayCollection; import mx.printing.PrintAdvancedDataGrid; include "SimpleHierarchicalData.as"; // Create a PrintJob instance. private function doPrint():void { // Create an instance of the FlexPrintJob class. var printJob:FlexPrintJob = new FlexPrintJob(); // Initialize the PrintAdvancedDataGrid control. var printADG:PrintAdvancedDataGrid = new PrintAdvancedDataGrid(); // Exclude the PrintAdvancedDataGrid control from layout. printADG.includeInLayout = false; printADG.source = adg; // Add the print-specific control to the application. addElement(printADG); // Start the print job. if (printJob.start() == false) { // User cancelled print job. // Remove the print-specific control to free memory. removeElement(printADG); return; } // Add the object to print. Do not scale it. printJob.addObject(printADG, FlexPrintJobScaleType.NONE); // Send the job to the printer. printJob.send(); // Remove the print-specific control to free memory. removeElement(printADG); } ]]> </fx:Script> <mx:VBox id="myVBox" width="100%" height="100%"> <mx:AdvancedDataGrid id="adg" width="100%" height="100%"> <mx:dataProvider> <mx:HierarchicalData source="{dpHierarchy}"/> </mx:dataProvider> <mx:columns> <mx:AdvancedDataGridColumn dataField="Region"/> <mx:AdvancedDataGridColumn dataField="Territory_Rep" headerText="Territory Rep"/> <mx:AdvancedDataGridColumn dataField="Actual"/> <mx:AdvancedDataGridColumn dataField="Estimate"/> </mx:columns> </mx:AdvancedDataGrid> <mx:Button id="myButton" label="Print" click="doPrint();"/> </mx:VBox> </s:Application>
The executing SWF file for the previous example is shown below:
[Bindable] private var dpHierarchy:ArrayCollection = new ArrayCollection([ {Region:"Southwest", children: [ {Region:"Arizona", children: [ {Territory_Rep:"Barbara Jennings", Actual:38865, Estimate:40000}, {Territory_Rep:"Dana Binn", Actual:29885, Estimate:30000}]}, {Region:"Central California", children: [ {Territory_Rep:"Joe Smith", Actual:29134, Estimate:30000}]}, {Region:"Nevada", children: [ {Territory_Rep:"Bethany Pittman", Actual:52888, Estimate:45000}]}, {Region:"Northern California", children: [ {Territory_Rep:"Lauren Ipsum", Actual:38805, Estimate:40000}, {Territory_Rep:"T.R. Smith", Actual:55498, Estimate:40000}]}, {Region:"Southern California", children: [ {Territory_Rep:"Alice Treu", Actual:44985, Estimate:45000}, {Territory_Rep:"Jane Grove", Actual:44913, Estimate:45000}]} ]} ]);
This example uses thePrintAdvancedDataGrid.sourceproperty to initialize the PrintAdvancedDataGrid control from the AdvancedDataGrid control.
To support the AdvancedDataGrid control, the PrintAdvancedDataGrid control adds the following properties not available in the PrintDataGrid control:
Property |
Description |
---|---|
allowInteractions |
Iftrue, allow some interactions with the control, such as column resizing, column reordering, and expanding or collapsing nodes. The default value isfalse. |
displayIcons |
Iftrue, display the folder and leaf icons in the navigation tree. The default value istrue. |
source |
Initialize the PrintAdvancedDataGrid control and all of its properties from the specified AdvancedDataGrid control. |
validPreviousPage |
Indicates that the data provider contains data rows that precede the rows that the PrintAdvancedDataGrid control currently displays. |