Diese Klasse präsentiert einen Report-Container, der seine eingefügten Steuerelemente vertikal anordnet.
So erzeugen Sie ein neues ReportVBox-Objekt im Quelltext:
Dim hReportVBox As ReportVBox hReportVBox = New ReportVBox ([ Parent As ReportContainer ])
Anschließend können Sie wesentliche Style-Eigenschaften des Containers festlegen.
Hinweis: Es ist bei vielen Projekten sinnvoll, das Basis-Layout des Reports in der IDE im Report-Designer vorzugeben. Zum Basis-Layout gehören ReportVBoxen dazu, weil sie in vielen Fällen weitere Report-Steuerelemente aufnehmen, die dann aber sehr einfach per Quelltext eingefügt werden können. Diese Vorgehensweise hat sich in der Praxis bewährt:
Abbildung 22.11.3.1.1: Report-Struktur
Die in der Abbildung 22.11.3.1.1 angegebene Report-Struktur besteht in der Sektion 1 aus einem Header als ReportPanel mit zwei ReportLabeln, dem gelb markierten Content-Bereich als ReportVBox und einem Footer als ReportPanel.
Diese Klasse präsentiert einen Report-Container, der seine eingefügten Steuerelemente horizontal anordnet.
So erzeugen Sie ein neues ReportHBox-Objekt:
Dim hReportHBox As ReportHBox hReportHBox = New ReportHBox ( [ Parent As ReportContainer ] )
Anschließend können Sie wesentliche Style-Eigenschaften des Containers ReportHBox festlegen.
Beispiel – Datenbasis ist eine Datenbanktabelle
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
So sieht ein Report-Abschnitt mit alternierendem Hintergrund für den Datensatz in der Vorschau aus:
Abbildung 22.11.3.2.1: Abschnitt mit drei Report-HBoxen mit jeweils neun ReportLabeln
Bei dieser Klasse handelt es sich um einen Report-Container, der seine eingefügten Steuerelemente in Abhängigkeit vom zugewiesenen Wert der Eigenschaft 'Arrangement' anordnet. Der Standard ist ReportPanel.Arrangement = Arrange.None. Die Eigenschaft 'Arrangement' kann aber auch die Werte Arrange.Horizontal oder Arrange.Vertical annehmen.
So erzeugen Sie ein neues ReportPanel-Objekt:
Dim hReportPanel As ReportPanel hReportPanel = New ReportPanel ( [ Parent As ReportContainer ] )
Anschließend können Sie wesentliche Style-Eigenschaften des Containers wie im folgenden Beispiel festlegen :
Public Sub Report_Open() Report1.Padding = ReportPadding["10mm"] Report1.Spacing = "20mm" '-- Anordnung von 4 ReportLabeln untereinander 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 '-- Anordnung von 4 ReportLabeln nebeneinander 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 '-- Ausnutzung der effektiven Breite von 190mm beim ReportPanel2 für einen bündigen Abschluss ReportLabel8.Expand = True ... End
Beispiel:
Abbildung 22.11.3.3.1: Zwei Report-Panele mit jeweils 4 ReportLabeln im Report-Designer(!) in der IDE
Achtung: Erst nach dem Rendern sehen Sie die reale Anordnung der 8 ReportLabel – entweder vertikal (1-4) oder horizontal (5-8) – in einer Vorschau:
Abbildung 22.11.3.3.2: Zwei Report-Panele mit jeweils 4 ReportLabeln
Diese Klasse reimplementiert die Klasse 'Line' in der Komponente gb.qt5.
Den Linien-Stil legen Sie über eine der folgenden 6 Linien-Konstanten fest, wobei für die Konstante Line.None keine Linie gezeichnet wird:
Beispiel
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
Achtung: Wenn Sie eine weitere Linie in den Report einfügen, dann übernimmt diese Linie den Linien-Stil der zuerst definierten ersten Linie! Das geschieht auch dann, wenn Sie deren Stil separat festsetzen. Offensichtlich wird diese separate Festlegung ignoriert (Stand: 30. April 2023)!
Es ist zu überlegen, eine horizontale Abgrenzung durch einen oberen und/oder unteren Rand eines Report-Containers zu setzen, statt eine Linie in den Report einzufügen. Es folgt ein Beispiel für die Verwendung der Container-Eigenschaften Border und Padding statt einer separaten Linie.
'-- 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
Wenn Sie die Eigenschaft Report.ForceNewPage auf True setzen, dann erzwingen Sie die Erzeugung einer neuen Report-Seite – aber nur dann, wenn der Inhalt eines Containers nicht komplett auf die Seite passt. Im Gegensatz dazu können Sie die Klasse ReportPageBreak einsetzen, bei der ein neues Objekt einen Seitenumbruch nach einem Report-Steuerelement erzwingt, das aber als Parameter mitgegeben werden muss.
Beispiel
Wenn Sie für einen Text-Report einzelne Abschnitte eines Kapitels immer auf einer neuen Seite beginnen lassen wollen, dann müssen Sie zum Beispiel im originalen Text eine selbst definierte Markup-Zeichenkette wie zum Beispiel <NewPage> einfügen und nach dieser den Text parsen, um dann ein neues ReportPageBreak-Objekt zu erzeugen.
Hier ein Ausschnitt aus einem Text für einen 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. ...
Bezüglich der Markup-Zeichenkette könnten Sie zum Beispiel <NewPage> oder 2 Dollarzeichen oder andere Zeichen Ihrer Wahl in den Roh-Text einfügen und den Text dann bei der Erzeugung der Struktur des Reports parsen. Eine andere Idee wären Gleichheitszeichen wie in der Syntax von DokuWiki oder #-Zeichen bei MarkDown. Mit der Anzahl der Zeichen könnten Sie so auch die Text-Ebenen steuern.
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
Text in einem Report stellt besondere Anforderungen an die korrekte Aufbereitung des anzuzeigenden Textes in einem ReportLabel oder in einem ReportTextLabel. Wichtig zu wissen: ReportTextLabel sind die einzigen Label, die einen (automatischen) Textumbruch implementieren, den normale ReportLabel nicht besitzen.
Die folgenden Szenarien kommen vor:
Bei einer Überschrift aus wenigen Worten müssen Sie nur darauf achten, den Font so zu wählen, dass der Text in eine Zeile und zum Design passt .
Wenn Sie dagegen Daten aus einer Datenbank-Tabelle in einer Tabelle im Report anzeigen wollen, dann müssen Sie dafür sorgen, dass eine Zelle Ihrer Tabelle – dargestellt durch ein ReportLabel – mindestens so lang ist, wie die maximale Länge des ausgewählten Feldes. Berücksichtigen müssen Sie natürlich auch den Font, den Sie für den Report für die ReportLabel verwenden. Sie können die folgenden Funktionen nutzen, die jedoch für SQLite, MySQL und PostgreSQL differieren.
Mit der folgenden SQL-Anweisung ermitteln Sie die maximale Feld-Länge in einer Spalte (field_name) einer SQLite-Tabelle (table_name):
SELECT max(length(field_name)) FROM table_name;
Eine Besonderheit ist bei SQLite die Bestimmung der maximalen Länge für ein Datum. Intern wird ein Datum immer als DateTime wie 08.12.1981 00: 00: 00 mit leerem Wert für die Zeit abgespeichert. In der weiter unten angegebenen Prozedur SetMaxTextWidth(argFont As Font) wird dieser Besonderheit Rechnung getragen.
Die folgende SQL-Anweisung zeigt, wie Sie die maximale Feld-Länge in einer Spalte einer MySQL-Tabelle ermitteln:
SELECT max( length( `field_name` ) ) AS `max_len` FROM `table_name`
Mit dieser erweiterten SQL-Anweisung für MySQL wird Ihnen der Feldinhalt mit dem längsten Inhalt ausgegeben:
SELECT field_name FROM table_name WHERE length(`field_name`) = SELECT max(length(`field_name`)) FROM table_name
Für PostgreSQL nutzen Sie diese SQL-Anweisung zur Ermittlung der maximalen Feld-Länge:
SELECT max(char_length(field_name)) FROM table_name
Mit dieser erweiterten SQL-Anweisung für PostgreSQL wird Ihnen der Feldinhalt mit dem längsten Inhalt ausgegeben:
SELECT field_name FROM table_name WHERE char_length(field_name) = SELECT max(char_length(field_name)) FROM table_name
Hinweise
Anschließend müssen Sie permanent prüfen, ob der Tabellenkopf und alle Felder der (DB-)Tabelle beim verwendeten Font angezeigt werden. Passen zum Beispiel bei einem verwendeten Font nur 7 ReportLabel in den Container - das ist in den meisten Fällen eine ReportVBox - dann wird ein vorhandenes 8. ReportLabel einfach abgeschnitten; es wird einfach nicht mehr im Container angezeigt! Fangen Sie daher mit einer sehr kleinen Schriftgröße an, die Sie nach erfolgreicher Prüfung mit der Methode Report.Preview() schrittweise erhöhen können.
Beispiel: Berechnung der optimalen Breite für jedes DB-Feld (SQLite) in einem Report
Mit der Prozedur SetMaxTextWidth(argFont As Font) berechnen Sie einmalig die optimale Breite für jedes Feld-ReportLabel für SQLite, denn es wird eine Collection mit den Feldnamen als Key und der maximalen Feld-Länge+3 als Wert erzeugt.
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
Ob Sie die maximale Größe um 3 Pixel vergrößern – den Wert können Sie in gewissen Grenzen frei festlegen – oder für das ReportLabel mit geeigneten Werten für das Padding (links/rechts) arbeiten, ist Ihnen überlassen.
In einer Fußzeile, die auf bestimmten oder allen Seiten eines Reports angezeigt werden soll, können Sie auch Seitenzahlen einfügen. Informativ ist die Angabe der aktuellen Seitenzahl mit Verweis auf die Anzahl aller Seiten des Reports wie 'Seite 3 von 6'. Beachten Sie, dass es üblich ist, ein (optionales) Deckblatt nicht mit einer Fußzeile zu versehen.
Beim Einfügen von Seitenzahlen kommt es darauf an, ob Sie die Seitenanzahl in der IDE bei den Eigenschaften im entsprechenden Reportlabel in der Fußzeile einfügen oder diese im Quelltext deklarieren, denn die Syntax unterscheidet sich.
Angabe von Seitenzahlen im Report-Designer (IDE) bei der Text-Eigenschaft eines ReportLabels im Footer:
="Seite " & page & " von " & pages
Eingabe im Quelltext in einem 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 = ("=\"Seite \" & (page) & \" von \" & (pages)") rlblPageFooter.Alignment = Align.Left
Wenn Sie ein Deckblatt verwenden, dann ändern Sie die Festlegung so:
'-- Inserting page numbers with format. The cover sheet is NOT counted. rlblPageFooter.Text = ("=\"Seite \" & (page -1) & \" von \" & (pages -1)")
Das gewählte Layout und Design des Reports können Sie jederzeit über eine Report-Vorschau oder einen Ausdruck kontrollieren, um Korrekturen vorzunehmen. Eine schnelle Hilfe kann dabei die temporäre Festlegung der Eigenschaft Report.Debug auf den Wert True sein, weil in diesem Modus die Ränder und die Polsterung (Padding) aller im Report enthaltenen Objekte in der Vorschau farbig eingezeichnet werden.
'-- Description of the structure of the report Public Sub Report_Open() ... '-- Only for controls in the testing of the report project Report.Debug = True ...
Die Methode Report.Refresh() löscht alle internen Berechnungen und das aktuelle Layout. Die Berechnung des Layouts starten Sie mit der Methode Report.Layout(). Sie kann zum Beispiel vor dem Drucken manuell aufgerufen werden. Aber manchmal ist es nützlich, das Layout im Voraus zu berechnen – zum Beispiel für die Vorschau. Beiden Methoden kann das Rendern des Reports folgen, das auf jedem Medium (Vorschau oder Drucker) und in jeder Auflösung erfolgen kann.
In vielen Fällen ist es erforderlich, den erzeugten Report auszudrucken. Dafür stehen Ihnen unterschiedliche Report-Methoden zur Verfügung:
22.11.3.10.1 Report.Print() Die Methode Report.Print() startet ohne Report-Vorschau sofort einen Dialog, in dem Sie den ausgewählten Drucker vor dem Druck einrichten und dann drucken können:
Abbildung 22.11.3.10.1: Print-Dialog
Die Methode Report.Preview() ist effizient. Sie ermöglicht einerseits die Kontrolle des Reports vor dem Druck und bietet andererseits die Ausgabe auf den (Standard-)Drucker (Reiter Printer) mit der Option, bestimmte Druckereigenschaften und u.a. das Druck-Format (Längs- oder Quer-Format) festzulegen.
Alternativ können Sie den Report auch in eine PDF-Datei drucken. Wählen Sie den Reiter `File` und starten über die Button-Box am rechten Zeilenrand einen Dialog zur Festlegung des Pfades und des Dateinamens der PDF-Datei. Mit einem Klick auf den Print-Button startet der Druck des Reports in die angegebene PDF-Datei. Eine feine Sache, weil Sie diese Report-Datei anschließend auf elektronischem Weg – zum Beispiel via EMail – verteilen können.
Abbildung 22.11.3.10.2: Ausdruck in eine PDF-Datei (A4-Querformat)
So starten Sie eine Vorschau direkt in den IDE. Klicken Sie dazu im Projektverzeichnis mit der rechten Maustaste auf den „Report“ und wählen dann im Kontext-Menü „Klasse ausführen“.
Die Methode Report.Print(hPrinter) startet den Ausdruck sofort, da Sie im Quelltext bereits ein Drucker-Objekt erzeugt und mit passenden Eigenschaften versehen haben. Diese Methode eignet sich genau dann, wenn Sie stets mit dem gleichen (Standard-)Drucker drucken – ohne Vorschau und ohne Druckerdialog.
Dim hPrinter As Printer hPrinter = New Printer '-- Festlegung ausgewählter Druckereigenschaften hPrinter.Paper = Printer.A4 hPrinter.Orientation = Printer.Landscape hPrinter.GrayScale = True hPrinter.Duplex = Printer.Vertical Inc Application.Busy '-- Das Programm nimmt keine Eingaben mehr entgegen ... Report1.Print(hPrinter) '-- Der Druck wird gestartet Dec Application.Busy '-- Das Programm nimmt wieder Eingaben entgegen...