Erweiterte Programmierung


Berichte erstellen Sie mit dem Berichtsdesigner (iPlus-Steuerelement "gip.core.layoutengine. VBReportEditor"). Berichte werden wie UI-Designs in XAML programmiert und als Flussdokumente gespeichert. Machen Sie sich zuerst auf der Microsoft-Dokumentationsseite mit Dokumenten in WPF vertraut bevor Sie hier weiterlesen.

Die Klassen bzw. Elemente aus dem ".NET-Framework", die zur Erstellung eines Flussdokuments notwendig sind, sind im iPlus-Framework um folgenden Features erweitert worden:

  • Datenbindung,
  • Iteration von Datensammlungen,
  • Kopf- und Fußzeilen,
  • Einbindung von Ressourcen (z.B. Bilder...)
  • Formatierung von Daten

Diese iPlus Berichtselemente können nur im Kombination mit der Klasse "gip.core.reporthandler.Flowdoc.ReportPaginator" verwendet werdendie eine Erweiterung der DocumentPaginator-Klasse ist. Der ReportPaginator wiederum wird in der Klasse "gip.core.reporthandler.Flowdoc.ReportDocumentverwendet, um schließlich ein druckfähiges XpsDocument erstellen zu können. Die generierten XPS-Dokumente können


Berichte sind im Grunde genommen Designs, die mit Daten aufgefüllt und dann zu Papier bzw. zu einem leserlichen Format gebracht werden. Der Datenkontext eines Berichtes ist primär ein Geschäftsobjekt oder alternativ seine speicherbare Abfrage. Aus diesem Grund können für jedes Geschäftsobjekt Berichte verwaltet und hinzugefügt werden (Symbol  im Menüband).

 

VBBSOReport

Das Design wird dann mit dem Geschäftsobjekt gip.core.reporthandler.VBBSOReport (Berichtsdesigner) editiert. VBBSOReport wird als Kindinstanz des geöffneten Geschäftsobjektes Just-In-Time instanziiert.

Auch für die Druckvorschau (Symbol ) und zum Drucken wird eine VBBSOReport-Kindinstanz verwendet.

Der Druckvorgang benötigt nicht unbedingt eine Oberfläche, sondern er kann auch im Hintergrund (z.B. serverseitig) ausgelöst werden. Mehr dazu lesen im Abschnitt "Drucken im Hintergrund" mit VBBSOReport

 

Designer

Der Designer unterteilt sich in vier Registerkarten: 

 

Der grafische Designer unterstützt Sie lediglich bei der Formatierung. Die Struktur und die Bindung zu Dateninhalten müssen zurzeit noch in der XAML-Registerkarte editiert werden:

 

 

Bevor wir auf die Struktur und die wichtigsten XAML-Abschnitte in einem Bericht eingehen, vorab ein Überblick über die Vererbungshierarchie der iPlus-Berichtselemente:

 

Die hellblauen Klassen sind Elemente aus dem ".NET-Framework". Die gelben Klassen sind iPlus-Berichtselemente. Verwenden Sie iPlus-Berichtselemente im XAML-Code, damit Sie Daten aus Businessobjekten oder anderen Datenmodellen in ein Flussdokument einfügen können.

 

Das äußerste Element ist ein FlocDocument. Hier richten Sie mit den Eigenschaften PageHeight und PageWidth die Papiergröße fest. Die Angaben erfolgen in pixels, bei einer Punktdichte von 96 PPI (pixels per inch).  Im folgenden Beispielcode wurde ein Bericht für DIN-A4 definiert:

 

Binden Sie den Namespace mit xmlns:vbr="http://www.iplus-framework.com/ACFramework/report/xaml" ein, um iPlus-Berichtselemente nutzen zu können.

Innerhalb des Flow-Dokuments  dienen Section-Elemente zur Aufteilung einer Seite in Gruppen. Section-Elemente sind vergleichbar mit div-Elementen aus html. Verwenden Sie jedoch im Bericht die iPlus-Sections,

  • SectionReportHeader (optional),
  • SectionReportFooter (optional) und
  • SectionDataGroup (Minimum ein Vorkommen).

 

Kopf- und Fußzeilen

