[Delphi] Dicas do componente DBGrid

Imagem Artigo 132Saudações, leitores! Há algum tempo, tive de implementar uma funcionalidade que exibe uma imagem na célula de uma coluna do componente DBGrid. Enquanto pesquisava na internet, encontrei vários exemplos, no entanto, muitos deles estavam incompletos. Por conta disso, decidi reunir não só essa, mas também outras dicas desse componente neste artigo. Confira!

 

O componente TDBGrid é utilizado praticamente em todas aplicações para exibir dados na tela. No artigo, apresentarei 10 dicas para incrementar as funcionalidades desse componente e trazer uma melhor usabilidade para o usuário.
Sem mais delongas, mãos à obra!

1) Imagem dentro de uma célula
Considere que, na coluna “Ativo” de uma DBGrid, seja necessário exibir um ícone para “Sim” e outro para “Não”. Em primeiro lugar, é necessário utilizar um componente TImageList (paleta Win32) e adicionar 2 imagens, cada uma representando um estado da coluna “Ativo”, conforme ilustrado abaixo:

Imagens no ImageList

 

Em seguida, basta implementar o seguinte código no evento OnDrawColumnCell da DBGrid:

var
  nIndiceImagem: byte;
begin
  if UpperCase(Column.Field.FieldName) = 'ATIVO' then
  begin
    DBGrid.Canvas.FillRect(Rect);
 
    if Column.Field.AsString = 'N' then
      nIndiceImagem := 0
    else
      nIndiceImagem := 1;
 
    // desenha a imagem conforme a condição acima
    ImageList.Draw(DBGrid.Canvas, Rect.Left + 24, Rect.Top + 1, nIndiceImagem);
  end;
end;

 

 

2) Checkbox dentro de uma célula
Essa dica é bem semelhante ao código acima, salvo algumas exceções. Não precisamos da TImageList neste caso, portanto, somente o código abaixo é necessário para desenhar o CheckBox:

var
  nMarcar: word;
  oRetangulo: TRect;
begin
  if UpperCase(Column.FieldName) = 'ATIVO' then
  begin
    DBGrid.Canvas.FillRect(Rect);
 
    if Column.Field.AsString = 'S' then
      nMarcar := DFCS_CHECKED
    else
      nMarcar := DFCS_BUTTONCHECK;
 
    // ajusta o tamanho do CheckBox
    oRetangulo := Rect;
    InflateRect(oRetangulo, -2, -2);
 
    // desenha o CheckBox conforme a condição acima
    DrawFrameControl(DBGrid.Canvas.Handle, oRetangulo, DFC_BUTTON, nMarcar);
  end;
end;

Porém, observe que, quando clicamos na célula, ao invés de marcar ou desmarcar o CheckBox, aparece a letra “S” ou “N”. Para evitar isso, são 2 passos. Primeiro, vamos manter a coluna “Ativo” como somente leitura, adicionando o código abaixo no evento OnColEnter da DBGrid:

if UpperCase(DBGrid.SelectedField.FieldName) = 'ATIVO' then
  DBGrid.Options := DBGrid.Options - [dgEditing]
else
  DBGrid.Options := DBGrid.Options + [dgEditing];

Em seguida, precisamos “esconder” os valores “S” e “N” na célula através do evento OnGetText do campo (Field) “Ativo” no DataSet:

Text := EmptyStr;

Por útimo, claro, é necessário alterar os valores quando o usuário clicar no CheckBox. Para isso, implemente o seguinte código no evento OnCellClick da DBGrid:

var
  sValorColunaAtivo: string;
begin
  if UpperCase(Column.FieldName) = 'ATIVO' then
  begin
    if ClientDataSet.FieldByName('Ativo').AsString = 'S' then
      sValorColunaAtivo := 'N'
    else
      sValorColunaAtivo := 'S';
 
    // edita o DataSet, inverte o status e grava os dados
    ClientDataSet.Edit;
    ClientDataSet.FieldByName('Ativo').AsString := sValorColunaAtivo;
    ClientDataSet.Post;
  end;
end;

 

 

3) ComboBox dentro de uma célula
Para disponibilizar múltiplas opções para o usuário em forma de ComboBox, trabalharemos com a propriedade PickList da classe TColumn. No evento OnCreate ou OnShow do formulário, preencha essa propriedade com as opções que devem ser exibidas na coluna. No exemplo abaixo, ao clicar na coluna “Cidade”, a DBGrid apresenta as alternativas adicionadas:

var
  nIndiceColunaCidade: smallint;
begin  
  // encontra o índice da coluna "Cidade"
  nIndiceColunaCidade := ClientDataSet.FieldByName('Cidade').Index - 1;
 
  // preenche as opções
  DBGrid.Columns[nIndiceColunaCidade].PickList.Add('São Paulo');
  DBGrid.Columns[nIndiceColunaCidade].PickList.Add('Maringá');
  DBGrid.Columns[nIndiceColunaCidade].PickList.Add('Florianópolis');
  DBGrid.Columns[nIndiceColunaCidade].PickList.Add('Rio de Janeiro');
  DBGrid.Columns[nIndiceColunaCidade].PickList.Add('Rondonópolis');
  DBGrid.Columns[nIndiceColunaCidade].PickList.Add('Porto Alegre');
