quinta-feira, 24 de agosto de 2017

Retorna a hora da criação de um diretório

function DirectoryTime(aDir: String): String; 
var 
      srFile: TSearchRec; 
begin 
      if FindFirst('C:WINDOWS',faDirectory,srFile)=0 then 
      begin 
           result := TimeToStr(FileDateToDateTime(srFile.Time)); 
      end; 
end; 

Programar meu aplicativo para abrir arquivos a partir do Windows Explorer

Inclua na seção uses: Registry

Problema:

Criei um editor de textos no Delphi. Agora gostaria que o Windows Explorer usasse este editor para abrir arquivos com a extensão .dpg e .dan. Como fazer?

Solução:

Para fazer isto será necessária a criação de algumas chaves no Registro do Windows. O exemplo abaixo cria todas as chaves necessárias.

- Coloque um TButton e no evento OnClick dele coloque o código abaixo:

procedure TForm1.Button1Click(Sender: TObject);
var
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  try
  Reg.RootKey := HKEY_CLASSES_ROOT;
  Reg.LazyWrite := false;
  { Define o nome interno (ArquivoDaniel) e uma legenda que aparecerá no Windows Explorer (Arquivo do Daniel) }
  Reg.OpenKey('ArquivoDaniel', true);
  Reg.WriteString('', 'Arquivo do Daniel');
  Reg.CloseKey;
{ Define o comando a ser executado quando abrir um arquivo pelo Windows Explorer (NomeDoExe %1). O símbolo %1 indica que o arquivo a ser aberto será passado como primeiro parâmetro para o aplicativo - ParamStr(1). }
  Reg.OpenKey('ArquivoDaniel\shell\open\command', true);
  Reg.WriteString('', ParamStr(0) + ' %1'); { NomeDoExe %1 }
  Reg.CloseKey;
  { Define o ícone a ser usado no Windows Explorer:
  0 - primeiro ícone do EXE
  1 - segundo ícone do EXE, etc }
  Reg.OpenKey('ArquivoDaniel\DefaultIcon', true);
  Reg.WriteString('', ParamStr(0) + ',0'); { 0 = primeiro ícone }
  Reg.CloseKey;
  { Define as extensões de arquivos que serão abertos pelo meu aplicativo }
  { *.dpg }
  Reg.OpenKey('.dpg', true);
  Reg.WriteString('', 'ArquivoDaniel');
  Reg.CloseKey;
  { *.dan }
  Reg.OpenKey('.dan', true);
  Reg.WriteString('', 'ArquivoDaniel');
  Reg.CloseKey;
  finally
  Reg.Free;
  end;
end;
- Coloque um TMemo;

- No evento OnShow do Form coloque o código abaixo:

procedure TForm1.FormShow(Sender: TObject);
begin
  { Se o primeiro parâmetro for um nome de arquivo existente... }
  if FileExists(ParamStr(1)) then
  { Carrega o conteúdo do arquivo no memo }
  Memo1.Lines.LoadFromFile(ParamStr(1));
end;
*** Para testar ***

- Execute este programa;

- Clique no botão para criar as chaves no Registro do Windows;

- Feche o programa;

- Crie alguns arquivos com as extensões .dpg e .dan;

- Vá ao Windows Explorer e procure pelos arquivos criados;

- Experimente dar um duplo-clique sobre qualquer dos arquivos com uma das extensões acima.

Observações

Existem outros recursos que poderão ser configurados. Porém, para começar, este já é um bom exemplo. 

sábado, 19 de agosto de 2017

Mostrando a lista de último acesso dos arquivos aberto ultimamente

