Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Funktion/Prozedur abbrechen? (https://www.delphipraxis.net/184768-funktion-prozedur-abbrechen.html)

JulianT 20. Apr 2015 08:14

Delphi-Version: XE4

Funktion/Prozedur abbrechen?
 
Hallo, hier mein Quellcode:

Code:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids, System.Math;

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Edit3: TEdit;
    Label3: TLabel;
    Berechne: TButton;
    Abbruch: TButton;
    procedure AbbruchClick(Sender: TObject);
    procedure BerechneClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  Minpunkte : array [1..6] of Real;

implementation

uses Unit2;
{$R *.dfm}

procedure TForm1.AbbruchClick(Sender: TObject);
begin
close;
end;

procedure TForm1.BerechneClick(Sender: TObject);
var Schulnote,maxpunktzahl,schülerpunkte: Integer;
begin
  begin
    if Edit2.Text > Edit1.Text then ShowMessage('Invalide Eingabe');
  end;
  begin
    if Edit2.Text <= Edit1.Text then
        maxpunktzahl:=StrToInt(Edit1.Text);
        schülerpunkte:=StrToInt(Edit2.Text);
        Minpunkte[1]:=(maxpunktzahl/100)*96;
        Minpunkte[2]:=(maxpunktzahl/100)*80;
        Minpunkte[3]:=(maxpunktzahl/100)*60;
        Minpunkte[4]:=(maxpunktzahl/100)*40;
        Minpunkte[5]:=(maxpunktzahl/100)*20;
        Minpunkte[6]:=(maxpunktzahl/100)*0;
        StringGrid1.Cells[1,1]:=FloatToStr(Ceil(Minpunkte[1]));
        StringGrid1.Cells[1,2]:=FloatToStr(Ceil(Minpunkte[2]));
        StringGrid1.Cells[1,3]:=FloatToStr(Ceil(Minpunkte[3]));
        StringGrid1.Cells[1,4]:=FloatToStr(Ceil(Minpunkte[4]));
        StringGrid1.Cells[1,5]:=FloatToStr(Ceil(Minpunkte[5]));
        StringGrid1.Cells[1,6]:=FloatToStr(Ceil(Minpunkte[6]));

  begin
    if (schülerpunkte >= Minpunkte[1]) then Edit3.Text:='1' else
    begin
      if (schülerpunkte >= Minpunkte[2]) then Edit3.Text:='2' else
      begin
        if (schülerpunkte >= Minpunkte[3]) then Edit3.Text:='3' else
        begin
          if (schülerpunkte >= Minpunkte[4]) then Edit3.Text:='4' else
          begin
            if (schülerpunkte >= Minpunkte[5]) then Edit3.Text:='5' else
            begin
              if (schülerpunkte >= Minpunkte[6]) then Edit3.Text:='6' else
            end;
          end;
        end;
      end;
    end;
  end;
end;
end;




procedure TForm1.FormCreate(Sender: TObject);
var i : integer;
begin
  StringGrid1.Cells[0,0]:='Note';
  StringGrid1.Cells[1,0]:='ab';
  for i :=1 to 7 do StringGrid1.Cells[0,i]:=IntToStr(i);
end;

end.
Ich habe die Anweisung jetzt so gelegt, dass wenn die zu erreichende Punktzahl kleiner als die des Schülers ist, ein ShowMessage ausgegeben wird.

Ich hätte es aber lieber so gemacht, dass am Anfang eine if then Schleife die Validität überprüft, mit "Abort" abbricht und dann ShowMessage ausgibt. Allerdings kam dann in diesem Fall die ShowMEssage und das Programm lief im Hintergrund weiter. Könnte mir da jemand mal einen Ansatz geben?

Danke im Vorraus!:thumb:

baumina 20. Apr 2015 08:26

AW: Funktion/Prozedur abbrechen?
 
Innerhalb einer procedure/function kannst du mit Exit rausspringen.

Beispiel:
Delphi-Quellcode:
procedure Irgendwas(Schalter : Boolean);
begin
  showmessage('innerhalb der procedure');
  if Schalter then
  begin
    showmessage('Schalter = true');
    Exit;
  end;
  showmessage('Schalter = false');
end;

himitsu 20. Apr 2015 08:38

AW: Funktion/Prozedur abbrechen?
 
Ich sehe hier nirgendwo einen Grund für ein Exit, oder Dergleichen.
Du mußt einfach nur die "ELSE" richtig verschachteln.

Oder siehe Sir Rufo und du wirfst eine Exception.

www.if-schleife.de




Wobei die Codeformatierung auch nicht sonderlich gut ist, vorallem bezüglich der Einrückung.

Hier sieht es so aus, als wenn du was Anderes machen willst, als im Code da steht.
Delphi-Quellcode:
begin
    if Edit2.Text <= Edit1.Text then
        maxpunktzahl:=StrToInt(Edit1.Text);
        schülerpunkte:=StrToInt(Edit2.Text);
        Minpunkte[1]:=(maxpunktzahl/100)*96;
Zitat:

Delphi-Quellcode:
Edit2.Text > Edit1.Text

In den Edits stehen doch bestimmt Zahlen drin?
Wenn ja, dann ist ein Stringvergleich nicht sonderlich gut, denn '20' ist größer als '100'.

Sir Rufo 20. Apr 2015 08:44

AW: Funktion/Prozedur abbrechen?
 
Wenn etwas überprüft werden soll, dann gibt es den Fall, dass es gültig oder ungültig ist.

Wenn etwas ungültig ist und man aber eben etwas gültiges benötigt um was auch immer zu verarbeiten/berechnen, dann habe ich eine Ausnahme-Situation. Das ist dann eine Exception.

Und genau so eine Exception wirft man in diesem Fall:
Delphi-Quellcode:
procedure TForm1.BerechneClick(Sender: TObject);
var
  Schulnote, maxpunktzahl, schülerpunkte: Integer;
begin
  maxpunktzahl := StrToInt( Edit1.Text );
  schülerpunkte := StrToInt( Edit2.Text );

  if schülerpunkte > maxpunktanzahl then
    raise EArgumentOutOfRangeException.CreateFmt( 
      'Die Schülerpunkte (%d) dürfen nicht größer als die maximalen Punkte (%d) sein!',
      [schülerpunkte,maxpunktzahl] );
 
  // ab hier die normalen Berechnungen
end;

bernau 20. Apr 2015 11:54

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1298467)
Wenn etwas überprüft werden soll, dann gibt es den Fall, dass es gültig oder ungültig ist.

