A seguir, veremos como colocar um check box em um DBGrid. Criar interfaces do usuário visualmente mais atraentes para editar campos boolean dentro de um DBGrid.

Este é o primeiro artigo da série de artigos chamada "Adding components to a DBGrid" (“Adicionando componentes a um DBGrid”). A idéia aqui, é mostrar como colocar quase qualquer controle Delphi (componente visual), em uma célula de um DGBrid. Se não estiver familiarizado com a ideia, por favor, leia antes o artigo acima mencionado.

CheckBox em um DBGrid?

Como vimos no artigo anterior, há muitos modos (e razões) para considerarmos a personalização das saídas de um DBGrid: suponha que existe um campo boolean no dataset. Por padrão, o DBGrid exibe campos boolean como "True" ou "False", dependendo do valor do campo. Se você pensa como eu, concordará que será visualmente muito mais atraente se pudéssemos usar um "verdadeiro" controle check box, para habilitar a edição de estes campos.

Criando uma aplicação exemplo

Para começar, inicie o Delphi e, no formulário inicial padrão vazio, coloque um TDBGrid, um ADOTable, um TADOConnection e um TDataSource. Deixe os nomes dos componentes do jeito que o lphi 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 do ADOTable1 deverá apontar para a tabela Articles (para que o DBGrid passe a exibir os registros da mesma). Se você configurou todas as propriedades corretamente, quando rodar a aplicação (desde que a propriedade Active do componente ADOTable1 seja True) deverá obter o seguinte:

O que você precisa “reparar" na figura anterior é que, por padrão, o DBGrid exibe o valor do campo boolean como "True" ou" False", dependendo do valor do campo. O campo que armazena o valor boolean é "Winner". O que iremos mostrar neste artigo, é como fazer para que a figura anterior tenha o seguinte aspecto:

CheckBox em um DBGrid!

Ou, melhor dizendo, um DBCheckBox em um DBGrid.

Ok, aqui vamos nós. Para mostrar um check box dentro de uma célula de um DBGrid, precisaremos fazer com que um fique disponível em tempo de execução. Selecione “Data controls" na Component Palette, e escolha um TDBCheckbox. Coloque um em qualquer lugar do formulário (não importa onde, desde que a maior parte do tempo estará invisível ou flutuando por cima do grid). O TDBCheckBox é um controle alerta a dados (data-aware control), que permite ao usuário selecionar ou de-selecionar um único valor – muito apropriado para campos boolean.

A seguir, configure sua propriedade Visible para False. Altere a propriedade Color do DBCheckBox1 para a mesma do DBGrid (assim se confundirá com a do DBGrid) e remova o Caption. E o mais importante, certifique-se de que o DBCheckBox1 esteja conectado ao DataSource1 e para o campo correto (DataSource = DataSource1, DataField = Winner).

Repare que todos os valores de propriedade do DBCheckBox1, podem ser configurados no evento OnCreate do formulário:

procedure TForm1.FormCreate(Sender: TObject);
 begin
  DBCheckBox1.DataSource := DataSource1;
  DBCheckBox1.DataField := 'Winner';
  DBCheckBox1.Visible := False;
  DBCheckBox1.Color := DBGrid1.Color;
  DBCheckBox1.Caption := '';
  //explicado mais adiante no artigo
  DBCheckBox1.ValueChecked := 'Yes a Winner!'; 
  DBCheckBox1.ValueUnChecked := 'Not this time.'; 
 end;

O que vem a seguir é a parte mais interessante. Ao editarmos o campo boolean no DBGrid, precisamos ter certeza de que o DBCheckBox1 está posicionado acima ("flutuando") da célula do DBGrid que exibe o campo boolean. Para o resto das células (não focalizadas), contendo os campos boolean (na coluna "Winner"), precisamos prover alguma representação ao vivo do valor boolean (True/False).

Isto significa que precisamos pelo menos desenhar duas imagens: uma para o estado marcado (valor True) e outra para o estado desmarcado (valor False). O modo mais fácil de realizar isto, é usar a função DrawFrameControl da Windows API, para desenhar diretamente na tela do DBGrid.

A seguir, o código no manipulador de evento OnDrawColumnCell do DBGrid, que acontece quando o grid precisa pintar uma célula.

procedure TForm1.DBGrid1DrawColumnCell(
   Sender: TObject; const Rect: TRect; DataCol: 
   Integer; Column: TColumn; State: TGridDrawState);  
 const IsChecked : array[Boolean] of Integer = 
       (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED);
 var
   DrawState: Integer;
   DrawRect: TRect;
 begin
   if (gdFocused in State) then
   begin
     if (Column.Field.FieldName = DBCheckBox1.DataField) then
     begin
      DBCheckBox1.Left := Rect.Left + DBGrid1.Left + 2;
      DBCheckBox1.Top := Rect.Top + DBGrid1.top + 2;
      DBCheckBox1.Width := Rect.Right - Rect.Left;
      DBCheckBox1.Height := Rect.Bottom - Rect.Top;
      DBCheckBox1.Visible := True;
     end
   end
   else
   begin
     if (Column.Field.FieldName = DBCheckBox1.DataField) then
     begin
       DrawRect:=Rect;
       InflateRect(DrawRect,-1,-1);
       DrawState := ISChecked[Column.Field.AsBoolean];
       DBGrid1.Canvas.FillRect(Rect);
       DrawFrameControl(DBGrid1.Canvas.Handle, DrawRect, 
         DFC_BUTTON, DrawState);
     end;
   end; 
 end;

Para finalizar este passo, precisamos garantir que o DBCheckBox1 esteja invisível quando sairmos da célula:

 procedure TForm1.DBGrid1ColExit(Sender: TObject);
 begin
   if DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField then 
     DBCheckBox1.Visible := False
 end;

Precisamos apenas controlar mais dois eventos. Note que no modo de edição, todas as teclas pressionadas vão para a célula do DBGrid, temos que garantir que serão redirecionadas para o CheckBox. No caso de um CheckBox, estamos principalmente interessados nas teclas [Tab] e [Space]. A tecla [Tab] deveria mover o foco de entrada para a próxima célula e a tecla [Space] deveria alternar o estado do CheckBox.

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);
 begin
   if (key = Chr(9)) then 
     Exit;
   if (DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField) then
   begin
     DBCheckBox1.SetFocus;
     SendMessage(DBCheckBox1.Handle, WM_Char, word(Key), 0);
   end;
 end;

E finalmente, o último toque. Seria conveniente que o Caption do checkbox mudasse de acordo com a marcação ou desmarcação do usuário. Note que o DBCheckBox tem duas propriedades (ValueChecked e ValueUnChecked), usadas para especificar o valor do campo representado pelo check box quando for marcado/desmarcado. Minhas propriedades ValueChecked e ValueUnChecked contém 'Yes a Winner!' e 'Not this time', respectivamente.

 procedure TForm1.DBCheckBox1Click(Sender: TObject);
 begin
   if DBCheckBox1.Checked then
      DBCheckBox1.Caption := DBCheckBox1.ValueChecked
   else
      DBCheckBox1.Caption := DBCheckBox1.ValueUnChecked;
 end;

Isto é tudo. Rode o projeto e voila... Checkboxs em toda a coluna do campo Winner.

Se você precisar de qualquer ajuda com o código, envie suas perguntas ao Delphi Programming Forum.