Reference for unit 'msereport'
Reports with treport
uses |
System, |
classes, |
mclasses, |
|
Copy of the Free Pascal Classes unit, modified for MSEgui |
mseapplication, |
msegui, |
msegraphics, |
msetypes, |
msewidgets, |
|
msewidgets is the base unit of every visual component |
msegraphutils, |
mseclasses, |
mseinterfaces, |
mseificomp, |
msetabs, |
mseprinter, |
msestream, |
msearrayprops, |
mseguiglob, |
msesimplewidgets, |
msedrawtext, |
msestrings, |
mserichstring, |
msedb, |
mdb, |
msethread, |
mseobjectpicker, |
msepointer, |
mseevent, |
msesplitter, |
msestatfile, |
mselookupbuffer, |
mseformatstr, |
msegdiprint, |
msemenus, |
mseglob; |
Overview
About this guide
This guide explains how to build printed reports with the report generator of unit msereport. It is based on the explanations given by Martin Schreiber when the report generator was written (2007), checked against the current source code.
The report generator works differently from most reporting tools: there are no predefined header, footer, group or summary bands. A report is built from a few primitives (pages, band areas, bands, tabulators) whose visibility depends on the data, and from event handlers. Any widget can be placed on a report, not only the components of the 'Rep' tab of the component palette.
Structure of a report
A report is created in MSEide by 'File'-'New'-'Form'-'Report'. It is a treport containing one or more treportpages (popup menu item 'Add report page'); widgets can not be placed directly on the report, only on its pages.
- treportpage: one printed page layout. Widgets placed directly on the page (labels, trepvaluedisp, treppagenumdisp...) are printed on every page: use them for page headers and footers.
- tbandarea: a table area of the page. The bands placed into it are rendered from top to bottom, repeatedly, with continuous vertical displacement, until the area is full; then a new page is started. The vertical order of the bands at design time is not important, they are aligned at runtime. Other widgets placed into a band area stay in the background.
- trecordband: one line (or block) of the table. Its tabs show the data; widgets can also be placed into it.
- tbandgroup: a group of record bands rendered together, to show several lines per record.
All sizes and positions are in millimetres, except the line distances (dist), which are in pixels; tcustomreport.ppmm is the number of pixels per millimetre.
Data and record navigation
There are two basic layouts:
- One page per record: set treportpage.datasource. The page calls tdataset.next after it has been rendered.
- A table: place a tbandarea on the page and do not set treportpage.datasource. Place record bands into the area; every band whose datasource is set calls tdataset.next after it has been rendered.
A report can be fed by datasets, by code, or both. In the onbeforerender event of a band (or of a page), set the values to display and the empty parameter: rendering stops when no band and no page has data any more, that is when all connected datasets are at eof and all onbeforerender events return empty = true.
A page without data is not printed. Set rpo_once in treportpage.options to print a page once without data, for example a front page. In the same way, a band with bo_once in its options is printed once, for example a title or a column header.
To print the current record only (instead of all records), use a page with rpo_once, leave the datasource of the bands empty, and set the datasource of the tabulator items instead.
The onbeforerender event of a band can be called more than once for the same record, because a band which does not fit on the current page is rendered again on the next page. Code which must run once per record belongs in onafterrender.
Rendering
A report is printed by one of the render methods of tcustomreport: to a canvas, to a tstreamprinter (e.g. tpostscriptprinter) with a print command or a stream, or to a tgdiprinter.
Rendering runs in a separate thread. Consequences:
- render returns immediately. Do not free the report or start another rendering before it is finished: call waitfor, or react in onreportfinished or in the onafterrender parameter of render. These two are called in the main thread, after the end of the rendering.
- The other events of the report, its pages and its bands (onbeforerender, onpaint, the published onafterrender...) are called in the rendering thread, with the application locked. Keep them short, and do not show dialogs or free the report from them.
- Set reo_nothread in options to render in the main thread instead (e.g. for SQLite3, which should not be used from several threads); reo_waitdialog shows a dialog with a Cancel button during rendering (texts in dialogtext and dialogcaption).
With reo_autorelease, the report frees itself after rendering, which allows to create and print it in one statement:
procedure tmainfo.printexe(const sender: tobject);
begin
tmyreport.create(nil).render(printer);
end;
A report without reo_autorelease can be rendered several times, for example with different query parameters, as long as each rendering is finished (waitfor) before the next one starts.
Page numbers: "Page N of M"
The number of pages is only known after the report has been rendered. Set reo_prepass in treport.options: the report is then rendered twice, the first pass (tcustomreport.prepass = True) producing no output and only counting the pages.
In a treppagenumdisp, the characters 0 and 1 of format are replaced by the page number and by the page count: for example 'Page 0 of 1'. Text between quotes is not replaced.
Both passes are identical, so values accumulated in event handlers (totals...) must be reset before each pass, in treport.onbeforerender. In the same way, to show something on the last page only, test pagenum = lastpagecount - 1 (page numbers are zero-based); this only works with reo_prepass, because lastpagecount is 0 during the first pass.
Tables: tabulators and lines
The columns of a record band are defined by its tabs property (treptabulators), one item (treptabulatoritem) per column: position (pos), alignment on the position (kind: tak_left, tak_right, tak_centered, tak_decimal), textflags, font, and the value: a constant value, the field datafield of datasource, a lookup (lookupbuffer), or the result of the ongetvalue event.
- A field is shown with its DisplayText, so its DisplayFormat applies (persistent fields are needed to set it). The format property of the tabulator item overrides it.
- tf_wordbreak and tf_xjustify work in tabulator items, for multi-line texts; #n in a caption is a line break.
- linksource synchronizes the number and the positions of the tabs with those of another band, e.g. a column header band.
- color of a tabulator item fills the background of its cell. zebra_* properties of the band alternate a background color every n records.
Frame lines are set per band (lileft, litop, liright, libottom, livert of tabs) and per tabulator item (litop, livert, libottom):
- widthmm: line width; 0 (the default) draws no line.
- dashes: dash pattern, a string of lengths in pixels, e.g. #3#5 for 3 pixels of line and 5 of gap; if the last character is #0, the gaps are filled with colorgap.
- dist: shift of the line, in pixels (multiply millimetres by ppmm).
- capstyle: style of the line ends. Use cs_butt when vertical lines are thicker than horizontal ones, so that they do not jut out.
- visible: on which records the line is drawn (first or last of page, of group, of report...).
Groups and totals
- visidatasource and visidatafield: the band is not shown when the field is null.
- visidatasource and visigroupfield, with bo_visigroupfirst or bo_visigrouplast in options: the band is shown only on the first or the last record of each group, that is when the value of the group field changes (bo_visigroupnotfirst and bo_visigroupnotlast do the opposite). The group field must be a string or an integer field, and the dataset should be sorted on it.
- For nested groups, insert additional record bands into the tbandgroup, each with visigroupfield set to its group field.
- Totals: set rto_sum, rto_count or rto_average in the options of a tabulator item. The item accumulates its field for each record of its datasource, and is reset after it has been printed (unless rto_noreset is set). A summing tabulator in a band shown with bo_visigrouplast gives the subtotal of each group.
Example (from Martin Schreiber): a table with a header and a footer on every page.
- Place a tbandarea on the page, and a tbandgroup into it.
- Place the header band into the band group, and set in its optionsshow: bos_showfirstofpage, bos_hidenormalofpage, bos_hidelastofpage.
- Place the data band below the header band, into the band group.
- Place the footer band below the data band, into the band group, and set in its optionsshow: bos_hidefirstofpage, bos_hidenormalofpage, bos_showlastofpage, bos_showlastrecord.
- Set the datasource of the band group, and osc_expandy, osc_shrinky in its optionsscale.
Several reports in one print job
To print the same report several times on continuous output (e.g. once per selected customer), call restart of the band area from the onbeforerender event of its last band, when it has no more data:
procedure tmyreport.lastbandbeforerender(const sender: tcustomrecordband;
var empty: boolean);
begin
if empty and morereports then begin
setupnextreport; //change the query parameters, call first...
bandarea1.restart;
end;
end;
With several band areas, call restart of the report page.
Printing tips
- tgdiprinter can not draw lines thinner than one pixel: at 3 pixels per millimetre, a 0.1 mm line is rounded down to a hairline. Increase tcustomreport.ppmm (e.g. 10) or use tpostscriptprinter.
- Images are printed in gray if the printer canvas has colorspace = cos_gray, which is the default; set cos_rgb. The setting may also come from the statfile of the printer.
- The number of copies is a setting of the printing system (print command or printer dialog), not of the report.
- Use tfont.xscale for condensed text; it works on screen and with PostScript.