Wenn etwas ungültig ist und man aber eben etwas gültiges benötigt um was auch immer zu verarbeiten/berechnen, dann habe ich eine Ausnahme-Situation. Das ist dann eine Exception.

Und genau so eine Exception wirft man in diesem Fall:

Ich habe mir in den letzten Jahren immer mehr angewöhnt mit Exceptions zu arbeiten und Exceptions sind eine tolle Sache. Ich würde aber in diesem Fall das etwas anders sehen.

Eine Exception werfe ich dann, wenn wenn etwas unerwartet ist. Etwas worauf ich nicht vorbereitet bin darauf zu reagieren.

In dem Beispiel kann man aber erwarten, daß auch mal etwas falsch oder in der falschen Reihenfolge eingegeben wird. Das würde ich klassisch mit einem If then else abfangen. Oder von mir aus auch mit einem If then Exit.

Je mehr ich vorher "bewusst" abfange, desto besser. Je weniger Exceptions ich auslöse, desto besser.

himitsu 20. Apr 2015 12:18

AW: Funktion/Prozedur abbrechen?
 
Man dürfte ja erwarten, daß der Benutzer richtige Werte eingibt, also ist das schon ein bissl unerwartet. :angle2:
Genauso wie die Exceptions bei StrToInt, wenn der Benutzer keine gültigen Zahlen eingibt.

