2015-09-28 11 views
5

Ich benutze Delphi 6 und möchte die Funktionalität zum Sortieren eines ListView hinzufügen, wie es im Windows Explorer gemacht wird.ListView-Spalten mit Pfeilen sortieren

In einem ersten Test, ich habe (quick & schmutzig) ein paar Quellcodes von einigen Quellen kopiert und einige kleine Anpassungen vorgenommen:

Das ist, was ich bisher haben (nur schnell & schmutzig für jetzt):

uses 
    CommCtrls; 

var 
    Descending: Boolean; 
    SortedColumn: Integer; 

const 
    { For Windows >= XP } 
    {$EXTERNALSYM HDF_SORTUP} 
    HDF_SORTUP    = $0400; 
    {$EXTERNALSYM HDF_SORTDOWN} 
    HDF_SORTDOWN   = $0200; 

procedure ShowArrowOfListViewColumn(ListView1: TListView; ColumnIdx: integer; Descending: boolean); 
var 
    Header: HWND; 
    Item: THDItem; 
begin 
    Header := ListView_GetHeader(ListView1.Handle); 
    ZeroMemory(@Item, SizeOf(Item)); 
    Item.Mask := HDI_FORMAT; 
    Header_GetItem(Header, ColumnIdx, Item); 
    Item.fmt := Item.fmt and not (HDF_SORTUP or HDF_SORTDOWN);//remove both flags 
    if Descending then 
    Item.fmt := Item.fmt or HDF_SORTDOWN 
    else 
    Item.fmt := Item.fmt or HDF_SORTUP;//include the sort ascending flag 
    Header_SetItem(Header, ColumnIdx, Item); 
end; 

procedure TUD2MainForm.ListView3Compare(Sender: TObject; Item1, 
    Item2: TListItem; Data: Integer; var Compare: Integer); 
begin 
    if SortedColumn = 0 then 
    Compare := CompareText(Item1.Caption, Item2.Caption) 
    else 
    Compare := CompareText(Item1.SubItems[SortedColumn-1], Item2.SubItems[SortedColumn-1]); 
    if Descending then Compare := -Compare; 
end; 

procedure TUD2MainForm.ListView3ColumnClick(Sender: TObject; 
    Column: TListColumn); 
begin 
    TListView(Sender).SortType := stNone; 
    if Column.Index<>SortedColumn then 
    begin 
    SortedColumn := Column.Index; 
    Descending := False; 
    end 
    else 
    Descending := not Descending; 
    ShowArrowOfListViewColumn(TListView(Sender), column.Index, Descending); 
    TListView(Sender).SortType := stText; 
end; 

Die colums sortiert nach oben und unten werden kann, aber ich kann nicht Pfeile sehen.

Nach this question sollte meine Funktion ShowArrowOfListViewColumn() das Problem gelöst haben.

Ist es möglich, dass Delphi 6 diese Funktion nicht unterstützt, oder gibt es ein Problem in meinem Code? Auf der anderen Seite ist ListView IIRC ein Windows control, und daher erwarte ich, dass die WinAPI die Pfeilgrafiken und nicht die (sehr alte) VCL rendert.

Ich lese unter German website, dass die Pfeilgrafiken manuell hinzugefügt werden müssen, aber die Lösung dieser Website hat die Anforderung, CommCtrl.pas von Delphi zu ändern (wegen eines Fehlers beim Ändern der Größe der Spalte). Aber ich mag es wirklich nicht, die VCL-Quelle zu ändern, besonders seit ich OpenSource entwickle, und ich möchte nicht, dass andere Entwickler ihre Delphi-Quellen ändern/neu kompilieren.

Beachten Sie, dass ich meiner Binärdatei kein XP-Manifest hinzugefügt habe, daher sieht die App wie Win9x aus.

+0

Verwenden Sie comctl v6, d. H. XP Themes? Das erfordert Mike Lischkes Themenmanager. –

+0