unit Uultimoacesso; 
{object Form1: TForm1 
Left = 230 
Top = 186 
Width = 435 
Height = 167 
Caption = 'Ultimo Acesso' 
Font.Charset = DEFAULT_CHARSET 
Font.Color = clWindowText 
Font.Height = -16 
Font.Name = 'Arial' 
Font.Style = [] 
PixelsPerInch = 96 
TextHeight = 18 
object Label1: TLabel 
Left = 6 
Top = 11 
Width = 53 
Height = 18 
Caption = 'Arquivo' 
end 
object Label2: TLabel 
Left = 6 
Top = 58 
Width = 101 
Height = 18 
Caption = 'Último Acesso' 
end 
object EdArquivo: TEdit 
Left = 6 
Top = 28 
Width = 281 
Height = 26 
TabOrder = 0 
end 
object BtSeleciona: TButton 
Left = 226 
Top = 82 
Width = 87 
Height = 31 
Caption = 'Seleciona' 
TabOrder = 1 
OnClick = BtSelecionaClick 
end 
object EdUltimoAcesso: TEdit 
Left = 6 
Top = 82 
Width = 204 
Height = 26 
TabOrder = 2 
end 
object ODSelecionaArquivo: TOpenDialog 
Left = 352 
Top = 8 
end 
end 

interface 
uses 
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
StdCtrls; 
type 
TForm1 = class(TForm) 
EdArquivo: TEdit; 
BtSeleciona: TButton; 
Label1: TLabel; 
Label2: TLabel; 
EdUltimoAcesso: TEdit; 
ODSelecionaArquivo: TOpenDialog; 
procedure BtSelecionaClick(Sender: TObject); 
private 
{ Private declarations } 
public 
{ Public declarations } 
end; 
var 
Form1: TForm1; 
implementation 
{$R *.DFM} 
procedure TForm1.BtSelecionaClick(Sender: TObject); 
var 
FileHandle : THandle; 
LocalFileTime : TFileTime; 
DosFileTime : DWORD; 
LastAccessdTime : TDateTime; 
FindData : TWin32FindData; 
NomeArquivo : array[0..255] of char; 
begin 
if OdSelecionaArquivo.Execute then 
begin 
EdArquivo.Text := OdSelecionaArquivo.FileName; 
StrPCopy(NomeArquivo,OdSelecionaArquivo.FileName); 
FileHandle := FindFirstFile(NomeArquivo, FindData); 
if FileHandle = INVALID_HANDLE_VALUE then 
begin 
Windows.FindClose(Handle); 
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then 
begin 
FileTimetoLocalFileTime(FindData.ftLastWriteTime, LocalFileTime); 
FileTimeToDosDateTime(LocalFileTime, LongRec(DosFileTime).Hi, LongRec(DosFileTime).Lo); 
LastAccessdTime := FileDateToDateTime(DosFileTime); 
EdUltimoAcesso.Text := DateTimeToStr(LastAccessdTime); 
end; 
end; 
end; 
end; 
end. 

Listar Arquivos de Um Diretório

Uma dica sobre como listar qualquer tipo de arquivos de um diretório. o toque especial dessa procedure e que ela pode entrar em todos os diretórios que estão dentro do diretório inicial, para procurar arquivos do tipo especificado.

parametros da procedure:

diretorio inicial - diretório que a procedure começa a sua busca ex: c:\windows\ ou ainda c:\ mascara - mascara de arquivo ex: *.txt ou ainda *.* listtotaldir - quando true, ela fornece o caminho completo recursive - quando true, ativa a busca do arquivo dentro de outros diretórios, que estao dentro do diretório inicial

obs.:O form que ira chamar esta função deve possuir uma variavel global declarada da sequinte forma

listtemp2:Tstrings;


ele deve ser criada com o comando

listtemp2 := TstringList.Create;


depois de chamada a procedure o resultado final estara nesta variavel

procedure ListarArquivos(diretorioInicial, mascara: string; listtotaldir: boolean = false; recursive: boolean = true);
var
  i: integer;
  listatemp: TStrings;
  procedure ListarDiretorios(Folder: string; lista: Tstrings);
  var
    Rec: TSearchRec;
    i: integer;
    temps: string;
  begin
    lista.Clear;
    if SysUtils.FindFirst(Folder + '*', faDirectory, Rec) = 0 then
    try
      repeat
        lista.Add(rec.Name);
      until SysUtils.FindNext(Rec) <> 0;
    finally
      if lista.count <> 0 then
      begin
        // deleta o diretorio ..
        lista.Delete(1);
        // deleta o diretorio .
        lista.Delete(0);
        i := 0;
        //deleta os arquivos isto e fica apenas os diretorios
        if lista.count <> 0 then
        begin
          repeat
            temps := lista.Strings[i];
            temps := extractfileext(temps);
            if temps <> '' then
              lista.Delete(i)
            else
              inc(i);
          until i >= lista.Count;
        end;
      end;
    end;
  end;

  procedure ListarAtahos(Folder, mask: string; Lista: Tstrings);
  var
    Rec: TSearchRec;
  begin
    lista.Clear;
    if SysUtils.FindFirst(Folder + mask, faAnyFile, Rec) = 0 then
    try
      repeat
        lista.Add(rec.Name);
      until SysUtils.FindNext(Rec) <> 0;
    finally
      SysUtils.FindClose(Rec);
    end;
  end;

  procedure AddLIstInOther(ListSource, ListDestino: TStrings);
  var
    f: integer;
  begin
    for f := 0 to ListSource.Count - 1 do
    begin
      ListDestino.Add(ListSource.Strings[f]);
    end;
  end;
begin
  listatemp := TStringList.Create;
  ListarAtahos(diretorioInicial, mascara, listatemp);
  if listtotaldir = true then
  begin
    for i := 0 to listatemp.Count - 1 do
    begin
      listatemp.Strings[i] := diretorioInicial + listatemp.Strings[i];
    end;
  end;
  AddLIstInOther(listatemp, listtemp2);
  if recursive = true then
  begin
    ListarDiretorios(diretorioInicial, listatemp);
    for i := 0 to listatemp.Count - 1 do
    begin
      ListarArquivos(diretorioInicial + listatemp.Strings[i] + '\', mascara, listtotaldir, recursive);
    end;
  end;
  listatemp.Free;
end;



exemplo:
coloca-se um listbox, e um button no form.
depois declara-se a variavel global no form
ex:

var
  Form1: TForm;
  listtemp2: TStrings;



e no evento onclick do button a seguinte procedure


listtemp2 := TStringList.Create;
ListarArquivos('c:\windows\', '*.exe', true, true);
listbox1.items := listtemp2;
listtemp2.free;

Enviando um arquivo para a lixeira

uses ShellAPI;

Function DeleteFileWithUndo(sFileName : string ) : boolean;
var
fos : TSHFileOpStruct;
Begin
FillChar( fos, SizeOf( fos ), 0 );
With fos do
  begin
  wFunc := FO_DELETE;
  pFrom := PChar( sFileName );
  fFlags := FOF_ALLOWUNDO
  or FOF_NOCONFIRMATION
  or FOF_SILENT;
  end;
Result := ( 0 = ShFileOperation( fos ) );
end;
  

quinta-feira, 17 de agosto de 2017

Deletar um diretório inteiro de uma vez

Problemas para deletar um diretório com subdiretórios? Utilize a função abaixo:

Uses
  Shellapi, filectrl, //declare estas das units!!!

function DeleteFolder(FolderName: String; LeaveFolder: Boolean): Boolean;
var
  r: TshFileOpStruct;
begin
  Result := False;
  if not DirectoryExists(FolderName) then
  Exit;
  if LeaveFolder then
  FolderName := FolderName + ' *.* '
  else
  if FolderName[Length(FolderName)] = ' \ ' then
  Delete(FolderName,Length(FolderName), 1);
  FillChar(r, SizeOf(r), 0);
  r.wFunc := FO_DELETE;
  r.pFrom := PChar(FolderName);
  r.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION;
  Result := ((ShFileOperation(r) = 0) and (not r.fAnyOperationsAborted));
end;
Usa-se Assim:

procedure TForm1.Button1Click(Sender: TObject);
begin
  deleteFolder('c:\temp',false);
end;
  

Criar sub-diretório no diretório do EXE

Inclua na seção uses: FileCtrl, SysUtils

Problema:

Gostaria de criar um sub-diretório dentro do diretório onde se encontra o EXE de minha aplicação. Como fazer?

Solução: 

Primeiramente vamos conhecer algumas funções do Delphi que precisaremos usá-las:

ParamStr(Indice) - Retorna valores passados na linha de comando quando executamos o programa. Se o valor de Indice for 0 (zero) será retornado o caminho+nome do EXE. 

ExtractFilePath(NomeArq) - Retorna o caminho (path) do nome de arquivo informado. 

Exemplo: 

S := 'C:\NomeDir\Programa.exe';
ExtractFilePath(S); { retorna: 'C:\NomeDir\' }
DirectoryExists(CaminhoDir) - Retorna true se o diretório informado existe. False em caso contrário.

CreateDir(CaminhoDir) - Tenta criar o diretório informado.

Se conseguir, retorna true. Caso contrário retorna false.

Agora que sabemos como trabalham estas funções, vamos escrever uma função que precisamos para criar um sub-diretório conforme proposto.

function CriaSubDir(const NomeSubDir: string): boolean;
var
  Caminho: string;
begin
  Caminho := ExtractFilePath(ParamStr(0)) + NomeSubDir;
  if DirectoryExists(Caminho) then
  Result := true
  else
  Result := CreateDir(Caminho);
end;
Exemplo de uso:

- Chame a função no evento OnCreate do form:

procedure TForm1.FormCreate(Sender: TObject);
begin
  if not CriaSubDir('MeuSubDir') then
  ShowMessage('Não foi possível criar o sub-diretório MeuSubDir.');
end;

quarta-feira, 16 de agosto de 2017

Criando diretório

Para criar um diretório você precisa usar a função ForceDirectories, o exemplo a baixo testa se não existe um diretório e cria o diretório apartir de uma variável string testando se o diretório já existe 

Unit

FileCtrl

procedure TForm1.Button1Click(Sender: TObject);
var
  Dir: string;
begin
Dir := 'C:\APPS\SALES\LOCAL';

if not DirectoryExists(Dir) then
  ForceDirectories(Dir);
  Label1.Caption := Dir + ' foi criado';
end;

Copiar arquivos usando curingas (*.*)

Coloque um Button no Form;
Altere o evento OnClick deste Button conforme abaixo: 

procedure TForm1.Button2Click(Sender: TObject);
var
  SR: TSearchRec;
  I: integer;
  Origem, Destino: string;
begin
  I := FindFirst('c:\Origem\*.*', faAnyFile, SR);
  while I = 0 do begin
  if (SR.Attr and faDirectory) <> faDirectory then begin
  Origem := 'c:\Origem\' + SR.Name;
  Destino := 'c:\Destino\' + SR.Name;
  if not CopyFile(PChar(Origem), PChar(Destino), true) then
  ShowMessage('Erro ao copiar ' + Origem + ' para ' + Destino);
  end;
  I := FindNext(SR);
  end;
end;
Observações

No exemplo acima, se o arquivo já existir no destino, a função falha (não copia). Para que a função possa sobreescrever o arquivo destino (caso exista), altere o último parâmetro de CopyFile para false. CUIDADO! Se um arquivo for sobreescrito, estará perdido para sempre! 

Copiando Um Arquivo Com Um Gauge

Muitas vezes, quando temos a necessidade de copiar um arquivo de um lugar para outro, é interessante mostrar ao usuário o andamento da cópia.
Para tal, coloque em sua aplicação um gauge (optei por um gauge, mas poderia muito bem ser uma progressbar) e um botão para iniciar a cópia. No código onClick do botão, coloque este código. Neste exemplo, o programa cria um diretório de back-up cujo nome do mesmo é a data da cópia no formato AAAAMMDD. No nosso exemplo, chamei o gauge de ga_copia.

procedure Tfrm_Manut.bt_backupClick(Sender: TObject);
var
  strArqOrigem, // Nome do arquivo de origem da cópia
  strArqDestino: string; // Nome do arquivo de destino da cópia
  wDia,wMes,wAno: Word;
begin
  try
    // Aciona o indicativo de progresso da cópia
    ga_copia.Visible := True;
    ga_copia.Progress := 0;
    // Monta os nomes de arquivo - Primeiro recupera de um AdoConnection
    // o nome do arquivo a ser copiado
    strArqOrigem := dm_spark.ADO_Spark.Properties[7].Value;
    // Agora vai montar o nome do arquivo de destino.
    DecodeDate(Date, wAno, wMes, wDia);
    strArqDestino := 'C:\prodata\copia\' + FormatFloat('0000', WAno);
    strArqDestino := strArqDestino + FormatFloat('00', wMes);
    strArqDestino := strArqDestino + FormatFloat('00', wDia);
    strArqDestino := strArqDestino + '\' + ExtractFileName(strArqOrigem);
    // Desconecta o banco de dados
    dm_spark.ADO_Spark.Close;
    Repaint;
    // Inicia a cópia
    CopyFile(strArqOrigem, strArqDestino);
  finally
    // Reconecta o banco de dados
    dm_spark.ADO_Spark.Open;
    ga_copia.Visible := False;
  end;
end;



Agora que já definimos como e quando a cópia será disparada, vamos definir a procedure copyfile que é o motor da nossa cópia de arquivo. Esta procedure é que vai fazer a cópia e incrementar o Gauge.

procedure Tfrm_Manut.CopyFile(Source, Destination: string);
var
  FromF,ToF: file of byte;
  Buffer: array[0..4096] of char;
  NumRead: Integer;
  FileLength: LongInt;
  NewPath: string;
begin
  // Antes de copiar, verifica se já existe o diretório
  // Caso o diretório não exista, o mesmo vai ser criado
  NewPath := ExtractFilePath(Destination);
  if not DirectoryExists(NewPath) then
  begin
    CreateDir(NewPath);
  end
  else
  begin
    if FileExists(Destination) then
    begin
      if Application.MessageBox('O arquivo-destino da cópia de segurança já existe ' + #13#10 +
        'Deseja sobrepôr o mesmo com a nova cópia ?', 'Segurança',
        MB_YESNO + MB_ICONQUESTION) = MRNO then
        Exit;
    end;
  end;
  // Copia o arquivo
  // Abre o arquivo de origem e cria o arquivo destino
  AssignFile(FromF, Source);
  Reset(FromF);
  AssignFile(ToF, Destination);
  ReWrite(ToF);
  FileLength := FileSize(FromF);
  with ga_copia do
  begin
    MinValue := 0;
    MaxValue := FileLength;
    while FileLength > 0 do
    begin
      BlockRead(FromF, Buffer[0], SizeOf(Buffer), NumRead);
      FileLength := FileLength - NumRead;
      BlockWrite(ToF, Buffer[0], NumRead);
      AddProgress(NumRead);
    end;
    CloseFile(FromF);
    CloseFile(ToF);
  end;
end;

Abrir arquivos com aplicativo associado

Inclua a unit SHELLAPI na clausula uses do seu form.

procedure TForm1.ExecFile(F: String);
var
r: String;
begin
case ShellExecute(Handle, nil, PChar(F), nil, nil, SW_SHOWNORMAL) of
ERROR_FILE_NOT_FOUND: r := 'The specified file was not found.';
ERROR_PATH_NOT_FOUND: r := 'The specified path was not found.';
ERROR_BAD_FORMAT: r := 'The .EXE file is invalid (non-Win32 .EXE or error in .EXE image).';
SE_ERR_ACCESSDENIED: r := 'Windows 95 only: The operating system denied access to the specified file.';
SE_ERR_ASSOCINCOMPLETE: r := 'The filename association is incomplete or invalid.';
SE_ERR_DDEBUSY: r := 'The DDE transaction could not be completed because other DDE transactions were being processed.';
SE_ERR_DDEFAIL: r := 'The DDE transaction failed.';
SE_ERR_DDETIMEOUT: r := 'The DDE transaction could not be completed because the request timed out.';
SE_ERR_DLLNOTFOUND: r := 'Windows 95 only: The specified dynamic-link library was not found.';
SE_ERR_NOASSOC: r := 'There is no application associated with the given filename extension.';
SE_ERR_OOM: r := 'Windows 95 only: There was not enough memory to complete the operation.';
SE_ERR_SHARE: r := 'A sharing violation occurred.';
else
Exit;
end;
ShowMessage(r);
end;

Utilize a função assim:


procedure TForm1.Button1Click(Sender: TObject);
begin
ExecFile('c:\windows\ladrilhos.bmp');
end;

Codificando no FastReport


Como já mencionado nos artigos anteriores o FastReport oferece um ambiente para codificação de scripts, ou seja, código este que fica contido no próprio relatório e que interage com o mesmo. 

Para demonstrar esse recurso, vamos abrir o exemplo feito no artigo passado e vamos alterar o relatório Relatorio1.fr3 para que quando o salário for menor que 60 mil Reais, o label seja impresso em vermelho. Abra o designer do fastreport e abra o modelo Relatorio1.fr3, conforme explicado nos artigo anteriores. 

Clique sobre a banda Master e pressione F11, o object inspector do FastReport vai aparecer. Entre na guia Eventos e dê um clique duplo sobre o evento OnBeforePrint. 

Automaticamente o FastReport abre o editor de código. Nesse Editor você pode escolher a linguagem do script, no nosso caso é PascalScript. 

Codifique o evento conforme mostrado na Listagem 1. 


  1. procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
  2. begin
  3.   if ( < 60000) then
  4.      Memo6.Font.Color := clRed
  5.   else
  6.      Memo6.Font.Color := clBlack;
  7. end;


Listagem 1 – Evento BeforePrint 

Salve o relatório e execute a aplicação. O resultado será o visto na Figura 1. 

Clique para ver a imagem em seu tamanho real 
Figura 1. Preview do relatório 

Conclusão 

Ao utilizar o FastReport conseguimos uma flexibilidade grande na questão de relatórios. Poder escrever código no próprio relatório, independente do executável é ótimo. Fica aqui minha sugestão, espero que esta série introdutória ao FastReport tenha despertado o interesse pelo mesmo. Abraço. 

segunda-feira, 14 de agosto de 2017

Dicas para agilizar a consulta de dados em uma DBGrid

A lentidão ao carregar dados em uma DBGrid pode ser causada por vários fatores que, muitas vezes, nos passam despercebidos. As seis orientações abaixo podem ser úteis para agilizar o carregamento de dados ou, talvez, para evitar futuras lentidões.
1) Revise a instrução Select que consulta os dados
Assim como mencionei no artigo sobre práticas de otimização em banco de dados, as consultas SQL devem trazer apenas os dados que são necessários na visão da DBGrid. Por exemplo, se existem 10 colunas em uma tabela e somente 4 são exibidas em uma DBGrid, as outras 6 podem (e devem) ser retiradas da consulta. Para isso, substitua o asterisco pelas colunas requisitadas, conforme a comparação abaixo:
-- Seleciona TODAS as colunas da tabela, desnecessário na maioria das consultas
SELECT * FROM PRODUTOS
 
-- Seleciona somente as colunas necessárias para exibir os dados
SELECT Codigo, Descricao, Valor, Qtde FROM PRODUTOS
Além disso, em alguns casos, há tabelas com colunas do tipo BLOB, que armazenam dados binários de imagens e documentos ou textos longos. Se essas colunas forem adicionadas na consulta, o tempo de retorno será ainda mais demorado. A minha recomendação é trazer estes dados sob demanda em uma instrução separada, como comentei no artigo sobre eventos de tela.

2) Evite abrir e fechar o DataSet repetidas vezes
Quando for necessário aplicar um filtro nos dados, como um intervalo de datas, procure utilizar as propriedades Filter e Filtered, ao invés de comandos que fazem acesso ao banco, como CommandText, Close/OpenExecSQL. A explicação é que a propriedade Filter trabalha com dados em memória, isto é, que já foram carregados.
Entrando mais em detalhes técnicos, o ideal é substituir esses tipos de consulta:
DataSet.Close;
DataSet.CommandText := 'Select * from CLIENTES where Nome like ' + QuotedStr(Edit1.Text + '%');
DataSet.Open;
Por filtros como esse:
DataSet.Filter := 'Nome like ' + QuotedStr(Edit1.Text + '%');
DataSet.Filtered := True;

3) Atente-se aos eventos do DataSet, dos Fields e de pintura da DBGrid
Por falar em eventos de tela, o desenvolvedor também deve ser prudente ao utilizá-los, já que podem impactar no tempo de carregamento dos dados. Por exemplo, você sabia que o evento OnDrawColumnCell do componente DBGrid é chamado para cada registro que é carregado? Insira um ShowMessage neste evento, abra o DataSet e observe a quantidade de vezes que a mensagem é exibida. Logo, se houver um processamento neste evento, é evidente que os dados levarão um tempo maior para serem carregados.
A mesma orientação é válida para eventos do DataSet conectado à DBGrid, como BeforeOpenAfterOpenBeforeGetRecords e AfterGetRecords. Embora sejam executados apenas uma vez ao consultar os dados, podem apresentar lentidões se os processamentos forem extensos.
Seguindo a mesma lógica, o evento OnGetText do TField (campo da tabela), como trata a exibição dos dados de uma coluna, também é executado para cada registro e deve ser observado.

4) Adicione filtros na tela para evitar a consulta de vários registros
Muitos desenvolvedores costumam executar a consulta de dados logo quando a tela é aberta. Sendo assim, se houverem 10 mil registros, todos eles serão consultados de uma vez só! Bom, nem preciso comentar que isso é uma falha de desempenho indiscutível, não é?
Além da demora, esse procedimento também não deixa de ser uma questão de usabilidade. Muitas vezes, o usuário entra na tela para visualizar os dados de apenas 1 registro, e precisa esperar, desnecessariamente, a consulta de todos os registros da tabela.
A recomendação é disponibilizar filtros na tela (como código, descrição, tipo, período, etc…), e realizar a consulta somente quando estes filtros forem informados. Com essa alteração, já consigo apontar 3 vantagens: 1) não haverá “gargalos” ao abrir a tela; 2) o usuário visualiza somente o(s) registro(s) desejado(s); 3) reduz o tráfego de dados em um ambiente cliente/servidor.