Sir Rufo 20. Apr 2015 12:24

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von himitsu (Beitrag 1298502)
Man dürfte ja erwarten, daß der Benutzer richtige Werte eingibt, also ist das schon ein bissl unerwartet. :angle2:
Genauso wie die Exceptions bei StrToInt, wenn der Benutzer keine gültigen Zahlen eingibt.

Nicht nur unerwartet, sondern auch unlösbar.

In diesem konkreten Fall können eben drei Exceptions auftreten
  1. Delphi-Quellcode:
    Edit1.Text
    kann nicht in einen Integer umgewandelt werden
  2. Delphi-Quellcode:
    Edit2.Text
    kann nicht in einen Integer umgewandelt werden
  3. Die Punktzahl ist größer als die maximale Punktzahl
In jedem der drei Fälle immer das Gleiche passieren: Es erscheint ein Fenster mit dem Fehler.

Und womit knn ein Benutzer immer am einfachsten umgehen? Richtig, wenn immmer das Gleiche passiert.

Sir Rufo 20. Apr 2015 12:27

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von bernau (Beitrag 1298492)
Je mehr ich vorher "bewusst" abfange, desto besser. Je weniger Exceptions ich auslöse, desto besser.

Exceptions sollen so früh wie möglich und so oft wie nötig geworfen werden.

Bei der Implementierung einer Methode interessiert es mich nicht, was dort alles an Argumenten reinkommen könnte, sondern ausschliesslich, was diese Methode an Argumenten verarbeiten soll. Bei allen anderen Konstellationen werfe ich eine Exception. Dadurch bleibt die Methode klein und übersichtlich und macht genau das, was sie machen soll und verweigert ihren Dienst komplett, wenn ich dort Murks übergebe. So soll es sein.

bernau 20. Apr 2015 12:32

AW: Funktion/Prozedur abbrechen?
 
Ich habe nicht geschrieben, daß der Nutzer nicht informiert werden soll.

Aber mit der Art der Information bin ich flexibel, wenn ich nicht direkt mit einer Exception draufhaue. Ein Label mit dem Fehlerhinweis ist schon mal besser als ein Dialog. Grade bei zwei oder mehr eingabefeldern wird ja bei dem ersten Tastendruck schon eine Exception ausgelöst. weil das Zweite Edit noch leer ist und keinen definierten Wert hat. Dann doch lieber abfangen und in einem Label dezent darauf hinweisen.

Sir Rufo 20. Apr 2015 12:46

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von bernau (Beitrag 1298509)
Ich habe nicht geschrieben, daß der Nutzer nicht informiert werden soll.

Aber mit der Art der Information bin ich flexibel, wenn ich nicht direkt mit einer Exception draufhaue. Ein Label mit dem Fehlerhinweis ist schon mal besser als ein Dialog. Grade bei zwei oder mehr eingabefeldern wird ja bei dem ersten Tastendruck schon eine Exception ausgelöst. weil das Zweite Edit noch leer ist und keinen definierten Wert hat. Dann doch lieber abfangen und in einem Label dezent darauf hinweisen.

Hmmm, da die Methode
Delphi-Quellcode:
BerechnenClick
heißt, impliziere ich jetzt mal, dass diese aufgerufen wird, wenn auf einen Button geklickt wird, und zu diesem Zeitpunkt müssen alle Werte korrekt eingegeben sein, oder es ist eben Murks -> Exception

bernau 20. Apr 2015 13:00

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1298513)
Hmmm, da die Methode
Delphi-Quellcode:
BerechnenClick
heißt, impliziere ich jetzt mal, dass diese aufgerufen wird, wenn auf einen Button geklickt wird, und zu diesem Zeitpunkt müssen alle Werte korrekt eingegeben sein, oder es ist eben Murks -> Exception