Ich habe meiner Binärdatei kein XP-Manifest hinzugefügt, daher sieht die App wie Win9x aus. –

Antwort

3

HDF_SORTDOWN und HDF_SORTUP erfordern comctl32 v6. Dies wird in der Dokumentation für HDITEM angegeben:

HDF_SORTDOWN Version 6.00 und höher. Zeichnet einen Abwärtspfeil für diesen Gegenstand. Dies wird normalerweise verwendet, um anzuzeigen, dass Informationen im aktuellen Fenster für diese Spalte in absteigender Reihenfolge sortiert sind. Dieses Flag kann nicht mit HDF_IMAGE oder HDF_BITMAP kombiniert werden.

HDF_SORTUP Version 6.00 und höher. Zeichnet einen Pfeil nach oben auf diesen Gegenstand. Dies wird normalerweise verwendet, um anzuzeigen, dass Informationen im aktuellen Fenster für diese Spalte in aufsteigender Reihenfolge sortiert sind. Dieses Flag kann nicht mit HDF_IMAGE oder HDF_BITMAP kombiniert werden.

Wie Sie in Ihren Kommentaren erläutert haben, haben Sie das Manifest comctl32 v6 nicht mit einbezogen. Das erklärt, was du beobachtest.

Lösungen umfassen:

  • die comctl32 v6 manifestieren, oder
  • Benutzerdefinierte Zeichnungskopf Pfeile Hinzufügen.
+0

Hallo, vielen Dank für diesen Hinweis. Ich lese eigentlich "erfordert Windows XP", aber ich habe vergessen, dass Windows eine Fallback-Version von ComCtl32 verwenden wird, wenn kein Manifest bereitgestellt wird. - Ich bin immer noch ein bisschen überrascht darüber, weil die Pfeile seit Windows 95 existieren. Hat Microsoft diese Funktion bis Windows XP offengelegt, oder hat der Windows 95 Explorer ein anderes Steuerelement als das ListView verwendet? –

+0

Der Vollständigkeit halber habe ich eine VCL erstellt - die auch das Problem löst, dass die Pfeile auf jeder Spalte verschwinden: http://www.viathinksoft.de/~daniel-marschall/code/delphi/vcl/VTSListView.pas. Aber ich fürchte, ich habe das Rad neu erfunden. –

+0

Prob-Explorer in Win 95 verwendet ein anderes Steuerelement oder benutzerdefinierte zeichnete die Pfeile –

-1

Sie müssen die VCL-Quelle nicht ändern, um dem deutschen Beispiel zu folgen. Sie können die Code-Laufzeit einfach patchen.

DISCALMER Ich wollte meinen Code auf Delphi 6 testen, aber meine Delphi 6 Installation würde heute Morgen nicht starten, also wird es nur auf Delphi XE getestet!

Aber ich denke, es würde auch auf Delphi 6 funktionieren.

Zuerst müssen Sie eine Klasse eine Methode Runtime-Patch:

unit PatchU; 

interface 

type 
    pPatchEvent = ^TPatchEvent; 

    // "Asm" opcode hack to patch an existing routine 
    TPatchEvent = packed record 
    Jump: Byte; 
    Offset: Integer; 
    end; 

    TPatchMethod = class 
    private 
    PatchedMethod, OriginalMethod: TPatchEvent; 
    PatchPositionMethod: pPatchEvent; 
    public 
    constructor Create(const aSource, aDestination: Pointer); 
    destructor Destroy; override; 
    procedure Restore; 
    procedure Hook; 
    end; 

implementation 

uses 
    Windows, Sysutils; 

{ TPatchMethod } 

constructor TPatchMethod.Create(const aSource, aDestination: Pointer); 
var 
    OldProtect: Cardinal; 
