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.