:lol: Stimmt. Immer wieder erstaunlich, daß man seine eigene Vorgehensweise in einen fremden Code impliziert. Ich prüfe nämlich meist im OnChange eines Edits.

Dennoch denke ich, daß ich Benachrichtigungen an den Anwender nicht mit Exceptions durchführen sollte.

Popov 20. Apr 2015 13:01

AW: Funktion/Prozedur abbrechen?
 
Sowas ist nicht nötig:
Delphi-Quellcode:
  begin
    if Edit2.Text > Edit1.Text then ShowMessage('Invalide Eingabe');
  end;
Dafür braucht man keinen Begin End Block. Das reicht:
Delphi-Quellcode:
  if Edit2.Text > Edit1.Text then ShowMessage('Invalide Eingabe');
  //
Auch hier ist es unnötig:
Delphi-Quellcode:
  begin
    if Edit2.Text <= Edit1.Text then
        maxpunktzahl:=StrToInt(Edit1.Text);
        schülerpunkte:=StrToInt(Edit2.Text);
        Minpunkte[1]:=(maxpunktzahl/100)*96;
        Minpunkte[2]:=(maxpunktzahl/100)*80;
        Minpunkte[3]:=(maxpunktzahl/100)*60;
        Minpunkte[4]:=(maxpunktzahl/100)*40;
        Minpunkte[5]:=(maxpunktzahl/100)*20;
        Minpunkte[6]:=(maxpunktzahl/100)*0;
        StringGrid1.Cells[1,1]:=FloatToStr(Ceil(Minpunkte[1]));
        StringGrid1.Cells[1,2]:=FloatToStr(Ceil(Minpunkte[2]));
        StringGrid1.Cells[1,3]:=FloatToStr(Ceil(Minpunkte[3]));
        StringGrid1.Cells[1,4]:=FloatToStr(Ceil(Minpunkte[4]));
        StringGrid1.Cells[1,5]:=FloatToStr(Ceil(Minpunkte[5]));
        StringGrid1.Cells[1,6]:=FloatToStr(Ceil(Minpunkte[6]));

  begin
Geht man davon aus, dass die Einrückung nach
Delphi-Quellcode:
maxpunktzahl:=StrToInt(Edit1.Text);
nicht zufällig ist, sondern die anderen Zeilen mit bei der Abfrage einbezogen werden sollen, dann sollte das so aussehen:
Delphi-Quellcode:
    if Edit2.Text <= Edit1.Text then
      begin
        maxpunktzahl:=StrToInt(Edit1.Text);
        schülerpunkte:=StrToInt(Edit2.Text);
        Minpunkte[1]:=(maxpunktzahl/100)*96;
        Minpunkte[2]:=(maxpunktzahl/100)*80;
        Minpunkte[3]:=(maxpunktzahl/100)*60;
        Minpunkte[4]:=(maxpunktzahl/100)*40;
        Minpunkte[5]:=(maxpunktzahl/100)*20;
        Minpunkte[6]:=(maxpunktzahl/100)*0;
        StringGrid1.Cells[1,1]:=FloatToStr(Ceil(Minpunkte[1]));
        StringGrid1.Cells[1,2]:=FloatToStr(Ceil(Minpunkte[2]));
        StringGrid1.Cells[1,3]:=FloatToStr(Ceil(Minpunkte[3]));
        StringGrid1.Cells[1,4]:=FloatToStr(Ceil(Minpunkte[4]));
        StringGrid1.Cells[1,5]:=FloatToStr(Ceil(Minpunkte[5]));
        StringGrid1.Cells[1,6]:=FloatToStr(Ceil(Minpunkte[6]));
      end;
