Autor Beitrag
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10039
Erhaltene Danke: 1183

W2k .. W7pro .. W10ent
TP3 .. D7pro .. D10.1
BeitragVerfasst: Do 22.02.07 10:48 
Moin!

Es gibt sicher einige Ansätze, wie man eine Bestenliste umsetzen kann, ich persönlich finde dafür aber die VCL-Komponente TListView ausgesprochen gut geeignet. Unter anderem deshalb, weil sie benutzerdefinierte Sortierungen relativ leicht ermöglicht.

Wir erzeugen ein neues Projekt und legen ein ListView (aus dem Reiter "Win32") auf das Formular. Anschließend müssen wir die Komponente noch unseren Bedürfnissen entsprechend anpassen:
  • ausblenden Delphi-Quelltext
    1:
    Name := 'lvHighscore'					

  • ausblenden Delphi-Quelltext
    1:
    ViewStyle := vsReport					

  • ausblenden Delphi-Quelltext
    1:
    ColumnClick := False					

  • ausblenden Delphi-Quelltext
    1:
    GridLines := True					

  • ausblenden Delphi-Quelltext
    1:
    ReadOnly := True					

  • ausblenden Delphi-Quelltext
    1:
    RowSelect := True					

  • ausblenden Delphi-Quelltext
    1:
    SortType := stData					

  • Doppelklick auf der Eigenschaft Columns im ObjectInspector, um den Eigenschaftseditor zu öffnen
    • Auf das Blattsymbol mit dem Sternchen klicken, um eine neue Spalte im ListView anzulegen
    • Dann im ObjectInspector die folgenden Eigenschaften setzen: Caption := 'Name', MinWidth := 16, Width := 144
    • Noch eine Spalte anlegen, Werte: Alignment := taRightJustify, Caption := 'Punkte', MinWidth := 16, Width := 56
    • Und noch eine, Werte: Alignment := taRightJustify, Caption := 'Datum', MinWidth := 16, Width := 120


Damit ist die Komponente schon fast fertig konfiguriert. Da wir aber noch eine Vergleichsprozedur zur Verfügung stellen müssen (die größte Punktzahl soll ja oben stehen), wechseln wir im ObjectInspector auf die Seite "Ereignisse" und machen einen Doppelklick beim Ereignis OnCompare. Es wird ein Ereignishandler angelegt, den wir mit folgendem Code füllen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
uses
  ..., Math; // für CompareValue()

// Sortiervergleich für die Highscore-Liste - Version _mit_ CompareValue/Math
procedure TForm1.lvHighscoreCompare(Sender: TObject; Item1, Item2: TListItem;
  Data: Integer; var Compare: Integer);
begin
  Compare := CompareValue(Integer(Item2.Data),Integer(Item1.Data));
end;

Man kann jetzt darüber streiten, ob die Unit Math für die Vergleichsoperation unbedingt nötig ist. Sicher ist es für diesen kleinen Anwendungsfall etwas überzogen, aber es gibt eine Menge überladener Varianten von CompareValue für eine Vielzahl von Datentypen, die auch gleich das erwartete Vergleichsergebnis für den Ereignishandler liefern, so dass ein Blick in diese Unit sicher nicht schaden kann. ;)

Wer die Unit Math nicht hat bzw. verwenden möchte oder mit einer Delphi-Version kleiner 6 arbeitet (CompareValue gibt es erst seit D6), kann den Vergleich auch alternativ so durchführen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
// Sortiervergleich für die Highscore-Liste - Version _ohne_ CompareValue
procedure TForm1.lvHighscoreCompare(Sender: TObject; Item1, Item2: TListItem;
  Data: Integer; var Compare: Integer);
begin
  if (Integer(Item2.Data) < Integer(Item1.Data)) then
    Compare := -1
  else if (Item2.Data = Item1.Data) then
    Compare := 0
  else
    Compare := 1;
end;

Der Witz dieses Ereignishandlers ist nun, dass damit die Komponente in der Lage ist (und zwar selbstständig!), die Einträge sortiert zu halten. :D

Wie kommen wir nun zu den Einträgen? Dazu legen wir noch einen Button auf das Formular und erstellen einen OnClick-Handler, den wir mit folgendem Code füllen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.Button1Click(Sender: TObject);
begin
  with lvHighscore.Items.Add do begin // neuen Highscore-Eintrag machen (auto-sortiert!)
    Caption := 'Narses'// Name eintragen
    Data := Pointer(Random(100)); // Punktestand zum Sortieren
    SubItems.Add(IntToStr(Integer(Data))); // und für die Ausgabe

    SubItems.Add(DateTimeToStr(Now)); // Datum eintragen
  end;
end;

Was hat es mit dem Pointer() für den Punktestand auf sich? :gruebel: Jeder Listeneintrag (vom Typ TListItem) bietet in der Eigenschaft .Data die Möglichkeit, eine Referenz auf ein beliebiges, anderes Objekt zu speichern. Deshalb ist der Datentyp allgemein Pointer. Wir wollen aber nur einen Integer speichern, was zwar vom Speicherplatz her das gleiche ist, aber für den Compiler nicht. Deshalb müssen wir diesem mit der Typumwandlung ("typecast") klar machen, dass er ruhig den Integer als Pointer betrachten darf, wenn er dann glücklicher ist... :P

Wir starten das Projekt und testen mit dem Button die Funktion. Es sollten jetzt Einträge mit zufälliger Punktzahl angelegt werden - und zwar automatisch sortiert! ;)

In Anhang befindet sich ein Demo-Projekt, in dem auch das Laden und Speichern dieser Bestenliste behandelt ist, um den Beitrag hier nicht unnötig aufzublähen.

Viel Erfolg!

cu
Narses
Einloggen, um Attachments anzusehen!
_________________
There are 10 types of people - those who understand binary and those who don´t.