DropDownList dentro de um DBGrid - Parte II

 

A idéia aqui, é mostrar como colocar quase qualquer controle Delphi (componente visual) em uma célula de um DGBrid. Se você não estiver familiarizado com a idéia, por favor, leia antes o artigo acima mencionado.

Na primeira parte deste artigo (segundo da série), vimos campos lookup e quais são as opções de exibição de um campo lookup em um DBGrid. Está na hora de ver como colocar um DBLookupComboBox em uma célula de um DGBrid, para permitir ao usuário a escolha de um valor para um campo lookup de um drop-down list.

 

A seguir, mostraremos como colocar um DBLookupComboBox em um DBGrid. Crie interfaces do usuário visualmente mais atraentes para editar campos lookup dentro de um DBGrid – coloque um DBLookupComboBox em uma célula de um DBGrid.

DBLookupComboBox em um DBGrid?

Sim, da mesma maneira que podemos colocar um check box em uma célula de um DBGrid (para editar campos boolean), podemos usar a mesma técnica para pôr um combobox em uma célula que contém um campo lookup.

 

Lembremos que um DBLookupComboBox é usado em aplicações banco de dados, para apresentar ao usuário uma lista restrita de escolhas para configurar um valor de campo válido. Quando um usuário selecionar um item da lista, o valor do campo correspondente será alterado no dataset subjacente. Em geral, você usa um DBLookupComboBox em um formulário de entrada de dados, sem relação com um DBGrid. Agora, faremos com que um DBLookupComboBox flutue por cima de uma célula que contém um campo lookup.

Criando uma aplicação exemplo

Note que já criamos, no artigo anterior, uma aplicação exemplo que contém um DBGrid e vários componentes relacionados a DB. Vejamos como:

Para começar, inicie o Delphi e, no formulário padrão vazio coloque um TDBGrid, um TADOTable, um TADOConnection e um TDataSource. Deixe os nomes dos componentes como o Delphi os chamou quando colocados no formulário (DBGrid1, ADOQuery1, AdoTable1,...).

 

Use o Object Inspector para configurar a propriedade ConnectionString do componente ADOConnection1 (TADOConnection) para apontar para o banco de dados de exemplo QuickiesContest.mdb do MS Access. Conecte o DBGrid1 ao DataSource1, o DataSource1 ao ADOTable1 e finalmente o ADOTable1 ao ADOConnection1. A propriedade TableName de ADOTable1 deverá apontar para tabela Articles (fazendo com que o DBGrid exiba os registros da mesma). Nota (novamente): nada o impede de usar qualquer outro componente dataset do Delphi, tal como TTable, TQuery,...

 

Agora, dê uma olhada no campo AuthorEmail. Note que este campo pode aceitar só valores de campo E-mail da tabela Authors (definido através de integridade referencial). O que você precisa “observar" na figura abaixo é que, ao editar este campo sem lookup, se você tentar substituir um valor com outro valor não encontrado na tabela Authors (tabela "mestre"), receberá a mensagem de erro "You cannot add or change a record because a related record is required in table Authors" (“Você não pode adicionar ou alterar um registro porque um registro relacionado é requerido na tabela Authors”).

 

Figura 1.

O que iremos ver neste artigo, é como fazer com que a figura anterior tenha o seguinte aspecto:

 

Figura 2.

Ok. Primeiro precisamos adicionar vários outros componentes em um formulário, junto com um DBLookupComboBox. A seguir temos configurá-los e fazer um pouco de magia Delphi ;).

Vejamos como colocar um DBLookupComboBox em um DBGrid. Crie interfaces do usuário visualmente mais atraentes para editar campos lookup dentro de um DBGrid – coloque um DBLookupComboBox em uma célula de um DBGrid.

Esta é que é uma grande idéia! Vejamos como criar os melhores editores de dados para um grid!

Criando um lookup com um DBLookupComboBox

Para exibir um DBLookupComboBox dentro de uma célula de um DBGrid, precisaremos tornar um disponível em tempo de execução. Selecione "data controls" na Paleta de Componentes e escolha um DBLookupComboBox. Coloque-o em qualquer lugar no formulário (deixe o nome padrão, "DBLookupComboBox1") - não importa onde, pois a maior parte do tempo será invisível ou flutuará por cima do grid.

Em seguida, temos que adicionar mais um DataSource e um componente DataSet para preencher o "combobox” com valores. Coloque um TDataSource (nome: DataSource2) e um TAdoQuery (nome: AdoQuery1) em qualquer lugar do formulário.

 

Para que o DBLookupComboBox trabalhe corretamente, outras propriedades devem ser configuradas, estas propriedades são fundamentais para a conexão de lookup:

·         DataSource e DataField determinam a conexão principal. O DatField é um campo no qual inserimos o valores lookup.

·         ListSource é a fonte do dataset de lookup.

·         KeyField identifica o campo no ListSource que tem que casar com o valor do campo DataField.

·         ListFields é(são) o(s) campo(s) (um ou mais) do dataset de lookup que serão de fato exibidos no combobox. ListField pode mostrar mais de um campo.

Nomes de campo múltiplos deveriam ser separados através de ponto-e-vírgulas. Você tem que configurar um valor grande o bastante para o DropDownWidth (de um ComboBox), para realmente ver colunas de dados múltiplas. Vejamos como configurar todas as propriedades importantes do código (no manipulador de evento OnCreate do formulário):

 

procedure TForm1.FormCreate(Sender: TObject);

begin

 with DBLookupComboBox1 do

 begin

   DataSource := DataSource1; // -> AdoTable1 -> DBGrid1

   ListSource := DataSource2;

   DataField   := 'AuthorEmail'; // de AdoTable1 – exibido no DBGrid

   KeyField  := 'Email';

   ListFields := 'Name; Email'; 

   Visible    := False;

 end;

 

 DataSource2.DataSet := AdoQuery1;

 AdoQuery1.Connection := AdoConnection1;

 AdoQuery1.SQL.Text := 'SELECT Name, Email FROM Authors';

 AdoQuery1.Open;

end;

 

Nota: quando você quiser exibir mais de um campo em um DBLookupComboBox, como no exemplo acima, tem que ter certeza que todas as colunas estarão visíveis. Isto é determinado configurando a propriedade DropDownWidth. Porém, você verá que inicialmente temos que configurá-la com um valor muito grande, o que resulta em um lista drop-down muito larga na maioria dos casos.

 

Uma solução alternativa é configurar o DisplayWidth de um Campo particular mostrado em uma drop-down list. O próximo trecho de código, colocado dentro do evento OnCreate do formulário, garante que o nome de autor e o e-mail é exibido dentro da drop-down list:

 

AdoQuery1.FieldByName('Email').DisplayWidth:=10;

AdoQuery1.FieldByName('Name').DisplayWidth:=10;

AdoQuery1.DropDownWidth:=150;

 

Magia...

O que falta é fazer com que efetivamente o combobox paire por cima de uma célula (quando em modo de edição), exibindo o campo AuthorEmail. Já vimos a teoria - mostraremos aqui apenas o código (você poderá carregar o projeto inteiro depois):

Primeiro, precisamos ter certeza de que o DBLookupComboBox1 está sendo movido (e redimensionado) por cima da célula na qual o campo AuthorEmail é exibido.

 

procedure TForm1.DBGrid1DrawColumnCell

  (Sender: TObject;

   const Rect: TRect;

   DataCol: Integer;

   Column: TColumn;

   State: TGridDrawState);

begin

  if (gdFocused in State) then

  begin

    if (Column.Field.FieldName = DBLookupComboBox1.DataField) then

    with DBLookupComboBox1 do

    begin

      Left := Rect.Left + DBGrid1.Left + 2;

      Top := Rect.Top + DBGrid1.Top + 2;

      Width := Rect.Right - Rect.Left;

      Width := Rect.Right - Rect.Left;

      Height := Rect.Bottom - Rect.Top;

      Visible := True;

    end;

  end

end;

 

Portanto, quando sair da célula, temos que esconder o combobox:

 

procedure TForm1.DBGrid1ColExit(Sender: TObject);

begin

  if DBGrid1.SelectedField.FieldName = DBLookupComboBox1.DataField then

    DBLookupComboBox1.Visible := False

end;

 

Agora, note que no modo de edição, todas as teclas pressionadas são encaminhadas para a célula do DBGrid, temos que redirecioná-las para o DBLookupComboBox. No caso de um DBLookupComboBox, estamos principalmente interessados nas teclas [Tab] ([Tab] deveria mover o foco de entrada para a próxima célula).

 

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);

begin

  if (key = Chr(9)) then

    Exit;

  if (DBGrid1.SelectedField.FieldName = DBLookupComboBox1.DataField) then

  begin

    DBLookupComboBox1.SetFocus;

    SendMessage(DBLookupComboBox1.Handle, WM_Char, word(Key), 0);

  end

end;

 

Isso é tudo. Rode o projeto e voila... Temos um drop-down listbox (com duas colunas!) nas colunas do campo AuthorEmail.

O que acontece quando você seleciona um valor (Nome; E-mail) da drop-down list? Quando você escolhe um item ("row") de um DBLookupComboBox, o valor ou o campo KeyField correspondente é armazenado como o valor do campo DataField.