sábado, 12 de fevereiro de 2011

Gerando imagem para validação de números.


Gerando imagem para validação de números
Hoje em dia está sendo muito usado a geração de imagens para confirmação de alguma ação. Um exemplo disse é o email da uol, onde você tem uma opção de solicitar a confirmação do envio.
Bom, segue uma função que faz isso:

Primeiro adicione no seu form os componentes TButton, TImage e TEdit.

unit Unit1;
  interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
type
  TForm1 = class(TForm)
  Image1: TImage;
  Button1: TButton;
  Edit1: TEdit;
  procedure Button1Click(Sender: TObject);
  procedure FormShow(Sender: TObject);
private
  { Private declarations }
public
 { Public declarations }
  function GeraImagem(Img: TImage): string;
end;
 
var
  Form1: TForm1;
  validapost : string;
implementation
{$R *.dfm}
{ TForm1 }
function TForm1.GeraImagem(Img: TImage): string;
  const
    f: array [0..4] of string =  ('Courier New', 'Impact', 'Times New Roman', 'Verdana', 'Arial');
    s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    C : ARRAY [0..14] OF tcOLOR = (clAqua, clBlack, clBlue, clFuchsia, clGray, clGreen, clLime, clMaroon, clNavy, clOlive, clPurple, clRed, clSilver, clTeal, clYellow);
 
var
  i, x, y : integer;
  r : string;
begin
  randomize;
  Img.Width := 160;
  Img.Height := 60;
  for i := 0 to 3 do
  r := r + s[Random(length(s)-1)+1];
  with Img.Picture.Bitmap do
  begin
    width := Img.Width;
    Height := Img.Height;
    Canvas.Brush.Color := $00EFEFEF;
    Canvas.FillRect(Img.ClientRect);
    for i := 0 to 3 do
    begin
      Canvas.Font.Size := random(20) + 20;
      Canvas.Font.Name := f[High(f)];
      Canvas.Font.Color := c[random(High(c))];
      Canvas.TextOut(i*40,0,r[i+1]);
    end;
    for i := 0 to 2 do
    begin
      Canvas.Pen.Color := c[random(High(c))];
      Canvas.Pen.Width := 2;
      canvas.MoveTo(random(Width),0);
      Canvas.LineTo(random(Width),Height);
      Canvas.Pen.Width := 1;
      x := random(Width-10);
      y := random(Height-10);
      Canvas.Rectangle(x,y,x+10,y+10);
    end;
  end;
  Result := r;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  if (Edit1.Text = validapost) then
    Application.MessageBox('Parabéns, muito bem','Sucesso',
  MB_OK + MB_ICONINFORMATION);
  FormShow(self);
end;
 
procedure TForm1.FormShow(Sender: TObject);
begin
  validapost := GeraImagem(Image1);
end;
 
end.

terça-feira, 8 de fevereiro de 2011

Chave Estrangeira (Foreign Key) com DBExpress e ClientDataSet


Com o uso de DBExpress não existe mais a hipótese de campos Lookup para se trazer um determinado campo de uma chave estrangeira, como fazíamos antigamente. Isso porque para criarmos um campo Lookup, precisaríamos deixar a tabela aberta por completo na memória para que os outros datasets pudessem buscar a informação nela através do campo Lookup, e isto sai completamente dos conceitos do ClientDataSet...
    Para resolver este problema, usa-se JOINs em instruções SQL, que "performaticamente" falando, são mais rápidos, porém, um pouco mais trabalhoso, no sentido em que, ao digitarmos os dados para um ClientDataSet, estes ficam na memória e o Join não é executado no BD, até que o dado seja gravado. Dessa maneira, ao informarmos o ID da chave estrangeira não veríamos o campo que desejamos até que este registro seja gravado. Porém, pode-se “forçar” a busca deste campo através de uma função que você pode mais abaixo.

    Um cenário interessante para podermos exemplificar é um cadastro de venda, onde na tabela de vendas existe uma chave estrangeira da tabela de clientes para se gravar o ID do cliente que esta efetuando aquela compra. Se estivermos fazendo uma consulta, automaticamente o JOIN faz o seu papel e retorna o NOME deste cliente, se a SQL for algo do tipo:
SELECT
  V.CODVENDA,
  V.DATA,
  V.CODCLIENTE,
  C.NOME AS NOME_CLIENTE
FROM
  VENDAS V
     INNER JOIN CLIENTES C ON V.CODCLIENTE = C.CODCLIENTE

    Porém, ao inserir um novo registro o JOIN não é executado como já comentamos, pois os dados não estão no banco ainda, somente em Cache. Resolvendo isso, a função:

function GetFieldByID(var SQLConn: TSQLConnection;
                                   Table, ResultField, IDField: String;
                                   ID: integer): string;
var qry: TSQLQuery;
      SQL: String;
begin
  Result := '';
  SQL:='SELECT ' + ResultField + ' FROM ' + Table + ' WHERE '+ 
  IDField + '=' + IntToStr(ID);
  qry := nil;
  try
    SQLConn.Execute(SQL, nil, @qry);
    if Assigned(qry) then
      Result := qry.FieldByName(ResultField).AsString;
  finally
    qry.Free;
  end;
end;

Entendendo a Função

    Passamos para a função qual o SQLConnection (SQLConn) responsável pela execução da qry e também informamos em qual Tabela (Table) ela deverá buscar, através de qual campo (IDField) e qual ela irá trazer (ResultField), além é claro do ID que estamos procurando.
    A função monta então a SQL e a executa no SQLConnection especificado. Este por sua vez, retorna um ResultSet que fica armazenado na variável qry (que é um TSQLQuery). Se o SQLConnection não retornar o ResultSet é porque a SQL não retornou nada caso contrario (if Assigned(qry)), retorna para o Result da função o valor do campo pesquisado com a SQL montada.

    Para usá-la, no evento OnValidate do campo CODCLIENTE basta fazer:

cdsVendas.FieldByName('NOME_CLIENTE').AsString := GetFieldByID(SQLConnection1,'CLIENTES','NOME','CODCLIENTE',Sender.AsInteger);

    Você também pode tratar o campo verificando se a função retornou algum valor, e se não, gerar uma exceção avisando que o código não foi encontrado, por exemplo:

Var str: string;
begin
  str:=GetFieldByID(SQLConnection1,'CLIENTES','NOME','CODCLIENTE',Sender.AsInteger);
  if str<>'' then
    cdsProdutor.FieldByName('RAZAOS').AsString := str
  else
  begin
    MessageDlg('Código Não Encontrado!', mtWarning, [mbOK], 0);
    Abort;
  end;