end;

Legal, porém, há um problema: o ComboBox só é exibido quando clicamos duas vezes na coluna, prejudicando a usabilidade. Como alternativa, utilize o evento OnColEnter da DBGrid e habilite a propriedade EditorMode para essa coluna:

if UpperCase(DBGrid.SelectedField.FieldName) = 'CIDADE' then
  DBGrid.EditorMode := True;

Faça o mesmo para o evento OnCellClick para permitir a exibição do ComboBox ao alterar de linha:

if AnsiUpperCase(Column.FieldName) = 'CIDADE' then
    DBGrid.EditorMode := True;

 

4) Ordenar os registros ao clicar no título da coluna
Essa dica é uma boa, hein? E melhor, é fácil!
Para essa funcionalidade, devemos utilizar o evento OnTitleClick da DBGrid, inserindo o código a seguir:

var
  sIndexName: string;
  oOrdenacao: TIndexOptions;
  i: smallint;
begin
  // retira a formatação em negrito de todas as colunas
  for i := 0 to DBGrid.Columns.Count - 1 do
    DBGrid.Columns[i].Title.Font.Style := [];
 
  // configura a ordenação ascendente ou descendente
  if ClientDataSet.IndexName = Column.FieldName + '_ASC' then
  begin
    sIndexName := Column.FieldName + '_DESC';
    oOrdenacao := [ixDescending];
  end
  else
  begin
    sIndexName := Column.FieldName + '_ASC';
    oOrdenacao := [];
  end;
 
  // adiciona a ordenação no DataSet, caso não exista
  if ClientDataSet.IndexDefs.IndexOf(sIndexName) < 0 then
    ClientDataSet.AddIndex(sIndexName, Column.FieldName, oOrdenacao);
 
  ClientDataSet.IndexDefs.Update;
 
  // formata o título da coluna em negrito
  Column.Title.Font.Style := [fsBold];
 
  // atribui a ordenação selecionada
  ClientDataSet.IndexName := sIndexName;
 
  ClientDataSet.First;
end;

Observe que ao clicar na título das colunas, uma linha vertical aparece temporariamente na DBGrid. Isso ocorre em função da propriedade DBGrid > Options > dgColumnResize, que permite o redimensionamento das colunas com o mouse. Caso essa função não seja conveniente, desative-a para evitar esse efeito.

 

5) Alterar a fonte das linhas
Algumas vezes, é necessário formatar a fonte de algumas linhas para destacar o estado do registro. Utilizando o mesmo exemplo da coluna “Ativo”, suponha que os registros que estiverem inativos (“N”) devem ser marcados em vermelho e itálico. Essa funcionalidade também deve ser adicionada no evento OnDrawColumnCell:

// verifica se o registro está inativo
if ClientDataSet.FieldByName('Ativo').AsString = 'N' then
begin
  // formata a linha em vermelho e itálico
  DBGrid.Canvas.Font.Style := [fsItalic];
  DBGrid.Canvas.Font.Color := clRed;
 
  // pinta a linha
  DBGrid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;

 

6) Linhas zebradas
Aproveitando a dica anterior, confira também como deixar a DBGrid “zebrada” no evento OnDrawColumnCell, ou seja, alternar as cores de cada registro para aprimorar a visibilidade.

var
  nLinha: integer;
begin
  // obtém o número do registro (linha)
  nLinha := DBGrid.DataSource.DataSet.RecNo;
 
  // verifica se o número da linha é par ou ímpar, aplicando as cores
  if Odd(nLinha) then
    DBGrid.Canvas.Brush.Color := clMenu
  else
    DBGrid.Canvas.Brush.Color := clMoneyGreen;
 
  // pinta a linha
  DBGrid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;

 

7) Mover a posição da linha junto com a barra de rolagem
Quando o usuário move a barra de rolagem vertical na DBGrid, já notou que posição do registro não acompanha essa movimentação? Procurei alguma explicação na documentação do Delphi, mas não obtive sucesso. Mesmo assim, há uma forma de contornar esse comportamento, embora seja um pouco complexo. Primeiramente, é preciso herdar um componente da classe TDBGrid para que possamos declarar o método que responde à mensagem de Scroll:

type
  TDBGrid = class(Vcl.DBGrids.TDBGrid)
  private
    procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  end;

Em seguida, implementamos a função com o código abaixo:

procedure TDBGrid.WMVScroll(var Message: TWMVScroll);
begin
  if Message.ScrollCode = SB_THUMBTRACK then
    Message.ScrollCode := SB_THUMBPOSITION;
 
  inherited;
end;

Agora, execute a aplicação, mova a barra de rolagem vertical e observe que a posição do DataSet acompanha automaticamente a movimentação  da barra!

 

