Veremos como adicionar uma barra de progresso (ou qualquer outro componente Delphi) a um controle ListView. A maioria dos gerenciadores de download, utilizam um ListView para visualização do andamento do carregamento para cada item. O ListView exibe uma lista de items de várias maneiras.

Um ProgressBar exibe uma barra de progresso simples. Barras de progresso proporcionam aos usuários visualização sobre o progresso de um procedimento dentro de uma aplicação. Veremos como colocar uma barra de progresso em uma coluna do ListView.

ProgressBar em ListView

Quando atribuímos vsReport à propriedade ViewStyle de um ListView, cada item aparece em sua própria linha com as informações organizadas em colunas. A coluna mais à esquerda, contém o pequeno ícone e o rótulo e as colunas subseqüentes contêm subitens conforme especificados pela aplicação. Provavelmente todo dia vemos isto ao utilizarmos o Windows Explorer em modo de visão Details.

Suponhamos que temos um ListView em um formulário com vsReport em ViewStyle. Duas colunas são definidas utilizando a propriedade Columns: a primeira coluna contém o nome do item, enquanto a segunda coluna deveria conter um indicador de progresso.

Quando clicarmos o botão Adicionar um novo item com uma barra de progresso deveria ser adicionado à lista:

 
procedure TForm1.AddItemButtonClick(Sender: TObject);
  const
    pbColumnIndex = 1;
    pbMax = 100;
  var
    li: TListItem;
    lv: TListView;
    pb: TProgressBar;
    pbRect : TRect;
  begin
    lv := ListViewEx1;
    li := lv.Items.Add;
    li.Caption := 'Item ' + IntToStr(lv.Items.Count);
    pb := TProgressBar.Create(nil);
    pb.Parent := lv;
    li.Data := pb;
    pbRect := li.DisplayRect(drBounds);
    pbRect.Left := pbRect.Left + 
    lv.Columns[-1 + pbColumnIndex].Width;
    pbRect.Right := pbRect.Left + 
    lv.Columns[pbColumnIndex].Width;
    pb.BoundsRect := pbRect;
end; //AddItemButtonClick

Quando clicamos no botão um novo item de lista (TListItem) é acrescentado ao controle ListView (chamado ListViewEx1). Uma barra de progresso é criada, uma referência para a barra de progresso é adicionada à propriedade Data do item de lista e, finalmente, uma barra de progresso é colocada na coluna especificada por pbColumnIndex. Alguns cálculos são utilizados para fazer a barra de progresso aparecer no lugar certo.

Quando quisermos remover um item da lista, precisamos ter certeza de que a memória da barra de progresso “anexada” seja liberada e todas as barras de progresso sob a selecionada são movidas uma posição para cima (como acontece com todos os itens restantes de um ListView):

procedure TForm1.RemoveItemButtonClick( Sender: TObject);
  var
    lv: TListView;
    li: TListItem;
    i, idx: integer;
    pb: TProgressBar;
  begin
    lv := ListViewEx1;
    li := lv.Selected;
    if li <> nil then
  begin
    idx := li.Index;
    TProgressBar(li.Data).Free;
    lv.Items.Delete(idx);
    for i := idx to -1 + lv.Items.Count do
      begin
      li := lv.Items.Item[i];
      pb := TProgressBar(li.Data);
      pb.Top := pb.Top - 
      (pb.BoundsRect.Bottom - 
      pb.BoundsRect.Top);
    end;
  end;
end; //RemoveItemButtonClick

Apenas para testarmos, adicionaremos algum código dentro do evento OnTimer de um Timer, para ter algum progresso nas barras (colocar um Timer em um formulário e utilizar o código abaixo para o evento OnTimer). Obviamente, em aplicações reais atualizaríamos a barra de progresso de um item com base em algum critério, só precisamos localizar “a” barra de progresso (“armazenada“ na propriedade Data de um item):

procedure TForm1.Timer1Timer(
  Sender: TObject);
var
   idx: integer;
   pb: TProgressbar;
   lv: TListView;
begin
   lv := ListViewEx1;
   if lv.Items.Count = 0 then Exit;
     idx := Random(lv.Items.Count);
     pb := TProgressBar(lv.Items[idx].Data);
   if pb.Position < pb.Max then
     pb.StepIt
   else
   pb.Position := 0;
end;//Timer1Timer

Rodamos a aplicação, clicamos no botão Adicionar várias vezes e vemos como se comporta o progresso. Agora, tentemos redimensionar qualquer coluna... uuoopps! Quando redimensionamos as colunas necessárias para reposicionar a barra de progresso, ListView não fornece nenhum evento para tratar o redimensionando de colunas!

TListViewEx - ListView com eventos de redimensionamento de colunas

A solução para tratar o redimensionamento de colunas, consiste em utilizar um ListView derivado: TListViewEx. O TListViewEx (componente que esta disponível junto com o exemplo do artigo) é um descendente de TListView com os eventos BeginColumnResize, ColumnResize e EndColumnResize publicados. Agora, com o poder do TListViewEx, podemos controlar facilmente o redimensionamento de colunas. Veremos como tratar o evento OnEndColumnResize para ajustar a largura e a margem esquerda das barras de progresso:

procedure TForm1.ListViewEx1EndColumnResize(
    sender: TCustomListView;
    columnIndex,
    columnWidth: Integer);
 var
    lv: TListViewEx;
    idx: integer;
    pb: TProgressBar;
 begin
    lv := ListViewEx1;
    if columnIndex = 0 then
    begin
      for idx := 0 to -1 + lv.Items.Count do
    begin
      pb := TProgressBar(lv.Items[idx].Data);
      pb.Left := columnWidth;
      end;
    end;
 
  if columnIndex = 1 then
  begin
  for idx := 0 to -1 + lv.Items.Count do
  begin
    pb := TProgressBar(lv.Items[idx].Data);
    pb.Width := columnWidth;
    end;
  end;
 end;