6String_Coder
6String_Coder

Reputation: 547

TAniIndicator not spinning - loading a tlistview

I'm new with the TAniindicator component, so for testing purposes I have put together a project that will build a listview and display/spin the Aniindicator whilst the listview is being built.

type
TLoadThread = class(TThread)
public
 constructor Create; reintroduce;
protected
 procedure Process;
 procedure Execute; override;
end;    

constructor TLoadThread.Create;
begin
 inherited Create(True);
 FreeOnTerminate := True;
end;

procedure TLoadThread.Process;
begin
 Form1.BuildListView;
end;

procedure TLoadThread.Execute;
begin
 inherited;
 FreeOnTerminate := True;
 Synchronize(Process);
end;

var _loadThread : TLoadThread;

procedure TForm1.ThreadTerminated(Sender: TObject);
begin  
 AniIndicator1.Enabled := False;
 AniIndicator1.Visible := False;
end;

procedure TForm1.BuildListView;
var i : integer;
    LI : TListViewItem;
 begin
  Listview1.BeginUpdate;
 try
  for i := 1 to 2000 do
  begin
   LI := Listview1.Items.Add;
   LI.Text := 'Listview Item ' + IntToStr(i);
  end;
 finally
  Listview1.EndUpdate;
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 AniIndicator1.Visible := False;
 _loadThread := nil;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 _loadThread := TLoadThread.Create;
 _loadThread.OnTerminate := ThreadTerminated;
 _loadThread.Start;
 AniIndicator1.Enabled := True;
end;

I thought I was on the right track but this doesn't appear to work, can anyone explain what I'm doing wrong please?

Upvotes: 1

Views: 1326

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596156

Your worker thread is spending all of its time inside of its Process() method, which is being called by TThread.Synchronize() so it runs in the main UI thread. Process() is not processing UI messages, which is why TAniIndicator does not work.

As-is, your worker thread is completely useless. All of your code is running in the main UI thread. So, you may as well get rid of TLoadThread altogether:

procedure TForm1.FormCreate(Sender: TObject);
begin
  AniIndicator1.Visible := False;
end;

procedure TForm1.BuildListView;
var
  i : integer;
  LI : TListViewItem;
begin
  AniIndicator1.Visible := True;
  AniIndicator1.Enabled := True;
  ListView1.BeginUpdate;
  try
    for i := 1 to 2000 do
    begin
      LI := ListView1.Items.Add;
      LI.Text := 'ListView Item ' + IntToStr(i);
      if (i mod 100) = 0 then
        Application.ProcessMessages;
    end;
  finally
    ListView1.EndUpdate;
    AniIndicator1.Enabled := False;
    AniIndicator1.Visible := False;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  BuildListView;
end;

Otherwise, if you use a thread, do not synchronize the loop itself, only the pieces that actually touch the UI:

type
  TLoadThread = class(TThread)
  public
    constructor Create; reintroduce;
  protected
    procedure Execute; override;
  end;

constructor TLoadThread.Create;
begin
  inherited Create(True);
  FreeOnTerminate := True;
end;

procedure TLoadThread.Execute;
begin
  Form1.BuildListView;
end;

var
  _loadThread : TLoadThread = nil;

procedure TForm1.ThreadTerminated(Sender: TObject);
begin
  _loadThread := nil;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  AniIndicator1.Visible := False;
end;

procedure TForm1.BuildListView;
var
  i : integer;
begin
  TThread.Synchronize(nil,
   procedure
   begin
     AniIndicator1.Visible := True;
     AniIndicator1.Enabled := True;
     ListView1.BeginUpdate;
   end
  );
  try
    for i := 1 to 2000 do
    begin
      TThread.Synchronize(nil,
        procedure
        var
          LI : TListViewItem;
        begin
          LI := ListView1.Items.Add;
          LI.Text := 'ListView Item ' + IntToStr(i);
        end
      );
    end;
  finally
    TThread.Synchronize(nil,
      procedure
      begin
        ListView1.EndUpdate;
        AniIndicator1.Enabled := False;
        AniIndicator1.Visible := False;
      end
    );
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if _loadThread <> nil then
  begin
    _loadThread := TLoadThread.Create;
    _loadThread.OnTerminate := ThreadTerminated;
    _loadThread.Start;
  end;
end;

Upvotes: 2

Related Questions