Using FlexCel with TMS Aurelius (Delphi)
Note
This demo is available in your FlexCel installation at <FlexCel Install Folder>\Demo\Delphi\Modules\20.Reports\A1.TMS Aurelius and also at https://github.com/tmssoftware/TMS-FlexCel.VCL-demos/tree/master/Delphi/Modules/20.Reports/A1.TMS Aurelius
Overview
You can run a report in data from TMS Aurelius the same way you would run a report from a TList<T>.
Concepts
There is no need to use TAureliusDataSet. FlexCel can bind directly to the TList<T> managed by Aurelius.
Aurelius has 2 specific types that must be handled differently: Nullable<T> and TBlob. In the unit AureliusFlexCelSupport, we add support for those types in FlexCel. To run your own reports with Aurelius, copy the unit "AureliusFlexCelSupport.pas" in your own app, and call SetupAurelius(Report) after creating the TFlexCelReport instance.
Files
AureliusFlexCelSupport.pas
unit AureliusFlexCelSupport;
interface
{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
implementation
{$ELSE}
uses RTTI, FlexCel.Report, Aurelius.Types.Blob, Aurelius.Mapping.RttiUtils;
procedure SetupAurelius(const Report: TFlexCelReport);
implementation
uses TypInfo, StrUtils;
function ConvertAureliusTypes(const v: TFlexCelDataConversionArgs; out r: TReportValue): boolean;
var
Blob: TBlob;
TName: string;
v0: TValue;
RttiType: TRttiType;
FieldHasValue, FieldValue: TRttiField;
HasValue: boolean;
begin
v0 := v.v;
Result := false;
if v0.IsEmpty then
begin
r := TReportValue.Empty;
exit(true);
end;
if (v0.Kind <> tkRecord) then exit(false);
if v0.TryAsType<TBlob>(Blob) then
begin
r := Blob.AsBytes;
exit(true);
end;
if (v0.TypeInfo = nil) then exit(false);
TName := GetTypeName(v0.TypeInfo);
if StartsStr('Nullable<', TName) then
begin
RttiType := v.Rtti.GetType(v0.TypeInfo);
FieldHasValue := RttiType.GetField('FHasValue');
if FieldHasValue = nil then exit(false);
FieldValue := RttiType.GetField('FValue');
if FieldValue = nil then exit(false);
HasValue := FieldHasValue.GetValue(v0.GetReferenceToRawData).AsBoolean;
if not HasValue then
begin
r := TReportValue.Empty;
exit(true);
end;
r := TReportValue.Create(FieldValue.GetValue(v0.GetReferenceToRawData).AsVariant);
exit(true);
end;
end;
procedure SetupAurelius(const Report: TFlexCelReport);
begin
Report.DataConversionEvent :=
function (const v: TFlexCelDataConversionArgs; out r: TReportValue): boolean
begin
Result := ConvertAureliusTypes(v, r);
end;
end;
{$IFEND}
end.
DataModel.pas
unit DataModel;
interface
{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
implementation
{$ELSE}
uses
SysUtils, Generics.Collections, Aurelius.Mapping.Attributes, Aurelius.Types.Blob, Aurelius.Types.DynamicProperties, Aurelius.Types.Nullable, Aurelius.Types.Proxy, Aurelius.Criteria.Dictionary;
type
TEmployees = class;
TOrders = class;
TShippers = class;
TEmployeesTableDictionary = class;
TOrdersTableDictionary = class;
TShippersTableDictionary = class;
[Entity]
[Table('Employees')]
[Id('FEmployeeID', TIdGenerator.IdentityOrSequence)]
TEmployees = class
private
[Column('EmployeeID', [TColumnProp.Required, TColumnProp.NoInsert, TColumnProp.NoUpdate])]
FEmployeeID: integer;
[Column('LastName', [TColumnProp.Required], 20)]
FLastName: string;
[Column('FirstName', [TColumnProp.Required], 10)]
FFirstName: string;
[Column('Title', [], 30)]
FTitle: Nullable<string>;
[Column('TitleOfCourtesy', [], 25)]
FTitleOfCourtesy: Nullable<string>;
[Column('BirthDate', [])]
FBirthDate: Nullable<TDateTime>;
[Column('HireDate', [])]
FHireDate: Nullable<TDateTime>;
[Column('Address', [], 60)]
FAddress: Nullable<string>;
[Column('City', [], 15)]
FCity: Nullable<string>;
[Column('Region', [], 15)]
FRegion: Nullable<string>;
[Column('PostalCode', [], 10)]
FPostalCode: Nullable<string>;
[Column('Country', [], 15)]
FCountry: Nullable<string>;
[Column('HomePhone', [], 24)]
FHomePhone: Nullable<string>;
[Column('Extension', [], 4)]
FExtension: Nullable<string>;
[Column('Photo', [TColumnProp.Lazy])]
FPhoto: TBlob;
[Column('Notes', [TColumnProp.Lazy])]
FNotes: TBlob;
[Column('PhotoPath', [], 255)]
FPhotoPath: Nullable<string>;
[Association([TAssociationProp.Lazy], [])]
[JoinColumn('ReportsTo', [], 'EmployeeID')]
FReportsTo: Proxy<TEmployees>;
function GetReportsTo: TEmployees;
procedure SetReportsTo(const Value: TEmployees);
public
property EmployeeID: integer read FEmployeeID write FEmployeeID;
property LastName: string read FLastName write FLastName;
property FirstName: string read FFirstName write FFirstName;
property Title: Nullable<string> read FTitle write FTitle;
property TitleOfCourtesy: Nullable<string> read FTitleOfCourtesy write FTitleOfCourtesy;
property BirthDate: Nullable<TDateTime> read FBirthDate write FBirthDate;
property HireDate: Nullable<TDateTime> read FHireDate write FHireDate;
property Address: Nullable<string> read FAddress write FAddress;
property City: Nullable<string> read FCity write FCity;
property Region: Nullable<string> read FRegion write FRegion;
property PostalCode: Nullable<string> read FPostalCode write FPostalCode;
property Country: Nullable<string> read FCountry write FCountry;
property HomePhone: Nullable<string> read FHomePhone write FHomePhone;
property Extension: Nullable<string> read FExtension write FExtension;
property Photo: TBlob read FPhoto write FPhoto;
property Notes: TBlob read FNotes write FNotes;
property PhotoPath: Nullable<string> read FPhotoPath write FPhotoPath;
property ReportsTo: TEmployees read GetReportsTo write SetReportsTo;
end;
[Entity]
[Table('Orders')]
[Id('FOrderID', TIdGenerator.IdentityOrSequence)]
TOrders = class
private
[Column('OrderID', [TColumnProp.Required, TColumnProp.NoInsert, TColumnProp.NoUpdate])]
FOrderID: integer;
[Column('OrderDate', [])]
FOrderDate: Nullable<TDateTime>;
[Column('RequiredDate', [])]
FRequiredDate: Nullable<TDateTime>;
[Column('ShippedDate', [])]
FShippedDate: Nullable<TDateTime>;
[Column('Freight', [])]
FFreight: Nullable<double>;
[Column('ShipName', [], 40)]
FShipName: Nullable<string>;
[Column('ShipAddress', [], 60)]
FShipAddress: Nullable<string>;
[Column('ShipCity', [], 15)]
FShipCity: Nullable<string>;
[Column('ShipRegion', [], 15)]
FShipRegion: Nullable<string>;
[Column('ShipPostalCode', [], 10)]
FShipPostalCode: Nullable<string>;
[Column('ShipCountry', [], 15)]
FShipCountry: Nullable<string>;
[Column('EmployeeID', [])]
FEmployeeID: Integer;
[Association([TAssociationProp.Lazy], [])]
[JoinColumn('ShipVia', [], 'ShipperID')]
FShipVia: Proxy<TShippers>;
function GetShipVia: TShippers;
procedure SetShipVia(const Value: TShippers);
public
property OrderID: integer read FOrderID write FOrderID;
property OrderDate: Nullable<TDateTime> read FOrderDate write FOrderDate;
property RequiredDate: Nullable<TDateTime> read FRequiredDate write FRequiredDate;
property ShippedDate: Nullable<TDateTime> read FShippedDate write FShippedDate;
property Freight: Nullable<double> read FFreight write FFreight;
property ShipName: Nullable<string> read FShipName write FShipName;
property ShipAddress: Nullable<string> read FShipAddress write FShipAddress;
property ShipCity: Nullable<string> read FShipCity write FShipCity;
property ShipRegion: Nullable<string> read FShipRegion write FShipRegion;
property ShipPostalCode: Nullable<string> read FShipPostalCode write FShipPostalCode;
property ShipCountry: Nullable<string> read FShipCountry write FShipCountry;
property EmployeeID: integer read FEmployeeID write FEmployeeID;
property ShipVia: TShippers read GetShipVia write SetShipVia;
end;
[Entity]
[Table('Shippers')]
[Id('FShipperID', TIdGenerator.IdentityOrSequence)]
TShippers = class
private
[Column('ShipperID', [TColumnProp.Required, TColumnProp.NoInsert, TColumnProp.NoUpdate])]
FShipperID: integer;
[Column('CompanyName', [TColumnProp.Required], 40)]
FCompanyName: string;
[Column('Phone', [], 24)]
FPhone: Nullable<string>;
public
property ShipperID: integer read FShipperID write FShipperID;
property CompanyName: string read FCompanyName write FCompanyName;
property Phone: Nullable<string> read FPhone write FPhone;
end;
TDicDictionary = class
private
FEmployees: TEmployeesTableDictionary;
FOrders: TOrdersTableDictionary;
FShippers: TShippersTableDictionary;
function GetEmployees: TEmployeesTableDictionary;
function GetOrders: TOrdersTableDictionary;
function GetShippers: TShippersTableDictionary;
public
destructor Destroy; override;
property Employees: TEmployeesTableDictionary read GetEmployees;
property Orders: TOrdersTableDictionary read GetOrders;
property Shippers: TShippersTableDictionary read GetShippers;
end;
TEmployeesTableDictionary = class
private
FEmployeeID: TDictionaryAttribute;
FLastName: TDictionaryAttribute;
FFirstName: TDictionaryAttribute;
FTitle: TDictionaryAttribute;
FTitleOfCourtesy: TDictionaryAttribute;
FBirthDate: TDictionaryAttribute;
FHireDate: TDictionaryAttribute;
FAddress: TDictionaryAttribute;
FCity: TDictionaryAttribute;
FRegion: TDictionaryAttribute;
FPostalCode: TDictionaryAttribute;
FCountry: TDictionaryAttribute;
FHomePhone: TDictionaryAttribute;
FExtension: TDictionaryAttribute;
FPhoto: TDictionaryAttribute;
FNotes: TDictionaryAttribute;
FPhotoPath: TDictionaryAttribute;
FReportsTo: TDictionaryAssociation;
public
constructor Create;
property EmployeeID: TDictionaryAttribute read FEmployeeID;
property LastName: TDictionaryAttribute read FLastName;
property FirstName: TDictionaryAttribute read FFirstName;
property Title: TDictionaryAttribute read FTitle;
property TitleOfCourtesy: TDictionaryAttribute read FTitleOfCourtesy;
property BirthDate: TDictionaryAttribute read FBirthDate;
property HireDate: TDictionaryAttribute read FHireDate;
property Address: TDictionaryAttribute read FAddress;
property City: TDictionaryAttribute read FCity;
property Region: TDictionaryAttribute read FRegion;
property PostalCode: TDictionaryAttribute read FPostalCode;
property Country: TDictionaryAttribute read FCountry;
property HomePhone: TDictionaryAttribute read FHomePhone;
property Extension: TDictionaryAttribute read FExtension;
property Photo: TDictionaryAttribute read FPhoto;
property Notes: TDictionaryAttribute read FNotes;
property PhotoPath: TDictionaryAttribute read FPhotoPath;
property ReportsTo: TDictionaryAssociation read FReportsTo;
end;
TOrdersTableDictionary = class
private
FOrderID: TDictionaryAttribute;
FOrderDate: TDictionaryAttribute;
FRequiredDate: TDictionaryAttribute;
FShippedDate: TDictionaryAttribute;
FFreight: TDictionaryAttribute;
FShipName: TDictionaryAttribute;
FShipAddress: TDictionaryAttribute;
FShipCity: TDictionaryAttribute;
FShipRegion: TDictionaryAttribute;
FShipPostalCode: TDictionaryAttribute;
FShipCountry: TDictionaryAttribute;
FEmployeeID: TDictionaryAssociation;
FShipVia: TDictionaryAssociation;
public
constructor Create;
property OrderID: TDictionaryAttribute read FOrderID;
property OrderDate: TDictionaryAttribute read FOrderDate;
property RequiredDate: TDictionaryAttribute read FRequiredDate;
property ShippedDate: TDictionaryAttribute read FShippedDate;
property Freight: TDictionaryAttribute read FFreight;
property ShipName: TDictionaryAttribute read FShipName;
property ShipAddress: TDictionaryAttribute read FShipAddress;
property ShipCity: TDictionaryAttribute read FShipCity;
property ShipRegion: TDictionaryAttribute read FShipRegion;
property ShipPostalCode: TDictionaryAttribute read FShipPostalCode;
property ShipCountry: TDictionaryAttribute read FShipCountry;
property EmployeeID: TDictionaryAssociation read FEmployeeID;
property ShipVia: TDictionaryAssociation read FShipVia;
end;
TShippersTableDictionary = class
private
FShipperID: TDictionaryAttribute;
FCompanyName: TDictionaryAttribute;
FPhone: TDictionaryAttribute;
public
constructor Create;
property ShipperID: TDictionaryAttribute read FShipperID;
property CompanyName: TDictionaryAttribute read FCompanyName;
property Phone: TDictionaryAttribute read FPhone;
end;
function Dic: TDicDictionary;
implementation
var
__Dic: TDicDictionary;
function Dic: TDicDictionary;
begin
if __Dic = nil then __Dic := TDicDictionary.Create;
result := __Dic
end;
{ TEmployees}
function TEmployees.GetReportsTo: TEmployees;
begin
result := FReportsTo.Value;
end;
procedure TEmployees.SetReportsTo(const Value: TEmployees);
begin
FReportsTo.Value := Value;
end;
{ TOrders}
function TOrders.GetShipVia: TShippers;
begin
result := FShipVia.Value;
end;
procedure TOrders.SetShipVia(const Value: TShippers);
begin
FShipVia.Value := Value;
end;
{ TDicDictionary}
destructor TDicDictionary.Destroy;
begin
inherited;
if FEmployees <> nil then FEmployees.Free;
if FOrders <> nil then FOrders.Free;
if FShippers <> nil then FShippers.Free;
end;
function TDicDictionary.GetEmployees: TEmployeesTableDictionary;
begin
if FEmployees = nil then FEmployees := TEmployeesTableDictionary.Create;
result := FEmployees;
end;
function TDicDictionary.GetOrders: TOrdersTableDictionary;
begin
if FOrders = nil then FOrders := TOrdersTableDictionary.Create;
result := FOrders;
end;
function TDicDictionary.GetShippers: TShippersTableDictionary;
begin
if FShippers = nil then FShippers := TShippersTableDictionary.Create;
result := FShippers;
end;
{ TEmployeesTableDictionary}
constructor TEmployeesTableDictionary.Create;
begin
FEmployeeID := TDictionaryAttribute.Create('EmployeeID');
FLastName := TDictionaryAttribute.Create('LastName');
FFirstName := TDictionaryAttribute.Create('FirstName');
FTitle := TDictionaryAttribute.Create('Title');
FTitleOfCourtesy := TDictionaryAttribute.Create('TitleOfCourtesy');
FBirthDate := TDictionaryAttribute.Create('BirthDate');
FHireDate := TDictionaryAttribute.Create('HireDate');
FAddress := TDictionaryAttribute.Create('Address');
FCity := TDictionaryAttribute.Create('City');
FRegion := TDictionaryAttribute.Create('Region');
FPostalCode := TDictionaryAttribute.Create('PostalCode');
FCountry := TDictionaryAttribute.Create('Country');
FHomePhone := TDictionaryAttribute.Create('HomePhone');
FExtension := TDictionaryAttribute.Create('Extension');
FPhoto := TDictionaryAttribute.Create('Photo');
FNotes := TDictionaryAttribute.Create('Notes');
FPhotoPath := TDictionaryAttribute.Create('PhotoPath');
FReportsTo := TDictionaryAssociation.Create('ReportsTo');
end;
{ TOrdersTableDictionary}
constructor TOrdersTableDictionary.Create;
begin
FOrderID := TDictionaryAttribute.Create('OrderID');
FOrderDate := TDictionaryAttribute.Create('OrderDate');
FRequiredDate := TDictionaryAttribute.Create('RequiredDate');
FShippedDate := TDictionaryAttribute.Create('ShippedDate');
FFreight := TDictionaryAttribute.Create('Freight');
FShipName := TDictionaryAttribute.Create('ShipName');
FShipAddress := TDictionaryAttribute.Create('ShipAddress');
FShipCity := TDictionaryAttribute.Create('ShipCity');
FShipRegion := TDictionaryAttribute.Create('ShipRegion');
FShipPostalCode := TDictionaryAttribute.Create('ShipPostalCode');
FShipCountry := TDictionaryAttribute.Create('ShipCountry');
FEmployeeID := TDictionaryAssociation.Create('EmployeeID');
FShipVia := TDictionaryAssociation.Create('ShipVia');
end;
{ TShippersTableDictionary}
constructor TShippersTableDictionary.Create;
begin
FShipperID := TDictionaryAttribute.Create('ShipperID');
FCompanyName := TDictionaryAttribute.Create('CompanyName');
FPhone := TDictionaryAttribute.Create('Phone');
end;
initialization
finalization
if __Dic <> nil then __Dic.Free
{$IFEND}
end.
Queries.pas
unit Queries;
interface
{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
implementation
{$ELSE}
uses Generics.Collections, DataModel,
Aurelius.Drivers.Interfaces, Aurelius.Drivers.dbGo,
Aurelius.Sql.MSSQL,
Aurelius.Engine.ObjectManager,
ADODb,
Aurelius.Criteria.Base, Aurelius.Criteria.Linq;
type
TAureliusQuery = class
private
ADOConnection: TADOConnection;
Connection: IDBConnection;
Manager: TObjectManager;
FEmployees: TObjectList<TEmployees>;
FOrders: TObjectList<TOrders>;
public
constructor Create(const DBFile: string);
destructor Destroy; override;
function GetEmployees: TList<TEmployees>;
function GetOrders: TList<TOrders>;
end;
implementation
uses SysUtils;
const BAseConnectionString = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Northwind.mdb;Persist Security Info=False;';
constructor TAureliusQuery.Create(const DBFile: string);
begin
ADOConnection := TADOConnection.Create(nil);
ADOConnection.ConnectionString := StringReplace(BaseConnectionString, 'Northwind.mdb', DbFile, []);
Connection := TDbGoConnectionAdapter.Create(ADOConnection, false);
Manager := TObjectManager.Create(Connection);
end;
destructor TAureliusQuery.Destroy;
begin
FOrders.Free;
FEmployees.Free;
Manager.Free;
Connection := nil;
ADOConnection.Free;
inherited;
end;
function TAureliusQuery.GetEmployees: TList<TEmployees>;
begin
FEmployees.Free;
FEmployees := Manager.Find<TEmployees>
.List;
Result := FEmployees;
end;
function TAureliusQuery.GetOrders: TList<TOrders>;
begin
FOrders.Free;
FOrders := Manager.Find<TOrders>
.List;
Result := FOrders;
end;
{$IFEND}
end.
UMainForm.pas
unit UMainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
FlexCel.VCLSupport, FlexCel.Core, FlexCel.XlsAdapter, FlexCel.Report, FlexCel.Render,
{$if CompilerVersion >= 23.0} System.UITypes, {$IFEND}
ShellApi,
Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
type
TMainForm = class(TForm)
btnCancel: TButton;
btnExportHTML: TButton;
SaveDialogXls: TSaveDialog;
Label1: TLabel;
btnExportPdf: TButton;
btnExportExcel: TButton;
SaveDialogPdf: TSaveDialog;
SaveDialogHtml: TSaveDialog;
procedure btnCancelClick(Sender: TObject);
procedure btnExportHTMLClick(Sender: TObject);
procedure btnExportExcelClick(Sender: TObject);
procedure btnExportPdfClick(Sender: TObject);
private
function RunReport(const Xls: TExcelFile; const SaveDialog: TSaveDialog): boolean;
function GetDataPath: string;
procedure ShowOpenResult(const SaveDialog: TSaveDialog);
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses IOUtils, DataModel, Queries, AureliusFlexCelSupport;
{$R *.dfm}
function DBFile: string;
begin
Result := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), '..\..\..\SharedData\Northwind.mdb');
end;
procedure TMainForm.btnCancelClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.btnExportExcelClick(Sender: TObject);
var
Xls: TExcelFile;
begin
Xls := TXlsFile.Create(true);
try
if not RunReport(Xls, SaveDialogXls) then exit;
Xls.Save(SaveDialogXls.FileName);
ShowOpenResult(SaveDialogXls);
finally
Xls.Free;
end;
end;
procedure TMainForm.btnExportHTMLClick(Sender: TObject);
var
Xls: TExcelFile;
Html: TFlexCelHtmlExport;
begin
Xls := TXlsFile.Create(true);
try
if not RunReport(Xls, SaveDialogHtml) then exit;
Html := TFlexCelHtmlExport.Create(Xls, true);
try
Html.HtmlVersion := THtmlVersion.Html_5;
Html.EmbedImages := true;
Html.Export(SaveDialogHtml.FileName, '');
ShowOpenResult(SaveDialogHtml);
finally
Html.Free;
end;
finally
Xls.Free;
end;
end;
procedure TMainForm.btnExportPdfClick(Sender: TObject);
var
Xls: TExcelFile;
Pdf: TFlexCelPdfExport;
begin
Xls := TXlsFile.Create(true);
try
if not RunReport(Xls, SaveDialogPdf) then exit;
Pdf := TFlexCelPdfExport.Create(Xls, true);
try
Pdf.Export(SaveDialogPdf.FileName);
ShowOpenResult(SaveDialogPdf);
finally
Pdf.Free;
end;
finally
Xls.Free;
end;
end;
function TMainForm.GetDataPath: string;
begin
Result := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), '..\..');
end;
{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
function TMainForm.RunReport(const Xls: TExcelFile; const SaveDialog: TSaveDialog): boolean;
begin
raise Exception.Create('Aurelius doesn''t support Delphi XE. To run this demo you need XE2 or newer');
end;
{$ELSE}
function TMainForm.RunReport(const Xls: TExcelFile; const SaveDialog: TSaveDialog): boolean;
var
Report: TFlexCelReport;
Query: TAureliusQuery;
begin
if not SaveDialog.Execute then exit(false);
Report := TFlexCelReport.Create(true);
try
SetupAurelius(Report);
Query := TAureliusQuery.Create(DBFile);
try
Report.AddTable<TEmployees>('Employees', Query.GetEmployees, TDisposeMode.DoNotDispose);
Report.AddTable<TOrders>('Orders', Query.GetOrders, TDisposeMode.DoNotDispose);
//Note that we've defined EmployeeID as an integer and not a TProxy, to avoid loading
//the full employee for every order. If we had left EmployeeID as a proxy we would have
//to define:
//Report.AddRelationship('Employees', 'Orders', 'EmployeeID', 'EmployeeID.EmployeeID');
//It would work, but it would be slower.
Report.AddRelationship('Employees', 'Orders', 'EmployeeID', 'EmployeeID');
Report.SetValue('Date', Now);
Xls.Open(TPath.Combine(GetDataPath, 'TMS Aurelius.template.xls'));
Report.Run(Xls);
finally
Query.Free;
end;
finally
Report.Free;
end;
Result := true;
end;
{$IFEND}
procedure TMainForm.ShowOpenResult(const SaveDialog: TSaveDialog);
begin
if MessageDlg('Do you want to open the generated file?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
ShellExecute(0, 'open', PCHAR(SaveDialog.FileName), nil, nil, SW_SHOWNORMAL);
end;
end;
end.