end;

    A vantagem de se usar esta função, é que muda-se pouco de seu uso para outros campos, por exemplo, se quiséssemos saber o nome do vendedor supondo que este também esteja previsto na SQL através de um JOIN, seria:

cdsVendas.FieldByName('NOME_VENDEDOR').AsString :=  GetFieldByID(SQLConnection1,'VENDEDORES','NOME','CODVENDEDOR',Sender.AsInteger);

domingo, 6 de fevereiro de 2011

Chega de Showmessage!!!

Se você pensa..."Que Mensagem para validação de campos é muito chato!". Vai encontrar uma maneira bem simples de fugir deste terror.
O que veremos neste artigo é ao invés de mandarmos uma mensagem para o usuário, que é muito chato, você poder fazer o usuário se interagir com o sistema, veja...
Para este Artigo vamos precisar criamor uma Procedure.
Dei o Nome desta Procedure de TrocaCor com a sintaxe abaixo

Procedure TrocaCor(Campo : TWinControl ; CorTrocada , CorMeio, CorFinal : TColor ; Tempo : Cardinal );

O intuito desta rotina é o Seguinte:

- no lugar de mandarmos uma mensagem, faremos com que o campo "pisque", e que receba o foco depois deste "pisca-pisca"

o Parâmetro Campo, é do Tipo WinControl , pois poderá ser um TEdit, TComboBox , TListBox e Etc...(que recebam foco).

Os Parâmetro de Cores:
-CorTrocada
-CorMeio
-CorFinal

São do Tipo TColor, pois são elas as responsáveis de fornecedor o efeito de "piscar".

E a última, Tempo, serve para executar em qual período de Tempo;

vamos Lá Entao:

Implemente esta procedure com CTRL + SHIFT + C, e vamos aos códigos:

if (Campo is TEdit) Then
Begin
  (Campo as TEdit ).Color := CorTrocada;
  (Campo as TEdit).Update;
  Sleep(Tempo);
  (Campo as TEdit).Color := CorMeio;
  (Campo as TEdit).Update;
  Sleep(Tempo);
  (Campo as TEdit).Color := CorTrocada;
  (Campo as TEdit).Update;
  Sleep(Tempo);
  (Campo as TEdit).Color := CorFinal;
  (Campo as TEdit).SetFocus;
End //TEdit
else if (Campo is TComboBox) Then
Begin
  (Campo as TComboBox ).Color := CorTrocada;
  (Campo as TComboBox).Update;
  Sleep(Tempo);
  (Campo as TComboBox).Color := CorMeio;
  (Campo as TComboBox).Update;
  Sleep(Tempo);
  (Campo as TComboBox).Color := CorTrocada;
  (Campo as TComboBox).Update;
  Sleep(Tempo);
  (Campo as TComboBox).Color := CorFinal;
  (Campo as TComboBox).SetFocus;
  End;//TComboBox
End;

Bom, no caso acima eu imaginei que os campos fossem somente de dois tipos, TEdit e TComboBox, mas você poderá ir além disso, ou mais além ainda. Criar uma variável onde armazene a classe e economizar nos códigos...;)

Vamos usá-la Agora:
Coloque um botão no Formulário, um Edit e um ComboBox

E no envento OnClick do Botão Digite:
if Edit1.Text = 'Edit1' Then
Begin
  TrocaCor(Edit1 , clRed, clBlue, clBlack , 50);
  Exit;
End;

Entendendo, o código acima testará se o conteúdo do Edit1 for = Edit1
se Sim ele Executa nossa procedure e sai da Rotina. num intervalo de 50 milisegundos

Lembrando que quanto menor o número mais rápido será a piscagem....

Questões de segurança em banco de dados


Uma das principais preocupações nos dias de hoje é com a segurança das informações que estão armazenadas nos bancos de dados dos sistemas dos nossos clientes.
Para tanto, os desenvolvedores gastam muito tempo com o desenvolvimento de mecanismos que protejam estas informações, como senhas de acesso, níveis de segurança nos programas e políticas de usuários nos bancos de dados.

O que muitos analistas ou desenvolvedores não sabem ou não levam em consideração, é a segurança, não do sistema, não do banco de dados como um servidor, mas dos ARQUIVOS que compões este banco de dados.

Em muitas vezes, enquanto enumerava os benefícios que meu sistema traria a empresa, caso ele substituísse o sistema em uso, eu mostrava esta fragilidade, disfarçada em fortes esquemas de proteção via software.

No início, eram as tabelas PARADOX. Elas, aparentemente, podiam proteger os dados utilizando uma senha "embutida" diretamente nas tabelas. O sistema, contendo entrincados meios de autenticar os usuários, "abriam" estas tabelas com a senha pré-definida.

A principal falha deste método reside no fato de que a senha está em algum ponto no executável, e com um editor de arquivos que mostre hexadecimal, poderá ser encontrada.

Os programadores, motivados pela perspicácia dos invasores, resolveram codificar a senha, mas a necessidade de se obter informações sempre é mais forte, e os invasores começaram a ver que:

"A corrente parte sempre em seu elo mais fraco"

Como os programas estavam cada vez mais protegidos, as atenções se  voltaram para os bancos de dados. Afinal, o objetivo é roubar a informação e não invadir o programa.

Recuperar dados de um sistema não é a coisa mais difícil do mundo quando o programador se apoia somente na segurança do sistema operacional e do servidor de banco de dados. No caso do PARADOX, eu lembro quando recebi um desafio de pegar os dados de uma pessoa em um sistema de cadastro que possuía vários métodos de segurança.
O sistema realmente era bem montado, com senha e políticas de segurança, e o banco continha senha.
Um pouco de tempo na internet e eu consegui acessar todos os dados, com um programa de quebra senhas de tabelas PARADOX, o que mostrou que este tipo de banco de dados não é muito seguro se não for bem estruturado.

Para efeito de didática, eu disponibilizo o programinha em meu site.
Clique aqui para baixar...

Por favor, não o use em bancos de dados de terceiros.

Na empresa em que trabalhei, os programadores (e analistas) tinham o péssimo hábito de trabalhar com o Interbase da pior forma possível:

Eles colocavam uma máquina para ser o servidor do Interbase, até aí tudo bem.
Depois, acreditem, compartilhavam a pasta onde estava o banco de dados GDB usando o compartilhamento de pastas do Windows. Em seguida, as estações que rodavam o programa, acessavam o banco de dados através do mapeamento de pastas da Microsoft, criando um drive para a pasta compartilhada do servidor e acessando o GDB usando o protocolo local.

Eu considero isto a maior heresia de todos os tempos em se tratando de Interbase...
Me perdoem os programadores que lerem este artigo e adotam esta doutrina.

