Listboxen stellen auswählbare Elemente zur Verfügung, z.B. eine Zusammenstellung aller Fahrernamen, Fahrzeuge, etc.
Die Einträge in der Liste lassen sich per Mausklick auswählen. Anschließend ist es möglich, auf den Mausklick zu reagieren und z.B. das ausgewählte Element in einer Meldung anzuzeigen oder anderweitig zu verarbeiten.
Ist die MultiSelect-Eigenschaft aktiviert, sind mehr als ein Eintrag auswählbar. Für zusammenhängende Bereiche hält man nach dem Klick mit der Maus auf das erste Element die <Umschalt>-Taste gedrückt und klickt auf den letzten zu verarbeitenden Eintrag. Alle Elemente zwischen diesen beiden Listenpunkten sind dann markiert.
Die Auswahl nicht direkt benachbarter Einträge erfolgt per Mausklick auf jedes einzelne Element mit gedrückter <Strg>-Taste.
Eine ausführliche Dokumentation zur ListBox gibt es im Internet, z.B. hier
Das folgende Beispiel zeigt den per Doppelklick ausgewählten Listeneintrag in einer Meldung an. Es ist ein Standardcode aus einem Tutorial, weil ich dieses Steuerelement bisher nicht verwendet habe.
procedure TForm1.ListBox1Click(Sender: TObject); var listBox : TListBox; index : Integer; begin // Variable mit dem richtigen Objekttyp initialisieren listBox := TListBox(Sender); // Indexnummer in der Liste speichern index := listBox.ItemIndex; // Den passenden Wert zur Indexnummer in Meldung ausgeben ShowMessage(listBox.Items[index]); end;
Bei der ComboBox handelt es sich um eine platzsparende Variante der ListBox. Die Programmierung erfolgt auf gleiche Weise. Allerdings belegt die ComboBox nur eine Zeile.
Die hinterlegte Liste mit Elementen erscheint erst per Mausklick auf das meist rechts positionierte Steuerelement (Button) mit Pfeil nach unten. Sie klappt dann nach unten auf.
Die Auswahl eines Elements erfolgt per Mausklick. In der Regel führt diese Aktion dazu, dass die Liste wieder eingeklapt wird.
Eine brauchbare Dokumentation gibt es online, z.B. hier
Das folgende Beispiel erzeugt ein Formular, mit dem sich für einzelne Fahrer Strafpunkte mit Auswahl eines Vergehens vom Rennleiter vergeben lassen. Die Eingabe eigener Vergehen und Strafpunkte ist möglich.
Das Formular wird dynamisch anhand der Anzahl Teilnehmer aus dem StartCenter generiert. Gibt es drei Fahrer, hat das Formular Felder für die Regler-IDs 1-3, fahren 6 Piloten um den Sieg, gibt es für jeden Starter eine eigene Zeile. Das Formular wird entsprechend größer.
Wer sich jetzt vorstellen kann, wie dieses Formular aussieht, wenn es mit ListBoxen designt ist, hat eine ungefähre Idee, wie sich die Verwendung der ComboBox positiv auf die Größe auswirkt.
var form : TForm; b : TButton; l1 : TLabel; l2 : TLabel; l3 : TLabel; e : array[1..6] of TEdit; c : array[1..6] of TComboBox; m : array[1..6] of TMemo; fine : Boolean; // ======================================================================= // Eingabeformular für Strafpunkte, die vom Rennleiter verhängt werden // Das ist z.B. der Fall, wenn der Fahrer seinen Schalter nicht drückt. procedure StrafpunkteVonRennleiter(); var i : Integer; begin // Formular definieren form := TForm.Create(nil); form.Caption := 'Chaos Rennleiter'; form.BorderStyle := bsDialog; form.SetBounds(200, 100, 570, 410); // Label für Spaltenköpfe definieren l1 := TLabel.Create(form); l1.Name := 'Label1'; l1.Parent := f; l1.Caption := 'Slot/ID:'; l1.Font.Name := 'Impact'; l1.Font.Size := -16; l1.SetBounds(20,20,60,30); l2 := TLabel.Create(form); l2.Name := 'Label2'; l2.Parent := f; l2.Caption := 'Strafkatalog:'; l2.Font.Name := 'Impact'; l2.Font.Size := -16; l2.SetBounds(100,20,240,30); l3 := TLabel.Create(form); l3.Name := 'Label3'; l3.Parent := f; l3.Caption := 'Punkte:'; l3.Font.Name := 'Impact'; l3.Font.Size := -16; l3.SetBounds(480,20,140,30); for i := 1 to cpCountOfSlots do begin // Eingabefeld Slot/ID m[i] := TMemo.Create(form); with m[i] do begin Name := 'Memo'+IntToStr(i); Parent := f; Alignment := taCenter; ReadOnly := true; WantTabs := false; WantReturns := false; WordWrap := false; Font.Name := 'Impact'; Font.Size := -20; SetBounds(20,60+((i-1)*40),55,35); end; // Auswahlliste Strafkatalog c[i] := TComboBox.Create(form); with c[i] do begin // Feld Merkmale definieren Name := 'ComboBox'+IntToStr(i); Parent := f; Font.Name := 'Impact'; Font.Size := -20; SetBounds(100,60+((i-1)*40),360,35); AutoComplete := true; end; // Eingabefeld Strafpunkte e[i] := TEdit.Create(form); with e[i] do begin Name := 'Textfeld'+IntToStr(i); Parent := f; Text := ''; Font.Name := 'Impact'; Font.Size := -20; SetBounds(480,60+((i-1)*40),60,35); end; end; // Button definieren b := TButton.Create(form); b.Name := 'Button1'; b.Parent := f; b.Font.Name := 'Impact'; b.Font.Size := -24; b.SetBounds(180, 320, 200, 40); b.Caption := 'Ab dafür ...'; b.OnClick := @ButtonClick; // Formular geöffnet lassen, bis fine true wird // Das passiert nach dem Klick auf den Button und // ist im Handler @buttonClick definiert fine := False; // Formular anzeigen cpShow(form); // ComboBox mit Einträgen füllen for i := 1 to cpCountOfSlots do begin with m[i] do begin Lines.Add(IntToStr(i)); end; with c[i] do begin // Einträge einfügen Items.Add(''); Items.Add('Causing a collision'); Items.Add('Pushing another car off the track'); Items.Add('Unsafe Release'); Items.Add('Ignoring Blue Flag'); Items.Add('Jump Start'); Items.Add('Other - Text eingeben'); ItemIndex := 0; // Es wird etwas ausgewählt OnChange := @ComboBoxOnChange; end; end; // END for - i // Solange fine den Wert false hat, immer wieder kurz warten while not fine do begin cpSleep(50); // Kurz bevor der RBS geschlossen wird (Event BeforeClose), wird die // Variable CloseForm gesetzt und das Formular geschlossen, falls es // zu diesem Zeitpunkt noch geöffnet ist if cpGetIntegerVar('CloseForm') = 99 then begin fine := True; end; end; // Formular schließen cpFormFree(form); end; // ======================================================================= // Handler, wenn ComboBox Inhalt ausgewählt wurde procedure ComboBoxOnChange( Sender: TComboBox ); var i : Integer; begin // für alle 6 Vergehen Strafpunkte definieren for i := 1 to 6 do begin case c[i].ItemIndex of -1,0 : e[i].Text := ''; // Blank 1 : e[i].Text := IntToStr(7); // causing a collision 2 : e[i].Text := IntToStr(10); // pushing another car off track 3 : e[i].Text := IntToStr(6); // Unsafe Release 4 : e[i].Text := IntToStr(4); // Ignoring Blue Flag 5 : e[i].Text := IntToStr(2); // Jump Start 6 : e[i].Text := IntToStr(DefaultStrafpunkteRL); // Other end; // END case c[i] end; // END for - i end; // ======================================================================= // Handler, wenn Button geklickt wurde procedure ButtonClick( Sender: TButton ); var id : Integer; i : Integer; PunkteFuerSlot : Integer; letzteZeile : Integer; fs : TFileStream; begin // Für alle IDs durchlaufen for i := 1 to cpCountOfSlots do begin m[i].SetFocus; letzteZeile := m[i].Lines.Count-1; PunkteFuerSlot := StrToInt( copy(m[i].Lines[letzteZeile], 1, 1) ); // Strafpunkte verteilen // Fall 1: Kein Vergehen ausgewählt, keine Punkte eingetragen. // Vorgang protokolliert. Keine Auswirkungen für ID if (c[i].Text = '') AND (e[i].Text = '') then begin cpSetStringVar('sVergehen', ''); cpSetIntegerVar('StrafPunkteSpur', 0); Continue; end // Fall 2: Vergehen ausgewählt, keine Punkte eingetragen. // DefaultStrafpunkteRennleiter gehen auf's Konto else if (c[i].Text <> '') AND (e[i].Text = '') then begin cpSetStringVar('sVergehen', c[i].Text); cpSetIntegerVar('StrafPunkteSpur', DefaultStrafpunkteRennleiter); end // Fall 3: Kein Vergehen ausgewählt, Punkte eingetragen. // Eingetragene Punkte gehen auf's Konto else if (c[i].Text = '') AND (e[i].Text <> '') then begin cpSetStringVar('sVergehen', 'Vergehen nicht angegeben, Strafpunkte erhalten.'); cpSetIntegerVar('StrafPunkteSpur', StrToInt(e[i].Text) ); end // Fall 4: Vergehen ausgewählt, Punkte eingetragen. // ID erhält Strafpunkte auf Konto else begin cpSetStringVar('sVergehen', c[i].Text); cpSetIntegerVar('StrafPunkteSpur', StrToInt(e[i].Text) ); end; // Slot einstellen Cockpit.Slot := PunkteFuerSlot; id := Cockpit.SlotID; // Punktekonto für gewählte Spur erhöhen cpSetIntegerVar('StrafPunkteKontoID'+IntToStr(id), cpGetIntegerVar('StrafPunkteKontoID'+IntToStr(id)) + cpGetIntegerVar('StrafpunkteSpur') ); // Punktestand prüfen PruefeStrafpunkteStand(PunkteFuerSlot); // Button gedrückt form.ModalResult := mrOk; // Fine sorgt für das Schließen des Formulars fine := true; end; ...
Dieses Codebeispiel ist am Linden Park Speedway im Einsatz. Kann eine Chaos-Phase direkt einem Fahrer zugeordnet werden, greift ein Chaos-Joker-Strafen-System.
Wird die Strafe durch Eingreifen des Rennleiters verhängt, gibt es einen im Hintergrund geführtes Strafpunktekonto. Nach Erreichen der eingestellten Maximalpunktzahl, erhält der Teilnehmer automatisch eine Strafe.
Die Strafpunkte und das zugewiesene Vergehen exportiert das AddOn in eine Textdatei, die nach Rennende mit einer Tabellenkalkulation ausgewertet werden kann. Diese Teile des Codes sind hier nicht vorhanden.
Durch die Angabe der Gründe, lässt sich später trefflich diskutieren, warum man bestraft wurde und wie sich das auf den Rennausgang ausgewirkt haben könnte.
Bei der ComboBox ist unbedingt die Reihenfolge von Initialisierung des Steuerelements und der eigentlichen Inhalte zu beachten.
Wer die Zeilen oben aufmerksam verfolgt, fragt sich vielleicht, warum die Listenwerte erst definiert werden, nachdem das Formular mit cpShow(form) aufgerufen ist.
Der Grund ist einfach und doch schwer nachvollziehbar. Aber erst, wenn die Elemente definiert sind und das Formular zu sehen ist, ist es möglich, dort Werte zu belegen. Vorher existiert es quasi noch nicht. Resultat ist eine kryptische Fehlermeldung, an der ich fast verzweifelt bin.