Erweiterte Programmierung


Wie Sie aus dem vorigen Kapitel erfahren haben, wird eine ACComponent oder in der Regel eher ein Businessobjekt geklont bevor es gedruckt wird. Beim Klonen werden dieselben Datenbankabfragen ausgeführt wie auf der Original-Instanz und auch alle Eigenschaften kopiert, die der iPlus-Runtime mittels ACProperty-Attributklassen bekannt sind.

Weitere unbekannte Eigenschaften oder andere unbekannte Datenfilter, die für den Druckbericht nötig sind müssen Sie explizit beim Klonvorgang setzen. Der folgende Code ist der Klasse BSOInOrder aus dem Beispielprojekt entnommen:

public override object Clone()
{
BSOInOrder clone = base.Clone() as BSOInOrder;
if (clone != null)
clone.PrintPropExample = this.PrintPropExample;
return clone;
}

Hier wird beim Klonvorgang die lokale Eigenschaft "PrintPropExample" kopiert.


Nach dem Klonvorgang wird der Druckvorgang gestartet (genauer gesagt: Das Ausfüllen des Flowdocument-Templates mit Daten). Damit Sie noch Eingriffsmöglichkeiten vor und nach dem Druckprozess haben, können Sie die virtuelle Methode OnPrintingPhase, die in der Klasse ACComponent definiert ist, überschreiben:

public override void OnPrintingPhase(object reportEngine, ACPrintingPhase printingPhase)
{
base.OnPrintingPhase(reportEngine, printingPhase);
if (printingPhase == ACPrintingPhase.Started) { ... }
else if (printingPhase == ACPrintingPhase.Cancelled) { ... }
else if (printingPhase == ACPrintingPhase.Completed) { ... }
}

Diese virtuelle Methode wird zweimal aufgerufen:

  1. ACPrintingPhase.Started: Bevor die ReportPaginator-Klasse aktiv wird und die Berichtsdaten generiert, können Sie hier z.b. weitere Druckdaten der ReportData-Instanz hinzufügen (siehe nächsten Abschnitt).
  2. ACPrintingPhase.Completed oder ACPrintingPhase.Cancelled: Nach Erfolgreichem oder abgebrochenem Druckvorgang (z.B. bei einer unbehandelten Ausnahme), können hier Abschlussarbeiten programmiert werden. In der Regel wird diese Phase zum Deabonnieren von Druck-Ereignissen benötigt (siehe übernächsten Abschnitt)

Innerhalb der Druckphase "Started" können Sie weitere Druckdaten hinzufügen, die Sie per DictKey in den iPlus-Druckelementen referenzieren.

Schauen Sie sich zuerst folgenden Beispielcode an:

ReportDocument currentPrintingDoc = reportEngine as ReportDocument;
ReportData reportData = (currentPrintingDoc.ReportData as IList<ReportData>).FirstOrDefault();
if (reportData != null && reportData.ACClassDesign != null)
{
if (reportData.ACClassDesign.ACIdentifier == "TableRowDataDyn")
{
DataTable dataTable = CreateOrderTable();
InsertOrders(dataTable);
if (reportData.ReportDocumentValues.ContainsKey("DataTableOrder"))
reportData.ReportDocumentValues.Remove("DataTableOrder");
reportData.ReportDocumentValues.Add("DataTableOrder", dataTable);
}
}

Die in OnPrintingPhase übergebene reportEngine-Variable können Sie in "gip.core.reporthandler.Flowdoc.ReportDocument" casten.

(Falls Sie einen Bericht mit ListAndLabel erstellt haben (Design-Kategorien mit Präfix "DULL...", ) casten Sie reportEngine nach gip.core.reporthandler.VBListLabel (Ableitung von ListLabel).)

ReportData erhalten Sie durch Zugriff auf das erste Element in der Liste "ReportDocument.ReportData". Durch Zugriff auf die Eigenschaft ACClassDesign erfahren Sie welcher Bericht gedruckt werden soll. Das Beispielprojekt enthält zwei Beispielberichte "TableRowData" und "TableRowDataDyn".

Der Bericht mit dem ACIdentifier "TableRowDataDyn" enthält folgende Zeile, die auf ein Objekt in ReportData zugreifen möchte, das unter dem Schlüssel "DataTableOrder" gespeichert ist:

<vbr:TableRowDataDyn DictKey="DataTableOrder" ColumnWidthInfos="300;" />

Dieses Objekt ist eine DataTable die per "ReportDocumentValues.Add()" in die Dictionary unter dem Schlüssel "DataTableOrder" hinzugefügt wird.

In ReportDocumentValues können Sie also beliebig einfache oder komplexe Objekte hinzufügen. Sind es Objekte, die per ACClassInfo und ACClassPropertyInfo der iPlus-Runtime bekannt sind, können Sie den Pfad zur Eigenschaft in VBContent als ACUrl definieren. Bei unbekannten Klassen verwenden Sie bitte die klassische Punkt-Notation für Property-Paths


Auf jedes iPlus-Druckelement, das während des Druckvorgang vom ReportPaginator verarbeitet wird, können Sie Einfluss nehmen, indem Sie ein passendes Druckereignis abbonieren:

currentPrintingDoc.NextRow += OnPrinting_NextRow;
currentPrintingDoc.NewCell += OnPrinting_NewCell;
currentPrintingDoc.SetFlowDocObjValue += OnPrinting_SetFlowDocObjValue;