Não fossem todos os problemas com o banco de dados que esta prática causa, ela afeta diretamente a segurança do banco, visto que o Interbase protege os dados a nível de servidor, ou seja, toda a segurança do interbase se baseia na pemissa de que o cliente não acessará o arquivo diretamente, mas sim através da engine do servidor,
que aplicará os métodos de segurança elaborados pelo DBA (users e roles).

Tudo isso significa que, o banco de dados, na forma de arquivo, não possui capacidade para se proteger. Caso o arquivo GDB seja copiado para outro servidor, o SYSDBA poderá acessar os dados da mesma forma (ou usuários com os mesmos nomes). Então, basta instalar um servidor Interbase com a senha padrão do SYSDBA e colocar lá o GDB que se quer invadir.

Obviamente, se o GDB está em uma pasta compartilhada, não será necessário colocar senhas nos programas nem no banco de dados, pois serão inúteis...

Uma outra prática muito usada pelos programadores, talvez por desconhecer o uso correto da conexão IB, é usar o formato \\\Pasta\Arquivo.GDB como DatabaseName do IBDataBase.
Este formato também força o cliente a conectar-se ao banco em modo LOCAL, visto que esta é uma convenção de nomes de rede para compartilhamento de pastas, o que indica que o servidor não estará sendo usado.
Neste caso, o formato correto seria: ::\Caminho\Arquivo.GDB
Sendo "DRIVE" e "caminho" fazendo referência ao sistema de arquivos do SERVIDOR.
Este método é muito eficiente e permite que o IB seja conectado até através da internet, pois dispensa o uso do NetBios.

A primeira vista, soluções simples poderão ser adotadas para proteger os dados e dar um pouco mais de trabalho para a pessoa que desejar roubar informações.

A principal premissa a se assumir é:
"Meu servidor sempre estará exposto de alguma forma"

No caso do PARADOX, eu desaconselho o uso. Nada contra o banco de dados, mas eu não acho que ele tenha sido criado para sistemas que estão em produção. Comparando com os servidores SQL atuais de baixo custo (e até grátis), ele não é a melhor solução.

No caso do Interbase, é obvio que o GDB jamais poderá cair em mãos erradas.
Para evitar, ou diminuir o risco, o ideal é, acima de tudo, utilizar o protocolo de rede para acessar o servidor.

Uma aproximação mais eficiente seria utilizar camadas de acesso ao servidor.
Este método só traria vantagens para o usuário e para o programador.
Para melhorar ainda mais a segurança, a rede em que está localizado o servidor poderia ser diferente da rede onde estão os usuários (não fisicamente, mas logicamente).

Um computador com duas placas de rede (e dois endereços IP) poderia conter a camada de acesso, que é visível aos usuários. Esta camada receberia os usuários por uma interface de rede e se comunicaria com o servidor pela outra. Esta abordagem tornaria o servidor simplesmente inacessível diretamente aos usuários, pois ele estaria em uma faixa de IP incompatível com a rede deles.

Caso o programador não tenha acesso aos métodos descritos, seria ideal utilizar a criptografia para proteger os dados. Escrevi alguns artigos na revista (foram matéria de capa das edições 14 e 20) e alguns artigos no site que descrevem como melhorar a segurança dos dados sem confiar esta tarefa ao servidor ou a máquina onde o servidor foi instalado.

Seguem os links destes artigos:

http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=236 
http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=145
http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=127
http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=123

Uma outra saída, e talvez a mais eficiente, seria manter o nível de paranóia acima do normal (indicado como seguro pelos médicos) e criptografar tudo o que ver pela frente.
Isto inclui todos os dados do banco nas tabelas e também o tráfego na rede!
Eu já ultrapassei a barreira da sanidade ao fazer isso, como um bom gestor de segurança da informação...

Caso o leitor queira chegar a estas fronteiras, faça o download do servidor de SQL que estou aprimorando (sim, é um derivado do FlashFiler da TurboPower). Este servidor de SQL tem todos os componentes para o Delphi e com a vantagem de criptografar tudo o que é armazenado nas tabelas e também criptografar o que é transmitido entre o servidor e os clientes.
A desvantagem é que será uma má notícia para o DBA quando ele descobrir que não lembra da senha do banco ao retornar de férias...

quarta-feira, 2 de fevereiro de 2011

Gaste tempo e depois ganhe mais ainda ao ler este artigo


Algumas idéias citadas abaixo ajudarão profissionais que trabalham com banco de dados a serem mais produtivos e atingir metas firmadas no acordo de nível de serviços (SLAs)
Não paramos e não podemos parar, desde o momento em que acordamos até a hora de se deitar. O mundo está cada vez mais competitivo, lutando por sobrevivência, e podemos afirmar que certamente neste momento há alguém estudando e aprendendo novas tecnologias ou tentando inventar um novo “Google”, por exemplo. Ninguém tem tempo a perder, ao ler este artigo você está gastando ele, mas espero que após a leitura lhe ajude a ganhar outros tantos que lhe dará tempo e uma vida mais calma.

A rotina de quem trabalha com dados é incessante, a infinidade de lugares, formatos e tamanhos, no qual é obrigado a controlar para manter simples conjuntos de bytes em algo compreensível é importante, faz com que se gaste muito tempo criando e executando rotinas estressantes que exigem muita habilidade e conhecimentos técnicos.


Hoje em dia os Databases Administrators(DBAs) são obrigados a gerenciar ambientes complexos que podem envolver diferentes plataformas de banco de dados, nas quais dependem de uma série de tarefas e rotinas, que precisam ser aprendidas em horas e dominadas em dias para serem aplicadas em todas as instâncias. Além disso tem a necessidade de aprender a utilizar a ferramenta de gestão de cada fabricante, que aplicam conceitos diferentes e tornam o aprendizado ainda mais demorado.


Uma forma de resolver estes problemas e também é uma dica que ofereço, é escolher uma ferramenta que permita gerenciar de maneira igual diferentes tipos de banco de dados, permitindo que o DBA domine uma única ferramenta e o seu aprendizado já seja inserido e replicado em qualquer plataforma de banco de dados, direcionando a preocupação do DBA em resolver o problema rápido, sem intervenção das peculiaridades de uma plataforma.


Ao pensarmos em modelagem, muitos a utilizam na fase de criação da estrutura do banco de dados e após a produção da aplicação abandonam o modelo, deixando obsoleto e sem manutenção. Desta maneira começam a surgir as famosas “gambiarras” para manter o sistema em funcionamento, mas de forma “sofrível”.


A modelagem não deve ser apenas utilizada na fase de criação da aplicação, ao integrar a ferramenta de modelagem no banco de dados, possibilitará ainda controle de todo o ciclo de vida e das mudanças, que forem realizadas e propagadas de maneira mais rápida, contribuindo também para uma documentação atualizada e sempre disponível de forma automática.