5) Ative a paginação de registros com a propriedade PacketRecords
Quem disse que não dá para fazer paginação com Delphi? A propriedade PacketRecords do DataSet permite definir a quantidade de registros que serão “paginados”, ou carregados por vez.
Faça um teste: preencha a propriedade PacketRecords com o valor 100 e abra um DataSet que tenha aproximadamente 1000 registros. Observe que apenas 100 registros serão exibidos inicialmente na DBGrid, porém, ao navegar até a última linha, os próximos 100 registros da tabela serão carregados e exibidos automaticamente.
Este recurso pode ser bastante útil quando a tela exige a consulta de milhares de registros, por uma questão técnica ou por solicitação do cliente, ou mesmo quando a terceira dica, sobre filtros na tela, é inviável.

6) Modere na utilização de componentes de terceiros
Às vezes precisamos de algum comportamento adicional, não existente no componente TDBGrid nativo do Delphi. Como solução, normalmente instalamos componentes de terceiros para atender a nossa necessidade. No entanto, vale ressaltar que estes componentes trazem vários outros comportamentos que, em sua maior parte, pode não nos interessar no momento. Por exemplo, suponha que um componente de terceiro monte toda uma estrutura de agrupamento dinâmico, ative campos Lookup ou faça uma indexação do conteúdo para buscas, quando, o que realmente precisamos, é só um totalizador de valores no rodapé. Por estarem acoplados ao componente, estes outros comportamentos “paralelos” também podem impactar indiretamente no carregamento dos dados.
A minha sugestão é criar um novo componente herdado da classe TDBGrid e adicionar os comportamentos desejados, mesmo que exija um pouco mais de tempo. Além de dispensar a instalação de componentes de terceiros, o desenvolvedor ganha a liberdade de customizar o componente de acordo com suas próprias necessidades.

Como trocar a coluna do DBGrid de lugar


Olá, neste artigo mostrarei um forma bastante interessante de trabalhar com as colunas do DBGrid. Faremos a troca de posição das colunas usando o evento onTitleClick do DBGrid. 

Quando o usuário clicar em uma coluna esta passará a ser a primeira coluna e a primeira trocará de lugar com a coluna clicada. A primeira coluna poderia ser usada para filtrar e/ou ordenar a tabela e sendo a coluna 0 (zero) fica mais fácil para identificar por qual coluna nossa table está ordenada/filtrada. 

Desenhando o projeto 

O design para este exemplo é bem compacto. Insira um TTable, um DataSource e um DBGrid. Altere suas propriedades conformes as tabelas abaixo. Sua tela deve se parecer com a Figura 1. 

Clique para ver a imagem em seu tamanho real 
Tabela 3. Propriedades do TDbGrid 

Coluna colorida 

Uma coisa que uso com bastante freqüência é colorir a primeira coluna, assim fica ainda mais fácil identificar por qual coluna o filtro/ordenação será feito. Para isso: 

Clique duas vezes no DBGrid, adicione os todos campos. Em seguida selecione o primeiro campo, referente a primeira coluna, e mude a propriedade Color para clInfoBk ou uma cor de sua preferência. 