8) Obrigar letras maiúsculas nas células
Como as células da DBGrid não possuem a propriedade CharCase para configurar a digitação em maiúsculas, podemos implementar esse comportamento no evento OnKeyPress:

Key := AnsiUpperCase(Key)[1];

 

9) Exibir “Sim” para o valor “S” e “Não” para o valor “N”
Se a intenção é converter os valores “S” e “N” para “Sim” e “Não”, respectivamente, basta utilizar o evento OnGetText do campo (Field) do DataSet. No exemplo abaixo, faço essa conversão com uma coluna que armazena estes valores:

if Sender.AsString = 'S' then
  Text := 'SIM'
else
  Text := 'NÃO';

 

10) Navegar entre as células com as teclas de seta, mantendo o efeito de linha selecionada (dgRowSelect)
Quando ativamos a propriedade dgRowSelect, a DBGrid impede a navegação entre as células com as teclas de seta. Lembro-me que um dos usuários da minha aplicação reportou uma queixa desse comportamento. Para solucioná-lo, deasativei a opção dgRowSelect dos Options da DBGrid e implementei o código abaixo no evento OnDrawColumnCell:

if Rect.Top = TStringGrid(DBGrid).CellRect(0, TStringGrid(DBGrid).Row).Top then
begin
  DBGrid.Canvas.FillRect(Rect);
  DBGrid.Canvas.Font.Color := clWhite;
  DBGrid.Canvas.Brush.Color := clHighlight;
  DBGrid.DefaultDrawDataCell(Rect, Column.Field, State)
end;

 

Pessoal, e agora vem a melhor parte: desenvolvi um pequeno exemplo com todas as dicas mencionadas acima! Baixe-o neste link.

Um grande abraço a todos!


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