Neben diesen drei Ereignissen gibt es noch vier weitere. Mit ImageProcessingImageProcessed und ImageError können Sie den Druckvorgang von Bildern kontrollieren und per GetPageCompleted werden Sie benachrichtigt, wenn ein Seitenumbruch stattgefunden hat.

Vergessen Sie nicht beim zweiten Aufruf der OnPrintingPhase-Methode (Phase Completed oder Cancelled) die abonnierten Ereignisse wieder zu kündigen!

 

SetFlowDocObjValue-Ereignis

Dieses Ereignis wird ausgelöst, wenn ein einfaches Druckelement mit Daten gefüllt wurde. Im folgenden Beispiel soll die Zahl im Feld "TargetQuantity" hervorgehoben werden:

void OnPrinting_SetFlowDocObjValue(object sender, PaginatorOnSetValueEventArgs e)
{
ReportDocument currentPrintingDoc = sender as ReportDocument;
if (currentPrintingDoc != null && e.FlowDocObj.VBContent == "TargetQuantity")
{
TextElement element = e.FlowDocObj as TextElement;
if (element != null)
element.FontWeight = FontWeights.Bold;
}
}

 

NextRow-Ereignis

NextRow wird ausgelöst, wenn eine neue Tabellenzeile geklont wurde während der Iteration einer Datensammlung.

void OnPrinting_NextRow(object sender, PaginatorNextRowEventArgs e)
{
}

 

NewCell-Ereignis

NewCell wird ausgelöst, wenn eine Tabellenzelle einer geklonten Tabellenzeile mit Daten gefüllt wurde. Im folgenden Beispiel soll eine spezielle Formatierung für die einen Wert aus der "TargetQuantity"-Spalte druchgeführt werden:

void OnPrinting_NewCell(object sender, PaginatorNewTableCellEventArgs e)
{
if (e.FieldName == "TargetQuantity" && e.FieldValue != null && e.FieldValue is double)
{
double value = (double)e.FieldValue;
if (value > 0.0001)
{
string valueAsString = InlineValueBase.FormatValue(value, "0.0000",
(e.TableRow as TableRowDataBase).CultureInfo,
(e.TableRow as TableRowDataBase).MaxLength);
((e.NewCell.Blocks.FirstBlock as Paragraph).Inlines.FirstInline as Run).Text
= valueAsString;
}
}
}

 


Hier nochmal alle Themen aus den oberen Abschnitten zusammengefasst:

 


Berichte können nicht nur vom Anwender über die Oberfläche gedruckt werden, sondern es kann auch serverseitig im Hintergrund erfolgen. 

Hier gibt es zwei Möglichkeiten:

  1. Sie möchten während eines Workflow-Ablaufs drucken, dann verwenden Sie bitte die Workflow-Klasse gip.core.autocomponent.PWNodePrint. In der Konfiguration können Sie dann angeben welches Businessobjekt und welcher Report gedruckt werden sollen.
  2. Sie lösen den Druckvorgang programmgesteuert aus. Dazu vorab folgendes Codebeispiel aus der Basisklasse ACComponent, mit dem der aktuelle Zustand einer Instanz ausgedruckt wird:

 

Im Grunde genommen gibt es vier Schritte:

 

1.  ACComponent-Instanz

Sie benötigen eine ACComponent-Instanz, die Sie Drucken möchten. Entweder ist es der this-Pointer selbst oder eine Referenz, die Sie per ACUrlCommand erhalten oder neu erzeugt haben. Beispiele:

ACClass iPlusType = typeof(BSOInOrder).GetACType() as ACClass;
BSOInOrder refToPrintComp = StartComponent(iPlusType, iPlusType, null) as BSOInOrder;
refToPrintComp.AccessPrimary.NavACQueryDefinition.ACFilterColumns.FirstOrDefault()
.SetSearchValue<string>("123456789");
refToPrintComp.Search();
ACComponent refToPrintComp = ACUrlCommand("\\MyApp\\InstanceWithReports") as ACComponent;

 

2. Design (Bericht)

Von dieser Instanz benötigen Sie einen Bericht, den Sie drucken möchten. Rufen Sie dazu die Methode eine der mehrfach überladenen GetDesign(...)-Methoden auf, um eine Referenz auf ACClassDesign zu erhalten (Der XAML-Code ist in ACClassDesign.XMLDesign gespeichert).

ACClassDesign designToPrint = refToPrintComp.GetDesign("MyReportName");

 

3. ReportData

Rufen Sie die statische Methode ReportData.BuildReportData() auf, um die Berichtsdaten für den Berichtsgenerator bereitzustellen. Übergeben Sie dabei die Referenz der zu druckenden ACComponent (refToPrintComp). Bitte bedenken Sie, dass die Instanz geklont wird und in der Out-Variable cloneInstantiated bestätigt wird:

bool cloneInstantiated = false;
ReportData reportData = ReportData.BuildReportData(out cloneInstantiated,
Global.CurrentOrList.Current, refToPrintComp, null, designToPrint);

 

4. Drucken mit dem Berichtsgenerator

Zuletzt benötigen Sie eine Instanz des Berichtsgenerators (Klasse VBBSOReport). Entweder Sie haben irgendwo eine dauerhafte Referenz im Anwendungsbaum oder Sie erzeugen sich während der Laufzeit eine eigene. Rufen Sie die Print-Methode auf und übergeben das Design und die Berichtsdaten:

ACClass reportEType = typeof(VBBSOReport).GetACType() as ACClass;
VBBSOReport reportEngine = StartComponent(reportEType, reportEType, null) as VBBSOReport;
int copies = 5;
reportEngine.Print(designToPrint, false, "hpLaserJetA4", reportData, copies);