Und was die Abfragen angeht, aus dem:
Delphi-Quellcode:
    if (schülerpunkte >= Minpunkte[1]) then Edit3.Text:='1' else
    begin
      if (schülerpunkte >= Minpunkte[2]) then Edit3.Text:='2' else
      begin
        if (schülerpunkte >= Minpunkte[3]) then Edit3.Text:='3' else
        begin
          if (schülerpunkte >= Minpunkte[4]) then Edit3.Text:='4' else
          begin
            if (schülerpunkte >= Minpunkte[5]) then Edit3.Text:='5' else
            begin
              if (schülerpunkte >= Minpunkte[6]) then Edit3.Text:='6' else
            end;
          end;
        end;
      end;
    end;
mach das:
Delphi-Quellcode:
    if (schülerpunkte >= Minpunkte[1]) then Edit3.Text:='1'
    else if (schülerpunkte >= Minpunkte[2]) then Edit3.Text:='2'
    else if (schülerpunkte >= Minpunkte[3]) then Edit3.Text:='3'
    else if (schülerpunkte >= Minpunkte[4]) then Edit3.Text:='4'
    else if (schülerpunkte >= Minpunkte[5]) then Edit3.Text:='5'
    else if (schülerpunkte >= Minpunkte[6]) then Edit3.Text:='6'
    else
    begin

    end;

Sir Rufo 20. Apr 2015 13:34

AW: Funktion/Prozedur abbrechen?
 
Oder um es kurz und knapp zu schreiben:
Delphi-Quellcode:
unit Form.Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

const
  NotenSchlüssel: array [1 .. 5] of Single = ( 96.0, 80.0, 60.0, 40.0, 20.0 );

type
  TForm1 = class( TForm )
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click( Sender: TObject );
  private
    {Private-Deklarationen}
  public
    {Public-Deklarationen}
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function GetSchulnoteFromPercent( APercent: Single ): Integer;
begin
  // Guard
  if ( APercent < 0 ) or ( APercent > 100 ) then
    raise EArgumentOutOfRangeException.Create( 'Der Wert muss zwischen 0% und 100% liegen!' );
 
  Result := Low( NotenSchlüssel );
  repeat
    if APercent >= NotenSchlüssel[Result] then
      Exit;
    Inc( Result );
  until Result > High( NotenSchlüssel );
end;

function GetSchulnoteFromPunkte( AErreicht, AMax : Integer ) : Integer;
begin
  // Guard
  if AErreicht < 0 then
    raise EArgumentOutOfRangeException.Create( 'Die erreichte Punktzahl darf nicht kleiner 0 sein!' );
  if AMax <= 0 then
    raise EArgumentOutOfRangeException.Create( 'Die maximale Punktzahl darf nicht kleiner oder gleich 0 sein!' );
  if AErreicht > AMax then
    raise EArgumentOutOfRangeException.CreateFmt( 'Die erreichte Punktzahl (%d) kann nicht größer als die maximale Punktzahl (%d) sein!',
      [AErreicht, AMax] );

  Result := GetSchulnoteFromPercent( AErreicht / AMax * 100 );
end;

procedure TForm1.Button1Click( Sender: TObject );
var
  LErreichtePunktZahl, LMaxPunktZahl: Integer;
begin
  LErreichtePunktZahl := StrToInt( Edit1.Text );
  LMaxPunktZahl := StrToInt( Edit2.Text );

  Label1.Caption := IntToStr( GetSchulnoteFromPunkte( LErreichtePunktZahl, LMaxPunktZahl ) );
end;

end.

BadenPower 20. Apr 2015 14:18

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1298506)
In diesem konkreten Fall können eben drei Exceptions auftreten
  1. Delphi-Quellcode:
    Edit1.Text
    kann nicht in einen Integer umgewandelt werden
  2. Delphi-Quellcode:
    Edit2.Text
    kann nicht in einen Integer umgewandelt werden
  3. Die Punktzahl ist größer als die maximale Punktzahl

Es sind mehr als 3.

Du hast die Eingabe von negativen Werten vergessen.

Sir Rufo 20. Apr 2015 14:24

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von BadenPower (Beitrag 1298541)
Zitat:

Zitat von Sir Rufo (Beitrag 1298506)
In diesem konkreten Fall können eben drei Exceptions auftreten
  1. Delphi-Quellcode:
    Edit1.Text
    kann nicht in einen Integer umgewandelt werden
  2. Delphi-Quellcode:
    Edit2.Text
    kann nicht in einen Integer umgewandelt werden
  3. Die Punktzahl ist größer als die maximale Punktzahl

Es sind mehr als 3.

Du hast die Eingabe von negativen Werten vergessen.

Erwischt ... habe ich in dem kurz und knapp Beispiel noch ergänzt :)

himitsu 20. Apr 2015 14:40

AW: Funktion/Prozedur abbrechen?
 
Und extrem große Zahlen?
2 Milliarden Punkte ist schon ein bissl übertrieben.
Oder vielleicht war jemand soooo schlecht, daß es mehr Punktabzüge gab, als Punkte. :stupid:

Also Grundsätzlich kann man das alles als eine "Prüfung auf gültige Eingabewerte (Wertebereich)" ansehn.

BadenPower 20. Apr 2015 14:54

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von himitsu (Beitrag 1298545)
Also Grundsätzlich kann man das alles als eine "Prüfung auf gültige Eingabewerte (Wertebereich)" ansehn.

Und das sollte man meiner Meinung nach nicht unbedingt mit Exceptions machen.

Sind die Eingaben laut Eingabeprüfung fehlerhaft, dann sollte/muss man den Benutzer darauf aufmerksam machen. Dazu gibt man eine normale Meldung aus.

Die Benutzung von Exceptions hierfür kann auch ungewollt dazu führen, dass der Benutzer dies gar nicht mitbekommt, oder eine Fehlermeldung erhält, welche nicht dem Ursprünglichen Fehler entspricht.

Man stelle sich vor, dass in einer Eingabeprüfungs-Prozedur Exceptions geworfen werden, wie in Sir Rufo's Beispiel, und diese von einer anderen Prozedur aufgerufen wird in der irgendwo ein Try-Except-Block verwendet wird.

himitsu 20. Apr 2015 15:02

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von BadenPower (Beitrag 1298550)
in der irgendwo ein Try-Except-Block verwendet wird.

Dann ist der Try-Except-Block falsch, denn Fehlermeldungen dürfen/sollten niemals verfälscht oder "blind" abgefangen werden.

Wenn man GUI und Code trennt (MVVM oder Dregleichen), dann darf/sollte man im Code schon eine Exception werfen, wenn die Eingaben "ungültig" sind.

Was die GUI damit dann macht, ist ihr überlassen.
* durchreichen und als Fehlermeldung anzeigen
* oder abfangen und irgendwie anders auf das Problem hinweisen

Sir Rufo 20. Apr 2015 15:03

AW: Funktion/Prozedur abbrechen?
 
Zitat:

Zitat von BadenPower (Beitrag 1298550)
Man stelle sich vor, dass in einer Eingabeprüfungs-Prozedur Exceptions geworfen werden, wie in Sir Rufo's Beispiel, und diese von einer anderen Prozedur aufgerufen wird in der irgendwo ein Try-Except-Block verwendet wird.

Und, wo ist da das Problem? Wenn diese Prozedur diese Exceptions behandeln kann, dann ist doch alles so wie es sein soll, und wenn nicht, dann stellt sich doch die Frage, ob der Programmierer den Sinn und Zweck von Exceptions verstanden hat.

Denn sowas hier
Delphi-Quellcode:
try
  foo;
except
end;
ist voll daneben ... und das hier
Delphi-Quellcode:
try
  foo;
except
  on E: Exception do
    ShowMessage( E.ClassName + ': ' + E.Message );
end;
in einer GUI-Anwendung zeigt die Unfähigkeit/Unwissenheit des Programmierers


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:32 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz