bgx:components logo
© 2004 -2005
Bernhard Gaul



Printing the Content of a DataGrid Instance
Flash MX 2004

The Task

I know that there are examples on the internet that show how to print the content of a DataGrid instance by re-scaling the DataGrid before printing, scrolling the data to certain positions, adding page snapshots to the spooler and then resizing the DataGrid back to its original form.

Personally I find it much handier to create a hidden MovieClip to which TextFields and MovieClips can be added to get the desired print effect. I don't think it is more complicated and you certainly have more control over the output.

Suggested Solution

The following is just one way of dynamically creating a print output clip and adding new TextFields corresponding to the DataGrid data. It is quite generic as it takes column information directly from the specified DataGrid. However you may want to change text size, styles and more.

There is no dynamic scaling or word wrap handling in this example but I guess it is enough to understand the principle of adding TextFields for the data fields including margins and repeating the column headers on each page.

Try it out.

[Note: I put the code to generate the temporay clip in a separate function from the one that creates the PrintJob, this way it can be re-used easier. If you want you could even create a Class that creates the printOutput clip. I was thinking of it but left it for now as there would have to be more thought put into which display options (TextFormats, colors, zoom factor, margins ..) would make sense without getting too complicated to use.]

Script Limit and Single Frame Restriction

I created this example for ActionScript 2.0 which restricted the PrintJob object to a single frame, therefore the function to create the hidden movieclip had to be called directly from the function that creates and uses the PrintJob object. That means if you had a lot of TextFields to create you may run into the infamous ScriptLimit problem, producing the "A script in this movie is causing Flash Player to run slowly" alert (see e.g. http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=tn_15512)

