2015-09-28 42 views
5

我正在使用Delphi 6並希望添加對ListView進行排序的功能,就像它在Windows資源管理器中所做的那樣。使用箭頭排序ListView列

在第一測試中,我有(快速&髒)複製從幾個來源幾個源代碼,並做了一些小的調整:

這是我到目前爲止(僅快&髒現在):

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; 

colums可以向上和向下排序,但我看不到箭頭。

根據this question,我的函數ShowArrowOfListViewColumn()應該已經解決了這個問題。

Delphi 6是否可能不支持此功能,或者在我的代碼中存在問題?另一方面,ListView是IIRC a Windows control,因此我期望WinAPI呈現箭頭圖形,而不是(非常陳舊的)VCL。

我在German website上看到箭頭圖形必須手動添加,但該網站的解決方案需要更改Delphi的CommCtrl.pas(由於調整列大小時會出現毛刺)。但我真的不喜歡修改VCL源代碼,尤其是在我開發OpenSource之後,我不希望其他開發人員更改/重新編譯他們的Delphi源代碼。

請注意,我沒有將XP清單添加到我的二進制文件,所以該應用程序看起來像Win9x。

+0

您是否使用comctl v6,即XP主題?這需要Mike Lischke的主題經理。 –

+0

我沒有將XP清單添加到我的二進制文件,所以該應用程序看起來像Win9x。 –

回答

3

HDF_SORTDOWN and HDF_SORTUP requires comctl32 v6。這是在文檔中陳述了HDITEM

HDF_SORTDOWN 6.00版及更高版本。在此項目上繪製一個向下箭頭。這通常用於指示當前窗口中的信息按降序排列在此列上。該標誌不能與HDF_IMAGE或HDF_BITMAP結合使用。

HDF_SORTUP版本6.00和更高版本。在這個項目上繪製一個向上的箭頭。這通常用於指示當前窗口中的信息按升序排序在此列上。該標誌不能與HDF_IMAGE或HDF_BITMAP結合使用。

正如您在註釋中所述,您未包含comctl32 v6清單。這解釋了你所觀察到的。

解決方案包括:

  • 添加COMCTL32 V6清單,或
  • 定製圖形標題的箭頭。
+0

你好,非常感謝你的提示。我實際上閱讀「需要Windows XP」,但我忘了Windows將使用ComCtl32的後備版本,如果沒有提供清單。 - 我對此仍感到有些驚訝,因爲Windows 95以後就存在箭頭了。微軟是否保留了這個功能,直到Windows XP或Windows 95 Explorer使用與ListView不同的控件? –

+0

爲了完整起見,我創建了一個VCL - 它也解決了每列調整大小時箭頭消失的問題:http://www.viathinksoft.de/~daniel-marschall/code/delphi/vcl/VTSListView.pas。但我擔心我重新發明了車輪。 –

+0

勝利95中的Prob資源管理器使用了不同的控件,或者自定義繪製了箭頭 –

-1

您不必更改VCL源代碼以遵循德語示例,您只需修補代碼運行時即可。

DISCALMER我想在Delphi 6上測試我的代碼,但是我的Delphi 6安裝不會在今天上午開始,所以它只能在Delphi XE上測試!

但我想它也適用於Delphi 6。

首先,你需要一個類來修補的方法運行:

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. 

這時我們就需要使用它。加索爾窗體上的列表視圖的那麼這段代碼:

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. 

正如你看到的,然後我的演示中,我heavly您發佈的代碼啓發。我只是刪除了全球變數。在我的例子中,我什麼都不做,只是調用原始程序,但是你必須調用Geraman例子中的代碼。

所以基本上我只是想告訴你如何改變VCL編輯原始源代碼。這應該讓你去。

+2

你不需要像這樣破解。你可以在我的答案中使用代碼,而不必在你的答案中使用任何討厭的黑客。 –

+0

它它是TListView = class(ComCtrls.TListView)部分還是補丁部分,你稱之爲討厭的黑客? –

+1

繞道是不必要的。無論如何,你已經錯過了這一點。這個問題已經告訴你,我的其他答案中的代碼沒有任何效果。你需要解釋爲什麼會如此。缺乏XP主題是真正的原因。 –