73 comentários

  1. Fantástico André … eu não fazia ideia que poderia inserir imagem na DBGRID essa matéria já me deu algumas ideias legais para implementar no meu sistema, porem fica uma duvida como alterar apenas a cor de uma coluna ? Ex: na dbgrid aparece a coluna código, cliente e data de vencimento como fazer a células da data de vencimento ficarem de cores diferentes em casa de atraso? eu sei com fazer a linha inteira mas só uma célula não . Muito Obrigado por toda ajuda e continue com esse excelente trabalho .

    1. Opa, André, tudo certo?
      Para alterar a cor apenas de uma coluna é preciso envolver o código de pintura em uma função IF que indique essa coluna.
      Utilizando o cenário que você mencionou, sobre a data de vencimento, este seria a codificação no evento OnDrawColumnCell:
      if AnsiUpperCase(Column.FieldName) = 'DATAVENCIMENTO' then
      begin
      // se o cliente estiver atrasado
      if ClientDataSet.FieldByName('Situacao').AsString = 'A' then
      begin
      // formata a linha em vermelho
      DBGrid2.Canvas.Font.Color := clRed;
      DBGrid2.DefaultDrawColumnCell(Rect, DataCol, Column, State);
      end;
      end;

      Abraço!

  2. André,

    Gostaria de parabeniza-lo, pela clareza e funcionalidade das dicas encontradas em seu Blog. Nunca tinha visto dicas tão fáceis de implementar, e tão bem explicadas quanto as suas.

    Eu sou um programador das antigas (DBase, Clipper, etc) e estou começando a me aventurar no mundo do Delphi. Fiz um Curso de Delphi, e tenho assistidos vários tutoriais…

    Gostei muito das suas dicas, e o mais legal.. os Códigos Fonte dos exemplos que baixei do seu site, realmente Funcionaram de primeira. Coisa rara, pelo que percebi em vários outros sites por ai…

    Eles estão me dando excelente dicas para alguns projetos que estou tentando desenvolver.

    Um grande abraço.

    1. Olá, André, tudo bem?

      Agradeço fortemente pelo seu comentário e pelo reconhecimento! Sempre comento com a minha noiva que são comentários como o seu que me mantém motivado para continuar o trabalho no blog.

      Fico contente que os artigos e os exemplos lhe ajudaram! Espero continuar contribuindo com a comunidade Delphi e apoiando cada vez mais programadores!
      Conte comigo para o que precisar nessa nova empreitada com o Delphi.

      Obrigado novamente!
      Grande abraço!

  3. André,

    Percebi que você é cara bastante experiente em Delphi, então vou te pedir uma ajuda. Eu já li seu artigo sobre os erros de Access Violation, e me ajudou muito. Mas estou com um erro destes que está me deixando de “cabelos brancos”.

    O erro está descrito neste Forum do DevMedia no seguinte link: http://www.devmedia.com.br/forum/access-violation-at-adress-0057943a/530850

    Se você conseguir me ajudar eu te agradeço muito.

    André

  4. Fala André,

    Parabéns pelo artigo, ficou muito bem explicado todas as 10 dicas e foram muito úteis.

    Gostaria de saber se existe uma maneira de aumentar a altura das linhas do TDBGrid, eu tentei com THackGrid(DBGrid1).DefaultRowHeight mas não funcionou.

    Muito obrigado e parabéns mais uma vez.

    1. Olá, Robson, tudo certo?
      Que estranho, o código que você informou deveria funcionar. Fiz um teste no projeto do artigo e a altura mudou de tamanho.
      Vou entrar em contato com você, ok?

      Abraço!

    2. Robson e Batera, bom dia. Deu certo alterar a altura da linha do dbgrid?
      Estou utilizando desta forma, porém sem sucesso.:
      depois da sessão uses e antes da declaração type do form

      type
      THackDBGrid = class (TDBGrid)
      procedure AllRows (DBGrid: TDBGrid; NewHeight: Word);
      end;

      Após a diretiva {$R *.dfm}:
      procedure THackDBGrid.AllRows (DBGrid: TDBGrid; NewHeight: Word);
      var
      i: DWord;
      begin
      for i := 0 to THackDBGrid (DBGrid).RowCount – 1 do
      THackDBGrid (DBGrid).RowHeights [i] := NewHeight;
      end;

      E chamo assim:

      Se quiser alterar a altura de todas as linhas use:
      THackDBGrid(DBGrid1).AllRows (DBGrid1, 40);
      Se quiser alterar a linha de apenas uma linha use:
      THackDBGrid(DBGrid1).RowHeights[0] := 40;

    3. Fala batera, blz companheiro?

      Eu consegui o resultado que precisava da seguinte forma:
      No Object Inspector, nas propriedades do DBGrid, deu defini o fonte size como 16, por exemplo;

      No evento OnDrawColumnCell do DBGrid eu modifico o font.size para um tamanho menor, e utilizo o DefaultDrawColumnCell para definir a altura levando em conta a font.size que esta definido lá nas propriedades.

      DBGrid1.Canvas.Font.Size := 10;
      DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);

      Funciona que é uma maravilha, não consegui ainda centralizar o texto na vertical.

    4. Muito obrigado pela contribuição, Ivan!
      Pesquisei bastante, mas essa solução nem me passou pela cabeça, rsrs.
      Mais um problema resolvido! 🙂

      Grande abraço!

    5. nao funcionou comigo essa ideia de aumentar o tamanho e diminuir DrawColumnCell.. Visualmente funciona, mas basta clicar que fica duas escrita o tamanho GRANDE e o tamanho 10

    6. Olá, Daniel, tudo certo?
      Depois de alguns testes, observamos que essa solução realmente não é eficiente.
      A ideia que sugeri para o Robson, na época, era utilizar uma Grid de terceiros que já tenha essa funcionalidade ou criar a própria Grid, embora essa última opção exija um pouco mais de pesquisa e conhecimento da VCL do Delphi.

      Abraço!

  5. Olá Andre! Boas dicas e muito úteis. Obrigado. Veja se pode ajudar-me nesta questão: queria inverter o dbgrid, colocar as colunas como linhas e as linhas como colunas. Se souberes e puder ajudar-me, agradeço desde já. Valeu. Abçs… Flavio

    1. Olá, Flávio, tudo certo?
      Rapaz, acredito que esse tipo de inversão só pode ser feito com componentes de terceiro. Uma vez fui informado que o componente InfoPower TwwDBGrid trazia essa funcionalidade, mas nunca tive a oportunidade de utilizá-lo. Se eu souber de algo, entro em contato.

      Abraço!

  6. Bom dia André,
    Faço minhas as palavras do André Marsola, achei o conteúdo fantástico.
    Estou usando a dica: 1) Imagem dentro de uma célula
    Mas não consegui identificar a parte referente a “OnGetText” no meu caso o tipo de dados é booleano, mas pelo o que eu vi é indiferente, não consegui achar o evento citado no dataset.
    Estou usando o RAID STUDIO XE7 com o FireDAC, mais especificamente o FDQuery.

    Será que poderia me ajudar?
    Desde já muito obrigado e parabéns, serei um leitor assíduo.

    1. Olá, Luiz, tudo certo?
      Muito obrigado pelo feedback! 🙂
      O evento “OnGetText”, na verdade, é do componente TField, e não do DataSet. No caso do TFDQuery, clique com o botão direito no componente e depois em Fields Editor. A janela que for exibida exibirá todos os campos (Fields) existentes na Query. São nesses campos que o evento “OnGetText” é disponibilizado!

      Espero ter ajudado!

      Abraço!

  7. Olá,

    muito boa suas dicas. Tenho uma pergunta, terias algumas dicas para agilizar o carregamento de dados no dbgrid? Por exemplo, usar o DisableControls antes de dar o CommandText e depois EnableControls acelera o carregamento do dataset para o dbgrid?

    Agradeço desde já.

    1. Olá, Artur, tudo bem?
      Obrigado pelo feedback!
      Como já me questionaram algo semelhante, ao invés de responder a sua pergunta, eu vou elaborar um artigo, pode ser?
      Vou postá-lo essa semana ou na semana que vem!

      Abraço!

  8. Tem como colorir apenas uma célula? por exemplo colorir apenas a célula onde fica um valor de apenas determinada linha.

    1. Olá, Jackson!
      Sim, é possível. Basta controlar as condições no evento OnDrawColumnCell. Veja o exemplo abaixo:

      if AnsiUpperCase(Column.FieldName) = 'SITUACAO' then
      begin
        // se o cliente estiver atrasado
        if ClientDataSet1.FieldByName('Situacao').AsString = 'A' then
        begin
          // formata a célula em vermelho
          DBGrid1.Canvas.Font.Color := clRed;
          DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
        end;
      end;

      Abraço!

  9. André, primeiramente parabéns pelos codigos.
    Muito bem explicados e totalmente praticos e funcionais.
    Mas to com uma dúvida…
    Gostaria de que ao maximizar a tela, ou fazer um resize qualquer… eu escolher uma coluna para acompanhar o resize, ou então todas as colunas se encaixem ao layout da tela.
    Já vi vários codigos na intenet mas ta dificil algum que realmente funcione.
    Isso é para eliminar o espaço em branco que fica quando as colunas estão fixas.
    Agradeço desde já;

  10. Bom dia Andre, desde ja fica aqui o meu agradecimento, tenho lido os seus tutorias e eles tem me ajudado bastante, estou com dois problemas queria saber se pode me ajudar.
    1º – Tenho uma estrutura de Tabelas assim: Um tabela de atendimento, o qual o campo cliente dessa tabela atendimento puxa esse dado cliente de outra tabela, no meu DBGrid, mostra o código do cliente invés do nome, como poderia fazer para mostrar o nome invés do código?? (Minha base dados e conectada com os componentes dbexpress e base firebird).

    1. Olá, Felipe, tudo bem?
      Para exibir o nome na consulta, você precisará trabalhar com comandos JOINs na sua instrução SQL. Esses comandos fazem junções entre 2 ou mais tabelas, trazendo valores relacionados.
      Por exemplo, considere a consulta abaixo na tabela de Atendimentos:

      SELECT * FROM ATENDIMENTOS

      Essa consulta irá retornar apenas o código do cliente, certo? Para trazer o nome, é necessário unir as tabelas de Atendimentos e Clientes através de um INNER JOIN:

      SELECT A.*, C.NOME
      FROM ATENDIMENTOS A INNER JOIN CLIENTES C
      ON A.CodCliente = C.CodCliente

      Quando possível, confira o link abaixo para aprender melhor estes comandos:

      http://thomaslarock.com/2012/04/real-world-sql-join-examples/

      Abraço!

  11. Boa Tarde, obrigado pela atenção e pela resposta. Vamos la, eu acho que pelo jeito que estou fazendo aqui esse comando não ira funcionar. Quando estou no meu aplicativo (na tabela atendimento) vou preenchendo os campos, quando chego no campo cliente, esse campo é um DBLookupCombobox que esta ligado em outra tabela cliente, nessa tabela já tem vários clientes cadastrados. No meu DBGrid aparece o código desse cliente invés de ser o nome.

    1. Olá, Felipe!
      Aparentemente é uma configuração incorreta do campo Lookup no DataSet.
      Para exibir o nome, você deve criar o campo Lookup com as seguintes propriedades:
      KeyField: Campo que está ligado à outra tabela (Código do Cliente da tabela de Atendimento)
      DataSet: DataSet de origem (DataSet de Clientes)
      LookupKeys: Campo do DataSet de origem (Código do Cliente da tabela de Clientes)
      Result Field: Campo de nome do cliente (Nome do cliente da tabela de Clientes)

      Dessa forma, quando você informar o código do cliente no cadastro do atendimento, o campo Lookup (Nome) exibirá automaticamente o nome do cliente.

      Abraço!

  12. Nos componentes DBEdit, Lookup, estao exibindo corretamente os nomes e os codigos, porem no DBGrid que aparace os códigos, e quero os nomes.

    1. Certo, Felipe. Então você deve adicionar o Field Lookup (que traz o nome) na DBGrid também. Para isso, acesse o Columns Editor da DBGrid e adicione uma nova coluna, indicando o campo Lookup na propriedade FieldName.

      Abraço!

  13. Você pode enviar novamente? Obrigado, pela ajuda Andre e me desculpe a insistência, muito obrigado mesmo, ainda bem que existe pessoas como você. Obrigado

  14. Olá andré estou com uma duvida no dbgrid, pois eu preciso criar uma nova coluna que não esta vinculado no meu data set, para que o usuário digite nessa nova coluna um valor direto pelo dbgrid, mas o dbgrid não permiti ele digitar, e se eu tento forçar pelo codigo ele da um erro de acces violation, você pode me informar se é possível eu fazer tal alteração pelo dbgrid em uma coluna nova que não está vinculada ao dataset e como posso fazer isso?

  15. Olá Andre, tudo blz?
    Sou Anderbelluno do Active.
    Estou seguindo suas dicas, me veio uma duvida aqui,
    teria como usar o IBDataSet no lugar o ClientDataSet?
    Estou tentando modificar, mas estou com um pouco de dificuldades.
    Teria como vc me dar uma força?
    Obrigado.

  16. Bom dia! Eu segui a dica do Ivan Juste para aumentar a altura da linha. Funcionou mas, os dados não ficam alinhados verticalmente (o texto fica no alto da célula). Haveria algum jeito de centralizar na vertical. Agradeço.

    1. Olá, Luis, tudo bem?

      Encontrei um método no site ScriptBrasil (e fiz algumas adaptações) que talvez poderá ajudá-lo:

      procedure AlinharVertical(Texto: string; Rect: TRect; Alinhamento: TAlignment);
      var
        nAlinhamento: integer;
        nXOffSet: integer;
        nYOffSet: integer;
        nTamanhoRect: integer;
      begin
        DBGrid1.Canvas.Brush.Style := bsSolid;
        DBGrid1.Canvas.FillRect(Rect);
        DBGrid1.Canvas.MoveTo(Rect.Right + 1, Top - 1);
        DBGrid1.Canvas.LineTo(Rect.Right + 1, Rect.Bottom + 1);
        DBGrid1.Canvas.LineTo(Rect.Left - 1, Rect.Bottom + 1);
      
        nTamanhoRect := Rect.Right - Rect.Left + 1;
        case Alinhamento of
          taLeftJustify:
            begin
              nAlinhamento := DT_LEFT;
              nXOffSet := Rect.Left + 2;
            end;
          taCenter:
            begin
              nAlinhamento := DT_CENTER;
              nXOffSet := Rect.Left +(nTamanhoRect - DBGrid1.Canvas.TextWidth(Texto)) div 2;
            end;
          taRightJustify:
            begin
              nAlinhamento := DT_RIGHT;
              nXOffSet := Rect.Left +(nTamanhoRect - DBGrid1.Canvas.TextWidth(Texto)) - 4;
            end;
        end;
        DBGrid1.Canvas.Brush.Style := bsClear;
        nYOffSet := ((Rect.Bottom - Rect.Top) - DBGrid1.Canvas.TextHeight('Mj')) div 2;
        DBGrid1.Canvas.TextRect(Rect, nXOffSet, Rect.Top + nYOffSet, Texto);
      
        if not (dgRowSelect in DBGrid1.Options) then
          DBGrid1.Canvas.DrawFocusRect(Rect);
      end;

      Basta chamá-lo no evento OnDrawColumnCell, após redefinir o tamanho da fonte:

      AlinharVertical(Column.Field.AsString, Rect, Column.Alignment);

      Espero ter ajudado. Abraço!

  17. Funcionou perfeitamente! O problema é que a gente sempre quer tudo ao mesmo tempo, agora! kkkk
    Meu DBGrid é zebrado, tem ícones e quando seleciono a linha ela muda de cor. Quando eu coloco estas firulas, o alinhamento vertical não acontece. O Rect é mudado. O que eu poderia alterar para que fosse centralizado? Outra pergunta: É possível que a linha selecionada aparecesse mesmo quando se movimentasse o Scroll. No meu código ela some. Agradeço

    procedure TformPrincipal.DBGrid2DrawColumnCell(Sender: TObject;
    const [Ref] Rect: TRect; DataCol: Integer; Column: TColumn;
    State: TGridDrawState);
    var
    nLinha: integer;
    image_1: TImage;
    begin
    image_1 := TImage.Create(Self);
    image_1.Picture.LoadFromFile(‘C:\SGE\imagens\Trash-32.png’);

    DBGrid2.Canvas.Font.Size := 10;
    AlinharVertical(Column.Field.AsString, Rect, Column.Alignment);

    nLinha := DBGrid2.DataSource.DataSet.RecNo;

    if Odd(nLinha) then
    DBGrid2.Canvas.Brush.Color := clWhite
    else
    DBGrid2.Canvas.Brush.Color := $00F4E6D9;

    DBGrid2.DefaultDrawColumnCell(Rect, DataCol, Column, State);
    if Rect.Top = TStringGrid(DBGrid2).CellRect(0, TStringGrid(DBGrid2).Row).Top then
    begin
    DBGrid2.Canvas.FillRect(Rect);
    DBGrid2.Canvas.Font.Color := clWhite;
    DBGrid2.Canvas.Brush.Color := clHighlight;
    DBGrid2.DefaultDrawDataCell(Rect, Column.Field, State);
    image_1.Picture.LoadFromFile(‘C:\SGE\imagens\Cancel2-32.png’);
    end;

    if Column.Field.FieldName = ‘VISUALIZAR’ then
    begin
    DBGrid2.Canvas.Draw(Rect.Left + 15, Rect.Top + 5,image_1.Picture.Graphic);
    end;
    image_1.Free;
    end;

    1. Olá, Luis!
      Bom, já que você tem outros tratamentos no mesmo evento, devemos tomar cuidado para que um comportamento não anule o outro, que é o que provavelmente está acontecendo. Como a sua necessidade é um pouco mais complexa, eu vou conversar com você por e-mail, ok?

      Abraço!

  18. Olá André ! Cara, parabéns pelo seu artigo. Show de bola. Minha dificuldade: Não uso ClientDataSet, estou usando firedac, TFDQuery. Para ordernar a coluna, da erro, pois os parametros são outros. Vc ja fez ordenação usando um dataset do firedac ? se tive, tem como mandar o código ? Vlw !!!

    1. Olá, Romeu, tudo bem?
      Realmente, para o componente TFDQuery, o código é um pouco diferente. Confira:

      var
        sIndexName: string;
        oOrdenacao: TFDSortOption;
        i: smallint;
      begin
        // retira a formatação em negrito de todas as colunas
        for i := 0 to DBGrid.Columns.Count - 1 do
          DBGrid.Columns[i].Title.Font.Style := [];
      
        // configura a ordenação ascendente ou descendente
        if FDQuery1.IndexName = Column.FieldName + '_ASC' then
        begin
          sIndexName := Column.FieldName + '_DESC';
          oOrdenacao := soDescending;
        end
        else
        begin
          sIndexName := Column.FieldName + '_ASC';
          oOrdenacao := soNoCase;
        end ;
      
        // adiciona a ordenação no DataSet, caso não exista
        if not Assigned(FDQuery1.Indexes.FindIndex(sIndexName)) then
          FDQuery1.AddIndex(sIndexName, Column.FieldName, EmptyStr, [oOrdenacao]);
      
        // formata o título da coluna em negrito
        Column.Title.Font.Style := [fsBold];
      
        // atribui a ordenação selecionada
        FDQuery1.IndexName := sIndexName;
      
        FDQuery1.First;
      end;

      Espero ter ajudado. Abraço!

  19. Ah, André, sou cristão, e o que vou dizer, não é apologia. Mas hoje vi esta frase, e creio que ela se aplica a você com toda certeza: “Grandeza não é se destacar acima dos pequenos, mas ajudar os pequenos a se destacarem acima dos grandes”. Cara, parabéns e muto obrigado pela sua “disponibilidade” em nos fazer crescer. Vlw mesmo.

    1. Boa tarde, Romeu!
      Fiquei muito feliz com as suas palavras! Receber um feedback como o seu é o combustível que me motiva a continuar ajudando a comunidade de programadores Delphi.

      Muito obrigado e que Deus lhe abençoe!

  20. Bom dia Andre, tudo bem?
    Preciso que na coluna da grid apareça a imagem ou um texto qualquer.
    Estou utilizando o imagelist, apenas para uma condição deve-se aparecer a imagem, para as demais mostrar o próprio texto que estiver retornando.
    Isso é possível?

    1. Olá, Michel!
      Sim, é possível. Basta codificar a condição no evento OnDrawColumnCell conforme o 2º exemplo do artigo:

      // Verificar se é a coluna que deve exibir a imagem/texto
      if UpperCase(Column.FieldName) = 'IMAGEM' then
      begin
        // verifica se deve exibir a imagem
        if CondicaoParaExibirImagem = 'S' then
          { código para exibir a imagem}
         else
          { código para exibir o texto }
      end;

      Para exibir a imagem dentro da DBGrid, siga as orientações do link abaixo:

      http://delphi.help-me-please.com/tipsandtrick/drawing-an-image-in-a-cell-of-a-delphi-dbgrid/

      Espero que lhe ajude.
      Abraço!

  21. Oi André! Os seus exemplo foram muito úteis para mim, funcionou perfeitamente. Estou agora com uma dúvida. Não sei se o que preciso é possível fazer… eu criei o check para um valor no banco de dados que pode ser T ou F. Eu precisaria que quando fosse F este valor, a célula do lado pudesse estar habilitada, e quando fosse T, a célula do lado estar desabilitada para edição. Se eu fizer por exemplo,
    GridMov.Columns[11].ReadOnly := True vai estar desabilitado para todas as células daquela coluna… não sei se isso é possível fazer….

  22. Bom dia! Apenas para dar um feedback sobre o que o André me retornou e resolveu meu problema!
    No evento OnCellClick da célula que será habilitada/desabilitada.

    if AnsiUpperCase(Column.FieldName) = 'NOME_COLUNA_AO_LADO' then
    begin
      Column.ReadOnly := DataSet.FieldByName('Valor').AsString = 'T';
    end;

    Se o valor for “T”, a coluna fica ReadOnly.

    Mais uma vez obrigada André pela sua ajuda!

  23. fiz a dica 2 tudo certinho ta tudo blz, só falta uma coisa: Salvar no Banco de Dados
    ja tentei colocar um botão com o ApplyUpdates(0); mas sem sucesso

    1. Olá, Elias.
      Não consigo orientá-lo com precisão sem analisar o código, mas experimente utilizar o evento OnReconcileError do DataSet. Por algum motivo, está ocorrendo algum erro na gravação do registro ou o DataSet não está incrementando o ChangeCount após a alteração.

      Abraço!

  24. Fala André, bacana teu artigo. Já fiz uma implementação usando um dos seus exemplos. Obrigado pelo artigo.
    Estou à procura de uma solução para um DBGrid que está com a propriedade MultiSelected marcada. Preciso que quando teclar ESPAÇO ele deixa aquela linha marcada.

    Só esta marcando quando teclo CONTRO+CLIQUE na linha. Tem alguma dica, man? Abraço.

    1. Olá, Cleiton, tudo bem?
      É possível configurar esse comportamento na TDBGrid, sim!
      Para isso, é necessário trabalhar com o evento OnKeyDown, adicionando o seguinte código:

      var
        sValorColunaAtivo: string;
      begin
        // 32 é o código ASCII referente ao caracter de espaço
        if Key = 32 then
        begin
          // verifica se o foco está na coluna "ATIVO"
          if AnsiUpperCase(DBGrid.SelectedField.FieldName) = 'ATIVO' then
          begin
            if ClientDataSet.FieldByName('Ativo').AsString = 'S' then
              sValorColunaAtivo := 'N'
            else
              sValorColunaAtivo := 'S';
      
            // edita o DataSet, alterna o status e grava os dados
            ClientDataSet.Edit;
            ClientDataSet.FieldByName('Ativo').AsString := sValorColunaAtivo;
            ClientDataSet.Post;
          end;
        end;
      end;

      Tranquilo, né? 🙂
      O detalhe está na primeira condição, que verifica se a tecla pressionada pelo usuário é a barra de espaço. O código dentro da condição alterna o valor entre ‘S’ e ‘N’ para marcar/desmarcar o checkbox.

      Abraço!

  25. Boa tarde,
    Tudo bem ?

    Veja se pode me ajudar. Estou com a seguinte dificuldade.

    Tenho a classe abaixo, e precisava popular os campos no DBGrdi com RTTi
    como devo fazer ? Obrigado.

    TColumn = class(TCustomAttribute)
    { Mapeia um campo de uma tabela do banco de dados }
    private
    FName: String;
    FCaption: String;
    FLength: Integer;
    FLocalDisplay: TLocalDisplayColumn;
    FTransiente: Boolean;
    public

    [TableName(‘TELEFONES’)]
    TTelefones = class(TPersistent)
    private
    public
    published
    [TColumn(‘FONE’, ‘Fone’, 35,[ldGrid, ldLookup, ldComboBox], False)]
    property TELEFONE: string read FTELEFONE write setTELEFONE;
    end;

  26. e ai ANDRE, conseguiu uma solução para este último problema RTTI? Tambem estou eperando pela resposta, obrigado.

    1. Olá, Wilton!
      No caso do Alexandre, que postou o comentário anterior, o problema estava relacionado com o tutorial que ele acompanhou para desenvolver o projeto.
      Como este assunto é bem específico, envie a dúvida com o máximo de detalhes para “contato@andrecelestino.com”.

      Abraço!

  27. Olá André, parabéns pelo seus artigos, me ajudou muito e melhorou muito meus códigos, pois conseguia fazer muita coisa, mas ficava uns códigos meio complicado de entender e extenso. Obrigado. Espero que continue compartilhando de seu conhecimento com a comunidade DELPHI.

    Obrigado!

    1. Olá, Cristiano, tudo bem?
      Agradeço pelo feedback e também pelos elogios!
      Fiquei feliz ao saber que o artigo melhorou a qualidade do seu código. Continue acompanhando!
      Abraço!

  28. Boa Noite andré, parabéns pelo seu artigo, sou mais um que estou precisando do seu conhecimento, tenho um dbgrid que listo os produtos vendidos, gostaria de pegar todos
    os produtos de um determinado campo, por exemplo: no dbgrid tem o campos ‘produto’ , ‘tipo’, valor….ok logo no exemplo esta listado no campo produtos os seguistes itens : ‘calça’, ‘blusa’, ‘cinto’, … ok … entao gostaria de pegar todos os produtos { ‘calça’, ‘blusa’, ‘cinto ‘ } e coloca em um Edit. deste ja agrateço

    1. Boa noite, saezo, tudo bem?
      Este comportamento pode ser feito de uma forma bem simples. Primeiro, sabemos que o DBGrid está conectado a um DataSet, certo? Podemos percorrer esse DataSet e concatenar o valor do campo “Produto” em uma variável. Depois, basta atribuir essa variável no componente TEdit. Veja o exemplo:

      var
        Produtos: string;
      begin
        // Inicializa a variável
        Produtos := EmptyStr;
        
        // Posiciona no primeiro registro
        DataSet.First;
        
        // Percorre os registros do DataSet
        while not DataSet.Eof do
        begin
          // Concatena o nome do produto na variável
          Produtos := Produtos + 
            DataSet.FieldByName('Produto').AsString;
            
          // Move para o próximo registro
          DataSet.Next;
        end;
        
        // Exibe os dados no Edit
        Edit.Text := Produtos;
      end;

      Espero ter ajudado.
      Abraço!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.