Im sechsten Artikel der mORMot Vorstellungsreihe wird gezeigt, wie mittels eines Mediators Daten im JSON-Format für die Anzeige in FastReport aufbereitet werden. Mediatoren sind eine elegante Möglichkeit Funktionalität zu kapseln und die einfache und sichere Anwendung zu erreichen. Wieder mit dabei ist die eierlegende Wollmilchsau
DocVariant. Die Beispiel-Anwendung beinhaltet den Endbenutzer-Bericht-Designer von FastReport. Für alle Leser, die nur die eingeschränkte Embarcadero Edition kennen, eine Möglichkeit die Vollversion in Augenschein zu nehmen. Nicht alle Funktionen sind freigeschaltet! Die enthaltenen Verweise zur Dokumentation verlinken zur aktuell verfügbaren
mORMot1 Hilfe. Für das Beispiel wird
mORMot2 verwendet. Die Namen für Klassen und Funktionen können sich leicht unterscheiden.
Eins, zwei, drei, fertig ist die Bericht-Einbinderei
Zumindest dann, wenn man sich Unterstützung holt. Schon die Gang of Four (GoF) hat in ihrem Buch
Design Patterns von 1994 das Muster
Vermittler beschrieben. Vermittler/Mediator ist im wörtlichen Sinne zutreffend. Wer schon einige Artikel von mir gelesen hat, weiß, der Mediator hat einen festen Platz im Repertoire. Oft ist das Problem, dass die gezeigten Anwendungsbeispiele unterkomplex sind. Heute zeige ich mehr Möglichkeiten am Beispiel eines Inhouse Tools. Das Beschleunigungspotenzial für die Anwendungsentwicklung lässt sich an einem ReportMediator gut darstellen. Viele sich wiederholende Schritte werden automatisch erledigt und der Objektinspektor ist außen vor. Anmerkung: Der Begriff Mediator wird im Text allgemeiner verwendet, als es der Kontext des gleichnamigen Design-Patterns vorgibt.
Im Anhang befindet sich der Sourcecode und das ausführbare Programm.
Der Sourcecode der Beispiel-Anwendung kann nicht kompiliert werden. Der Mediator hat die Fähigkeit, verschiedene Datenquellen anzubinden. Dazu zählen die generischen Typen der mORMot und Spring4D Collections. Aber auch proprietäre Libraries von FastReport, DevExpress usw. werden verwendet. Den Mediator ausschließlich auf freie Bibliotheken umzuschreiben, sprengt den Rahmen eines Artikels. Die besprochenen Auszüge vermitteln aber einen guten Überblick über die grundsätzliche Herangehensweise.
Disclaimer: Der Sourcecode ist weder getestet noch optimiert. Die Benutzung der zur Verfügung gestellten Materialien erfolgt auf eigene Gefahr.
Vier Schritte ins Glück
Grundsätzlich sind nur wenige Schritte notwendig, einen Bericht einzubinden:
- Für jeden Bericht einen eigenen ReportMediator instanttieren.
- Provider für die Layout-Daten instanttieren und zuweisen.
- DataSet(s) und Variablen hinzufügen.
- Vorschau oder Designer ausführen.
Delphi-Quellcode:
var
data: TDocVariantData;
begin
FReportMediator := TReportMediator.Create(Self);
FReportMediator.ReportFileProvider := TReportFileFR3Provider.Create(FReportFileName);
// Add DataSet
data.InitJsonFromFile(dataSetFileName, JSON_FAST_FLOAT);
FReportMediator.AddDocVariantDataSet(GetFileNameWithoutExt(ExtractFileName(dataSetFileName)), Variant(data), []);
// Add variables
data.InitJsonFromFile(dataVarsFileName, JSON_FAST_FLOAT);
FReportMediator.AddDocVariantVariables('ReportVars', data);
FReportMediator.ShowReport(0, {IsEditable=} True);
Den Weg über die universelle DocVariant Ladefunktion bin ich nur gegangen, um überhaupt ein wenig Sourcecode für die weiteren Erläuterungen zeigen zu können. Die spezialisierten Funktionen erledigen das in einem Aufwasch. Das Gleiche gilt für das direkte Hinzufügen von Objekten, Listen, Arrays und was man sonst noch verarbeiten kann.
Die DocVariant Ladefunktion
Auch alle
Add*-Funktionen des Mediators realisieren sich aus wenigen Zeilen Sourcecode. Der Grund ist die sehr einfache Methode, wie FastReport Daten rekrutiert. Die Daten werden über eine Event-Funktion zugeführt. Mehr dazu im nächsten Abschnitt. Erst einmal die
AddDocVariantDataSet Funktion:
Delphi-Quellcode:
function TReportMediator.AddDocVariantDataSet(const pmcName: String; const pmcDataVariant: Variant;
const pmcFilterFields: array of RawUtf8): TCustomReportDataSet;
var
dataSet: TDocVariantReportDataSet;
begin
dataSet := TDocVariantReportDataSet.Create(FDataSetRoot, pmcName);
dataSet.GetFieldNames(pmcDataVariant, pmcFilterFields);
dataSet.PrepareData(pmcDataVariant);
Result := dataSet;
end;
Die Angabe
Name wird im Bericht für die Bezeichnung der Datenquelle verwendet.
FilterFields bietet die Möglichkeit, unerwünschte Felder der Datenquelle auszublenden.
Der DocVariant DataSet
Um einen eigenen DataSet zu verwenden, gibt es in FastReport die Klasse
TfrxUserDataSet. Die Klasse ist minimalistisch. Sie enthält nur wenige Eigenschaften zur Steuerung der Datenzulieferung. Die zwei wichtigsten sind:
Fields, was nichts anderes ist als eine Stringliste zur Definition der Feldnamen, und das Ereignis
OnGetValue. Der Prototyp ist in der
Unit frxClass wie folgt definiert:
TfrxGetValueEvent = procedure(const VarName: String; var Value: Variant) of object;
Als Alternative gibt es das Ereignis
OnNewGetValue, das wie folgt aussieht:
TfrxNewGetValueEvent = procedure(Sender: TObject; const VarName: String; var Value: Variant) of object;
Die Definition für den DataSet beschränkt sich auf wenige Zeilen:
Delphi-Quellcode:
TDocVariantReportDataSet = class(TCustomReportDataSet)
private
FDataVariant: TDocVariantData;
procedure DoDataGetValue(const pmcName: String; var pmvValue: Variant);
public
procedure GetFieldNames(const pmcData: Variant; const pmcFilterFields: array of RawUtf8); reintroduce;
procedure PrepareData(const pmcData; pmOnOwnGetValue: TReportDataSetGetValueEvent = Nil); override;
end;
Die Klasse TCustomReportDataSet ist ein direkter Nachfahre von TfrxUserDataSet. Die Implementation erklärt sich von selbst:
Delphi-Quellcode:
procedure TDocVariantReportDataSet.DoDataGetValue(const pmcName: String; var pmvValue: Variant);
begin
case FDataVariant.Kind of
dvObject:
pmvValue := FDataVariant.Value[pmcName];
dvArray:
pmvValue := FDataVariant.Values[RecNo].Value[pmcName];
end;
end;
procedure TDocVariantReportDataSet.GetFieldNames(const pmcData: Variant; const pmcFilterFields: array of RawUtf8);
var
i: Integer;
run: PDocVariantData;
fieldName: RawUtf8;
begin
Fields.Clear;
run := _Safe(pmcData);
case run.Kind of
dvObject:
;
dvArray:
run := _Safe(run.Value[0]);
else
Exit; //=> dvUndefined
end;
for i := 0 to High(run.Names) do
begin
fieldName := run.Names[i];
if FindPropName(pmcFilterFields, fieldName) < 0 then
Fields.Add(Utf8ToString(fieldName));
end;
end;
procedure TDocVariantReportDataSet.PrepareData(const pmcData);
begin
OnGetValue := DoDataGetValue;
FDataVariant := _Safe(Variant(pmcData))^;
case FDataVariant.Kind of
dvObject:
RangeEndCount := 1;
dvArray:
RangeEndCount := FDataVariant.Count;
else
RangeEndCount := 0;
end;
RangeBegin := rbFirst;
RangeEnd := reCount;
First;
end;
Wie man sieht, spielt es keine Rolle, ob die Datenquelle vom Typ Objekt oder Array ist. Einschränkend ist, dass nur die erste Ebene unterstützt wird. Zur Erinnerung: DocVariant ist eine beliebig komplexe Datenstruktur aus Objekt(en) und/oder Arrays oder aus Kombinationen von beiden. Die Funktion
_Safe stellt sicher, dass es
immer einen DocVariant gibt. Notfalls einen leeren Fake DocVariant.
Die Beispiel-Anwendung: kurz und bündig
Das Programm ist eine Spielumgebung für mORMot und FastReport. Es stehen fünf Funktionen zur Verfügung: Neues Projekt, Projekt öffnen und schließen, Berichtvorschau oder Berichtdesigner anzeigen. Um gleich loslegen zu können, ist ein Projekt "Test01" fertig erstellt. Grundsätzliches zur Bedienung:
- Jedes Projekt hat seinen eigenen Ordner. Das voreingestellte Projects Verzeichnis befindet sich unterhalb des Programm-Verzeichnisses.
- Der Name der FastReport Layout-Datei ist der Verzeichnisname plus Erweiterung .fr3. Sie wird durch das Speichern eines Berichts im Designer erstellt.
- Der Name der Variablendatei im JSON Objekt Format ist der Verzeichnisname plus Erweiterung .variables. Beim Anlegen eines neuen Projekts wird eine leere Datei erstellt.
- Alle Dateien mit der Endung *.json werden als DataSets eingebunden. Der Name des DataSets ist der Dateiname. Es wird das JSON Objekt und Array Format unterstützt. Ein DataSet wird durch Hinzufügen der JSON-Datendatei in das Projektverzeichnis in den Bericht eingebunden. Drag-and-drop in/aus der Dateiansicht wird unterstützt.
Viel Spaß beim Spielen. Ich hoffe, ich habe am Anfang nicht zu viel versprochen und das Weiterlesen hat sich gelohnt.
Zusammenfassung
Die Möglichkeiten zum Einsatz eines Mediators enden hier nicht. Konsequent setzt es sich bei der Anbindung an die
GUI fort. Ob ein Button die Vorschau und/oder den Designer aufruft, warum die Eigenschaft Caption, Image oder ein OnClick Ereignis im Objektinspektor zuweisen. Die Registrierung mit
BindButton(btnPrintPreview, TReportMediator.BuildReportID(2008));
reicht aus, egal ob 10x oder 100x in einer Anwendung. Auch der heutige Artikel war vor allem durch die Notwendigkeit zum radikalen Kürzen eine besondere Herausforderung. Bei dieser Aktion besteht immer die Gefahr, dass der rote Faden zum Verständnis verloren geht. Ich hoffe, die Ausführungen blieben nachvollziehbar.
mORMot ist gut dokumentiert. Die Hilfe umfasst mehr als 2500 Seiten. Davon enthalten die ersten ca. 650 Seiten einen sehr lesenswerten allgemeinen Teil, der Rest ist
API Dokumentation. mORMot muss nicht in der
IDE installierten werden! Es reicht aus, die entsprechenden Bibliothekspfade einzufügen. Es stehen viele Beispiele und ein freundliches
Forum zur Verfügung.
In der mORMot Reihe bisher veröffentlicht:
- ORM und DocVariant kurz vorgestellt
- ZIP-Datei als Datenspeicher mit verschlüsseltem Inhalt
- Einführung in methodenbasierte Services - Server und Client
- Einführung in Interface-based Services - Server und Client
- Mustache Editor mit integriertem HTTP-Server zum Anzeigen von HTML Seiten
Bis bald...
Thomas