Um dos recursos mais utilizados no Windows certamente é o Drag-and-Drop que significa arrastar-e-soltar. Em toda minha experiência com programação vejo pouca utilização deste recurso em aplicações Delphi e muitas dúvidas em fóruns, por isso elaborei alguns exemplos para entendermos melhor como funciona este recurso.

Entendendo a lógica

O recurso de drag-and-drop é ativado partindo do evento onMouseDown do controle Origem. É nele que iniciamos a operação usando o método BeginDrag do objeto. A finalização do arrasto é controlada pelo evento onEndDrag também do objeto Origem.

No objeto Destino usamos os eventos onDragOver para descobrirmos se o Source (objeto Origem) é o objeto esperado e assim aceitar a ação. Ainda no objeto Destino, no evento onDragDrop, ou seja, quando o usuário soltar o mouse sobre o objeto, chamamos novamente o evento onEndDrag do objeto Origem passando o parâmetro True. Isso fará com que a ação seja finalizada. Vamos desenvolver um exemplo prático pra que fique mais claro.

1º Exemplo: Arrastando um TAnimate

Nosso primeiro exemplo é bastante simples. Insira um TListBox, um TPanel e dentro do TPanel um TAnimate. No TListBox insira os seguintes valores na propriedade Items:

  • aviNone
  • aviFindFolder
  • aviFindFile
  • aviFindComputer
  • aviCopyFiles
  • aviCopyFile
  • aviRecycleFile
  • aviEmptyRecycle
  • aviDeleteFile

Como o componente TAnimate não possui os eventos onDragOver e onDragDrop, vamos usar o TPanel como “controlador”, ou seja, ele que receberá o arrasto e definirá o novo AVI do componente e o porá em execução.

No evento onMouseDown do ListBox (neste exemplo deu-se o nome de LstAvi) digite o algoritmo da Listagem 1. Este algoritmo verifica se o objeto (Sender) é o LstAvi e ainda se o botão esquerdo do mouse está pressionado. Em caso positivo, dá início ao processo de drag chamando o método BeginDrag.

Listagem 1. Evento onMouseDown do LstAvi (ListBox)


procedure TFrmDragDrop.LstAviMouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 begin
   if (Sender = LstAvi) and (Button = mbLeft) then
     LstAvi.BeginDrag(False); // inicia a operacao
 end;

Agora vamos mexer com o nosso TPanel. Em seu evento onDragDrop vamos fazer uma simples verificação. Se o Source (objeto origem) for o LstAvi então chamamos o método EndDrag, bem simples, como mostra a Listagem 2.

Listagem 2. Evento onDragDrop do Panel


procedure TFrmDragDrop.Panel1DragDrop(Sender, Source: TObject; X,
   Y: Integer);
 begin
   if Source = LstAvi then
     LstAvi.EndDrag(True); // finaliza o processo
 end;

Ainda no TPanel, porém agora no evento onDragOver, faremos outra verificação para aceitar ou não a ação. Se o objeto Origem (Source) for LstAvi então aceitamos: Accept := True, como mostra a Listagem 3.

Listagem 3. Evento onDragOver do Panel


procedure TFrmDragDrop.Panel1DragOver(Sender, Source: TObject; X,
   Y: Integer; State: TDragState; var Accept: Boolean);
 begin
   if Source = LstAvi then
     Accept := True; // aceita a ação
 end;

Pra finalizar este primeiro exemplo, basta programarmos o evento onEndDrag do LstAvi fazendo com que o mesmo mude o avi do TAnimate e ative-o.

Para isso verificamos se o alvo (Target) é diferente de Nil, nos informando que houve uma chamada ao evento por outro componente, assim sendo comandamos as ações que trocam o avi e ativam-o. Veja como fica na Listagem 4.

Listagem 4. Evento onEndDrag do LstAvi


procedure TFrmDragDrop.LstAviEndDrag(Sender, Target: TObject; X,
   Y: Integer);
 begin
   if Target <> nil then
   begin    
     if Animate1.CommonAVI <> aviNone then
       Animate1.Stop;
     Animate1.CommonAVI := TCommonAVI(LstAvi.ItemIndex);
     if Animate1.CommonAVI <> aviNone then
       Animate1.Play(Animate1.StartFrame, Animate1.StopFrame, 0);
   end;
 end;

2º Exemplo: Trocando itens entre ListBox

Nosso segundo exemplo é um pouco mais trabalhoso, mas segue a mesma lógica aplicada no exemplo anterior. Vamos “trocar” de lugar os itens de dois ListBox, ou seja, poderemos arrasta-los de um lado para o outro.

Para este exemplo precisaremos apenas de dois listbox em tela com os nomes de ListaOrigem e ListaDestino.