begin 
    PatchPositionMethod := pPatchEvent(aSource); 
    OriginalMethod := PatchPositionMethod^; 
    PatchedMethod.Jump := $E9; 
    PatchedMethod.Offset := PByte(aDestination) - PByte(PatchPositionMethod) - SizeOf(TPatchEvent); 

    if not VirtualProtect(PatchPositionMethod, SizeOf(TPatchEvent), PAGE_EXECUTE_READWRITE, OldProtect) then 
    RaiseLastOSError; 

    Hook; 
end; 

destructor TPatchMethod.Destroy; 
begin 
    Restore; 
    inherited; 
end; 

procedure TPatchMethod.Hook; 
begin 
    PatchPositionMethod^ := PatchedMethod; 
end; 

procedure TPatchMethod.Restore; 
begin 
    PatchPositionMethod^ := OriginalMethod; 
end; 

end. 

Dann müssen wir es benutzen. Pau eine Listenansicht auf einem Formular ein dann dieser Code:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, ComCtrls, PatchU; 

type 
    TListView = class(ComCtrls.TListView) 
    protected 
    procedure ColClick(Column: TListColumn); override; 
    end; 

    TForm1 = class(TForm) 
    ListView1: TListView; 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

uses 
    CommCtrl; 

var 
    ListView_UpdateColumn_Patch: TPatchMethod; 

type 
    THooked_ListView = class(TListView) 
    procedure HookedUpdateColumn(AnIndex: Integer); 
    end; 

    { TListView } 

procedure TListView.ColClick(Column: TListColumn); 
var 
    Header: HWND; 
    Item: THDItem; 
    NewFlag: DWORD; 
begin 
    Header := ListView_GetHeader(Handle); 
    ZeroMemory(@Item, SizeOf(Item)); 
    Item.Mask := HDI_FORMAT; 
    Header_GetItem(Header, Column.Index, Item); 

    if Item.fmt and HDF_SORTDOWN <> 0 then 
    NewFlag := HDF_SORTUP 
    else 
    NewFlag := HDF_SORTDOWN; 

    Item.fmt := Item.fmt and not(HDF_SORTUP or HDF_SORTDOWN); // remove both flags 
    Item.fmt := Item.fmt or NewFlag; 
    Header_SetItem(Header, Column.Index, Item); 

    inherited; 
end; 

{ THooked_ListView } 

procedure THooked_ListView.HookedUpdateColumn(AnIndex: Integer); 
begin 
    ListView_UpdateColumn_Patch.Restore; 
    try 
    UpdateColumn(AnIndex); 
    finally 
    ListView_UpdateColumn_Patch.Hook; 
    end; 
end; 

initialization 

ListView_UpdateColumn_Patch := TPatchMethod.Create(@TListView.UpdateColumn, @THooked_ListView.HookedUpdateColumn); 

finalization 

ListView_UpdateColumn_Patch.Free; 

end. 

Wie Sie dann durch den Code meine Demo sehen i heavly inspiriert Sie veröffentlicht. Ich habe gerade die globalen Variablen entfernt. In meinem Beispiel tue ich nichts anderes, als die ursprüngliche Prozedur aufzurufen, aber Sie müssen den Code aus dem Geraman-Beispiel aufrufen.

Also im Grunde wollte ich Ihnen nur zeigen, wie Sie die VCL ohne Bearbeitung des ursprünglichen Quellcodes ändern können. Das sollte dich in Gang bringen.

+2

Sie müssen nicht so hacken. Sie können den Code von meiner Antwort dort ohne irgendwelche der fiesen Hacks in Ihrer Antwort hier verwenden. –

+0

Es ist die TListView = Klasse (ComCtrls.TListView) Teil oder die Patch-Teil Sie einen fiesen Hack nennen? –

+1

Der Umweg ist unnötig. In jedem Fall haben Sie den Punkt verpasst. Die Frage hat dir bereits gesagt, dass der Code in meiner anderen Antwort keinen Effekt hat. Sie müssen erklären, warum das so wäre. Das Fehlen von XP-Themen ist der wahre Grund. –