AsuYuHomepage
トップページ サイトの説明 子供の成長記録 JM BAR Delphi リンク集
Home>>プログラミング>>Tips&Tricks>>OwerDataプロパティの使用でTListViewの表示を高速化する
Delphi Tips
Delphi Win32API
ダウンロード

Delphi Tips & Tricks

OwerDataプロパティの使用でTListViewの表示を高速化する

 TListViewに、多数の項目を追加すると、表示に時間がかかってしまうことがあります。
 そんなときはOwerDataプロパティを True にして、OnDataイベントで項目のCaptionなどを指定してやることで驚くほど高速な表示になります。

今回は普通に10000項目追加する場合と、OwnerData = True のときの処理時間を比較してみました。
var
  ST: TStringList; //項目文字列のの格納用

procedure TForm1.FormCreate(Sender: TObject);
begin
  ST :=TStringList.Create;
  ListView2.OwnerData :=True; //設計時に指定も可能
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ST.Free;
end;

//普通に10000個のItemを追加
//10000個の解放にも処理時間がかかるためフォームを閉じてから
//IDEに処理が戻るまで時間がかかります。要注意
procedure TForm1.Button1Click(Sender: TObject);
var
  i : Integer;
  LI: TListItem;
  t : Cardinal;
begin
  ListView1.Clear;
  t :=GetTickCount;
  ListView1.Items.BeginUpdate;
  for i := 1 to 10000 do
  begin
    LI := ListView1.Items.Add;
    LI.Caption := inttostr(i);
  end;
  ListView1.Items.EndUpdate;
  //私の環境では約4.5秒かかりました。
  ShowMessage(IntToStr(GetTickCount -t));
end;
  
//OwnerData = True のバージョン
procedure TForm1.Button2Click(Sender: TObject);
var
  i: integer;
  t: Cardinal;
begin
  t :=GetTickCount;
  ST.Clear;
  for i :=0 to 10000 do
    ST.Add(InttoStr(i));

  ListView2.Items.Count :=ST.Count;
  //私の環境では約0.05秒ほどでした。
  ShowMessage(IntToStr(GetTickCount -t));
end;

//OwnerData = Trueのときに必要な OnDataイベントの処理
procedure TForm1.ListView2Data(Sender: TObject; Item: TListItem);
begin
  //項目ごと動的にデータを与える
  Item.Caption :=ST[Item.Index];
end;

TListを使って、TListItemの情報を格納していく方法もあります。
OnDataイベント内でSubItems.Addをするとメモリリークを起こすらしいですが、TList内の保持しているポインタをちゃんと解放してやれば(ClearList手続きがそれです)大丈夫そうです。MemCheackをやってみてもメモリリークは発生していませんでした。
var
  vList: TList;

procedure TForm1.FormCreate(Sender: TObject);
begin
  vList := TList.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ClearList(vList);
  vList.Free;
end;

//一旦リストの中のTListItemを解放するヘルパー手続き
procedure TForm1.ClearList(List: TList);
var
  i :integer;
begin
  for i := vList.Count -1 downto 0 do
  begin
    TListItem(vList[i]).Free;
    vList.Delete(i);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i : Integer;
  LI : TListItem;
begin
  ListView1.Items.BeginUpdate;
  ClearList(vList);
  for i := 1 to 10000 do
  begin
    LI := TListItem.Create(ListView1.Items);
    LI.Caption := InttoStr(i);
    LI.SubItems.Add(InttoStr(i * 2));
    vList.Add(LI);
  end;

  ListView1.Items.Count := vList.Count;
  ListView1.Items.EndUpdate;
end;

//TListのポインタをTListItemにキャストして使用
procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
begin
  Item.Caption := TListItem(vList[Item.Index]).Caption;
  Item.SubItems.Add(TListItem(vList[Item.Index]).SubItems[0]);
end;