quinta-feira, 8 de setembro de 2011

Ocultar aplicação da lista de tarefas - CTRL+ALT+DEL

- Declare a função abaixo antes da palavra implementation:

function RegisterServiceProcess(dwProcessID, dwType: Integer): 
  Integer; stdcall; external 'KERNEL32.DLL';

- Coloque dois botões no Form;
- No evento OnClick do Button1 coloque:

RegisterServiceProcess(GetCurrentProcessID, 1);

- No evento OnClick do Button2 coloque:

RegisterServiceProcess(GetCurrentProcessID, 0);

=== Para testar ===

Clique no Button1 e pressione CTRL+ALT+DEL. O seu programa
não aparecerá na lista.

Clique no Button2 e pressione CTRL+ALT+DEL. Agora seu programa
aparecerá na lista.

quarta-feira, 7 de setembro de 2011

Mudar a cor do Edit ao receber o foco

Alguns programas mostram o Edit que está com o foco em uma cor diferente dos demais. 
Como fazer isto em Delphi?
Na seção private do form declare o procedimento abaixo:
  
  private
    procedure MudancaDeFoco(Sender: TObject);
  public
  end;

Na seção implementation, escreva o código do procedimento:

{ Esta rotina será chamada através do evento OnExit (perda do foco)
  de todos os componentes do tipo TEdit que existirem no form. }
procedure TForm1.MudancaDeFoco(Sender: TObject);
var
  I: integer;
  Ed: TEdit;
begin
  { Percorre a matriz de componentes do form }
  for I := 0 to ComponentCount - 1 do
    { Se o componente é do tipo TEdit... }
    if Components[I] is TEdit then
    begin
      { Faz um type-casting pata o tipo TEdit }
      Ed := Components[I] as TEdit;

      { Se o Edit está com o foco... }
      if Ed.Focused then
        Ed.Color := clYellow { Amarelo }
      else
        Ed.Color := clWhite; { Branco }
    end;
end;

No evento OnCreate do Form, coloque o código abaixo:

procedure TForm1.FormCreate(Sender: TObject);
var
  I: integer;
begin
  { Percorre a lista de componentes do form (matriz de componentes)
    e verifica cada componente para saber se é um TEdit. Se for,
    associa o evento OnExit do componente com a procedure
    "MudancaDeFoco". }
  for I := 0 to ComponentCount - 1 do
    if Components[I] is TEdit then
      (Components[I] as TEdit).OnExit := MudancaDeFoco;
end;

No evento OnActivate coloque:

procedure TForm1.FormActivate(Sender: TObject);
begin
  { Esta chamada é necessária para que o estado inicial seja
    controlado. }
  MudancaDeFoco(nil);
end;

Observações

Existem outras técnicas mais profissionais para resolver o problema proposto. Uma alternativa excelente é a criação de um novo componente herdado da classe TEdit (ou TCustomEdit) que implemente a mudança de cor no método DoEnter e DoExit.

Arredondamento financeiro

É muito comum encontrar programadores Delphi que têm dúvidas sobre como arredondar um valor real para "n" casas após o separador decimal. A princípio parece um problema simples, pois o próprio Delphi já possui uma função que arredonda para o inteiro mais próximo, a qual poderia facilmente ser utilizada para arredondar para qualquer quantidade de casas decimais. Exemplo:

{ x receberá o valor de y arredondado para 2 casas após o separador. }
x := Round(y * 100) / 100;

{ z receberá o valor de y arredondado para 3 casas após o separador. }
z := Round(y * 1000) / 1000;
No entanto dois problemas poderão aparecer com os exemplos acima:
  • O arredondamento feito pelo Delphi difere daquele feito pelas calculadores financeiras, bem como bancos de dados como InterBase e FireBird.
  • poderão ocorrer pequenos arredondamentos devido ao modo como o Delphi trata números reais, tais como aparecer 3.9999999... em vez de 4.
A função abaixo resolve estes dois problemas.
{ Esta função faz arredondamento de valores reais para "n" casas
  decimais após o separador decimal, seguindo os critérios das
  calculadoras financeiras e dos bancos de dados InterBase e FireBird.
}
function TBRound(Value: Extended; Decimals: integer): Extended;
var
  Factor, Fraction: Extended;
begin
  Factor := IntPower(10, Decimals);
  { A conversão para string e depois para float evita
    erros de arredondamentos indesejáveis. }
  Value := StrToFloat(FloatToStr(Value * Factor));
  Result := Int(Value);
  Fraction := Frac(Value);
  if Fraction >= 0.5 then
    Result := Result + 1
  else if Fraction <= -0.5 then
    Result := Result - 1;
  Result := Result / Factor;
end;

Consultar por mês de um campo data

Problema:

Tenho um cadastro de clientes com Codigo, Nome, DataNasc, etc.
Preciso fazer uma consulta onde apareceão apenas os clientes
que fazem aniversário em determinado mês. Como fazer?

