Autor Beitrag
Stefan S.
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 184


D5
BeitragVerfasst: Mo 02.04.07 11:27 
Hallo allerseits.
Ich verwende eine ListView-Komponente zum Anzeigen der Daten von Personen in einer Tabelle (mit ViewStyle = vsReport). Diese Daten können auch nach den einzelnen Kategorien sortiert werden, indem man auf die Spalten-Header klickt (wenn man ein zweites Mal klickt, werden die Daten umgekehrt geordnet, wie auch im Windows-Explorer). Dabei kommt es aber zu zweierlei Problemen:

Zum einen tue ich mich schwer, bei einem Klick auf einen Tabelleneintrag darauf zu schließen, um welchen Eintrag es sich handelt. Denn "Selected.Index" richtet sich ja nach der Position des Eintrags in der Tabelle und das bringt mir nichts, weil durch das Sortieren theoretisch jeder Eintrag an jeder Position sein kann. Natürlich habe ich die Caption des jeweils ausgewählten Eintrages und könnte dann in dem Array, in dem die Daten gespeichert sind, nach der Person suchen, die diesen Namen hat, allerdings finde ich das sehr unschön, weil schließlich auch zwei Personen den selben Namen haben könnten (selbst wenn das in der Praxis praktisch nicht vorkommen sollte). Woher also kann ich wissen, um welchen Eintrag es sich bei dem ausgewählten handelt? Geht das vielleicht mit der "Data"-Eigenschaft der einzelnen Items, die auf eine Datenstruktur zeigen kann (die dann vielleicht die Kennnummern der Einträge enthält)?

Ein weiteres Problem habe ich beim Sortieren. Das normale Sortieren (beim ersten Klick auf einen Spalten-Header) funktioniert ohne Probleme, aber das umgekehrte Sortieren (beim zweiten Klick) nur zum Teil. Um das umzusetzen hab ich hier im Forum mal ein bisschen rumgesucht und bin fündig geworden, warum der Code bei mir nicht hunderprozentig funktioniert, ist mir jetzt aber nicht klar.

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
var Sortier_Spalte: Integer;

procedure TfoMain.lvSpielerlisteCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var i: Integer; Wert1, Wert2: String;
begin
 If Sortier_Spalte = 0 then //Vergleich der Namen in "Caption"
  Compare := CompareText(Item1.Caption,Item2.Caption)
 Else //Vergleich der Werte in Subitems
  begin
   i := Sortier_Spalte - 1;
   If lvSpielerliste.Columns[i+1].Tag >= 1 then //Spalten mit Integer als Inhalt: Tag >= 1
    begin
     Wert1 := Item1.SubItems[i];
     Wert2 := Item2.SubItems[i];
     try
      Compare := StrToInt(Wert1) - StrToInt(Wert2);
     except
      //sollte der Inhalt der Spalte keine Zahl sein
      Compare := 0;
     end;
    end
   Else //die normalen String-Spalten: Tag = 0
    Compare := CompareText(Item1.SubItems[i],Item2.SubItems[i]);
  end;
 //Ergebnis wird umgekehrt, wenn die Liste umgekehrt geordnet werden soll
 If Sortierung_Invers = True then
  Compare := not Compare;
end;

procedure TfoMain.lvSpielerlisteColumnClick(Sender: TObject;
  Column: TListColumn);
begin
 //damit bei einem zweiten Klick umgekehrt sortiert wird:
 If Sortier_Spalte <> column.Index then
  Sortierung_Invers := False
 Else
  Sortierung_Invers := not Sortierung_Invers;
 //die Variable Sortier_Spalte übergibt den Index der angeklickten Spalte
 Sortier_Spalte := Column.Index;
 (Sender as TCustomListView).AlphaSort;
end;


Und so funktioniert der Code:
Ich habe zu Testzwecken einige Einträge mit den Nachnamen Ahrens, Becker, Mayer, Müller, Schmidt, Schneider und Werner eingefügt (Caption = Nachname). Beim normalen Sortieren sind sie genau in dieser Reihenfolge, wie es sein soll, wenn man ein zweites Mal auf den Spalten-Header klickt und damit "Sortierung_Invers" = True ist, sieht es jedoch so aus:

