Reputation: 6051
In a 32-bit VCL Application in Windows 10 in Delphi 11 Alexandria, I have a TListView
with two columns, client-aligned to the form. I am using this code to handle the sort-arrows in the ListView Header:
procedure TformMain.ListView1ColumnClick(Sender: TObject; Column: TListColumn);
begin
FSortedColumn := Column.Index;
case FSortedColumn of
0: FColumn0SortedUp := not FColumn0SortedUp;
1: FColumn1SortedUp := not FColumn1SortedUp;
end;
SetListHeaderSortArrow(FSortedColumn);
end;
procedure TformMain.SetListHeaderSortArrow(const aColumnIndex: Integer);
begin
var Header: HWND;
var Item: Winapi.CommCtrl.THDItem;
case aColumnIndex of
0:
begin
Header := Winapi.CommCtrl.ListView_GetHeader(ListView1.Handle);
Winapi.Windows.ZeroMemory(@Item, SizeOf(Item));
Item.Mask := Winapi.CommCtrl.HDI_FORMAT;
Winapi.CommCtrl.Header_GetItem(Header, 0, Item);
Item.fmt := Item.fmt and not (HDF_SORTUP or HDF_SORTDOWN); // remove both flags
if FColumn0SortedUp then
Item.fmt := Item.fmt or HDF_SORTUP // include the sort ascending flag
else
Item.fmt := Item.fmt or HDF_SORTDOWN; // include the sort descending flag
Header_SetItem(Header, 0, Item);
end;
1:
begin
Header := Winapi.CommCtrl.ListView_GetHeader(ListView1.Handle);
Winapi.Windows.ZeroMemory(@Item, SizeOf(Item));
Item.Mask := Winapi.CommCtrl.HDI_FORMAT;
Winapi.CommCtrl.Header_GetItem(Header, 1, Item);
Item.fmt := Item.fmt and not (HDF_SORTUP or HDF_SORTDOWN); // remove both flags
if FColumn1SortedUp then
Item.fmt := Item.fmt or HDF_SORTUP // include the sort ascending flag
else
Item.fmt := Item.fmt or HDF_SORTDOWN; // include the sort descending flag
Header_SetItem(Header, 1, Item);
end;
end;
end;
procedure TformMain.ListView1Resize(Sender: TObject);
begin
// This gets inexplicably automatically executed 3 times at program start!!
// This must be in OnResize, otherwise the sort-arrows get hidden when resizing the ListView:
SetListHeaderSortArrow(FSortedColumn);
end;
When I click the column header of the SECOND column, the sort arrow on the second column appears, but the sort arrow on the first column does not disappear! Only when I resize the ListView (by resizing the form), the sort arrow on the first column disappears. So how can I make the sort arrow on the first column immediately disappear when clicking the second column header?
Upvotes: 0
Views: 313
Reputation: 595402
When changing the flags, you are not removing the flags from the previous selected column before adding the flags to the new column.
Try something more like this instead:
private:
FColumnSortedUp: array[0..1] of Boolean;
FSortedColumn: Integer;
...
procedure TformMain.FormCreate(Sender: TObject);
begin
FSortedColumn := -1;
end;
procedure TformMain.ListView1ColumnClick(Sender: TObject; Column: TListColumn);
begin
if FSortedColumn <> -1 then
SetListHeaderSortArrow(FSortedColumn, False);
if FSortedColumn = Column.Index then
FColumnSortedUp[FSortedColumn] := not FColumnSortedUp[FSortedColumn];
else
FSortedColumn := Column.Index;
SetListHeaderSortArrow(FSortedColumn, True);
// sort ListView items as needed...
end;
procedure TformMain.SetListHeaderSortArrow(const aColumnIndex: Integer;
const aEnabled: Boolean);
var
Header: HWND;
Item: THDItem;
begin
Header := ListView_GetHeader(ListView1.Handle);
ZeroMemory(@Item, SizeOf(Item));
Item.Mask := HDI_FORMAT;
Header_GetItem(Header, aColumnIndex, Item);
Item.fmt := Item.fmt and not (HDF_SORTUP or HDF_SORTDOWN); // remove both flags
if aEnabled then
begin
if FColumnSortedUp[aColumnIndex] then
Item.fmt := Item.fmt or HDF_SORTUP // include the sort ascending flag
else
Item.fmt := Item.fmt or HDF_SORTDOWN; // include the sort descending flag
end;
Header_SetItem(Header, aColumnIndex, Item);
end;
Also note that the sort arrow does not disappear when the ListView is resized, but when the column is resized. So you will have to hook the ListView to handle HDN_ENDTRACK
notifications to detect when each column is resized, eg:
private
...
OldWndProc: TWndMethod;
procedure ListViewWndProc(var Message: TMessage);
...
uses
..., Winapi.Messages, Winapi.CommCtrl;
procedure TformMain.FormCreate(Sender: TObject);
begin
...
OldWndProc := ListView1.WindowProc;
ListView1.WindowProc := ListViewWndProc;
end;
procedure TformMain.ListViewWndProc(var Message: TMessage);
begin
OldWndProc(Message);
if Message.Msg = WM_NOTIFY then
begin
if TWMNotify(Message).NMHdr.code = HDN_ENDTRACK then
begin
if PHDNotify(TWMNotify(Message).NMHdr).Item = FSortedColumn then
SetListHeaderSortArrow(FSortedColumn, True);
end;
end;
end;
Upvotes: 2
Reputation: 6051
I have found a workaround that solves the problem:
procedure PALockWinControl(const WC: Vcl.Controls.TWinControl; ALock: Boolean);
begin
if (not Assigned(WC)) or (WC.Handle = 0) then EXIT;
if ALock then
WC.Perform(WM_SETREDRAW, 0, 0)
else
begin
WC.Perform(WM_SETREDRAW, 1, 0);
RedrawWindow(WC.Handle, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_ALLCHILDREN);
end;
end;
procedure MyRedrawWorkaround;
begin
with formMain do
begin
PALockWinControl(ListView1, True);
try
ListView1.Align := alNone;
ListView1.Width := lvMRUProjects.Width - 1;
ListView1.Align := alClient;
finally
PALockWinControl(ListView1, False);
end;
end;
end;
procedure TformMain.ListView1ColumnClick(Sender: TObject; Column: TListColumn);
begin
FSortedColumn := Column.Index;
case FSortedColumn of
0: FColumn0SortedUp := not FColumn0SortedUp;
1: FColumn1SortedUp := not FColumn1SortedUp;
end;
SetListHeaderSortArrow(FSortedColumn);
MyRedrawWorkaround;
end;
Upvotes: 0