Apparently AS 3.0 removed the single frame restriction therefore it should be possible to split my sample code over several intervals (e.g. create the PrintJob object, call the function to ctreate the movieclip but set an interval let's say after every 100 rows that are created to call the function again to create the next 100 rows etc. until you are finished and then call PrintJob.send()). As I hardly work with Flash at the moment and don't even have the latest version I leave it up to you to work out the details if you need them.

Documentation PrintJob in AS 3.0

The Sample

Screenshot of Print Output

Resources

Download the source file
(ZIP file, 227 kB)

View the print output as PDF file
(ZIP file, 32 kB)


History

2005-07-13: Changed the depth of the dynamic movieclip to the static value 100000 because using this.getNextHighestDepth() resulted in the mc not being removed using removeMovieClip()

2007-01-05: Added notes about Script Limit and Single Frame Restriction

 

Code

import mx.controls.DataGrid;
import mx.controls.gridclasses.DataGridColumn;


//create arbitrary data
var SampleData = new Array();

for (var i = 0; i < 100; i ++)
{
    var rowObj = new Object();
	rowObj["c1"] = "row " + i;
	rowObj["c2"] = Math.random();
	rowObj["c3"] = rowObj.c2 * 0.5;
  	SampleData.push(rowObj);
}

myDataGrid.columnNames= ["c1", "c2","c3"];
myDataGrid.getColumnAt(0).width = 50;
myDataGrid.getColumnAt(1).width = 160; 
myDataGrid.getColumnAt(2).width = 170;
myDataGrid.dataProvider = SampleData;

btnPrint.onRelease = doPrint;


function doPrint():Void
{
	var pagesToPrint:Number = 0;
	var _objPrintJob:PrintJob = new PrintJob();	
	// display print dialog box
	var _printOK:Boolean = _objPrintJob.start();
	//abort if cancelled;
	if (!_printOK)
	{
		delete _objPrintJob; 
		return;
	}
	
	/*NOTE: If you want you could add other pages to the print output
	before or after the DataGrid pages. E.g. the following would add a 
	snapshot of the main timeline as initial page:
	if (_objPrintJob.addPage(0))
	{
		pagesToPrint++;
	}
	End Note */
 
	//START ADDING PAGES FOR THE DATAGRID
	//create new movieclip for the datagrid data
	var printOutput:MovieClip = createDgPrintClip(myDataGrid,_objPrintJob);
	//define the height of the page snapshots depending on how many rows fit
	//into the pageheight returned by the printer
	var _pageHeight:Number = Math.floor(_objPrintJob.pageHeight/printOutput.rowHeight)*printOutput.rowHeight;

	var _yMin:Number  = 0;
	var xMargin:Number  = -40;
	var rightMargin = 30;

	
	while(_yMin <  printOutput._height)
	{
		var _yMax:Number = _yMin + _pageHeight;
		if (_objPrintJob.addPage(printOutput,{xMin:xMargin,xMax:_objPrintJob.pageWidth + xMargin -rightMargin,yMin:_yMin,yMax:_yMax} ))
		{
			pagesToPrint++;
		}
		_yMin = _yMax;
	}
	//END ADDING PAGES FOR THE DATAGRID
		
	// send pages from the spooler to the printer
	if (pagesToPrint > 0)
	{
		_objPrintJob.send(); 
	}
	// clean up
	delete _objPrintJob; 
	printOutput.removeMovieClip();
}

function createDgPrintClip(targetDG:DataGrid,printJob:PrintJob):MovieClip
{
	var headerFormat:TextFormat = new TextFormat()
	headerFormat.size = 12;
	headerFormat.font = "Arial";
	headerFormat.bold = true;
	headerFormat.leftMargin = 4;
	
	var txtFormat:TextFormat = new TextFormat()
	txtFormat.size = 12;
	txtFormat.font = "Arial";
	txtFormat.leftMargin = 4;
	
	var _rowHeight:Number = 18;
	//calculate rows per page taking off 4 rows for margin
	var _rowsPerPage:Number = Math.floor(printJob.pageHeight/_rowHeight) - 4;

	//note: the static value 100000 is used for the mc depth as using 
//this.getNextHighestDepth() will result in the mc not being removed
//using removeMovieClip()

var printClip:MovieClip = createEmptyMovieClip("print_mc", 100000); printClip._visible = false; //get colums var columns:Array = targetDG.columnNames; //Start printing headers var xPos:Number = 0; var tbWidth:Number = 0; //leave 2 rows margin at top var rowY:Number = _rowHeight * 2; for (var i = 0; i < columns.length; i++) { //define xPos by adding the tbWidth of the last loop xPos = xPos + tbWidth; var column:DataGridColumn = targetDG.getColumnAt(i); //get width of this column tbWidth = column.width; //add textField printClip.createTextField("header_" + i , printClip.getNextHighestDepth(), xPos, rowY,tbWidth, _rowHeight); var thisTb:TextField = printClip["header_" + i]; thisTb.setNewTextFormat(headerFormat); thisTb.border = true; thisTb.borderColor = 0xCCCCCC; thisTb.background = true; thisTb.backgroundColor = 0xD5EAFF; thisTb.text = column.headerText; } //End printing headers //Start printing rows //start row counter taking in count the header row var pageRows:Number = 1; var pages:Number = 1; var dataArray:Object = targetDG.dataProvider; //add data rows for (var j = 0; j < dataArray.length; j++) { //insert margins if rows per page are full if (pageRows == _rowsPerPage) { rowY += _rowHeight * 5; pageRows = 1; pages++; //add headers xPos = 0; tbWidth = 0; for (var i = 0; i < columns.length; i++) { //define xPos by adding the tbWidth of the last loop xPos = xPos + tbWidth; var column:DataGridColumn = targetDG.getColumnAt(i); //get width of this column tbWidth = column.width; //add textField printClip.createTextField("header_" + pages +"_" + i , printClip.getNextHighestDepth(), xPos, rowY,tbWidth, _rowHeight); var thisTb:TextField = printClip["header_" + pages +"_" + i]; thisTb.setNewTextFormat(headerFormat); thisTb.border = true; thisTb.borderColor = 0xCCCCCC; thisTb.background = true; thisTb.backgroundColor = 0xD5EAFF; thisTb.text = column.headerText; } } rowY += _rowHeight; pageRows++; //add TextFields for each row xPos = 0; tbWidth = 0; var rowData:Object = dataArray.getItemAt(j); for (var i = 0; i < columns.length; i++) { //define xPos by adding the tbWidth of the last loop xPos = xPos + tbWidth; var column:DataGridColumn = targetDG.getColumnAt(i); //get width of this column tbWidth = column.width; //add textField printClip.createTextField("field_"+ j + "_" + i , printClip.getNextHighestDepth(), xPos, rowY,tbWidth, _rowHeight); var thisTb:TextField = printClip["field_"+ j + "_" + i]; thisTb.setNewTextFormat(txtFormat); thisTb.border = true; thisTb.borderColor = 0xCCCCCC; //as columns was defined as the array of field identifiers (= columnNames) //for the datagrid we can use the array items to identify the properties //in the dataProvider thisTb.text = rowData[columns[i]]; } } //End printing rows //store rowHeight with printclip to be retrieved later printClip.rowHeight = _rowHeight; return printClip; }

If you have any comments or suggestions please let me know at info@bgxcomponents.com.