Werner, Schmidt, Schneider, Müller, Mayer, Ahrens, Becker

Wie man sieht ist das Ergebnis im großen und ganzen richtig, nur sind der 2. und 3. Wert sowie der vorletzte und letzte komischerweise vertauscht. Findet jemand den Fehler?
Keldorn
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 2266
Erhaltene Danke: 4

Vista
D6 Prof, D 2005 Pro, D2007 Pro, DelphiXE2 Pro
BeitragVerfasst: Mo 02.04.07 19:37 
Hallo

bei Müller steht ein ü, damit kommt comparetext nicht klar und du erhälst falsche Ergebnisse.
Verwende stattdessen ansicompartext, dann gehts.
Bei Delphi 6 ging dann nicht mehr compare:=not compare, mit compare:=(-1)* compare funktionierts aber dann.

Mfg Frank

_________________
Lükes Grundlage der Programmierung: Es wird nicht funktionieren.
(Murphy)
Stefan S. Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 184


D5
BeitragVerfasst: Di 03.04.07 11:34 
Wunderbar, es funktioniert perfekt. :) Dankeschön.

Kann mir bei dem ersten Problem auch noch jemand helfen? Wie finde ich bei einer beliebig sortierten Liste raus, um welches Element es sich bei dem aktuell ausgewählten handelt? Kann ich jedem Eintrag neben "Caption" und "Subitems" auch noch irgendwie eine "Kennnummer" verpassen? Oder geht es wirklich nicht anders als dass ich anhand einer der Spalten darauf schließe?
uro3
Hält's aus hier
Beiträge: 3



BeitragVerfasst: Di 03.04.07 11:59 
Du kannst jeden Eintrag ein Object zu ordnen.
Wenn du eine Zahl zu ordnen willst geht
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
var
  Nr : Integer;

begin
........

lvListView1.AddItem('dein text',TObject(Nr));


oder du benutzt die Data Eigenschaft vom TListItem
Stefan S. Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 184


D5
BeitragVerfasst: Mi 04.04.07 09:50 
Ich hab es jetzt mal so gemacht:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
with foMain.lvSpielerliste.Items.Add do
 begin
  Caption := Spieler[i-1].Name;
  (...)
  Data := @Spieler[i-1];
 end;


Spieler ist ein dynamischer Array von TSpieler, ein Record, das u.a. das Feld "Name" (String) enthält.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TfoMain.lvSpielerlisteChange(Sender: TObject; Item: TListItem;
  Change: TItemChange);
var P: ^TSpieler;
begin
 If lvSpielerliste.Selected <> nil then
  begin
   //der Zeiger P zeigt auf die Daten des ausgewählten Spielers
   P := lvSpielerliste.Selected.Data;
   //diese werden jetzt in den Labels angezeigt
   laSpieler_Name.Caption := P^.Name;
   (...)
  end;
end;


Das ganze funktioniert auch wunderbar, mit einer Einschränkung: Ich habe zwei Checkboxen, mit denen man einstellen kann, dass Einträge mit bestimmten Eigenschaften angezeigt oder eben nicht angezeigt werden sollen. Wenn man jetzt bei einer von beiden das Häkchen entfernt sind folglich weniger Einträge in der Liste, insgesamt auch weniger, als der dynamische Array "Spieler" lang ist. Nun kommt es an der Stelle, an der ich auf P^.Name zugreife, zu einer Zugriffsverletzung, egal auf welchen Eintrag ich klicke. Man könnte vermuten, dass P auf ein Spieler[i] zeigt, das nicht existiert, kann aber eigentlich nicht sein, weil "Selected" ja immer der ausgewählte Eintrag ist und "Data" dabei stets auf die selbe Stelle zeigt, egal ob jetzt 7 Einträge in der Liste sind wie am Anfang oder 2 (Häkchen vor Checkbox 1 weg) bzw. 5 (Checkbox 2). Wie kann es also zu der Zugriffsverletzung kommen?


EDIT: Hab das Problem gefunden: War mal wieder etwas ganz dummes, nämlich hab ich einfach an einer anderen Stelle die Zuweisung von "Data" vergessen. Damit wäre dann alles geklärt.


Zuletzt bearbeitet von Stefan S. am Mi 04.04.07 10:08, insgesamt 3-mal bearbeitet