This is a Class and 4 custom views that implements a basic reportwriter for B4j. I have developed this for an app I am working on, I've tried to make it generic enough to be of use to others but it is certainly not fully tested for all possibilities as they could be varied.
The 4 custom views are:
StaticField
ColumnHeaderField
ColumnField
TotalField
You can create a columnar report that implements a subtotal and total row, or totally freeform report using just static fields, or a mixture of both.
The required layout (See the layout ReportWriterTemplate in the demo app) has 3 required panes:
pnHeader, pnContent and pnFooter. Any of these can be empty or invisible, but they must be there.
pnHeader and pnFooter are self explanatory, they can contain any number of Static fields pnContent is explained below.
How it works
A report layout is defined using the B4j designer. It's name is passed to the initialize method of the reportwriter, along with an initial size for the report. The final size is determined by selection of a printer using the print dialog.
For StaticFields, you assign a name to each field in the designer and then in your program add the data to a map with a key of the same name. The data is then displayed in the field at runtime.
For the columnar part of a report, you do not assign names to the fields, they are populated from a list of arrays added to the data map with a key of "RowData" that contains rows of data in the order in which it is to be displayed. It sounds complicated, but have a look at the ColumnReport in the example, it's really fairly straightforward.
pnContent:
pnColumnHeader should only contain ColumnHeaderFields. It's text is displayed in the ColumnHeaderRow. The column header field also allows you to assign a format string that is applied to all of the fields in that column. The format string is used to feed options to the NumberFormat2 function and takes the form
See Numberformat2 for clarification, the trailing comma turns on Grouping.
pnRow should only contain Columnfields, you can add an identifier for a subtotal and total which must match an Identifier in a TotalField in pnSubTotal and/or pnTotal whichever is required.
pnSubTotal and pnTotal can contain labels and Totalfields. The labels are only used to display text, the total fields will display a subtotal or grandtotal depending on which pane they are in. TotalFields have their own FormatString that can be entered in the designer.
You can also add one or more styleclasses to the custom views for formatting with CSS. Each pane also has it's own styleclass (see the code for reference).
StaticFields:
You can put staticfields pretty much anywhere else in a report (see the InformationPage example), they should be contained in panes which have a unique id in a tag, these should then be registered with the reportwriter so that the fields can be added to the field map and matched to the data map when you pass data to it. You can enter a FormatString for Static fields, this will cause an error if you enter a formatstring and the data passed to the field is not a number.
There are 3 examples in the attached project (RW.Zip), ColumnReport (mainly uses columns, subtotals and totals), InformationPage (uses StaticFields) and Invoice (a mixture of the two).
This is just a quick overview, see the comments in the code and post any questions and I'll do my best to answer them.
Known Issues:
For some reason, a border is still printed if a fields borderwidth is set to 0, so you also need to set the border color to match the background color (or make it transparent) to stop it being seen.
Matching output to a printed page size can be tricky, you may need to experiment with variant sizes in the designer to get it to look exactly how you want it. The reportwriter does provide the ability to scale a page to fit the available printer page size (and optionally keep the aspect ratio) which may help, or it may not.
The demo app depends on the javafx8 print library
This should be considered an alpha release, please test it thoroughly before use in a production app.
There is currently very little data validation, you need to make sure that the number of columns passed to the reportwriter is the same as defined in the layout. Let's see what other problems are found which will depend largely on how you try to use it and we may be able to make it a bit more resilient.
I hope you find it useful.
Updated to V0.6 to work in Obsfuscated mode.
Updated to V0.7 with a ReportWriterPreview class, provides a better preview. See post #4 Depends on jNumberSpinner Lib.
The 4 custom views are:
StaticField
ColumnHeaderField
ColumnField
TotalField
You can create a columnar report that implements a subtotal and total row, or totally freeform report using just static fields, or a mixture of both.
The required layout (See the layout ReportWriterTemplate in the demo app) has 3 required panes:
pnHeader, pnContent and pnFooter. Any of these can be empty or invisible, but they must be there.
pnHeader and pnFooter are self explanatory, they can contain any number of Static fields pnContent is explained below.
How it works
A report layout is defined using the B4j designer. It's name is passed to the initialize method of the reportwriter, along with an initial size for the report. The final size is determined by selection of a printer using the print dialog.
For StaticFields, you assign a name to each field in the designer and then in your program add the data to a map with a key of the same name. The data is then displayed in the field at runtime.
For the columnar part of a report, you do not assign names to the fields, they are populated from a list of arrays added to the data map with a key of "RowData" that contains rows of data in the order in which it is to be displayed. It sounds complicated, but have a look at the ColumnReport in the example, it's really fairly straightforward.
pnContent:
pnColumnHeader should only contain ColumnHeaderFields. It's text is displayed in the ColumnHeaderRow. The column header field also allows you to assign a format string that is applied to all of the fields in that column. The format string is used to feed options to the NumberFormat2 function and takes the form
B4X:
MinInt.MinFrac.MaxFrac,
See Numberformat2 for clarification, the trailing comma turns on Grouping.
pnRow should only contain Columnfields, you can add an identifier for a subtotal and total which must match an Identifier in a TotalField in pnSubTotal and/or pnTotal whichever is required.
pnSubTotal and pnTotal can contain labels and Totalfields. The labels are only used to display text, the total fields will display a subtotal or grandtotal depending on which pane they are in. TotalFields have their own FormatString that can be entered in the designer.
You can also add one or more styleclasses to the custom views for formatting with CSS. Each pane also has it's own styleclass (see the code for reference).
StaticFields:
You can put staticfields pretty much anywhere else in a report (see the InformationPage example), they should be contained in panes which have a unique id in a tag, these should then be registered with the reportwriter so that the fields can be added to the field map and matched to the data map when you pass data to it. You can enter a FormatString for Static fields, this will cause an error if you enter a formatstring and the data passed to the field is not a number.
There are 3 examples in the attached project (RW.Zip), ColumnReport (mainly uses columns, subtotals and totals), InformationPage (uses StaticFields) and Invoice (a mixture of the two).
This is just a quick overview, see the comments in the code and post any questions and I'll do my best to answer them.
Known Issues:
For some reason, a border is still printed if a fields borderwidth is set to 0, so you also need to set the border color to match the background color (or make it transparent) to stop it being seen.
Matching output to a printed page size can be tricky, you may need to experiment with variant sizes in the designer to get it to look exactly how you want it. The reportwriter does provide the ability to scale a page to fit the available printer page size (and optionally keep the aspect ratio) which may help, or it may not.
The demo app depends on the javafx8 print library
This should be considered an alpha release, please test it thoroughly before use in a production app.
There is currently very little data validation, you need to make sure that the number of columns passed to the reportwriter is the same as defined in the layout. Let's see what other problems are found which will depend largely on how you try to use it and we may be able to make it a bit more resilient.
I hope you find it useful.
Updated to V0.6 to work in Obsfuscated mode.
Updated to V0.7 with a ReportWriterPreview class, provides a better preview. See post #4 Depends on jNumberSpinner Lib.
Attachments
Last edited: