Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi String Entscheidungen mit case treffen!!! (https://www.delphipraxis.net/66843-string-entscheidungen-mit-case-treffen.html)

Christian18 5. Apr 2006 09:35


String Entscheidungen mit case treffen!!!
 
Hallo,

ich habe mal eine kurze Frage zu case! Ich will Entscheidungen mit case treffen.

Hier mein Code den ich bis jetzt habe:
Delphi-Quellcode:
procedure TMain.Menu(Sender: TObject);
  var s : String;
begin
  s:=TLabel(Sender).Caption;
  case s of
    'Übersicht' : begin
                    f.Close;
                  end;
  end;
end;
Diese Procedure wird immer dann ausgelöst, wenn ein Label auf meiner Form geclickt wird. Der Compiler sagt mit aber einen Fehler mit den ich nichts anfangen kann.

Hier der Fehler:

[Fehler] UMain.pas(136): Ordinaltyp erforderlich
[Fehler] UMain.pas(137): Inkompatible Typen: 'Integer' und 'String'

Kann man mit case nur Integer Werte Entscheiden lassen???

Mit freundlichen Grüßen

Christian18

Kroko1999 5. Apr 2006 09:39

Re: String Entscheidungen mit case treffen!!!
 
Delphi-Quellcode:
procedure TMain.Menu(Sender: TObject);
  var s : String;
begin
  s:=TLabel(Sender).Caption;
  case Pos (S,'ÜbersichtTeil2Teil3') of
    1 : begin
                    f.Close;
                  end;
    10: ; //teil2
    16: ; //Teil3
  end;
end;
ich würde aber lieber dier Tag-Eigenschaft von TLabel mißbrauchen und die in der case-Anweisung nehmen!

Christian18 5. Apr 2006 09:49

Re: String Entscheidungen mit case treffen!!!
 
Hallo,

warum kann ich nicht auch einen String Entscheiden lassen??? Mit if funktioniert es doch auch!

ich soll diesen Entscheidungsbau, der eigentlich gar keiner ist, mit case machen. Kein Entscheidungsbau, weil die If Anweidungen nicht ineinander verzweigt sind. Wie kann ich das mit case machen???

Delphi-Quellcode:
procedure TMain.FormShow(Sender: TObject);
begin
  if TLabel(Sender).Caption = 'Übersicht' then
    begin
      ...
    end;

  if TLabel(Sender).Caption = 'Kunden' then
    begin
      ...
    end;

  if TLabel(Sender).Caption = 'Einstellungen' then
    begin
      ...
    end;

  if TLabel(Sender).Caption = 'Impressum' then
    begin
      ...
    end;

  ...

end;
Mein vorschlag wäre dieser gewesen, der funktioniert aber leider nicht!!!

Delphi-Quellcode:
procedure TMain.FormShow(Sender: TObject);
begin

  case TLabel(Sender).Caption of
    'Übersicht' : begin
                    ...
                  end;

    'Kunden' : begin
                 ...
               end;

    'Übersicht' : begin
                    ...
                  end;

    'Einstellungen' : begin
                        ...
                      end;

    'Impressum' : begin
                    ...
                  end;
  end;

Kroko1999 5. Apr 2006 09:51

Re: String Entscheidungen mit case treffen!!!
 
Delphi erlaubt nun mal in der Case-Anweisung keine Strings sondern nur ordinale Typen! :gruebel:

marabu 5. Apr 2006 10:00

Re: String Entscheidungen mit case treffen!!!
 
Hallo.

Den "Ordinalen Typen" ist ein Kapitel in der Delphi Online-Hilfe gewidmet. Das sind alle Typen die sich mit der Funktion Ord() vertragen. Ein String ist intern ein Record und deshalb kein ordinaler Typ. Man kann zwar für jeden String in einer abzählbaren Menge eine Ordnungsnummer angeben, aber bei einem offenen Wertebereich eben nicht mehr.

Grüße vom marabu

Christian18 5. Apr 2006 10:08

Re: String Entscheidungen mit case treffen!!!
 
Hallo,

und was wäre jetzt die eleganteste Variante dieses Problem zu lösen??? Über eine Verschachtelte If Anweisung???

MFG Christian18

r2c2 5. Apr 2006 10:43

Re: String Entscheidungen mit case treffen!!!
 
Zitat:

Zitat von Christian18
und was wäre jetzt die eleganteste Variante dieses Problem zu lösen??? Über eine Verschachtelte If Anweisung???

Jo:
Delphi-Quellcode:
  if s = 'string1' then
  begin

  end
  else if s = 'string2' then
  begin

  end
  else if ...
mfg

Christian

himitsu 5. Apr 2006 10:59

Re: String Entscheidungen mit case treffen!!!
 
Das Eleganteste hat Kroko1999 schon gesagt ... einfach TLabel(Sender).Tag nutzen
Delphi-Quellcode:
Case TLabel(Sender).Tag of
  0{Übersicht}: Begin
    ...
  End;
  1{Kunden}: Begin
    ...
  End;
  2{Einstellungen}: Begin
    ...
  End;
  3{Impressum}: Begin
    ...
  End;
  ...
End;
Dazu muß natürlich noch den TAG's der Labels der entsprechende Wert zugewiesen werden ;)



Ein Pointer sollte ja auch einen ordinalen Typ darstellen, demnach müßte es doch auch möglich sein diese zu vergleichen?
Delphi-Quellcode:
Case TLabel(Sender) of
  Label1{Übersicht}: Begin
    ...
  End;
  Label2{Kunden}: Begin
    ...
  End;
  ...
End;

Case TLabel(Sender) of
  UebersichtLabel: Begin
    ...
  End;
  KundenLabel: Begin
    ...
  End;
  ...
End;

//halt die definierten Namen der Labels

Hawkeye219 5. Apr 2006 11:01

Re: String Entscheidungen mit case treffen!!!
 
Die Unit StrUtils enthält die Funktion AnsiIndexText, mit der das Problem auch lösbar ist:

Delphi-Quellcode:
case AnsiIndexText(Caption, ['Übersicht', 'Kunden', 'Einstellungen', 'Impressum']) of
  0 :; // Übersicht
  1 :; // Kunden
  :
end;
Lesbarer dürfte allerdings das Mehrfach-IF sein...

Gruß Hawkeye

alzaimar 5. Apr 2006 11:18

Re: String Entscheidungen mit case treffen!!!
 
Zuallererst würde ich den Label1, Label2 etc. Komponenten etwas bessere Namen geben, zumal Du von Übersichtlichkeit und Lesbarkeit redest.
Anschließend würde ich statt der '0','1' etc. im CASE Konstanten definieren:
Delphi-Quellcode:
Const
  liUebersicht = 0; // li = Label Index
  liKunden = 1;
...
Dann würde ich im FormCreate-Ereignis den Tag-Eigenschaften der Label-Komponenten diese Konstanten zuweisen:
Delphi-Quellcode:
Procedure TMyForm.FormCreate (Sender : TObject);
Begin
  lbKunden.Tag := liKunden;
  lbUebersicht.Tag := liUebersicht;
...
End;
Und dann über die Tag-Eigenschaft der Komponente das Case realisieren.
Delphi-Quellcode:
Case TLabel(Sender).Tag Of
  liKunden : Begin
    ...
    End;
  liUebersicht : Begin
    ...
    End;
End;
Übersichtlicher (und vor allen Dingen: Nachvollziehbarer) würde ich das nicht hinbekommen.
Der Nachteil der Stringvergleiche ist ganz klar, das dein Programm wegschmiert, sobald jemandem die Beschriftung nicht passt und er die SW patcht. So, wie das lokalisierende Programme machen. Die Captions stehen in den Resourcen, werden also übersetzt. Die Stringkonstanten stehen aber im Code und werden deshalb ignoriert.

marabu 5. Apr 2006 11:42

Re: String Entscheidungen mit case treffen!!!
 
Ob sich der Vorteil eines shared event handlers nicht allmählich in einen Nachteil verkehrt? Ich glaube, ich würde den Labels eher einen eigenen event handler spendieren, bevor ich in der Richtung weiter überlege, die sich im thread abzeichnet.

marabu

alzaimar 5. Apr 2006 11:47

Re: String Entscheidungen mit case treffen!!!
 
Stimmt eigentlich, lieber 10 eigene Events, als nur einen mit einer Case-Krücke.

Fuchtel 5. Apr 2006 11:51

Re: String Entscheidungen mit case treffen!!!
 
Hallo,

wenn wan unbedingt mit Case arbeiten will:


Delphi-Quellcode:
  If length(TLabel(Sender).Caption) > 0 then
    case TLabel(Sender).Caption[1] of
      'Ü' : begin                //Übersicht'
               ...
            end;

      'K' : begin   // Kunden
              ...
            end;

      'E' : begin   // Einstellungen
              ...
            end;

      'I' : begin // Impressum
              ...
            end;
    end;
Gruß Fuchtel

alzaimar 5. Apr 2006 11:56

Re: String Entscheidungen mit case treffen!!!
 
Das meinst du jetzt aber nicht ernst, oder? :zwinker:

Hawkeye219 5. Apr 2006 12:12

Re: String Entscheidungen mit case treffen!!!
 
@alzaimar, @marabu :thumb:

Der Aufwand, den man treiben muß, ist schon recht groß und dabei bleibt dann die Übersichtlichkeit auf der Strecke. Die vorgestellten Lösungen sind also nur für den Fall gedacht, daß man (aus welchen Gründen auch immer) mit einem CASE arbeiten möchte. Die Tag-Lösung ist eigentlich auch nur eine halbe, weil der Objektinspektor mit den mühsam definierten Konstanten leider nichts anfangen kann. Fehler sind da bei der nächsten Änderung/Erweiterung vorprogrammiert. Die Lösung von himitsu funktioniert (zumindest mit Delphi 6) nicht. Ein Pointer ist wohl doch nicht so aufzählbar...

Die sauberste Lösung scheint also die mit einem Event pro Control zu sein.

Gruß Hawkeye

Luckie 5. Apr 2006 12:17

Re: String Entscheidungen mit case treffen!!!
 
Man könnte auch einfach mal in der CodeLib suchen: http://www.delphipraxis.net/internal...highlight=case

alzaimar 5. Apr 2006 12:20

Re: String Entscheidungen mit case treffen!!!
 
[quote="Hawkeye219"]@alzaimar, @marabu :thumb:
Die Tag-Lösung ist eigentlich auch nur eine halbe, weil der Objektinspektor mit den mühsam definierten Konstanten leider nichts anfangen kann. Fehler sind da bei der nächsten Änderung/Erweiterung vorprogrammiert.
{/quote]
Nicht, wenn man es so macht, wie ich vorgeschlagen habe. Das rentiert sich aber nur, wenn man wirklich einen shared event handler benötigt.
Zitat:

Zitat von Hawkeye219
Die sauberste Lösung scheint also die mit einem Event pro Control zu sein.

Yep, seh' ich auch so.

(Schreibt man Yep onder Jep :zwinker: ?)

marabu 5. Apr 2006 12:23

Re: String Entscheidungen mit case treffen!!!
 
Yep oder Jep?

Ich werde diese Frage auf dem nächsten Familientreffen erörtern.

marabu

alzaimar 5. Apr 2006 12:32

Re: String Entscheidungen mit case treffen!!!
 
@marabu: Der war gut! :thumb:

Hawkeye219 5. Apr 2006 12:50

Re: String Entscheidungen mit case treffen!!!
 
Zitat:

Zitat von alzaimar
Nicht, wenn man es so macht, wie ich vorgeschlagen habe.

Stimmt, ich hatte das FormCreate übersehen. :oops:

Hansa 5. Apr 2006 15:38

Re: String Entscheidungen mit case treffen!!!
 
Zitat:

Zitat von Hawkeye219
...Die Tag-Lösung ist eigentlich auch nur eine halbe, weil der Objektinspektor mit den mühsam definierten Konstanten leider nichts anfangen kann. Fehler sind da bei der nächsten Änderung/Erweiterung vorprogrammiert...

So sehe ich das auch. Ein Dritter würde zuletzt drauf kommen, daß die Tags zweckentfremdet werden, bzw. plötzlich eine reale Bedeutung erhalten. :zwinker: Nur, wer hindert einen daran, sich selber was quasi identisches zu bauen ? Eine published property "Menuepunkt", die per OI einzustellen ist ? Abgeleitet von geeignetem Control. Das sind im Prinzip max. 5 Zeilen. Dieser könnte man auch gleich mit SetSubcomponent (also das + im OI) eine Caption verpassen und fertig.

alzaimar 5. Apr 2006 16:29

Re: String Entscheidungen mit case treffen!!!
 
Leicht OT: Wozu sind die Tags denn dann gut, wenn nicht für sowas?

Hansa 5. Apr 2006 16:52

Re: String Entscheidungen mit case treffen!!!
 
Die sind für den absoluten Ausnahmefall da, aber IMHO nicht dafür, einen zu hindern so was einfaches selber einzubauen :

Delphi-Quellcode:
  TMenuelbl = class(TLabel)
    procedure SetMenueEbene (Value: integer);
  private
    FMenueEbene : integer;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property MenueEbene : integer read FMenueEbene write SetMenueEbene;
  end;

constructor Tlbl.Create(AOwner: TComponent);
begin
  inherited;
  MenueEbene := 0; // Standard
end;

procedure TlblRealEdit.SetMenueEbene (Value: integer);
begin
  FMenueEbene := Value;
end;
wie gesagt, wenn ich dem Teil noch eine eigene Caption/SubComponent verpassen will, dann wirds eben mehr (aber nicht viel). Vorteil : habe nicht irgendein "Tag", sondern das was nötig ist.

alzaimar 5. Apr 2006 16:57

Re: String Entscheidungen mit case treffen!!!
 
Nee, nee der Vorteil is klar. Wie viele Komponenten hast Du denn dann so installiert?

Hansa 5. Apr 2006 17:17

Re: String Entscheidungen mit case treffen!!!
 
Ein paar, verteilt auf 2 Registerreiter "Eigene" und "DB-Eigene". Das lohnt sich auf jeden Fall zu wissen wie das geht !

alzaimar 5. Apr 2006 17:22

Re: String Entscheidungen mit case treffen!!!
 
Na ja, dafür bastel ich mir aber keine Komponente. Aber Jedem das Seine.

marabu 5. Apr 2006 18:16

Re: String Entscheidungen mit case treffen!!!
 
Hi folks,

nicht immer ist eine spezielle Komponente eine gute Lösung. Im vorliegenden Fall wäre es nach meiner Erfahrung der falsche Weg, weil erstens Object Pascal eine Hybridsprache ist und zweitens die von Hansa gezeigte Komponente einen clone des tag properties schafft nur um jenes ungenutzt zu lassen.

Eine Komponente muss universell einsetzbar sein und eine deutliche Bereicherung im Vergleich zu ihrem Vorgänger darstellen. Diesen Leitgedanken drückt Steve Teixeira in seinem Kommentar zum tag property so aus:

Zitat:

Zitat von Steve Teixeira and Xavier Pacheco
This property should not be used by component writers - it is intended to be used by application writers. Because this value is an integer type, pointers to data structures - or even object instances - can be referred to by this property.

Der Entwurf eines Application Framework ist gut gelungen, wenn ich für Kleinigkeiten keine eigene Komponente entwickeln muss - egal wie geringfügig der Aufwand dafür sein mag.

marabu

Hansa 6. Apr 2006 00:31

Re: String Entscheidungen mit case treffen!!!
 
Das zitierte Zitat :mrgreen: paßt aber nicht so recht. Wie der richtig schreibt, sollte man das Tag eben nicht für eigene Zwecke (hier allerdings Komponente) mißbrauchen. Verwendet man es im nur Programm, dann steht es eben später und zwar auch in abgeleiteten Typen nicht mehr so zur Verfügung. Darauf wollte ich hinweisen. Insbesondere darauf, wie einfach man so was selber machen kann und aus allem Ungemach raus ist. Alllerdings ist das konkrete Thema tatsächlich recht einfach mit der Verwendung eines Tags zu machen. Das gibt nicht so viel her. Der Mini-Source enthält auch noch einen Fehler. Da steht irgendwo mittendrin noch was von TRealEdit. Und von da ist das als kleines Beispiel auch abgekupfert und vieles gelöscht worden, sonst wärs unverständlicher. Im Original habe ich keine Property MenueEbene, sondern eine die heißt Nachkommastellen. Die können im OI leicht eingestellt werden. Dies bedeutet, den Constructor dementsprechend anzupassen (auf Standardwert : 2), also eine einfache Zuweisung. In einem Abwasch kann ich nun gleich taRightJustify (wegen Zahl) beim Create setzen.

Also weiß der OI bereits ohne was ändern zu müssen, was in 99% der Fälle richtig ist und somit ist es auch überflüssig das jedesmal zu ändern. Und weils so schön ist wird natürlich noch das OnKeyPress gleich mitbehandelt. Das läßt eben nur zugelassene Zeichen zu. Und da gibts noch einiges mehr. Ist alles mit ein paar Zeilen direkt in Delphi einzubauen und keineswegs Hexerei ! Das Problem ist eben nur die Scheu vor "eigener Komponente". Siehe die geposteten 2 Zeilen (ohne Deklarationen). Schon habe ich meine MenueEbene im OI und kann die Tags in Ruhe lassen. Oft wird auch Komponente mit OI gleichgesetzt, weil viele nicht wissen was published eigentlich aussagt. Tja, wurde wieder mehr als geplant, aber ich gehe davon aus, daß weit über 90% der User nicht in der Lage wären eine einzige Eigenschaft richtig in den OI zu bekommen.

alzaimar 6. Apr 2006 07:59

Re: String Entscheidungen mit case treffen!!!
 
Ich finde, ein Thema, für einen eigenen Thread:
"Komponenten und anderes Geflügel"
wegen der Compon-Ente :lol: .

Hansa 6. Apr 2006 09:40

Re: String Entscheidungen mit case treffen!!!
 
Stimmt ! Aber das ist hoffnungslos. Hier herrscht ohne Widerstand C+P. :stupid: Das Geflügel wird dabei gnadenlos abgeschossen. Besonders gerne mit Kanonen oder Atombomben, also WinApi, Operatorüberladung, DLLS und sonstigen Massenvernichtungswaffen. Sogar goto habe ich schon gesehen. :lol:

DevilsCamp 6. Apr 2006 10:33

Re: String Entscheidungen mit case treffen!!!
 
Zitat:

Zitat von Hansa
Das zitierte Zitat :mrgreen: paßt aber nicht so recht. Wie der richtig schreibt, sollte man das Tag eben nicht für eigene Zwecke (hier allerdings Komponente) mißbrauchen. Verwendet man es im nur Programm, dann steht es eben später und zwar auch in abgeleiteten Typen nicht mehr so zur Verfügung.

Da verweise ich aber mal auf die OH. Da steht zu Tag eindeutig dabei, dass die Property zur FREIEN Verfügung für den Entwickler steht.

D.h. WAS ich damit mache ist also egal. Und gerade für sowas könnte man das Tag doch benutzen, oder?
Es sollte aber vielleicht in einem leicht sichtbaren Kommentar drauf hingewiesen werden, wozu der Tag benutzt wird. Dann sollte keiner mehr im dunklen stehen, wofür die Tags nun benutzt werden.

Sharky 6. Apr 2006 11:34

Re: String Entscheidungen mit case treffen!!!
 
Zitat:

Zitat von Hansa
Stimmt ! Aber das ist hoffnungslos. ...

Hai Hansa,

glaubst Du denn wirklich DU bist der einige der eine eigene Klasse ableiten kann :roll:
Ich habe da eher das gefühl das Du "denkst" für jede kleinigkeit eine eigene Klasse/Komponente erstellen zu müssen um dann jedem Sagen zu können das Du das super gut kannst.

Gerade in diesem Fall wurde doch der beste Weg gezeigt. -> Nämlich für jedes der Label ein eigenes OnClick zu verwenden. Und wenn man es doch nicht möchte dann ist die Verwendung des TAG-Poroperty der richtige Weg. Ob Du es nun einsiehst oder nicht.

Hansa 6. Apr 2006 12:47

Re: String Entscheidungen mit case treffen!!!
 
Sharky, hier ist eine kleine Diskussion mit mehreren Vorschlägen gelaufen. Unter anderem von mir ein Beispiel, wie man das auch innerhalb einer Komponente machen KANN. Wobei ich auch gleich dazu gesagt habe, daß für DIESEN anfangs erwähnten Zweck, das wohl nicht zwingend nötig ist. Man es aber auch nicht ausschließen soll, eine Komponente mal selber für eigene Zwecke zu machen. Denn die Tendenz geht IMHO (zumindest hier) eher in die Richtung, möglichst irgendwie um die Komponenten drumrumzukommen und diese Fähigkeit von Delphi nicht nutzen zu MÜSSEN. Siehe hier :
Zitat:

Zitat von Sharky
...da eher das gefühl das Du "denkst" für jede kleinigkeit eine eigene Klasse/Komponente erstellen zu müssen...

Da dies noch dazu von Moderatorenseite kam, wird das auch eher helfen diese Tendenz zu verstärken als umgekehrt. Ich finde es jedenfalls bedauerlich, daß durch nachträgliches reininterpretieren mittels Vermutungen in einen so gut wie beendeten Thread, zumindest bei Neulingen wohl der Eindruck entsteht, es sei schon besser, den Bogen den sie sowieso um die Komponenten machen, größer anstatt kleiner zu machen. Raus kommt im Endeffekt dann das : "Wer kennt eine Komponente, die....".

alzaimar 6. Apr 2006 12:59

Re: String Entscheidungen mit case treffen!!!
 
Hi Sharky, hi Hansa. Ich finde wirklich, es gehört in einen eigenen Thread. Es hat nun mal viel mit 'Programmierphilosophie' zu tun, ob und wie ich Komponenten einsetze. Wieso muss ich die dann eigentlich installieren? Kann im Projekt nicht (optional) vermerkt werden, welche zusätzlichen Komponenten das Projekt verwendet und die dann beim Laden des Projektes temporär installiert werden? Usw.

Aber "Sharky und Hansa" ist eine never ending Story, wat misch ick mia da überhaupt ein. :zwinker:

merlin17 6. Apr 2006 13:21

Re: String Entscheidungen mit case treffen!!!
 
evtl. so etwas (um wieder zum Thema zurück zu kommen...);
ist sicherlich noch ausbaufähig bzw. optimierbar <g>


Delphi-Quellcode:
 case StringToCaseSelect(Section.ChildNodes['TYP'].Text,
        ['INTEGER', 'BETRAG', 'STRING']) of
        0: CDS1.FieldDefs.Add(Section.ChildNodes['NAME'].Text,
            ftInteger, 0, false);
        1: CDS1.FieldDefs.Add(Section.ChildNodes['NAME'].Text,
            ftFloat, 0, false);
        2: CDS1.FieldDefs.Add(Section.ChildNodes['NAME'].Text,
            ftString, 50, false);
      end;



function TForm1.StringToCaseSelect
  (Selector: string;
  CaseList: array of string): Integer;
var
  cnt: integer;
begin
  Result := -1;
  for cnt := 0 to Length(CaseList) - 1 do
  begin
    if CompareText(Selector, CaseList[cnt]) = 0 then
    begin
      Result := cnt;
      Break;
    end;
  end;
end;

:-) thomas

Hawkeye219 6. Apr 2006 13:33

Re: String Entscheidungen mit case treffen!!!
 
@merlin17
Schaue Dir mal Beitrag #9 in diesem Thread an...

Gruß Hawkeye

merlin17 6. Apr 2006 13:49

Re: String Entscheidungen mit case treffen!!!
 
thx,
man sollte doch wieder öfters in diese mächtigen Utils schauen :oops:


:-) thomas

Phoenix 6. Apr 2006 16:41

Re: String Entscheidungen mit case treffen!!!
 
Hrm...
noch ne andere Version die mir gerade einfällt, die aber nicht wirklich zu den saubersten gehört:

1.) Es werden integer-Konstanten für die verschiedenen möglichen Strings definiert.
2.) Eine Funktion gibt diese Konstanten für den String zurück.
3.) Im Case wird nach der Konstante unterschieden.

Code:
const
   TEXT1 = 1;
   TEXT2 = 2;
   TEXT3 = 3;

...

function getIndex(string text): integer;
begin
   if text = 'Text1' then result := TEXT1;
   if text = 'Text2' then result := TEXT2;
   ...
end;

...

   case getIndex(theText) of
      TEXT1: begin
                ...
             end;
      TEXT2: begin
                ...
             end;
      ...
   end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:23 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