Autor Beitrag
schitho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: Mi 03.09.03 22:09 
Hi,

ich verwende TListView um ein Inhaltsverzeichnis anzuzeigen. Um es zu sortieren verwende ich folgenden Code:

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:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
function CustomSortProc(Item1, Item2: TListItem; SortColumn: Integer): Integer; stdcall;
// siehe http://www.swissdelphicenter.ch/de/showcode.php?id=1103
// Sortiert in Listview
var
  s1, s2: string;
  i1, i2: Integer;
  r1, r2: Boolean;
  d1, d2: TDateTime;

  { Helper functions }

  function IsValidNumber(AString : string; var AInteger : Integer): Boolean;
  var
    Code: Integer;
  begin
    Val(AString, AInteger, Code);
    Result := (Code = 0);
  end;

  function IsValidDate(AString : string; var ADateTime : TDateTime): Boolean;
  begin
    Result := True;
    try
      ADateTime := StrToDateTime(AString);
    except
      ADateTime := 0;
      Result := False;
    end;
  end;

  function CompareDates(dt1, dt2: TDateTime): Integer;
  begin
    if (dt1 > dt2) then Result := 1
    else
      if (dt1 = dt2) then Result := 0
    else
      Result := -1;
  end;

  function CompareNumeric(AInt1, AInt2: Integer): Integer;
  begin
    if AInt1 < AInt2 then Result := 1
    else
      if AInt1 = AInt2 then Result := 0
    else
      Result := -1;
  end;

begin
  Result := 0;

  if (Item1 = nil) or (Item2 = nil) then Exit;

  case SortColumn of
    -1 :
    { Compare Captions }
    begin
      s1 := Item1.Caption;
      s2 := Item2.Caption;
    end;
    else
    { Compare Subitems }
    begin
      s1 := '';
      s2 := '';
      { Check Range }
      if (SortColumn < Item1.SubItems.Count) then
        s1 := Item1.SubItems[SortColumn];
      if (SortColumn < Item2.SubItems.Count) then
        s2 := Item2.SubItems[SortColumn]
    end;
  end;

  { Sort styles }

  case LvSortStyle of
    cssAlphaNum : Result := lstrcmp(PChar(s1), PChar(s2));
    cssNumeric  : begin
                    s1:=TrimToInt(s1);
                    s2:=TrimToInt(s2);
                    r1 := IsValidNumber(s1, i1);
                    r2 := IsValidNumber(s2, i2);
                    Result := ord(r1 or r2);
                    if Result <> 0 then
                      Result := CompareNumeric(i2, i1);
                  end;
    cssDateTime : begin
                    r1 := IsValidDate(s1, d1);
                    r2 := IsValidDate(s2, d2);
                    Result := ord(r1 or r2);
                    if Result <> 0 then
                      Result := CompareDates(d1, d2);
                  end;
  end;

  { Sort direction }

  if LvSortOrder[SortColumn + 1] then
    Result := - Result;
end;


Aufgerufen wird das ganze so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.ListViewColumnClick(Sender: TObject;
  Column: TListColumn);
// Es wurde auf den Spaltenkopf geklickt .... -> Umsortieren
begin
  if SortIndex <> Column.Index then LvSortOrder[Column.Index]:=false;
  SortIndex:=Column.Index;
{ determine the sort style }
  if (SortIndex = 0) or (SortIndex = 2)then
    LvSortStyle := cssAlphaNum
  else
    LvSortStyle := cssNumeric;

  { Call the CustomSort method }
  ListView.CustomSort(@CustomSortProc, SortIndex -1);

  { Set the sort order for the column}
  LvSortOrder[SortIndex] := not LvSortOrder[SortIndex];
end;


Wie bring ich die Funktion dazu, dass sie zuerst die Ordner sortiert und dann erst die Files (bzw. umgekehrt). Also so wie beim Explorer?

_________________
(Sorry! Leider ewiger Delphi-Neuling)
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: Do 04.09.03 08:49 
Hi,

das Problem ist ja wie folgt zu beschreiben: Der User klickt auf die erste Spalte und ruft dadurch die TForm1.ListViewColumnClick Procedure auf. In dieser Procedure überprüfst du auf welche Spalte der User geklickt hat setzt entsprechend die Variable LvSortStyle auf den passenden Wert. Klickt der User wie gesagt auf die erste Spalte dann setzt du die Variable auf cssAlphaNum. Das ist schon der erste Fehler. Denn es soll ja nicht nur nach Alpha- und Nummerischen Zeichen in die Sortierung einbezogen werden sondern auch der Type (also Ordner oder Datei).

Um das Problem zu lösen würde ich wie folgt vorgehen: Du erweiterst die CustomSortProc um eine spezielle Sortiermethode die neben der Caption der Items auch die Data Eigenschaft in die Sortierung einbezieht. Diese Data Eigenschaft erwartet einen Pointer. Du könntest z. b. einen Interger Wert dort abspeichern. Je größer dieser Wert ist desto weiter tiefer wird das entsprechende Item in die Sortierung gesetzt. In deinem Fall musst du dann beim Einfügen der Items den Data Wert setzen. Zum Beispiel auf 0 wenn es sich um ein Ornder handelt und auf 1 bei einer Datei.

Das heißt also ich würde als erstes dem Aufzählungstypen TCustomSortStyle, der ja angibt wie sortiert werden soll, ein neues Element hinzufügen um der Sortierprocedure CustomSortProc mitzuteilen das es hier eine spezielle Sortierung durchführen muss:
ausblenden Delphi-Quelltext
1:
2:
Type
  TCustomSortStyle = (cssAlphaNum, cssNumeric, cssDateTime, cssDataAlphaNum);