Clique para ver a imagem em seu tamanho real 
Figura 1. Legenda explicativa da figura 

Programação 

Você verá que a programação não exige alto conhecimento. Apenas pegamos o nome da coluna selecionada e seu index. Em seguida “dizemos” que o FieldName da colunca selecionada é igual ao FieldName da coluna 0(zero), ou seja, o primeiro campo. Agora basta igualar o FieldName da coluna zero ao FieldName da coluna selecionada. Veja a Listagem 1. 

Listagem 1. Descrição da listagem 


  1. procedure TForm1.DBGrid1TitleClick(Column: TColumn);
  2. var
  3.   nmColSel: string;
  4.   idColSel: Integer;
  5. begin
  6.   nmColSel := Column.FieldName;
  7.   idColSel := Column.Index;
  8.   with DBGrid1 do
  9.   begin
  10.     Columns[idColSel].FieldName := Columns[0].FieldName;
  11.     Columns[0].FieldName := nmColSel;
  12.   end;
  13. end;



Conclusões 

Neste artigo aprendemos a trocar a coluna de um DBGrid em runtime e tornando sua visualização mais nítida. 

Por Adriano Santos 
Adriano Santos (artes@doiscliques.com) é desenvolvedor Delphi desde 1998. Professor e programador PHP. Bacharel em Comunicação Social pela Universidade Cruzeiro do Sul, SP. É colunista e membro da Comissão Editorial da revista ClubeDelphi.