Call the UpdateItems method after you sort the columns. For instance:.
.. col.Index := number; listview.UpdateItems(0, MAXINT); ..
Update:In my tests, apparently, I still need the above call. But the real problem is that "there is an error in the Delphi list view control."
Duplicate the problem with a simple project:
- Place the
TListView control in the VCL form, set its ViewStyle to 'vsReport' and set FullDrag to 'true'. - Put the following code in the
OnCreate handler of the form: ListView1.Columns.Add.Caption := 'col 1'; ListView1.Columns.Add.Caption := 'col 2'; ListView1.Columns.Add.Caption := 'col 3'; ListView1.AddItem('cell 1', nil); ListView1.Items[0].SubItems.Add('cell 2'); ListView1.Items[0].SubItems.Add('cell 3');
- Put a
TButton on the form and put the code below in your OnClick handler: ListView1.Columns.Add.Caption := 'col 4';
- Run the project and drag the column heading βcol 3β to the intermediate βcol 1β and βcol 2β. The following shows what you will see at the moment (everything is in order):

- Click the button to add a new column, the list now opens:

Note that "cell 2" has returned to its original position.
Error:
Columns a TListView ( TListColumn ) contain order information in the FOrderTag field. Whenever you change the order of a column (either by setting the Index property or by dragging the header), this FOrderTag updated accordingly.
Now, when you add a column to the TListColumns collection, the collection first adds a new TListColumn , and then calls the UpdateCols method. The following is the code for the UpdateCols TListColumns method in the D2007 VCL:
procedure TListColumns.UpdateCols; var I: Integer; LVColumn: TLVColumn; begin if not Owner.HandleAllocated then Exit; BeginUpdate; try for I := Count - 1 downto 0 do ListView_DeleteColumn(Owner.Handle, I); for I := 0 to Count - 1 do begin with LVColumn do begin mask := LVCF_FMT or LVCF_WIDTH; fmt := LVCFMT_LEFT; cx := Items[I].FWidth; end; ListView_InsertColumn(Owner.Handle, I, LVColumn); Items[I].FOrderTag := I; end; Owner.UpdateColumns; finally EndUpdate; end; end;
The above code removes all columns from the base API list control and then inserts them again. Notice how the code assigns an index counter to each inserted FOrderTag column:
Items[I].FOrderTag := I;
This is the order of the columns from left to right at this point in time. If the method is called whenever the columns are ordered differently than during creation, then this ordering is lost. And since the elements do not change their positions accordingly, all this is confused.
Fix:
The following modification of the method seems to work as hard as I tested, you need to do more tests (obviously, this fix does not apply to all possible cases, see the comments to torno below):
procedure TListColumns.UpdateCols; var I: Integer; LVColumn: TLVColumn; ColumnOrder: array of Integer; begin if not Owner.HandleAllocated then Exit; BeginUpdate; try SetLength(ColumnOrder, Count); for I := Count - 1 downto 0 do begin ColumnOrder[I] := Items[I].FOrderTag; ListView_DeleteColumn(Owner.Handle, I); end; for I := 0 to Count - 1 do begin with LVColumn do begin mask := LVCF_FMT or LVCF_WIDTH; fmt := LVCFMT_LEFT; cx := Items[I].FWidth; end; ListView_InsertColumn(Owner.Handle, I, LVColumn); end; ListView_SetColumnOrderArray(Owner.Handle, Count, PInteger(ColumnOrder)); Owner.UpdateColumns; finally EndUpdate; end; end;
If you are not using packages, you can place the modified copy of "comctrls.pas" in the project folder. Otherwise, you can continue to correct the code at runtime or write a bug report and wait for the fix.