Outro conselho é escolher uma ferramenta de modelagem que propicie a empresa trabalhar de forma articulada e centralizada, permitindo que analistas de negócios façam o levantamento de requisitos e necessidades de seus usuários, reduzindo o mal entendimento e possibilitando que áreas especialistas em captação de informação possam trabalhar de maneira integrada e automatizada com DBAs e arquitetos de dados. Isso tudo porque ao realizar o modelo conceitual que foi gerado a partir de uma necessidade de negócio será permitido a geração do modelo do banco de dados lógico e físico já direcionado ao usuário, evitando desperdício de esforço aplicado em retrabalho.


Ao termos um modelo atualizado e em uso podemos, ir mais além e, oferecer aos desenvolvedores acesso ao dicionário de dados padronizado seguindo templates pré-fabricados para serem reutilizados pelos desenvolvedores, evitando que eles “reinventem a roda”. Ao sugerir e criar alternativas ou até mesmo novos objetos para armazenar dados, o desenvolvimento de software tem conseguido ser satisfatório seguindo implementações como Gang of Four (GoF) e UML patterns ,do mesmo modo a construção de um banco de dados não precisa ser feito de maneira diferente.


Arquitetos podem disponibilizar para os desenvolvedores estruturas e associações que necessitarão ser reutilizadas com frequência como dados cadastrais de clientes ou produtos, indicando os campos obrigatórios e tipos de dados utilizados, o ganho de tempo estará na constante reutilização e na transformação dos dados para serem movidos ou carregados via ETL.


Boas práticas de mercado são ótimas para serem lidas, nos ajudam a entender como o mercado trabalha e para onde estamos caminhando. Entretanto sabemos que muitas vezes a cultura e até mesmo a dificuldade em adotar uma ou outra técnica acabam dependendo de diversos fatores, mas ao mesmo tempo em que você ou sua empresa não adota ou não se adapta ao mundo de hoje, serão obrigados a viver o mundo de ontem, no qual a tecnologia e seus concorrentes não fazem questão de lembrar.

Função FormatDateTime


FormatDateTime é uma função que retorna uma string de acordo com o formato passado como 1º parametro, tendo como base o 2º parametro que é a data. Veja nesta dica todos os detalhes e exemplos de uso desta função
Ela está presente na unit SysUtils (Delphi 7), com a seguinte implementação:
function FormatDateTime(const Format: string; DateTime: TDateTime): string;
begin 
  DateTimeToString(Result, Format, DateTime); 
end;
Segue um exemplo de uso, para ser colocado no evento onClick de um button, por exemplo:
begin 
  ShowMessage(FormatDateTime('dd/mm/yyyy', (Now + 1))); 
end;
Se hoje for 17/12/2010, este código resultará em uma mensagem contendo:
‘18/12/2010'.
Uma das grandes facilidade que a função nos traz é durante a interação com um banco de dados. Exemplo para o mySQL:

begin 
  MyQuery.SQL.Text := Format( 
    'SELECT * FROM `log` WHERE `date` BETWEEN ''%s'' AND ''%s''', 
    [ 
      // Data inicial 
      FormatDateTime('yyyy-mm-dd', (Now)),
      // Data final 
      FormatDateTime('yyyy-mm-dd', (Now + 1)) 
    ]); 
end;
Veja agora a tabela com os formatos possíveis e seus respectivos resultados, considerando a data e hora "02/09/2008 07:04:01":
Sintaxe Finalidade Resultado
FormatDateTime('c', Now) Resulta na Data e hora com o formato ShortDateFormat + LongTimeFormat. (Se for exatamente meia-noite não irá mostrar a hora) 02/09/2008 07:04:01
FormatDateTime('d', Now) Resulta no valor número que representa o Dia do mês ( sem 0 na frente ) 2
FormatDateTime('dd', Now) Resulta no valor número que representa o Dia do mês ( com 0 na frente [00] ) 02
FormatDateTime('ddd', Now) Resulta na abraviação do nome do dia da semana ter
FormatDateTime('dddd', Now) Resulta no nome do dia da semana terça-feira
FormatDateTime('ddddd', Now) Resulta na data formatada usando ShortDateFormat 02/09/2008
FormatDateTime('dddddd', Now) Resulta na data formatada usando LongDateFormat terça-feira, 2 de setembro de 2008
FormatDateTime('m', Now) Resulta no valor número que representa o mês ( sem 0 na frente ) 9
FormatDateTime('mm', Now) Resulta no valor número que representa o mês ( com 0 na frente [00] ) 09
FormatDateTime('mmm', Now) Resulta na abreviação do nome do mês set
FormatDateTime('mmm', Now) Resulta no nome do mês setembro
FormatDateTime('y', Now) Resulta no ano em dois digitos 08
FormatDateTime('yyyy', Now) Resulta no ano em quatro digitos 2008
FormatDateTime('h', Now) Resulta nas horas ( sem 0 na frente ) 7
FormatDateTime('hh', Now) Resulta nas horas ( com 0 na frente [00] ) 07
FormatDateTime('n', Now) Resulta nos minutos ( sem 0 na frente ) 4
FormatDateTime('nn', Now) Resulta nos minutos ( com 0 na frente [00] ) 04
FormatDateTime('s', Now) Resulta nos segundos ( sem 0 na frente ) 1
FormatDateTime('ss', Now) Resulta nos segundos ( com 0 na frente [00] ) 01
FormatDateTime('z', Now) Resulta nos milisegundos ( sem 0 na frente ) 31
FormatDateTime('zzz', Now) Resulta nos milisegundos ( com 0 na frente [000] ) 031
FormatDateTime('t', Now) Resulta na hora formatada usando ShortTimeFormat 07:04
FormatDateTime('tt', Now) Resulta na hora formatada usando LongTimeFormat 07:04:01
FormatDateTime('am/pm', Now) Reajusta a hora resultada para horários de 12 horas (manha e tarde)



segunda-feira, 31 de janeiro de 2011

Programa - Simulação de Lucro Com Rendimento Mensal


Galera Delphiana! Bom, esta dica que escrevo é muito simples e se trata de um simulador de rendimento de aplicação financeira mensalmente.
O arquivo do projeto encontra-se compactado e disponivel para download aqui (7,64 KB).
Dentro dele, estão todos os códigos desenvolvidos, que não são nada complexos.