Em cada listbox usaremos os eventos onMouseDown, onEndDrag, onDragOver e onDragDrop. Pra facilitar nossa vida, faremos um typecast nos eventos para identificar quem está sendo Origem e Destino em cada evento, desviando desta forma nosso fluxo de programação tornando os eventos genéricos.

Começaremos pelo ListaOrigem no evento onMouseDown. A dica é simples: basta verificar se o Sender é o ListaOrigem ou ListaDestino e assim chamar o método BeginDrag do TListBox, como mostra a Listagem 5.

Listagem 5. Evento onMouseDown do ListaOrigem (ListBox)


procedure TFrmDragDrop.ListaOrigemMouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 begin
   if (Button = mbLeft) and ((Sender = ListaOrigem) or (Sender = ListaDestino)) then
     TListBox(Sender).BeginDrag(False) // inicia a operacao
 end;

No evento onDragOver da ListaOrigem faremos o “aceite” da ação, como mostra a Listagem 6.

Listagem 6. Evento onDragOver


procedure TFrmDragDrop.Panel1DragOver(Sender, Source: TObject; X,
   Y: Integer; State: TDragState; var Accept: Boolean);
 begin
   if (Source = ListaOrigem) or (Source = ListaDestino) then
     Accept := True
 end;

Seguindo a mesma linha de raciocínio chamamos o método EndDrag no evento onDragDrop, como mostra aListagem 7.

Listagem 7. Evento onDragDrop


procedure TFrmDragDrop.Panel1DragDrop(Sender, Source: TObject; X,
   Y: Integer);
 begin
   if (Source = ListaOrigem) or (Source = ListaDestino) then
     TListBox(Sender).EndDrag(True);
 end;

Pra fechar o segundo exemplo basta agora programar a parte mais importante, o evento onEndDrag. Neste caso basta verificar se o alvo é diferente de Nil e então usar o método MoveSelection do componente TListBox, assim como mostra a Listagem 8.

Listagem 8. Evento onEndDrag

procedure TFrmDragDrop.LstAviEndDrag(Sender, Target: TObject; X,
   Y: Integer);
 begin
   if Target <> nil then
     // trata o fim de uma operacao que foi aceita
     TListbox(Sender).MoveSelection(TListBox(Target));
 end;

Note que em nenhum momento trabalhamos os eventos da ListaDestino, certo? Bom, agora basta associar os eventos onMouseDown, onEndDrag, onDragOver e onDragDrop do ListaDestino aos respectivos eventos de ListaOrigem.

3º Exemplo: Drag-and-drop X DBGrid

Para treinarmos ainda mais o recurso este terceiro exemplo mostra como usar o drag-and-drop com DBGrid. Vamos “trazer” o título da coluna do dbgrid para um Edit.

Precisaremos de: um DBGrid, um Table, um DataSource e um Edit. Ligue o Table da tabela Animals do aliás DBDEMOS. Ligue também o DataSource e o DBGrid.

A programação segue a mesma linha anterior, portanto não entrarei em detalhes. A listagem 9 mostra os eventos onMouseDown e onEndDrag do DBGrid e também onDragDrop e onDragOver do Edit1.

A única alteração em relação aos dois últimos exemplos é que precisamos pegar o título da coluna que o usuário arrastou para o Edit1. Para isso declare a variável Titulo pública para que possamos enxergá-la em toda unit (Listagem 10).

No evento onTitleClick do DBGrid atualizamos a variável Titulo com o nome da coluna. (Listagem 11).

Listagem 10. Eventos do DBGrid

procedure TFrmDragDrop.DBGrid1MouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 begin
   if (Sender = DbGrid1) and (Button = mbLeft) then
     DbGrid1.BeginDrag(False); // inicia a operacao<
 end;
  
 procedure TFrmDragDrop.DBGrid1EndDrag(Sender, Target: TObject; X,
   Y: Integer);
 begin
   if Target <> nil then
     Edit1.Text := Titulo;
 end;
  
 procedure TFrmDragDrop.Edit1DragDrop(Sender, Source: TObject; X,
   Y: Integer);
 begin
   if Source = DbGrid1 then
     DbGrid1.EndDrag(True); // comanda o final da operacao
 end;
  
 procedure TFrmDragDrop.Edit1DragOver(Sender, Source: TObject; X,
   Y: Integer; State: TDragState; var Accept: Boolean);
 begin
   if Source = DbGrid1 then
     Accept := True; // aceita se vier da lista
 end;

Listagem 11. Evento onTitleClick do DBGrid

procedure TFrmDragDrop.DBGrid1TitleClick(Column: TColumn);
 begin
    Titulo := Column.FieldName;
 end;

Bem, com isso finalizamos esta bateria de exemplos sobre Drag-and-drop.

Conclusões

Nestes exemplos vimos como é fácil trabalhar com um dos recursos mais utilizados nas interfaces para Windows. O drag-and-drop é útil em diversas ocasiões e ajuda na produtividade do sistema.