Table of Contents
22.11.3 Report project - Draft 2
22.11.3.1 Class ReportVBox
This class presents a report container that arranges its inserted controls vertically.
To create a new ReportVBox object in the source code:
Dim hReportVBox As ReportVBox hReportVBox = New ReportVBox ([ Parent As ReportContainer ])
You can then define the main style properties of the container.
Note: For many projects, it makes sense to specify the basic layout of the report in the IDE in the Report Designer. ReportVBoxes are part of the basic layout because in many cases they contain additional report control elements, which can then be inserted very easily via source code. This approach has proven itself in practice:
Figure 22.11.3.1.1: Report structure
The report structure shown in Figure 22.11.3.1.1 consists of a header as ReportPanel with two ReportLabels, the content area highlighted in yellow as ReportVBox and a footer as ReportPanel in section 1.
22.11.3.2 Class ReportHBox
This class presents a report container that arranges its inserted controls horizontally.
To create a new ReportHBox object:
Dim hReportHBox As ReportHBox hReportHBox = New ReportHBox ( [ Parent As ReportContainer ] )
You can then define the main style properties of the ReportHBox container.
Example - the data basis is a database table
- First, a separate ReportHBox container is inserted into a container of type ReportVBox for each DB data record present in the query result (hDBResult) and its main properties are defined.
- Then a ReportLabel is inserted into this ReportHBox for each DB field in turn.
- Finally - depending on the field name - its field value is entered in the ReportLabel (property `rlblDBField.Text`).
If hDBResult.Available Then For i = 0 To hDBResult.Max rhboxDBRow = New ReportHBox(rvboxContent) rhboxDBRow.Spacing = "3mm" rhboxDBRow.Height = "6mm" rhboxDBRow.Padding = ReportPadding["1mm"] If iMod = 0 Then If i Mod 2 = 0 Then rhboxDBRow.BackGround = ReportBrush["#E0E0E0"] Else If i Mod 2 <> 0 Then rhboxDBRow.BackGround = ReportBrush["#E0E0E0"] Endif '-- SurName rlblDBField = New ReportLabel(rhboxDBRow) rlblDBField.Font = hDBFieldFont rlblDBField.Width = cMaxField["vorname"] rlblDBField.Text = hDBResult["vorname"] ... '-- EMail rlblDBField = New ReportLabel(rhboxDBRow) rlblDBField.Font = hDBFieldFont rlblDBField.Width = cMaxField["email"] rlblDBField.Text = hDBResult["email"] ... hDBResult.MoveNext() Next Endif
This is what a report section with alternating background for the data record looks like in the preview:
Figure 22.11.3.2.1: Section with three report HBoxes, each with nine ReportLabels
22.11.3.3 ReportPanel
This class is a report container that arranges its inserted controls depending on the assigned value of the ‘Arrangement’ property. The default is ReportPanel.Arrangement = Arrange.None. However, the ‘Arrangement’ property can also have the values Arrange.Horizontal or Arrange.Vertical.
How to create a new ReportPanel object:
Dim hReportPanel As ReportPanel hReportPanel = New ReportPanel ( [ Parent As ReportContainer ] )
You can then define the main style properties of the container as in the following example:
- First, two ReportPanels are inserted directly into the report in the Report Designer in the IDE.
- Then 4 ReportLabels (1-4 and 5-8) are inserted into each of the two containers.
- The main properties of the two ReportPanels are then defined in the source code.
Public Sub Report_Open() Report1.Padding = ReportPadding["10mm"] Report1.Spacing = "20mm" '-- Arrangement of 4 report labels one below the other ReportPanel1.Arrangement = Arrange.Vertical ReportPanel1.Spacing = "3mm" ReportPanel1.Padding = ReportPadding["Top:1mm;Bottom:1mm"] ReportPanel1.Height = "35mm" ' = (1+6+3+6+3+6+3+6+1)mm '-- Arrangement of 4 report labels next to each other ReportPanel2.Arrangement = Arrange.Horizontal ReportPanel2.Spacing = "3mm" ReportPanel2.Padding = ReportPadding["Top:1mm;Bottom:1mm"] ReportPanel2.Height = "8mm" ReportPanel2.Width = "45mm" ' =((210-2*10)-4*45)/3 '-- Utilisation of the effective width of 190 mm on the ReportPanel2 for a flush finish ReportLabel8.Expand = True ... End
Example:
Figure 22.11.3.3.1: Two report panels with 4 ReportLabels each in the Report Designer(!) in the IDE
Attention: Only after rendering do you see the real arrangement of the 8 report labels - either vertically (1-4) or horizontally (5-8) - in a preview:
Figure 22.11.3.3.2: Two report panels with 4 report labels each
22.11.3.4 ReportLine
This class reimplements the ‘Line’ class in the gb.qt5 component.
You define the line style using one of the following 6 line constants, whereby no line is drawn for the Line.None constant:
- Line.None = 0
- Line.Solid = 1
- Line.Dash = 2
- Line.Dot = 3
- Line.DashDot = 4
- Line.DashDotDot = 5
Example:
- First, a line is inserted into the report at the intended position. The exact position depends on whether and which other report controls you insert into the report with the Report Designer in the IDE or with source code.
- The required properties of the line are then defined.
- Finally, the example also specifies that the dotted thin blue line should be drawn on all sides.
Public Sub Report_Open() Dim rLine As ReportLine Report1.Padding = ReportPadding["15mm"] rLineTop = New ReportLine (Report1) '-- TopLeft;Top;TopRight;Left;Right;BottomLeft;Bottom;BottomRight rLineTop.Direction = Align.Right '-- None;Solid;Dash;Dot;DashDot;DashDotDot rLineTop.LineStyle = Line.Dot '-- "m";"cm";"mm";"in";"pt";"px" rLineTop.LineWidth = "0.3mm" rLineTop.Brush = ReportBrush["#0000FF"] '-- Die Linie soll auf allen Seiten gezeichnet werden rLineTop.Fixed = True End
Attention: If you insert another line into the report, this line will adopt the line style of the first line defined! This also happens if you define its style separately. Obviously, this separate definition is ignored (as of 30 April 2023)!
It is worth considering setting a horizontal boundary with an upper and/or lower border of a report container instead of inserting a line into the report. The following is an example of using the container properties Border and Padding instead of a separate line.
'-- Inserting page numbers with format on each side rlblPageFooter.Fixed = True '-- Definition of selected properties rlblPageFooter.Border = ReportBorder["Top:0.1mm"] rlblPageFooter.Padding = ReportPadding["Top:1.3mm"] rlblPageFooter.Font = Font["Noto Sans,8"] rlblPageFooter.Height = GetTextHight(rlblPageFooter.Font) rlblPageFooter.Text = ("=\"Seite \" & (page) & \" von \" & (pages)") rlblPageFooter.Alignment = Align.Right
22.11.3.5 Inserting new pages into a report
If you set the Report.ForceNewPage property to True, you force the creation of a new report page - but only if the content of a container does not fit completely on the page. In contrast, you can use the ReportPageBreak class, in which a new object forces a page break after a report control, which must be specified as a parameter.
Example
If you always want to start individual sections of a chapter on a new page for a text report, then you must insert a self-defined markup string such as <NewPage> in the original text and parse the text after it in order to create a new ReportPageBreak object.
Here is an excerpt from a text for a text report:
... 13 Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. <NewPage>25.2.4 Mirum est notare quam littera gothica 14 Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. ...
With regard to the markup string, you could, for example, insert <NewPage> or 2 dollar signs or other characters of your choice into the raw text and then parse the text when generating the structure of the report. Another idea would be equal signs as in the syntax of DokuWiki or # characters for MarkDown. You could also use the number of characters to control the text levels.
Dim rtlblText As ReportTextLabel Dim iHeight, iUsableWidth As Integer Dim sRawText As String Dim aLines As String[] Dim sLine As String Dim hTextFont As Font Dim rPageBreak As ReportPageBreak '-- Loading Text sRawText = File.Load("./data/lorem.txt") aLines = Split(sRawText, "\n") hTextFont = Font["Noto Sans,11"] iHeight = hTextFont.H iUsableWidth = GetUsableWidth() '-- Inserting text with many paragraphs For Each sLine In aLines '-- Each new chapter starts on a new page '-- Individual NewPage-Markup is <NewPage> in sRawText If Left(sLine, 9) = "<NewPage>" Then rPageBreak = New ReportPageBreak(rvboxText) sLine = Replace(sLine, "<NewPage>", "") Endif rtlblText = New ReportTextLabel(rvboxText) rtlblText.Font = hTextFont rtlblText.Alignment = Align.Justify rtlblText.Text = sLine rtlblText.Height = CStr(GetNumberOfRows(sLine, iUsableWidth, hTextFont) * iHeight) & "px" Next
22.11.3.6 Text in a report
Text in a report places special demands on the correct formatting of the text to be displayed in a ReportLabel or in a ReportTextLabel. Important to know: ReportTextLabels are the only labels that implement an (automatic) text wrap, which normal ReportLabels do not have.
The following scenarios occur:
- Only individual words are required as identifiers - for example as a heading, identifier in a table header or as the content of a DB cell.
- A short text of a few lines, as in a partial summary in a report.
- Longer text as introductory text or for a report summary.
With a headline consisting of a few words, you only need to make sure that the font is chosen so that the text fits on one line and matches the design.
If, on the other hand, you want to display data from a database table in a table in the report, you must ensure that a cell in your table - represented by a report label - is at least as long as the maximum length of the selected field. Of course, you must also take into account the font that you use for the report for the ReportLabel. You can use the following functions, which differ for SQLite, MySQL and PostgreSQL.
22.11.3.6.1 SQLite
Use the following SQL statement to determine the maximum field length in a column (field_name) of an SQLite table (table_name):
SELECT max(length(field_name)) FROM table_name;
A special feature of SQLite is the determination of the maximum length for a date. Internally, a date is always saved as DateTime such as 08.12.1981 00: 00: 00 with an empty value for the time. In the procedure SetMaxTextWidth(argFont As Font) specified below, this special feature is taken into account.
22.11.3.6.2 MySQL
The following SQL statement shows how to determine the maximum field length in a column of a MySQL table:
SELECT max( length( `field_name` ) ) AS `max_len` FROM `table_name`
This extended SQL statement for MySQL outputs the field content with the longest content:
SELECT field_name FROM table_name WHERE length(`field_name`) = SELECT max(length(`field_name`)) FROM table_name
22.11.3.6.3 PostgreSQL
For PostgreSQL, use this SQL statement to determine the maximum field length:
SELECT max(char_length(field_name)) FROM table_name
With this extended SQL statement for PostgreSQL, the field content with the longest content is output:
SELECT field_name FROM table_name WHERE char_length(field_name) = SELECT max(char_length(field_name)) FROM table_name
Notes
You must then permanently check whether the table header and all fields of the (DB) table are displayed for the font used. If, for example, only 7 ReportLabels fit into the container for a font used - in most cases this is a ReportVBox - then an existing 8th ReportLabel is simply cut off; it is simply no longer displayed in the container! Therefore, start with a very small font size, which you can gradually increase after a successful check using the Report.Preview() method.
Example: Calculation of the optimum width for each DB field (SQLite) in a report
Use the procedure SetMaxTextWidth(argFont As Font) to calculate the optimum width for each field ReportLabel for SQLite once, as a collection is created with the field names as the key and the maximum field length+3 as the value.
Private cMaxField As Collection Private sTablename As String = "contacts" ... Public Sub SetMaxTextWidth(argFont As Font) Dim hDBResultMax As Result Dim vValue As Variant For Each vValue In cMaxField '-- Special editing of the date in SQLite3 '-- Example of the stored date in the DB table: 08.12.1981 00: 00: 00 If cMaxField.Key = "gebdatum" Then sSQLStatement = Subst("Select date(&1), Max(Length(&1)) From &2", cMaxField.Key, DB.Quote(sTablename, True)) hDBResultMax = DBCS.DBConnection.Exec(sSQLStatement) cMaxField[cMaxField.Key] = CStr(argFont.TextWidth(hDBResultMax[0]) + 3) & "px" Else sSQLStatement = Subst("Select &1, Max(Length(&1)) From &2", cMaxField.Key, DB.Quote(sTablename, True)) hDBResultMax = DBCS.DBConnection.Exec(sSQLStatement) cMaxField[cMaxField.Key] = CStr(argFont.TextWidth(hDBResultMax[0]) + 3) & "px" Endif Next '-- To check: ' For Each vValue In cMaxField ' Print cMaxField.Key; " -> "; vValue ' Next End
Whether you increase the maximum size by 3 pixels - you can freely define the value within certain limits - or work with suitable values for padding (left/right) for the ReportLabel is up to you.
22.11.3.7 Inserting page numbers
You can also insert page numbers in a footer that is to be displayed on certain or all pages of a report. It is informative to specify the current page number with a reference to the number of all pages of the report, such as ‘Page 3 of 6’. Please note that it is usual not to add a footer to an (optional) cover page.
When inserting page numbers, it depends on whether you insert the number of pages in the IDE in the properties in the corresponding report label in the footer or declare it in the source text, as the syntax is different.
Specifying page numbers in the Report Designer (IDE) for the text property of a report label in the footer:
="Page " & page & " von " & pages
Input in the source text in a ReportLabel ‘rlblPageFooter’:
'-- Inserting page numbers with format. rlblPageFooter.Fixed = True rlblPageFooter.Border = ReportBorder["Top:1px"] rlblPageFooter.Padding = ReportPadding["Top:3mm"] rlblPageFooter.Font = Font["Noto Sans,8"] rlblPageFooter.Height = GetTextHight(rlblPageFooter.Font) rlblPageFooter.Text = ("=\"Page \" & (page) & \" von \" & (pages)") rlblPageFooter.Alignment = Align.Left
If you are using a cover page, change the definition as follows
'-- Inserting page numbers with format. The cover sheet is NOT counted. rlblPageFooter.Text = ("=\"Page \" & (page -1) & \" von \" & (pages -1)")
22.11.3.8 Debugging
You can check the selected layout and design of the report at any time via a report preview or a printout in order to make corrections. Temporarily setting the Report.Debug property to True can be a quick help here, because in this mode the borders and padding of all objects contained in the report are drawn in colour in the preview.
'-- Description of the structure of the report Public Sub Report_Open() ... '-- Only for controls in the testing of the report project Report.Debug = True ...
22.11.3.9 Notes on the report methods Refresh() and Layout()
The Report.Refresh() method deletes all internal calculations and the current layout. You start the calculation of the layout with the Report.Layout() method. It can be called manually before printing, for example. But sometimes it is useful to calculate the layout in advance - for example for the preview. Both methods can be followed by rendering the report, which can be done on any medium (preview or printer) and in any resolution.
22.11.3.10 Print report
In many cases, it is necessary to print the generated report. Various report methods are available for this purpose:
- Report.Print()
- Report.Preview()
- Report.Print(hPrinter As Printer)
22.11.3.10.1 Report.Print()
The Report.Print() method immediately starts a dialogue without a report preview, in which you can set up the selected printer before printing and then print it:
Figure 22.11.3.10.1: Print dialogue
22.11.3.10.2 Report.Prieview()
The Report.Preview() method is efficient. On the one hand, it allows you to check the report before printing and, on the other hand, offers output to the (standard) printer (Printer tab) with the option of specifying certain printer properties and, among other things, the print format (landscape or landscape format).
Alternatively, you can also print the report to a PDF file. Select the ‘File’ tab and start a dialogue to define the path and file name of the PDF file using the button box on the right-hand side of the line. Click on the Print button to start printing the report to the specified PDF file. This is great because you can then distribute this report file electronically, for example by email.
Figure 22.11.3.10.2: Printout in a PDF file (A4 landscape format)
To start a preview directly in the IDE. To do this, right-click on the ‘Report’ in the project directory and then select ‘Execute class’ in the context menu.
22.11.3.10.3 Report.Print(hPrinter)
The Report.Print(hPrinter) method starts the printout immediately, as you have already created a printer object in the source code and provided it with suitable properties. This method is ideal if you always print with the same (standard) printer - without a preview and without a printer dialogue.
Dim hPrinter As Printer hPrinter = New Printer '-- Definition of selected printer properties hPrinter.Paper = Printer.A4 hPrinter.Orientation = Printer.Landscape hPrinter.GrayScale = True hPrinter.Duplex = Printer.Vertical Inc Application.Busy '-- The programme no longer accepts any input ... Report1.Print(hPrinter) '-- Printing is started Dec Application.Busy '-- The programme is accepting entries again...