Damit auf jeder Druckseite ein Kopf- und Fußbereich gedruckt wird, wenn der Dateninhalt so lang wird, dass Seitenumbrüche stattfinden müssen, verwenden Sie die Elemente <SectionReportHeader> und <SectionReportFooter>. Geben Sie mit den Eigenschaften PageHeaderHeight und PageFooterHeight einen Prozentwert an, wie viel die Kopf- und Fußzeilen bezogen auf die Gesamtseite an Platz einnehmen sollen. Achten Sie darauf, dass Sie den Inhalt der Kopf- und Fußzeile nicht größer machen als der fix reservierte Bereich in Prozent. Andernfalls wird der Inhalt abgeschnitten!

 

Inhalt

Der restliche Inhalt zwischen den optionalen Kopf- und Fußelementen sollte mit den Elementen <SectionDataGroup> deklariert werden.

Wichtig: Bitte verwenden Sie zwischen diesen drei Section-Typen keine anderen Block-Elemente wie z.B. Paragraph oder Tables weil sonst die Berechnung der Seitenumbrüche durch den ReportPaginator nicht stimmen würde. Alle weiteren Berichtselemente müssen sich immer innerhalb dieser drei Section-Typen befinden!


Paragraph ist das gängigste Element um fortlaufenden Text anzuzeigen. Für datenbasierte Berichte ist jedoch die exakte Positionierung eines Datenfeldes am wichtigsten. Dies bewerkstelligen Sie mit Table-Elementen. Mehr dazu unter https://docs.microsoft.com/de-de/dotnet/framework/wpf/advanced/table-overview.

Bei Tabellen gilt immer folgende Grundstruktur:

<Table>
  <Table.Columns>
    <TableColumn>
  <TableRowGroup>
    <TableRow>
      <TableCell>
        <Paragraph>

Hier ein Beispiel:

 

Verwenden Sie zwei <TableRowGroup>-Elemente innerhalb einer Tabelle um mit dem ersten Element Spaltenüberschriften zu beschreiben und mit dem zweiten die Daten auszugeben.

 

Bevor wir zu den Elementen kommen, die für die Ausgabe von Daten in einem Flussdokument zuständig sind, müssen Sie zunächst verstehen, wo der ReportPaginator (Berichtsgenerator) seine Daten findet. Beim Drucken wird dem ReportPaginator eine Instanz der Klasse ReportData übergeben, die die zu druckenden Berichtsdaten enthält und in der Dictionary ReportDocumentValues gespeichert sind:

public class ReportData
{
public Dictionary<string, object> ReportDocumentValues {get; set;}
}

Businessobjekte die gedruckt werden sollen, werden zuerst geklont und dann als Referenz in ReportDocumentValues eingetragen. Der Schlüssel ist der Klassenname (ACIdentifier) zu dem der druckende Report (ACClassDesign) angehört. Der Hintergrund warum ein Klon zum Drucken verwendet wird ist, dass der ReportPaginator Listen durchläuft und die Selected-Eigenschaften verändert. Würde er dies auf dem originalen Businessobjekt machen, würden sich die Daten an der Benutzeroberfläche während der Schleifendurchläufe ändern und dies ist ein Seiteneffekt, der unbedingt verhindert werden muss.

ReportDocumentValues kann anstatt eines Businessobjekt-Klons eine ACQueryDefintion-Instanz enthalten. Dann ist der Schlüssel die ChildACUrl der ACQueryDefinition-Instanz.

ReportDocumentValues können zusätzlich andere Datenobjekte beinhalten die jedoch durch eine individuelle Programmierung explizit hinzugefügt werden müssen.

 


Um nun die Daten auszugeben, die in ReportData gespeichert sind, werden Elementen benötigt, die Ableitungen von "gip.core.reporthandler.Flowdoc.InlinePropertyValueBase" sind. InlinePropertyValueBase besitzt zwei wichtige Eigenschaften:

  • string VBContent: Diese Eigenschaft haben Sie bereits im vorigen Kapitel "GUI Design IVBContent" kennengelernt. Sie dient dazu Eigenschaften relativ zum Datenkontext zu adressieren. Der Datenkontext ist das erste Element in ReportDocumentValues. Das ist entweder das zu druckende Geschäftsobjekt selbst oder eine ACQueryDefinition-Instanz. Soll ein anderes Datenobjekt aus ReportDocumentValues als Datenkontext verwendet werden dann muss die DictKey-Eigenschaft explizit gesetzt werden:
  • string DictKey: Name des Schlüssels in ReportDocumentValues, um ein anderes Datenobjekt zu verwenden.