Hier habe ich jetzt das Element cssDataAlphaNum hinzugefügt. Ich habe das Element deshalb so benannt weil die Sortierung erst nach der Data Eigenschaft des Items sortiert werden soll und anschließend, bei gleichem Wert, die normale AplhaNum Sortierung benutzt werden soll.

Die CustomSortProc musst du jetzt auch erweitern.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
{ ... }
case LvSortStyle of
    cssAlphaNum : Result := lstrcmp(PChar(s1), PChar(s2));
    cssDataAlphaNum : begin
                        i1 := Integer (Item1.Data);
                        i2 := Integer (Item2.Data);
                        Result := CompareNumeric (i2, i1);
                        If Result = 0 Then
                          Result = lstrcmp (PChar (s1), PChar (s2));
                      end;
    cssNumeric  : begin
{ ... }

Ich hab es nicht getest. Probier es mal aus ob es funktioniert.

Gruß
Tino
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: Do 04.09.03 22:27 
Danke für Deine ausführliche Antwort.

Hab es ausprobiert. Aber die Sortierung funktioniert nicht.

Wahrscheinlich ruf ich die Procedure noch falsch auf.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TFormShredExplor.ListViewColumnClick(Sender: TObject;
  Column: TListColumn);
// Es wurde auf den Splatenkopf geklickt .... -> Umsortieren
begin
  if SortIndex <> Column.Index then LvSortOrder[Column.Index]:=false;
  SortIndex:=Column.Index;
{ determine the sort style }
  if (SortIndex = 0) or (SortIndex = 2)then
    LvSortStyle := cssDataAlphaNum
  else
    LvSortStyle := cssNumeric;

  { Call the CustomSort method }
  ListView.CustomSort(@CustomSortProc, SortIndex -1);

  { Set the sort order for the column}
  LvSortOrder[SortIndex] := not LvSortOrder[SortIndex];
end;


Wie muss ich den Code ändern, damit es funktioniert?

_________________
(Sorry! Leider ewiger Delphi-Neuling)
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: Fr 05.09.03 07:45 
Hallo!

Wie gesagt habe ich das ganze nicht getestet... sollte aber eigentlich funktionieren.

Zeig doch mal die Stelle im Sourcecode an der du die Items hinzufügst und deren Data Eigenschaft auf den Wert 0 oder 1 setzt.

Gruß
Tino
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: Sa 06.09.03 12:14 
So lese ich die Daten ein:

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:
43:
44:
45:
46:
47:
48:
procedure VerzeichnisEinlesen(VerzName, filter:string; new :boolean=true);
// Liest den Inhalt eines Verzeichnisses ein und zeigt ihn an
var NewItem : TListItem;
    Info    : TSHFileInfo;
    SR      : TSearchRec;
    dgroesse: String; // Dateigröße-Text
    dsize   : Integer; // Dateigröße
    SortTyp : Integer; // 0 = Verzeichnis, 1 = Datei
begin
  if VerzName[length(VerzName)]<>'\' then
    VerzName:=VerzName+'\';
  if filter='' then filter:='*.*';
  with FormShredExplor do begin
    if new then ListView.Items.Clear;
    if FindFirst(VerzName+filter,faAnyFile  ,SR)=0 then begin
      repeat
        if (length(SR.Name)>0) and ((SR.Name <> '.') and (SR.Name <> '..')) then begin
          if (LowerCase(SR.Name)<>'desktop.ini') and (LowerCase(SR.Name)<>'recycled') then
          begin
            NewItem:=ListView.Items.Add;
            NewItem.Caption:=SR.Name;
            SHGetFileInfo(PChar(VerzName+SR.Name),0,Info,SizeOf(TSHFileInfo),SHGFI_SYSIconIndex or SHGFI_TYPENAME);
            NewItem.ImageIndex:=Info.IIcon;
            if (SR.Attr and faDirectory > 0) and (SR.Name[1] <> '.') then
            begin
              NewItem.SubItems.Add('');
              NewItem.SubItems.Add(Info.szTypeName);
              SortTyp:=1;
              NewItem.Data:=@SortTyp;
            end
            else
            begin
              dSize:=Round((SR.Size/1024)+0.5);
              dGroesse:=IntToStr(dSize);
              if Length(dgroesse)>3 then Insert('.',dGroesse,Length(dgroesse)-2);
              if Length(dgroesse)>7 then Insert('.',dGroesse,Length(dgroesse)-6);
              NewItem.SubItems.Add(dGroesse+ ' KB');
              NewItem.SubItems.Add(Info.szTypeName);
              SortTyp:=2;
              NewItem.Data:=@SortTyp;
            end;
          end;
        end;
      until FindNext(SR)<>0;
      FindClose(SR);
    end;
  end;
end;

_________________
(Sorry! Leider ewiger Delphi-Neuling)
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: Mo 08.09.03 20:58 
:cry:

Weiß denn keiner Rat, ob das so geht mit

ausblenden Delphi-Quelltext
1:
2:
SortTyp:=2;
NewItem.Data:=@SortTyp;


Ich glaub da ist doch ein Hund drinnen :(

_________________
(Sorry! Leider ewiger Delphi-Neuling)
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: Di 09.09.03 08:21 
schitho hat folgendes geschrieben:
Ich glaub da ist doch ein Hund drinnen :(

Jupp, genau :-D

Versuch mal die folgende Zuweisung:
ausblenden Delphi-Quelltext
1:
2:
3:
NewItem.Data:=Pointer (0);
{oder}
NewItem.Data:=Pointer (1);

Gruß
Tino
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: Di 09.09.03 22:05 
Danke!

Jetzt funktioniert es :dance:

_________________
(Sorry! Leider ewiger Delphi-Neuling)
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: Mi 10.09.03 15:07 
Na bitte :-D

Gruß
Tino