Solução:

Use uma Query como abaixo:
- Coloque no form os seguintes componentes:
  * TQuery
  * TDataSource
  * TDBGrid
  * TEdit
  * TButton

- Altere as propriedades dos componentes como abaixo:
  * Query1.DatabaseName = (alias do BDE)
  * DataSource1.DataSet = Query1
  * DBGrid1.DataSource = DataSource1
  
- Coloque o código abaixo no evento OnClick de Button1:

  Query1.Close;
  Query1.SQL.Clear;
  Query1.SQL.Add('select * from dCli');
  Query1.SQL.Add('where extract(month from DataNasc) = :Mes');
  Query1.ParamByName('Mes').AsInteger := StrToInt(Edit1.Text);
  Query1.Open;

- Execute. Digite um número de 1 a 12 no Edit e clique no 
  botão.

Observações

Os números de 1 a 12 representam, respectivamente, os meses de Janeiro a Dezembro. Este exemplo foi testado com Delphi4, BDE5 e tabela Paradox7.

DBGrid zebrado

Programe o evento OnDrawColumnCell do DBGrid como abaixo:

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject;
  const Rect: TRect; DataCol: Integer; Column: TColumn;
  State: TGridDrawState);
begin
  if State = [] then
  begin
    if Table1.RecNo mod 2 = 1 then
      DBGrid1.Canvas.Brush.Color := clAqua
    else
      DBGrid1.Canvas.Brush.Color := clWhite;
  end;
  DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;

Observação:
O objeto Table1 é da classe TTable (relativa ao BDE), mas esta dica poderá ser usada com outros DataSet's, tais como IBDataSet, ClientDataSet, etc.

domingo, 4 de setembro de 2011

Usando funções no MySQL


Vou tentar passar aqui, um pequeno exemplo de como usar funções do MySQL em nossas instruções SQL.
Em primeiro lugar, certifique-se que você possua instalado uma versão a partir da 5.x do MySQL em seu micro.
No nosso exemplo, criaremos e utilizaremos uma função que retornará a idade do cliente
Crie uma pequena tabela com a seguinte estrutura:

CREATE TABLE `clientes` (
`ID` int(11) NOT NULL auto_increment,
`NOME` varchar(45) default NULL,
`DATANASC` date default NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `ID` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;



Inclua as linhas abaixo na tabela, ou digite suas próprias informações:


INSERT I NTO `clientes` (`ID`, `NOME`, `DATANASC`) VALUES
(1, 'SILVIO SANTOS', '1971-09-18'),
(2, 'SILLAS SANTOS', '1993-04-27');


Agora, crie a função conforme o exemplo abaixo:


CREATE FUNCTION `idade`(pDATA DATE)
RETURNS varchar(15) CHARSET latin1
NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
COMMENT ''
Begin
DECLARE vIDADE varchar(30);
IF DATE_FORMAT(NOW( ),'00-%m-%d') >= DATE_FORMAT(pDATA,'00-%m-%d') THEN
SET @idade=DATE_FORMAT(NOW( ),'%Y')-DATE_FORMAT(pDATA,'%Y');
ELSE
SET @idade=DATE_FORMAT(NOW( ),'%Y')-DATE_FORMAT(pDATA,'%Y')-1;
END IF;
RETURN(concat(@idade,' anos'));
end;


Esta função, retornará a idade da pessoa de acordo com o campo DATANASC, da tabela ‘clientes’.
Veja que a função receberá um valor (pDATA, que no nosso exemplo será o campo ‘DATANASC’ de nossa tabela) e fará a subtração da “dat a corrente” pela data do campo DATANASC.
Dentro da função, criamos uma variável (vIDADE), onde armazenaremos o valor a ser retornado pela função. Note também que a função possui retorno do tipo String (RETURNS varchar(30)), que deverá ter o tamanho suficiente para comportar o resultado retornado. Ou seja, se o valor retornado tiver 15 caracteres, o ‘RETURNS’ da função precisa ter tamanho de 15 ou mais caracteres, senão, pode acontecer de não retornar nenhum valor.

Após ter criado a tabela, inserido as linhas na mesma e criado a função, execute a seguinte instrução:


SELECT NOME, idade(DATANASC) IDADE FROM clientes;


No SQL acima, a consulta irá retornar o Nome, e a idade assim: “XX anos”, para cada cliente.

Como você viu, utilizar funções dentro do banco de dados por ser muito mais prático do que fazê-lo dentro da aplicação (me refiro a funções para retorno de dados, claro).

Lembrando que este exemplo é apenas uma base, pois podemos criar funções para qualquer necessdiade, como: calculo de preços, juros de mora, datas, horas, concatenação de campos complexos, junções de tabelas (como nos inner, left e right joins (desde que precise retornar apenas um valor), e muito mais.

JSON e Generics - Serializando Objetos


Desta vez vou demonstrar duas novas tecnologias que estão sendo muito melhoradas no Delphi desde a versão 2009 até a ultima versão o Delphi XE. Generics é uma forma de poder

...passar parâmetro de tipos para classes e métodos, possibilitando assim reutilização de código, por exemplo e, JSON (JavaScript Object Notation) é uma forma de troca de dados entre aplicações, semelhante ao XML, porém com uma sintaxe bem mais simples e leve. O Delphi XE, como algumas versões anteriores, dá suporte a implementação usando JSON, e em nosso exemplo vamos criar um método totalmente genêrico que recebe um objeto e um tipo e usaremos o JSON para formatar este objeto e gravar no disco usando a nova classe de IO (input/output) do Delphi, e um método de leitura deste arquivo no formato JSON para retornar o mesmo a um Objeto, novamente passando o tipo do objeto e o caminho onde se encontra o arquivo.

Primeiro vamos criar uma nova classe do tipo TPessoa:

type
  TPessoa = class
  private
    { Private declarations }
    FPessIden: Integer;
    FPessNome: string;

  public
    { Published declarations }
    property PessIden: Integer read FPessIden write FPessIden;
    property PessNome: string read FPessNome write FPessNome;

  end;

Para conseguirmos desenvolver nossos métodos, temos antes que adicionar ao uses as unit’s citadas abaixo. Acima de cada unit tem a descrição de sua função no projeto:

uses
  SysUtils,

  { Unit que contém os novos métodos de I/O }
  IOUtils,

  { Unit's necessárioas para usar JSON }
  DBXJSONReflect, DBXJSON,

  { Unit necessária para usar Generic }
  Generics.Collections;

Agora vamos criar nossa classe que irá conter os métos necessários para carregar e salvar os dados no disco. O class antes da function indica que estes métodos serão estáticos, não sendo necessário instânciar a classe TJSONTools para usar estes métodos, e a descrição T : class, indica que este método recebe um tipo e que a variável obj passada por parâmetro é deste tipo e, na outra função que ela retorna o tipo especificado.

type
  TJSONTools = class
    public
    { Public declarations }
    class function saveFileJSON(obj: T; const filePath: string): Boolean;
    class function loadFileJSON(const filePath: string): T;
  end;

Agora nosso método que irá gravar no disco o objeto da classe descrita acima.:

class function TJSONTools.saveFileJSON(obj: T; const filePath: string): Boolean;
var
  Marshal: TJSONMarshal;

begin
  Marshal := TJSONMarshal.Create(TJSONConverter.Create());
  try
    try
      TFile.WriteAllText(filePath, (Marshal.Marshal(obj) as TObject).ToString);

      Result := True;
    except
      Result := False;
    end;
  finally
    FreeAndNil(Marshal);
  end;
end;

Agora o método responsável por ler o arquivo e carregar novamente os dados para um objeto:

class function TJSONTools.loadFileJSON(const filePath: string): T;
var
  Unmarshal: TJSONUnMarshal;
  obj: TJSONObject;

begin
  Unmarshal := TJSONUnMarshal.Create();
  try
    try
      if not(FileExists(filePath)) then
        Exit(nil);

      obj := TJSONObject.ParseJSONValue(
        TEncoding.ASCII.GetBytes(TFile.ReadAllText(filePath)),
        0) as TJSONObject;

      Result := T(Unmarshal.Unmarshal(obj));
    except
      Exit(nil);
    end;
  finally
    FreeAndNil(Unmarshal);
  end;
end;

Agora uma forma de poder usar nossas classes:

var
  objPessoa: TPessoa;

begin
  objPessoa:= TPessoa.Create();
  objPessoa.PessIden := 1;
  objPessoa.PessNome := 'Active Delphi';

  if (TJSONTools.saveFileJSON&ltTPessoa&gt(objPessoa, 'C:\pessoa.txt')) then
    ShowMessage('Criou o arquivo no caminho: ' + #13 + ' C:\pessoa.txt');

  objPessoa := TJSONTools.loadFileJSON&ltTPessoa&lt('C:\pessoa.txt');
  if (Assigned(objPessoa)) then
  begin
    ShowMessage(
      'PessIden: ' + IntToStr(objPessoa.PessIden) + #13 +
      'PessNome: ' + objPessoa.PessNome
    );
  end
  else
    ShowMessage('Não foi possivel des-serializar o objeto');
end;

Este foi apenas um exemplo didático usando JSON e Generics no Delphi. Estes métodos ainda podem ser refatorados e até criado validações para tratar possíveis erros de parâmetros, que podem ser identificados criando testes unitários sobres estes métodos.

Obrigado pela leitura e até a próxima!

Clique aqui para fazer o download do exemplo, feito com Delphi XE