Es gibt folgende Druckelemente, die Ableitungen von InlinePropertyValueBase  sind:

  • InlineDocumentValue: Zum Drucken eines Datenfeldes. (Siehe oben im XAML-Beispielcode für Tabellen).
  • InlineContextValue: Zum Drucken von vordefinierten Makros (enum ReportContextValueType). Setzen Sie DictKey mit einem dieser vordefinierten Konstanten: PageNumber, PageCount, ReportName, ReportTitle, PrintingDate
  • InlineACMethodValue: Zum Drucken eines Parameters aus einer virtuellen Methode
  • InlineTableCellValue: Zum Drucken eines Datenfeldes in einer Datenzeile. Datenzeilen werden bei der Iteration von TableRowData-Elementen generiert.

Um Datensammlungen an der Oberfläche anzuzeigen, benötigen Sie iPlus-ItemsControls, die die Schnittstelle IVBSource implementieren. IVBSource besitzt die Eigenschaft VBSource, um eine Datensammlung zu adressieren, die mit der Attributklasse ACPropertyList gekennzeichnet wurde. Genau dasselbe Prinzip ist in der abstrakten Klasse "gip.core.reporthandler.Flowdoc.TableRowDataBase" umgesetzt, damit Datensammlungen gedruckt werden können. TableRowDataBase besitzt daher die Eigenschaften "string VBSource" und "string DictKey". Wie auch bei ItemsControls ändert sich der Datenkontext mit der gebundenen Datenquelle. Bei allen Druckelemente (Kindelemente), die sich innerhalb eines TableRowDataBase befinden, wird der VBContent relativ zum Datenkontext angegeben.

TableRowDataBase ist eine Ableitung von TableRow und verhält sich wie eine Art Template. Bei der Iteration der gebundenen Datensammlung wird für jedes Datenobjekt das Template geklont und jede Zelle mit Daten gefüllt die mittels InlineTableCellValue adressiert sindDie mit Daten gefüllte neue Tabellenzeile wird dann der Rows-Liste der übergeordneten TableRowGroup hinzugefügt. Zum Schluss wird das TableRowDataBase -Template aus der Rows-Liste entfernt.

Es gibt folgende Druckelemente, die Ableitungen von TableRowDataBase sind:

  • TableRowData: Zum Drucken von Zellen die explizit mit InlineTableCellValue angegeben werden müssen.
  • TableRowDataDyn: Es werden dynamisch Zellen generiert für jede öffentliche Eigenschaft eines Objektes, das sich in der Datensammlung befindet.
    • Ist das Objekt ein IACObject, das in der iPlus-Runtime registriert ist, dann werden alle Eigenschaften ausgegeben, die mit der Attributklasse ACClassPropertyInfo veröffentlicht wurden.
    • Ist die Datensammlung eine ACQueryDefinition, dann werden die Eigenschaften ausgedruckt die in ACColumns deklariert sind.
    • Für unbekannte Klassen wird der Typ per Reflection analysiert und alle öffentlichen Eigenschaften ausgegeben.
    • Ist die Datensammlung eine DataTable, dann werden alle DataColumns ausgegeben.
  • TableRowDataDynHeader: Verwenden Sie diese Klasse um Spaltenüberschriften dynamisch zu erzeugen, wenn Sie TableRowDataDyn verwenden.

 

Beispiele

Um Auftragspositionen aus dem IVBSource-Beispiel auf Papier zu bringen würde der XAML-Code folgendermaßen aussehen:

 

Um alle Felder der Tabelle InOrderPos auszugeben, die in der partiellen Klasse mit ACPropertyEntity veröffentlicht wurden, können Sie TableRowDataDyn verwenden:

 


Mit BlockUIContainer können Sie Oberflächen-Elemente, die von UIElement abgeleitet auf Papier rendern lassen. Daher lassen sich damit auch Bilder drucken. Im Kapitel "Verwendung von Ressourcen" haben Sie die MarkupExtension VBStaticResource kennen gelernt mit der Sie Bitmaps aus der Datenbank laden können. VBStaticResource können Sie auch in Flussdokumenten verwenden wie Sie es im folgenden Beispiel sehen können:

 <BlockUIContainer>
  <vb:VBBorder Width="80" Height="50" HorizontalAlignment="Left">
    <Image Tag="vb:VBStaticResource ResourceKey=ACProject(Root)\\ACClass(Root)\\ACClass(Environment)\\ACClassDesign(iPlusLogoPNG)" Source="{vb:VBStaticResource ResourceKey=ACProject(Root)\\ACClass(Root)\\ACClass(Environment)\\ACClassDesign(iPlusLogoPNG)}" />
  </vb:VBBorder>
