Это удивительно, но FastReport VCL отлично вписался в Web при помощи фрэймворка UniGUI. Разберем пример из демо-версии UniGUI, который находится в
1 |
C:\Program Files (x86)\FMSoft\Framework\uniGUI\Demos\Desktop\FastReport |
Вот результат работы примера
Мы рассмотрим только основные моменты по коду. Пример целиком можно получить, скачав UniGUI фрэймворк и пройдя до демо папки, описанной выше.
Начали!
Создадим UniGUI приложение
Кнопку обработаем следующим образом
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
procedure TMainForm.UniButton1Click(Sender: TObject); begin if (Trim(UniDBLookupComboBox1.Text)<>'') then begin UniDBLookupComboBox1.Color := clWindow; UniForm1.fDynamic := UniCheckBox1.Checked; UniForm1.InvNum := UniDBLookupComboBox1.Text; UniForm1.Show(); end else begin UniDBLookupComboBox1.Color := clYellow; UniDBLookupComboBox1.SetFocus; end; end; |
Создадим отдельный юнит и добавим на него следующие компоненты
В дизайнере у нас уже заготовленный отчет
Этот отчет сохранен в папке files
Код Unit1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, uniGUITypes, uniGUIAbstractClasses, uniGUIClasses, uniGUIForm, frxClass, frxDBSet, DB, uniGUIBaseClasses, uniURLFrame, frxGradient, frxExportPDF, uniButton, {unimURLFrame} ServerModule {frxExportPDF frxGradient, uniButton}; type TUniForm1 = class(TUniForm) UniURLFrame1: TUniURLFrame; frxPDFExport1: TfrxPDFExport; frxReport1: TfrxReport; frxDBDataset1: TfrxDBDataset; frxGradientObject1: TfrxGradientObject; UniButton1: TUniButton; procedure UniButton1Click(Sender: TObject); procedure UniFormBeforeShow(Sender: TObject); private procedure OpenDS; procedure PrepareAndShow(Report: TfrxReport; Exp: TfrxPDFExport); procedure CloseDS; { Private declarations } public { Public declarations } InvNum: string; fDynamic : Boolean; end; function UniForm1: TUniForm1; implementation {$R *.dfm} uses MainModule, uniGUIApplication; function UniForm1: TUniForm1; begin Result := TUniForm1(UniMainModule.GetFormInstance(TUniForm1)); end; procedure TUniForm1.UniButton1Click(Sender: TObject); begin Close; end; procedure TUniForm1.CloseDS; begin UniMainModule.ADOQuery1.Close; end; procedure TUniForm1.OpenDS; begin UniMainModule.ADOQuery1.Close; UniMainModule.ADOQuery1.SQL.Clear; UniMainModule.ADOQuery1.SQL.Add('SELECT * FROM ((tblInv INNER JOIN tblCust ON tblCust.CustNum = tblInv.InvCustNum) INNER JOIN tblInvDetail ON tblInvDetail.InvDetailInvNum = tblInv.InvNum) WHERE InvNum=:Num;'); UniMainModule.ADOQuery1.Parameters.ParamByName('Num').Value := InvNum; UniMainModule.ADOQuery1.Open; end; procedure TUniForm1.UniFormBeforeShow(Sender: TObject); var FRp : TfrxReport; FXp : TfrxPDFExport; begin OpenDS; try if fDynamic then begin // Create report components dynamically. // Note: frxDBDataSet is not created dynamically because it requires all report dataware controls to be assigned a dataset too. // Please refer to below link to see how a report can be fully created using code: // https://www.fast-report.com/documentation/ProgMan/index.html?creating_a_report_form_from_code.htm FRp := TfrxReport.Create(nil); FXp := TfrxPDFExport.Create(nil); try FRp.LoadFromFile(UniServerModule.FilesFolderPath+'rp.fr3'); PrepareAndShow(FRp, FXp); finally FRp.Free; FXp.Free; end; end else PrepareAndShow(frxReport1, frxPDFExport1); // Use static report components finally CloseDS; end; end; procedure TUniForm1.PrepareAndShow(Report: TfrxReport; Exp: TfrxPDFExport); var AUrl : string; begin Report.PrintOptions.ShowDialog := False; Report.ShowProgress := false; Report.EngineOptions.SilentMode := True; Report.EngineOptions.EnableThreadSafe := True; Report.EngineOptions.DestroyForms := False; Report.EngineOptions.UseGlobalDataSetList := False; Exp.Background := True; Exp.ShowProgress := False; Exp.ShowDialog := False; Exp.FileName := UniServerModule.NewCacheFileUrl(False, 'pdf', '', '', AUrl); Exp.DefaultPath := ''; Report.PreviewOptions.AllowEdit := False; Report.PrepareReport; Report.Export(Exp); UniURLFrame1.URL := AUrl; end; end. |
Разберем основные моменты по коду
Статический и динамический варианты создания отчетов
Отчет можно подгружать статически, из компонента frxReport, или же динамически, из файла rp.fr3. Что выбрать – зависит от задачи. Если задаваться целью – экономить оперативную память, то, конечно, лучше динамический способ, если задаваться целью – сделать просто и быстро, то наверное сойдет и статический вариант. В данном примере создано оба варианта.
Обратите внимание, компонент frxDBDataSet даже в динамическом случае создается статически, то есть он просто должен быть добавлен на форму, до динамического создания frxReport.
Потокобезопасность
Здесь также разобран безопасный, с точки зрения потоков (threads), способ вывода отчетов. В документации FastReport указана следующая информация
FastReport может работать независимо в разных потоках, но имеется ряд особенностей: – Нельзя создавать TfrxDBDataSet с одинаковыми именами даже в разных потоках, т.к. для поиска используется “глобальный список” и обращение всегда будет происходить к первому созданному TfrxDBDataSet(использование глобального списка можно отключить, по умолчанию активен);
– Если во врем выполнения отчета происходит изменение св-в объектов(для примера Memo1.Left := Memo1.Left + 10 в скрипте), то следует помнить, что при последующим выполнении если св-во TfrxReport.EngineOptions.DestroyForms := False шаблон отчета уже будет модифицирован и его необходимо заново загрузить или использовать TfrxReport.EngineOptions.DestroyForms := True. При восстановления из потока нельзя использовать интерактивные отчеты, т.к. объекты скрипта уничтожаются после восстановления, поэтому в некоторых случаях рациональней использовать TfrxReport.EngineOptions.DestroyForms := False и восстанавливать шаблон самостоятельно при следующем цикле построения.
При необходимости глобальный список, по которому осуществляется поиск экземпляров TfrxDBDataSet можно отключить.
1234567891011 {DestroyForms можно отключить, если каждый раз восстанавливатьотчет из файла или потока}FReport.EngineOptions.DestroyForms := False;FReport.EngineOptions.SilentMode := True;{Данное св-во отключает поиск по глобальному списку}FReport.EngineOptions.UseGlobalDataSetList := False;{EnabledDataSets играет роль локального списка, нужноустанавливать до загрузки шаблона}FReport.EnabledDataSets.Add(FfrxDataSet);FReport.LoadFromFile(ReportName);FReport.PrepareReport;
В нашем случае этот кусок кода выглядит таким образом
1 2 3 4 5 6 |
... Report.EngineOptions.SilentMode := True; Report.EngineOptions.EnableThreadSafe := True; Report.EngineOptions.DestroyForms := False; Report.EngineOptions.UseGlobalDataSetList := False; ... |
В случае, если мы хотим работать со множеством потоков, можно на отдельной форме создать набор компонентов и вызывать экземпляр этой формы в нужный момент для конкретного потока, чтобы каждый раз не создавать по 33 компонента.