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