当我滚动项目列表时,ListView滞后

时间:2014-07-03 16:52:01

标签: delphi listview

我有问题。在ListView中使用含有500个项目的项目列表时,它不会滞后,但是当它包含超过1000个项目时,在滚动项目列表时会滞后。

以下是我的开始:

procedure thread.ListViewAdd;
begin
  Item:=Form1.ListView1.Items.Add;
  Item.SubItems.Add('2 column');
  Item.SubItems.Add('3 column');
  Item.SubItems.Add('4 column');
  Item.SubItems.Add('5 column');
end;

然后我在thread.Execute中同步了这个程序:

procedure thread.ListViewAdd;
begin
  ListView.Items.BeginUpdate;
try
  Item:=Form1.ListView1.Items.Add;
  Item.SubItems.Add('2 column');
  Item.SubItems.Add('3 column');
  Item.SubItems.Add('4 column');
  Item.SubItems.Add('5 column');
finally
  ListView.Items.EndUpdate; 

end;

已经解决了这个问题。问题出在这里:

procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
var
  i:integer;
begin

    for i:=0 to Form1.ListView1.Items.Count-1 do
    begin
      Form1.ListView1.Items[i].Caption:=IntToStr(i+1);
    end;

end;

这段代码一直在更新项目,所以它落后了

1 个答案:

答案 0 :(得分:2)

使用数据类和第二个线程进行UI通知。

uses
  System.Generics.Collections,
  System.Classes,
  System.SyncObjs;

type
  TNotifyItems<T> = procedure( Sender : TObject; AItems : TArray<T> ) of object;

  TNotificationThread<T : class> = class( TThread )
  private
    // Synchroobjects
    FCS : TCriticalSection;
    FEvent : TEvent;
    // Cache
    FItems : TObjectList<T>;
    // Event
    FOnNotify : TNotifyItems<T>;
    // Getter/Setter
    function GetOnNotify : TNotifyItems<T>;
    procedure SetOnNotify( const Value : TNotifyItems<T> );
    // Notification
    procedure DoNotifyItems;
  protected
    procedure Execute; override;
    procedure TerminatedSet;
  public
    constructor Create;
    destructor Destroy; override;

    procedure EnqueueItem( AItem : T );
    // Event
    property OnNotify : TNotifyItems<T> read GetOnNotify write SetOnNotify;
  end;

implementation

procedure TNotificationThread<T>.Execute;
begin
  inherited;
  while not Terminated do
    begin
      // waiting for 1000ms before next notify
      if FEvent.WaitFor( 1000 ) = wrTimeOut
      then
        Synchronize( DoNotifyItems );
    end;
end;

procedure TNotificationThread<T>.DoNotifyItems;
var
  LItems : TList<T>;
  LOnNotify : TNotifyItems<T>;
begin
  LItems := TObjectList<T>.Create( true );
  try
    FCS.Enter;
    try
      // Get the Items and the event
      FItems.OwnsObjects := False;
      try
        LItems.AddRange( FItems );
        FItems.Clear;
      finally
        FItems.OwnsObjects := true;
      end;
      LOnNotify := FOnNotify;
    finally
      FCS.Leave;
    end;

    if Assigned( LOnNotify ) and ( LItems.Count > 0 )
    then
      LOnNotify( Self, LItems.ToArray );
  finally
    LItems.Free;
  end;
end;

constructor TNotificationThread<T>.Create;
begin
  inherited Create( False );
  FCS := TCriticalSection.Create;
  FEvent := TEvent.Create( nil, False, False, '' );
  FItems := TObjectList<T>.Create;
end;

destructor TNotificationThread<T>.Destroy;
begin
  inherited;
  FItems.Free;
  FEvent.Free;
  FCS.Free;
end;

procedure TNotificationThread<T>.TerminatedSet;
begin
  inherited;
  FEvent.SetEvent;
end;

function TNotificationThread<T>.GetOnNotify : TNotifyItems<T>;
begin
  FCS.Enter;
  try
    Result := FOnNotify;
  finally
    FCS.Leave;
  end;
end;

procedure TNotificationThread<T>.SetOnNotify( const Value : TNotifyItems<T> );
begin
  FCS.Enter;
  try
    FOnNotify := Value;
  finally
    FCS.Leave;
  end;
end;

procedure TNotificationThread<T>.EnqueueItem( AItem : T );
begin
  FCS.Enter;
  try
    FItems.Add( AItem );
  finally
    FCS.Leave;
  end;
end;

现在从工作线程中向NotificationThread提供数据并继续工作。

procedure TWorkingThread.Execute;
begin
  inherited;
  while not Terminated do
  begin
    // do some work
    ...
    // Notify progress, thats all here
    FDataItemNotifier.EnqueueItem( TDataItem.Create( 'Caption', '2 column', '3 column' ) );
  end;
end;

您的表单现在需要该通知事件的事件处理程序

procedure TMyForm.DataItemNotification( Sender : TObject; AItems : TArray<TDataItem> );
var
  LItem : TDataItem;
  LListItem : TListItem;
begin
  // now updating the ListView with a bunch of data
  ListView1.Items.BeginUpdate;
  try
    for LItem in AItems do
    begin
      LListItem := ListView1.Items.Add;
      LListItem.Caption := LItem.Data1Str;
      LListItem.SubItems.Add( LItem.Data2Str );
      LListItem.SubItems.Add( LItem.Data3Str );
    end;
  finally
    ListView1.Items.EndUpdate;
  end;
end;