Apagando itens selecionados de um ListBox

 
Esta dica mostra como apagar vários itens de uma só vez de um ListBox, não importando se eles são selecionados em sequência ou aleatoriamente. Lembrando que para selecionarmos vários itens de um ListBox, sua propriedade MultiSelect deve estar ativada. Segue o código:
//A procedure tem que ser declarada na sessão "Public" ou "Private" da unit
procedure TForm1.ApagaVarios (var ListBox: TListBox);
var
  i: integer;
  lista: TStringList;
begin
  for i:=0 to ListBox.Items.Count-1 do
    if ListBox.Selected[i] then
      ListBox.Items[i] := '';

  lista:= TStringList.Create;
         
  for i:=0 to ListBox.Items.Count-1 do
    if ListBox.Items[i] <> '' then
      lista.Add(ListBox.Items[i]);

  Listbox.Clear;
  ListBox.Items.Assign(lista);  
  lista.Free;
end;
Para usá-la, coloque um componente ListBox e um componente Button em seu form. Adicione alguns itens no ListBox e ative a sua propriedade MultiSelect, conforme comentado no início e, no onClick do botão, faça o código que chama a procedure para apagar os itens selecionados:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ApagaVarios(ListBox1);
end;
Por: David
Contato: sapodiou2@bol.com.br



Dica do editor

Segue abaixo uma outra forma de se fazer o mesmo procedimento, de maneira mais otimizada:

procedure TForm1.ApagaVarios2(var ListBox: TListBox);
var
  i: integer;
begin
  i := 0;
  while i <= ListBox.Items.Count-1 do
  begin
    if ListBox.Selected[i] then
      ListBox.Items.Delete(i)
    else
      i := i + 1;
  end;
end;

sexta-feira, 28 de janeiro de 2011

Abreviar Nomes


Algumas vezes precisamos transformar, via programação, um nome como "Fulano Beltrano Ciclano" em "Fulano B. Ciclano". Veja nesta dica uma função para abreviar nomes completos e resolver casos como este.
Observação: É importante lembrar que o código ASCII #32 representa o caracter "espaço".

function AbreviarNome(Nome: String): String;
var
  Nomes: array[1..20] of string;
  i, TotalNomes: Integer;