</BlockUIContainer>

 


Barcodes drucken Sie mit dem Element InlineBarcode:

<TableCell>
<Paragraph TextAlignment="Center">
<vbr:InlineBarcode BarcodeType="QRCODE" QRPixelsPerModule="20"
VBContent="CurrentFacilityCharge.FacilityChargeID" />
</Paragraph>
<Paragraph TextAlignment="Center">
<vbr:InlineBarcode BarcodeType="CODE128" BarcodeWidth="800" BarcodeHeight="100"
VBContent="CurrentFacilityCharge.FacilityChargeID" />
</Paragraph>
</TableCell>

Die Eigenschaft "BarcodeType" erlaubt folgende Werte:

public enum BarcodeType
{
UNSPECIFIED = 0,
UPCA = 1,
UPCE = 2,
UPC_SUPPLEMENTAL_2DIGIT = 3,
UPC_SUPPLEMENTAL_5DIGIT = 4,
EAN13 = 5,
EAN8 = 6,
Interleaved2of5 = 7,
Interleaved2of5_Mod10 = 8,
Standard2of5 = 9,
Standard2of5_Mod10 = 10,
Industrial2of5 = 11,
Industrial2of5_Mod10 = 12,
CODE39 = 13,
CODE39Extended = 14,
CODE39_Mod43 = 15,
Codabar = 16,
PostNet = 17,
BOOKLAND = 18,
ISBN = 19,
JAN13 = 20,
MSI_Mod10 = 21,
MSI_2Mod10 = 22,
MSI_Mod11 = 23,
MSI_Mod11_Mod10 = 24,
Modified_Plessey = 25,
CODE11 = 26,
USD8 = 27,
UCC12 = 28,
UCC13 = 29,
LOGMARS = 30,
CODE128 = 31,
CODE128A = 32,
CODE128B = 33,
CODE128C = 34,
ITF14 = 35,
CODE93 = 36,
TELEPEN = 37,
FIM = 38,
PHARMACODE = 39,
QRCODE = 99
}

Für QR-Barcodes (2D) muss die Eigenschaft QRPixelsPerModule gesetzt werden, mit der Sie definieren, wie groß ein Modul in pixel ist (Kästchen). 

Für einfache Barcodes (1D) müssen die Eigenschaften BarcodeWidth und BarcodeHeight (pixel) gesetzt werden, mit der Sie definieren, wie groß das Bitmap sein soll das generiert werden soll.


Checkbox

Für Boolesche Werte können Sie InlineBoolValue verwenden:

<TableCell>
<Paragraph>
<vbr:InlineBoolValue VBContent="CurrentMaterial\UsageACProgram" />
</Paragraph>
</TableCell>

 

Aggregatfunktionen

Mit Aggregatfunktionen können Sie Feldwerte, die Sie mit InlinePropertyValueBase-Elementen (welche die Schnittstelle IAggregateValue implementieren) ausgegeben haben aggregieren und an einer anderen Stelle das Resultat ausgeben lassen.

Sie müssen dazu lediglich im InlinePropertyValueBase-Element die Eigenschaft "AggregateGroup" mit einem eindeutigen Schlüssel versehen. An einer anderen Stelle geben Sie dann das Druckelement InlineAggregateValue an und referenzieren diesen Schlüssel, damit der aggregierte Wert ausgegeben werden kann.

Schauen Sie sich dazu folgendes Beispiel an:

 

Am Ende werden fünf verschiedene Aggregierungsfunktionen aufgerufen und das Berechnungsergebnis ausgegeben.

Alle beziehen sich auf "QuantityGroup", die bei InlineTableCellValue definiert wurde und auf die Eigenschaft "TargetQuantity" verweist. Während der Iteration der InOrderPosList werden die Werte der Eigenschaft InOrderPos.TargetQuantity unter dem Schlüssel QuantityGroup gespeichert.

  • Bei dem ersten InlineAggregateValue wurde mit der Eigenschaft "AggregateValueType" eine Summenfunktion "Sum" deklariert. Das bedeutet, dass die zuvor gespeicherten "TargetQuantity"-Werte summiert werden sollen.
  • Mit "Average" wird der Durchschnitt aller "TargetQuantity" berechnet und ausgegeben.
  • Mit "Count", die Anzahl aller Auftragspositionen (bzw. Schleifendurchläufe).
  • Mit "Maximum" der größte "TargetQuantity"-Wert.
  • Mit "Minimum" der kleinste "TargetQuantity"-Wert.