TListView: VCL теряет порядок столбцов, если вы добавляете столбец
Я пытаюсь добавить столбец между существующими столбцами в TListView. Для этого я добавляю новый столбец в конце и перемещаю его, устанавливая индекс в указанное значение. Это работает, пока не добавится еще один новый столбец.
Что я сделал: добавьте столбец в последней позиции (Columns.Add) и добавьте подэлемент в последней позиции (Subitems.Add). После этого я перемещаю столбец, устанавливая его индекс в правильное положение. Это прекрасно работает, если только один столбец добавляется. При добавлении второго нового столбца подпункты облажаются. Новый подпункт первого столбца перемещается в последнюю позицию, например, так:
0 | 1 | new A | new B | 3
Caption | old sub 1 | old sub 3 | new Sub B | new sub A
Я был бы очень рад, если бы кто-то мог помочь!
Например, есть ли команда или сообщение, которое я могу отправить в ListView, чтобы он обновлял или сохранял свое сопоставление Столбец -> Подэлемент, которое я мог бы использовать после добавления первого нового столбца и его подэлементов, чтобы я мог обработать второй новый столбец так же, как и первый.
Или это просто ошибка столбца TListViews -> обработка подэлемента или TListColumns...?
пример кода для приложения vcl-форм (назначьте событие Form1.OnCreate):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
listview: TListView;
initButton: TButton;
addColumn: TButton;
editColumn: TEdit;
subItemCount: Integer;
procedure OnInitClick(Sender: TObject);
procedure OnAddClick(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
listview := TListView.Create(self);
with listview do
begin
Left := 8;
Top := 8;
Width := self.Width - 30;
Height := self.Height - 100;
Anchors := [akLeft, akTop, akRight, akBottom];
TabOrder := 0;
ViewStyle := vsReport;
Parent := self;
end;
initButton := TButton.Create(self);
with initButton do
begin
left := 8;
top := listview.Top + listview.Height + 20;
Width := 75;
Height := 25;
TabOrder := 1;
Caption := 'init';
OnClick := OnInitClick;
Parent := self;
end;
editColumn := TEdit.Create(self);
with editColumn do
begin
left := initButton.Left + initButton.Width + 30;
top := listview.Top + listview.Height + 20;
Width := 120;
Height := 25;
TabOrder := 2;
Parent := self;
Caption := '';
end;
addColumn := TButton.Create(self);
with addColumn do
begin
left := editColumn.Left + editColumn.Width + 10;
top := listview.Top + listview.Height + 20;
Width := 75;
Height := 25;
TabOrder := 1;
Enabled := true;
Caption := 'add';
OnClick := OnAddClick;
Parent := self;
end;
end;
procedure TForm1.OnInitClick(Sender: TObject);
var col: TListColumn;
i, j: integer;
item: TListItem;
begin
listview.Items.Clear;
listview.Columns.Clear;
// add items
for I := 0 to 2 do
begin
col := ListView.Columns.Add;
col.Caption := 'column ' + IntToStr(i);
col.Width := 80;
end;
// add columns
for I := 0 to 3 do
begin
item := ListView.Items.Add;
item.Caption := 'ItemCaption';
// add subitems for each column
for j := 0 to 1 do
begin
item.SubItems.Add('subitem ' + IntToStr(j+1));
end;
end;
subItemCount := 5;
end;
procedure TForm1.OnAddClick(Sender: TObject);
var number: integer;
col: TListColumn;
i: Integer;
ascii: char;
begin
listview.Columns.BeginUpdate;
number := StrToInt(editColumn.Text);
ascii := Chr(65 + number);
// create the new column
col := TListColumn(ListView.Columns.add());
col.Width := 80;
col.Caption := ascii;
// add the new subitems
for I := 0 to ListView.Items.Count-1 do
begin
ListView.Items[i].SubItems.Add('subitem ' + ascii);
end;
// move it to the designated position
col.Index := number;
listview.Columns.EndUpdate;
Inc(subItemCount);
end;
end.
Спасибо!
Редактировать: предлагаемое исправление от Sertac Akyuz работает нормально, хотя я не могу его использовать, потому что изменение исходного кода Delphi не является решением для моего проекта. Об ошибке сообщается.
Изменить: удалил второй вопрос, который был непреднамеренно включен в первый пост и открыл новый вопрос (см. Связанный вопрос и пересмотр вопроса).
Обновление: сообщенная ошибка теперь закрыта, как исправлено в Delphi XE2 Update 4.
1 ответ
Позвоните UpdateItems
метод после того, как вы расположили столбцы. Например:
..
col.Index := number;
listview.UpdateItems(0, MAXINT);
..
Обновить:
В моих тестах мне все еще, кажется, нужен вышеупомянутый вызов в некотором случае. Но настоящая проблема заключается в том, что "в элементе управления списком Delphi есть ошибка".
Дублирование проблемы с простым проектом:
- Поместите
TListView
управление в форме VCL, установите егоViewStyle
"vsReport" и установитьFullDrag
на "правда". - Поместите приведенный ниже код в
OnCreate
обработчик формы: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');
- Поместите
TButton
в форме, и поместите приведенный ниже код в егоOnClick
обработчик:ListView1.Columns.Add.Caption := 'col 4';
- Запустите проект и перетащите заголовок столбца "col 3" между "col 1" и "col 2". На картинке ниже вы увидите (все в порядке):
- Нажмите кнопку, чтобы добавить новый столбец, теперь представление списка становится:
Обратите внимание, что "ячейка 2" вернула свое первоначальное положение.
ошибка:
Столбцы TListView
(TListColumn
) содержит информацию о своем заказе в FOrderTag
поле. Всякий раз, когда вы меняете порядок столбца (либо путем установки Index
свойство или перетаскивая заголовок), это FOrderTag
обновляется соответственно.
Теперь, когда вы добавляете столбец в TListColumns
коллекция, коллекция сначала добавляет новый TListColumn
а затем вызывает UpdateCols
метод. Ниже приведен код UpdateCols
метод TListColumns
в 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;
Приведенный выше код удаляет все столбцы из базового элемента управления представления списка API, а затем вставляет их заново. Обратите внимание, как код назначает каждому вставленному столбцу FOrderTag
счетчик индекса:
Items[I].FOrderTag := I;
Это порядок столбцов слева направо в данный момент времени. Если метод вызывается всякий раз, когда столбцы упорядочены иначе, чем во время создания, то это упорядочение теряется. И поскольку предметы не меняют своих позиций соответственно, все это путается.
Fix:
Приведенная ниже модификация метода, казалось, работала так же мало, как я тестировал, вам нужно провести больше тестов (очевидно, это исправление не охватывает все возможные случаи, подробности смотрите в комментариях 'torno ниже):
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;
Если вы не используете пакеты, вы можете поместить измененную копию 'comctrls.pas' в папку вашего проекта. В противном случае вы можете продолжить исправление кода во время выполнения или подать отчет об ошибке и дождаться исправления.