begin
  Nome := Trim(Nome);
  Result := Nome;
  Nome := Nome + #32;
  i := Pos(#32, Nome);
  if i > 0 then
  begin
    TotalNomes := 0;
    while i > 0 do
    begin
      Inc(TotalNomes);
      Nomes[TotalNomes] := Copy(Nome, 1, i - 1);
      Delete(Nome, 1, i);
      i := Pos(#32, Nome);
    end;

    if TotalNomes > 2 then
    begin
      for i := 2 to TotalNomes - 1 do
        if Length(Nomes[i]) > 3 then
          Nomes[i] := Nomes[i][1] + '.';

      Result := '';
      for i := 1 to TotalNomes do
        Result := Result + Trim(Nomes[i]) + #32;

      Result := Trim(Result);
    end;
  end;
end;
Para testá-la, coloque em um form dois edits e um button, fazendo no onClick do button:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit2.Text := AbreviarNome(Edit1.Text);
end;

terça-feira, 25 de janeiro de 2011

Criando componente de data para Banco de Dados


Como percebemos em muitos fóruns, várias pessoas buscam componentes de data para Banco de Dados. Nesta matéria você vai aprender a criar o seu próprio componente para acesso a datas com banco de dados
Se você só desejar pegar o componente, segue abaixo a Unit correspondente ao Componente, que é derivado de TDateTimePicker. Mas se quer aprender e mais detalhes, logo mais abaixo terá as explicações.
unit DBDateTimePicker;

interface

uses
SysUtils, Classes, Controls, ComCtrls, Forms, Dialogs, Graphics, DBCtrls,DB, stdctrls; 

type
TDBDateTimePicker = class(TDateTimePicker)
private
  { Private declarations }
  FDataLink : TFieldDataLink;
  function GetDataField:String;
  procedure SetDataField(const Value : string);
  function GetDataSource:TDataSource;
  procedure SetDataSource(const Value : TDataSource);
  procedure DataChange(Sender:TObject);
  procedure UpdateData(Sender:TObject);
  procedure Change;override;
protected
  { Protected declarations }
public
  { Public declarations }
  constructor Create(AOwner:TComponent);override;
  destructor Destroy;override;
  property Field: TField read GetField;
published
  { Published declarations }
  property DataSource : TDataSource read GetDataSource Write SetDataSource;
  property DataField : String read GetDataField write SetDataField;
end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents("Fernando", [TDBDateTimePicker]);
end;

{ TDBDateTimePicker }

procedure TDBDateTimePicker.Change;
begin
  FDataLink.Modified;
  inherited Change;
  Try
    FDataLink.Edit;
    FDataLink.UpdateRecord;
  Except
  End;
end;

constructor TDBDateTimePicker.Create(AOwner: TComponent);
begin
  inherited;
  FDataLink := TFieldDataLink.Create;
  FDataLink.OnDataChange := DataChange;
  FDataLink.OnUpdateData := UpdateData;
  FDataLink.Control := self;
  Width := 100;
end;

procedure TDBDateTimePicker.DataChange(Sender: TObject);
begin
  Date := FDataLink.Field.AsDateTime;
end;

destructor TDBDateTimePicker.Destroy;
begin
  FDataLink.Free;
  inherited;
end;

function TDBDateTimePicker.GetDataField: String;
begin
  Result := FDataLink.FieldName;
end;

function TDBDateTimePicker.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

procedure TDBDateTimePicker.SetDataField(const Value: string);
begin
  FDataLink.FieldName := Value;
end;

procedure TDBDateTimePicker.SetDataSource(const Value: TDataSource);
begin
  FDataLink.DataSource := Value;
end;

procedure TDBDateTimePicker.UpdateData(Sender: TObject);
begin
  FDataLink.Field.AsDateTime := FDataLink.Field.AsDateTime;
end;

end.

Detalhes:
Quem já conhece o desenvolvimento de componentes para Banco de Dados, pode achar que basta criar as propriedades comuns em banco de dados:

DataSource - Que identifica o objeto da classe TDataSource ao qual o componente está vinculado.
DataField - O nome do campo da tabela cujo valor do registro corrente será exibido e/ou editado pelo componente. 
Porém, como a Borland é maravilhosa, ela desenvolveu uma nova classe chamada TFieldDataLink, será desta que criaremos um componente para acessos a Dados. Esta classe possui duas propriedades principais:

DataSource - Que identificará o componente da classe TDataSource ao qual o nosso componente será vinculado.
FieldName - Que indentificará o nome do campo ao qual o nosso componente será vinculado.

Agora, ao invés de criarmos campos internos, basta usar as propriedades da classe TFieldDataLink (que precisa ser criado e destruído por nosso Componente).

Antes de começarmos, precisamos declarar certas Units:

dbctrls - onde se encotra a classe TFieldDataLink.
db - onde se encontra a classe TDataSource.
uses SysUtils, Classes, Controls, ComCtrls, Forms, Dialogs, Graphics, DBCtrls,DB, stdctrls;
Vamos lá.. 

Precisaremos então criar um objeto interno do tipo TFieldDataLink.

Na sessão private:
FDataLink : TFieldDataLink;
Agora precisamos definir o método de leitura e de escrita dos campos DataSource e DataField. Normalmente, Get Leitura, Set escrita.
-----DataSource
function GetDataSource : TDataSource;
procedure SetDataSource(Value : TDataSource);
-----DataField
function GetDataField : string;
procedure SetDataField(Value : string);
Vamos declarar as propriedades que aparecerão no Object Inspector. Na área Published declarar:
property DataSource : TDataSource read GetDataSource write SetDataSource;
property DataField : string read GetDataField write SetDataField;

Iremos agora implementar esses métodos pressionando CTRL+SHIFT+C
function TDBDateTimePicker.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

procedure TDBDateTimePicker.SetDataSource(const Value: TDataSource);
begin
  FDataLink.DataSource := Value;
end;

function TDBDateTimePicker.GetDataField: String;
begin
  Result := FDataLink.FieldName;
end;

procedure TDBDateTimePicker.SetDataField(const Value: string);
begin
  FDataLink.FieldName := Value;
end;

Vamos definir o método de "Construção" e de "Destruição". Faremos assim, dentro de public:
destructor Destroy;override;
constructor Create(Aowner : TComponent);override;

Pressionando Novamente CTRL+SHIFT+C

Implementaremos esses métodos assim:
constructor TDBDateTimePicker.Create(AOwner: TComponent);
begin
  inherited;
  FDataLink := TFieldDataLink.Create;
end;

destructor TDBDateTimePicker.Destroy;
begin
  FDataLink.Free;
  inherited;
end;

Aparentemente nosso componente está funcionando. Mas somente se você desejar EXIBIR os dados. Se alterado o seu conteúdo, ele não sofrerá modificações, pois não há nenhum método para identificar esta modificação. Então vamos lá.

Na classe TFieldDataLink tem o evento OnDataChange, que é executado sempre que o valor armazenado for modificado.

Na área private:
procedure DataChange(Sender : TObject);
Implementamos com CTRL+SHIFT+C e completaremos da seguinte forma:
procedure TDBDateTimePicker.DataChange(Sender: TObject);
begin
  Date := FDataLink.Field.AsDateTime;
end;

Precisamor associá-lo ao evento OnDataChange de FDataLink, faremo isso no Create da seguinte forma:
FDataLink.OnDataChange := DataChange
Agora precisaremos criar um componente para refletir alterações feitas pelo próprio componente. O procedimento responsável por isso é o OnUpdateData do FDataLink, novamente na área Private:
procedure UpdateData(Sender : TObject).
Implementamos com CTRL+SHIFT+C e completaremos da seguinte forma:
procedure TDBDateTimePicker.UpdateData(Sender: TObject);
begin
  FDataLink.Field.AsDateTime := FDataLink.Field.AsDateTime;
end;

Precisamos associá-lo ao evento OnUpdateData de FDataLink. Faremos isso no Create da seguinte forma:
FDataLink.OnUpdateData := UpdateData;
Precisaremos ainda, criar o método change da classe-base de maneira que o valor armazenado no campo seja alterado sempre que o usuário alterar o texto exibido pelo componente

Na área private:
procedure Change;override;

Implementamos...

procedure TDBDateTimePicker.Change;
begin
  FDataLink.Modified;
  inherited Change;
  Try
    FDataLink.Edit;
    FDataLink.UpdateRecord;
  Except
  End;
end;

Stored Procedures, Triggers e Views


Conheça neste pequeno artigo os principais objetos de um SGBD, tendo em vista que o uso correto destes melhora a velocidade do banco, facilitando até a garantia da integridade entre as tabelas, sem que o programa precise se preocupar com isto

Stored Procedures (SPs) – Procedimentos Armazenados: são equivalentes às subrotinas da linguagem normal. Nelas podem ser inclusas estruturas de repetição e de decisão, executa cálculos, inserções e updates dentro do banco, além de poder ou não retornar datasets (conjunto de dados). Pode ser usado para confecção de relatórios que possuem vários parâmetros ou em rotinas que efetuam vários cálculos. A principal vantagem está na diminuição do tráfego pela rede, quando um comando SQL é mandado pela rede, será mandada toda a consulta e, no caso das SPs seria enviado pela rede apenas os parâmetros, além disso o motor do FB analisa se a expressão está OK, isto ocorre a cada requisição do comando SQL, já no uso de SPs sua otimização é feita no momento da criação, não precisando passar por um interpretador a cada requisição ao banco, ganhando muito em performance com relação às querys convencionais.

Triggers – Gatilhos: diferentemente das SPs as triggers não retornam datasets, porém tem uma característica muito importante: elas são acionadas automaticamente pela alteração da tabela à qual está vinculada. Qual a vantagem disto? As triggers podem ser utilizadas para garantir a integridade dos dados. Imagine que temos a tabela funcionários e a tabela histórico de salário, ao atualizar o salário de um funcionário temos que inserir os dados anteriores na tabela de histórico de salários. Com o uso de triggers isto acontece automaticamente sem que a aplicação precise se preocupar com isto.

Views – Visões: São úteis qando há realização de várias tabelas na mesma consulta. Uma view não é nada mais do que um “select” armazenado no banco, só que tem a vantagem de já estar otimizada, tendo as mesmas características das SPs neste quesito. No uso de views temos consultas com menor tempo de resposta, fazendo com que o Sistema fique mais leve.

NFe - Consumo Indevido do Ambiente de Autorização


O ano está acabando, mas o SEFAZ ainda está nos presentiando com novidades. O novo manual "Aplicação Cliente" Consumo Indevido do Ambiente de Autorização, traz algumas informações importantes que deverão ser levadas em consideração.
A obtenção da autorização de uso da NF-e é um processo que envolve diversos recursos de infraestrutura, hardware e software, tanto por parte da SEFAZ Autorizadora, quanto por parte das empresas. O mau funcionamento ou a indisponibilidade destes recursos pode prejudicar o processo de autorização da NF-e, com reflexos no processo de faturamento da empresa emissora da Nota Fiscal Eletrônica.
A alta disponibilidade é uma das premissas básicas do sistema da NF-e e o Sistema de Autorização de NF-e da SEFAZ foi construído para funcionar em regime de 24x7, no entanto existem diversos componentes do sistema e da infraestrutura que podem apresentar falhas e comprometer a disponibilidade dos serviços.
Por parte das SEFAZ, existem poucos Ambientes de Autorização de NF-e, cada um deles executando uma aplicação própria que de alguma forma tem sido depurada e aperfeiçoada pelo uso intensivo destes ambientes pelas empresas.
Por outro lado, existem milhares de "aplicações cliente" desenvolvidas pelas empresas, cujo comportamento indevido pode gerar um consumo excessivo de recursos do ambiente de autorização das SEFAZ, podendo inclusive vir a prejudicar o uso compartilhado deste ambiente.
Como exemplo maior do mau uso do ambiente de autorização, ressalta-se a falta de controle de algumas aplicações das empresas que entram em "loop", consumindo recursos de forma indevida, sobrecarregando principalmente o canal de comunicação com a Internet. Até agora, o Ambiente de Autorização da SEFAZ tem sido reforçado, com a ampliação de recursos de infraestrutura, mas esta prática não poderá ser mantida indefinidamente, principalmente quando é motivada para suportar o uso indevido do ambiente pela aplicação de algumas empresas.
Leia o manual no link: http://www.nfe.fazenda.gov.br/PORTAL/docs/Consumo_Indevido_Aplicacao_Cliente_v1.00.pdf

segunda-feira, 24 de janeiro de 2011

Texto Para Iniciantes

Introdução à Programação

Caso você já tenha uma base de programação pode pular esta parte, ou pode lê-la caso se sinta inseguro quanto ao seu conhecimento.
Bem, em primeiro lugar, o que este guia exige é uma noção de comandos básicos que todo (ou quase todo) tipo de linguagem exige.
Note que os exemplos serão baseados na programação Delphi, e não qualquer linguagem.

Variáveis

Variável é um nome conhecido por qualquer programador (ou pelo menos deveria ser), este nome significa um endereço na memória onde um certo valor fica guardado. Vejamos um exemplo: se você declara a variável i:

var i:integer; 

 ("var" é nome usado para declarar uma variável em Delphi, "integer" é o tipo de informação que ela deverá armazenar) daí você pode estabelecer um valor para i:

i := 12;  

este valor vai ficar "guardado na memória" para você poder usa-lo mais tarde, por exemplo, para fazer uma conta:

i := 4 * i;

Agora i vale 4 vezes 12 (48).

Loops

Se você já fez algum programa em uma linguagem qualquer deve se lembrar dos famosos FOR e WHILE (loop), caso contrário, vamos a uma introdução a estes comandos:
Em uma linguagem de programação, loop é um comando que faz com que um certo código seja executado enquanto uma certa condição for atendida, um exemplo o ajudará a entender melhor:

var i:integer; //(declara a variável i)
for i := 1 to 30 do
ShowMessage ('O VALOR DE "I" É: ' + IntToStr(I));

 
Este comando fará com que "PARA" (FOR) I = 1, ou seja, o valor inicial de i é 1, "ATÉ" (TO) 30 (inclusive) exiba uma mensagem (SHOWMESSAGE, é o comando de exibir mensagens) escrevendo o valor de i. Portanto serão exibidas 30 mensagens. O valor de i e incrementado automaticamente ao fim do loop. Veja:
for i := 1 to 30 do (i=1) => ShowMessage(...) => (retorna a origem) for i := 1 to 30 do (i=2) => ShowMessage (...) =>
(...)
for i := 1 to 30 do (i=30) => ShowMessage (...) => Fim do loop.

O comando While é semelhante, veja um exemplo de seu uso:

var k:integer; //(declara a variável k).
k := 0 //(define o valor de k como "0".
while k < 10 do
ShowMessage ('k é menor que 10');

 "QUANDO" (WHILE) k é menor que 10 exibe a mensagem "k é menor que 10" (a mensagem em Delphi realmente deve estar entre apóstrofos (aspas simples) e não aspas duplas (como em muitas linguagens)). No entanto este loop não deve ser usado desta forma, pois, ao contrário de "for", while NÃO incrementa automaticamente o número, portanto o loop nunca acabará, pois k é 0 e sempre será 0 (foi definido esse valor em "k := 0"). Portanto seria melhor fazer:

var k:integer; //(declara a variável k).
begin
k := 0 //(define o valor de k como "0". 
while k < 10 do
begin
ShowMessage ('k é menor que 10);
k := k + 1;
end;

 
Agora o valor de k será incrementado de 1 toda a vez que o loop "retornar", portanto depois de 10 mensagens o loop acabará (k vai de 0 até 9). Não se importe com o "begin" e "end" eles serão discutidos mais adiante.

Introdução a linguagem e migrando para Delphi

Variáveis

 Talvez programadores oriundos de outras linguagens tenham certa dificuldade para se acostumar com os padrões delphi. A declaração de variáveis e seu uso, provavelmente parecerá chato para programadores Visual Basic. Vamos a algumas regras práticas:

  1. Não é possível declarar variáveis em qualquer lugar, apenas no início do procedimento.

  2. Os tipos de dados Delphi não são compatíveis (como no VB), por exemplo, você não pode multiplicar a string "X" pela string "Y", mas sim transformá-las em números inteiros e depois multiplica-los, veja: Z := StrToInt(X) * StrToInt(Y);.

Nem mesmo números inteiros são compatíveis com reais.

Sinal de igual

 Não use o sinal de igual para atribuir valores (como em grande parte das linguagens) mas sim ":=" como na linha abaixo:

 i := 0;

 O sinal de igual é usado apenas para comparação como em comandos "If", veja:

 If i = 0 then
ShowMessage('O valor de i é 0');
else
ShowMessage('O valor de i não é 0';

 Note que não se usa o EndIf como no caso do Visual Basic, por exemplo... Mas pode-se usar o begin e end para determinar o início e o fim como em:

if i = 0 then
begin
ShowMessage('O valor de i é 0');
ShowMessage('Foi usado Begin e End porque foram utilizadas duas linhas');
end
else
ShowMessage('O valor de i não é 0';

 Quando se usa uma única linha não há necessidade de usar Begin e End.

IMPORTANTE

Você deve ter notado que em Delphi não se usam aspas duplas ("), mas aspas simples ('), ou apóstrofo, como preferir. Também é usado ponto e vírgula (;) no final de cada comando (mas não nos que terminam com comandos como If. Mas lembre-se que NÃO se deve usar ponto e vírgula antes de else.

 Outro motivo pelo qual no final de cada declaração usa-se o ponto e vírgula é para mostrar que acabou. Por esta razão pode-se continuar o comando na linha de baixo (menos partir strings ao meio), veja:

 Estaria CORRETO:

MessageDlg ('Esta é uma mensagem!',
mtinformation, [mbok], 0);

 Mas estaria INCORRETO:

MessageDlg ('Esta é uma
mensagem!', mtinformation, [mbok], 0);

 Se for necessário use:

MessageDlg ('Esta é uma' +
+ ' mensagem!', mtinformation, [mbok], 0);

Isso é correto.

Comentários

 Os comentários podem estar entre chaves:

{Este é um comentário que pode estar
em mais de uma linha}

 entre sinais de parênteses e asterisco, como o mostrado abaixo:

(* este é um comentário
que pode estar em mais de uma linha
até que seja colocado o sinal de asterisco e fecha parênteses *)

 Ou pode estar depois de duas barras, para comentários rápidos de apenas uma linha, veja:

// Este comentário não pode passar de uma linha...

Operadores

 Eles são:

* - Multiplicação aritmética.
/ - Divisão de números que obtém um número real (exemplo: 5/2 = 2.5).
div - Divisão de números que obtém um número inteiro (exemplo: 5/2 = 2).
+ - Adição.
- - Subtração.
= - Igual.
<> - Diferente.
> - Maior.
>= - Maior ou igual.
< - Menor.
<= - Menor ou igual.

 Exemplo:

Form1.Canvas.TextOut (1, 1, IntToStr((2 * 5) div 3));

 Form1: Nome do formulário;
Canvas: Propriedade (não apenas do formulário) relacionada com desenho. Neste caso ela é usada para escrever no formulário (com o TextOut), algo como o print do Visual Basic. Sua cor de fundo padrão é branca, mas é possível mudá-la acessando a propriedade brush. O primeiro parâmetro é o left, o segundo é o top e o terceiro é o texto a ser escrito.
IntToStr: Transforma um inteiro em caracteres (como já foi dito anteriormente, o Delphi não possui compatibilidade entre Integers e Strings, a não ser que sejam usadas Variants).

 Iniciantes:

Para quem ainda não está integrado com programação aqui vão alguns conceitos:
Parâmetros: São "valores extras", colocados entre parênteses para fornecer outras informações ao método (function ou procedure). No exemplo mostrado, a função precisa dos parâmetros inteiros (que foram determinado como 1 neste caso) para saber a que distância da margem esquerda e o topo, respectivamente;
Left: Valor inteiro que diz a distância até o canto esquerdo de um objeto;
Top: Valor inteiro que diz a distância até o topo de um objeto;


Dicas...

Usando funções externas (de DLLs)...

Usar funções externas, de uma ou mais DLLs é muito simples, basta declara-las da seguinte forma:

function NomeDaFuncao(Parâmetros: Tipo): TipoRetorno; stdcall; external 'NomeDaDLL';

Você pode ver o nome de uma função da DLL na Visualização Rápida do Windows (se esta estiver sido instalada), para isso encontre o arquivo DLL, clique nele com o botão direito do mouse e escolha visualização rápida.

Manipulando "Application"...

A classe TApplication proporciona muitas manipulações úteis do aplicativo como um todo. Por exemplo, caso você queira que quando um aplicativo for minimizado apareça a hora atual no título da barra de tarefas, e quando ele for restaurado não apareça, use o seguinte código no evento OnLoad do formulário, por exemplo:

Application.OnMinimize := ShowDate;
Application.OnRestore := NShowDate;
 
procedure ShowDate;
begin
TForm1.Timer1Timer(Sender);
Timer1.Enabled := True;
end;
 
procedure NShowDate;
begin
Timer1.Enabled := False;
Application.Title := 'Título do Aplicativo';
end;

Este exemplo considera que o aplicativo possua um relógio que atualiza de tempos em tempos (a cada 250 milésimos de segundo por exemplo). Este é desabilitado quando o formulário é restaurado.

Undo na caixa de texto (Memo)...

Use o seguinte código para Desfazer a última ação em uma caixa de Texto (Memo):

Memo1.Perform(EM_UNDO, 0, 0);

o valor retornado de Memo1.Perform(EM_CANUNDO, 0, 0) será diferente de zero se o comando de Desfazer estiver disponível.

Pegando a posição do cursor...

Use o seguinte código para ver a posição do cursor na tela, ele é muito útil quando o evento usado não possui como parâmetros "X" e "Y":

procedure GetPosition (var X; var Y);
var CPos: TPoint;
begin
GetCursorPos(CPos);
X := CPos.X;
Y := CPos.Y;
end;

Guia Inicial Rápido Para Otimização de Índices


Veja neste artigo a tradução de um post feito por Paul Beach em seu blog, que foi mencionado pelo site firebirdnews.org, mas cujos conceitos se aplicam a praticamente todos os SGBDs.
Abaixo, é apresentado um rápido guia inicial com cinco (na verdade seis) pontos para a otimização de índices, cortesia de Pavel Cisar.
1. Sempre defina TODAS as regras (constraints) de integridade: chaves primárias, estrangeiras e índices únicos. Isto será automaticamente utilizado na otimização de junções (joins).
2. Defina índices separados para CADA COLUNA que será utilizada em regras adicionais de suas consultas (ex: filtros em cláusulas WHERE), que não fazem parte do primeiro ponto (pk's, fk's e uniques).
3. Atualize as estatísticas de seus índices regularmente, visto que dados são inseridos / modificados / excluídos em sua base de dados. Uma boa regra geral é atualizar as estatísticas dentro de suas rotinas de manutenção (quando fizer backup, sweep, etc.), e/ou sempre que você inserir / alterar um quarto de todos os registros, uma vez que a tabela possua mais de 10.000 registros.
4. Uma vez tendo dados representativos do mundo real em seu banco de dados, você pode avaliar a usabilidade dos índices para eliminar aqueles que não ajudam suas instruções SQL (um índice insignificante apenas tornará lento seus inserts e updates!) e adicionar novos índices compostos ou de expressões, que aumentem a performance de instruções específicas.
5. Índices inúteis são normalmente aqueles com baixa seletividade (poucos valores distintos). Execute algumas consultas com filtros condicionais em colunas com baixa cardinalidade e verifique quando o otimizador usa o índice ou não. Se o índice de baixa seletividade é sempre ou frequentemente usado em outras condições de filtro, você pode melhorar a sua seletividade e consequentemente a sua usabilidade, criando um índice composto com as colunas que são usadas nestes outros filtros, ao invés de usar índices independentes em cada coluna.
6. Se você sempre / frequentemente usa um grupo de colunas para filtros, um índice composto nestas colunas pode melhorar a performance do comando, mas faça isso apenas se não estiver satisfeito com a performance que os índices em colunas individuais provêem.