Dicas de Delphi - Formulários


Forçar foco em janela

As funções abaixo forçam para que a janela informada fique em primeiro plano.

Primeira alternativa

function ForceForegroundWindow(hwnd: THandle): Boolean;
const
  SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
  SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
  ForegroundThreadID: DWORD;
  ThisThreadID: DWORD;
  timeout: DWORD;
begin
  if IsIconic(hwnd) then ShowWindow(hwnd, SW_RESTORE);

  if GetForegroundWindow = hwnd then Result := True
  else
  begin
    // Windows 98/2000 doesn't want to foreground a window when some other
    // window has keyboard focus

    if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
      ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
      ((Win32MajorVersion > 4) or ((Win32MajorVersion = 4) and
      (Win32MinorVersion > 0)))) then
    begin
      // Code from Karl E. Peterson, www.mvps.org/vb/sample.htm
      // Converted to Delphi by Ray Lischner
      // Published in The Delphi Magazine 55, page 16

      Result := False;
      ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow, nil);
      ThisThreadID := GetWindowThreadPRocessId(hwnd, nil);
      if AttachThreadInput(ThisThreadID, ForegroundThreadID, True) then
      begin
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hwnd);
        AttachThreadInput(ThisThreadID, ForegroundThreadID, False);
        Result := (GetForegroundWindow = hwnd);
      end;
      if not Result then
      begin
        // Code by Daniel P. Stasinski
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0),
          SPIF_SENDCHANGE);
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
      end;
    end
    else
    begin
      BringWindowToTop(hwnd); // IE 5.5 related hack
      SetForegroundWindow(hwnd);
    end;

    Result := (GetForegroundWindow = hwnd);
  end;
end; { ForceForegroundWindow }

Segunda alternativa

A função abaixo consegue forçar o foco na janela especificada criando-se um formulário com dimensão de um apenas 1 ponto e simulando um clique de mouse neste formulário para que a aplicação receba o foco de entrada. Em seguida a janela especificada é colocada em primeiro plano usando-se a função SetForegroundWindow da API do Windows.
procedure ForceForegroundWindow(hwnd: THandle);
  // (W) 2001 Daniel Rolf
  // http://www.finecode.de
  // rolf@finecode.de
var
  hlp: TForm;
begin
  hlp := TForm.Create(nil);
  try
    hlp.BorderStyle := bsNone;
    hlp.SetBounds(0, 0, 1, 1);
    hlp.FormStyle := fsStayOnTop;
    hlp.Show;
    mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
    mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
    SetForegroundWindow(hwnd);
  finally
    hlp.Free;
  end;
end;

Terceira alternativa

A biblioteca USER32.DLL possui uma função não documentada que propõe forçar o foco em determinada janela. A declaração da função é:
procedure SwitchToThisWindow(h1: hWnd; x: bool); stdcall;
  external user32 Name 'SwitchToThisWindow';
         {x = false: Size unchanged, x = true: normal size}

{ Exemplo }

procedure TForm1.Button2Click(Sender: TObject);
begin
  SwitchToThisWindow(FindWindow('notepad', nil), True);
end;

Atenção!

Nos testes que fiz usando Windows XP Professional e Delphi 6, a primeira e segunda alternativas funcionaram perfeitamente. A última não funcionou nos testes, mas foi mantida aqui para que outras pessoas possam experimentá-la.

Autor: Desconhecido

Início


Anexar dois forms

É comum encontrarmos aplicativos que possuem dois ou mais
formulários que se mantém o tempo todo "colados" um ao outro.
É o caso, por exemplo, do conhecido Winamp. Como fazer isto 
em aplicações Delphi? Vamos aos passos:

1. Crie um novo projeto com um form (Form1).
2. Adicione mais um form (Form2).
3. Declare os métodos abaixo na seção private do Form1:

  private
    procedure AjustarForm2;
    procedure WMMove(var Msg: TMessage); message WM_MOVE;

4. Abaixo da palavra implementation escreva:

procedure TForm1.AjustarForm2;
begin
  if Form2 <> nil then begin
    Form2.Width := Width;
    Form2.Left := Left;
    Form2.Top := Top + Height;
  end;
end;

procedure TForm1.WMMove(var Msg: TMessage);
begin
  AjustarForm2;
end;

5. Escreva o evento OnShow do Form1 como abaixo:

procedure TForm1.FormShow(Sender: TObject);
begin
  Form2.Show;
end;

6. Escreve o evento OnHide do Form1 como abaixo:

procedure TForm1.FormHide(Sender: TObject);
begin
  Form2.Hide;
end;

7. Escreve o evento OnReSize do Form1 como abaixo:

procedure TForm1.FormResize(Sender: TObject);
begin
  AjustarForm2;
end;

Pronto! Execute e experimente arrastar ou redimensionar o 
Form1 para ver o efeito.

Observações

Neste exemplo, se o usuário mexer no Form2 o Form1 não se ajustará automaticamente. Existem no mínimo duas alternativas para resolver este caso: deixar o Form2 sem borda ou codificar os eventos do Form2 para ajustar o Form1.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Impedir que o form seja fechado com Alt+F4

Este é um problema fácil de resolver. Vejamos porque.

Toda vez que um form recebe um comando para ser fechado, 
tal como Form1.Close ou mesmo uma mensagem WM_CLOSE, o evento
OnCloseQuery é disparado. Este evento passa um parâmetro por
referência normalmente chamado CanClose. Se alternarmos o valor
deste parâmetro para false o processo de fechar o formulário 
será cancelado.

Uma vez que queremos impedir que o form seja fechado com 
Alt+F4, temos que dar ao usuário outra forma de fechá-lo.
Neste exemplo vamos colocar um botão para esta tarefa.

Vamos aos passos:

1. Declare um campo (variável) na seção private do Form:
  
   private
     FPodeFechar: boolean;

2. No evento OnCreate do form coloque:

   FPodeFechar := false;

3. No evento OnCloseQuery do form coloque:

   CanClose := FPodeFechar;

4. Coloque um botão no form e no seu evento Click coloque:
   
   FPodeFechar := true;
   Close;   
  
Pronto! Execute e teste.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Impedir que o form seja arrastado para fora das margens da tela

- Na seção Private declare a procedure abaixo:

private
  procedure WMMove(var Msg: TWMMove); message WM_MOVE;

- Abaixo da palavra implementation escreva a procedure
  abaixo:

procedure TForm1.WMMove(var Msg: TWMMove); 
begin
  if Left < 0 then
    Left := 0;
  if Top < 0 then
    Top := 0;
  if Screen.Width - (Left + Width) < 0 then
    Left := Screen.Width - Width;
  if Screen.Height - (Top + Height) < 0 then
    Top := Screen.Height - Height;
end;

Para testar:

- Execute o programa e tente arrastar o form para fora
  das margens da tela e veja o que acontece.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Enviar comandos de rolagem vertical para um TMemo

Inclua na seção uses: Windows
Problema:

Gostaria que o meu programa rolasse automaticamente o 
conteúdo de um TMemo, simulando o deslizamento da barra de
rolagem vertical. Isto é possível no Delphi?

Solução:

Sim. Utilizando mensagens do Windows isto é fácil. Vejamos
algums exemplos:

SendMessage(Memo1.Handle, WM_VSCROLL, SBPAGEDOWN, 0);

Onde:
  Memo1.Handle = manipulador da janela do Memo1.
    WM_VSCROLL = Mensagem do Windows - rolagem vertical.
   SB_PAGEDOWN = Comanndo de rolagem - página para baixo.

Outros exemplos:

{ Página para cima }
SendMessage(Memo1.Handle, WM_VSCROLL, SBPAGEUP, 0);

{ Linha para baixo }
SendMessage(Memo1.Handle, WM_VSCROLL, SBLINEDOWN, 0);

{ Linha para cima }
SendMessage(Memo1.Handle, WM_VSCROLL, SBLINEUP, 0);

Observações

Além desta técnica existem API's do Windows que fazem um trabalho equivalente.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Construir a barra de título do form com um Panel

Pegue o arquivo tbtitle.zip na seção Download de www.tecnobyte.com.br

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Criar form sem título que possa ser arrastado

Problema:

Fazer um relógio num form é fácil. Porém gostaria que esse
form não possuísse a barra de título, mas que o usuário
ainda pudesse arrastá-lo com o mouse. Isto é possível 
no Delphi?

Solução:

Sim, é possível e é fácil. Siga os passos abaixo:

- Crie um novo projeto;
- Mude as seguintes propriedades do Form1: 
  BorderStyle = bsNone, FormStyle = fsStayOnTop,
- Coloque um Label;
- Coloque um Timer;
- Altere o evento OnTimer do Timer1 conforme abaixo:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label1.Caption := TimeToStr(Time);
end;

- Altere o evento OnCreate do Form1 conforme abaixo:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Width := 80;
  Height := 40;
  Label1.Left := 10;
  Label1.Top := 10;
end;

- Vá na seção private do Form1 e declare a procedure abaixo:

private
  procedure WMNCHitTest(var Msg: TMessage); 
    message WM_NCHitTest;
public
  { Public declarations }
end;

- Vá na seção implementation e escreva a procedure abaixo:

implementation

{$R *.DFM}

procedure TForm1.WMNCHitTest(var Msg: TMessage);
begin
  if GetAsyncKeyState(VK_LBUTTON) < 0 then
    Msg.Result := HTCAPTION
  else
    Msg.Result := HTCLIENT;
end;

- Execute e experimente arrastar form com o mouse. 

Observações

Para fechar este aplicativo pressione Alt+F4. Uma alternativa mais elegante é colocar um menu local (PopupMenu) com um comando para fechar.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Criar caixas de diálogo em tempo de execução

Inclua na seção uses: Forms, StdCtrls, Buttons
A função abaixo demonstra a criação de uma caixa de diálogo
que pode ser usada para permitir ao usuário digitar o seu
nome:

{ Esta função retorna true se for pressionado OK e false
  em caso contrário. Se for OK, o texto digitado pelo usuário
  será copiado para a variável Nome }

function ObterNome(var Nome: string): boolean;
var
  Form: TForm; { Variável para o Form }
  Edt: TEdit; { Variável para o Edit }
begin
  Result := false; { Por padrão retorna false }
  { Cria o form }
  Form := TForm.Create(Application);
  try
    { Altera algumas propriedades do Form }
    Form.BorderStyle := bsDialog;
    Form.Caption := 'Atenção';
    Form.Position := poScreenCenter;
    Form.Width := 200;
    Form.Height := 150;
    { Coloca um Label }
    with TLabel.Create(Form) do begin
      Parent := Form;
      Caption := 'Digite seu nome:';
      Left := 10;
      Top := 10;
    end;
    { Coloca o Edit }
    Edt := TEdit.Create(Form);
    with Edt do begin
      Parent := Form;
      Left := 10;
      Top := 25;
      { Ajusta o comprimento do Edit de acordo com a largura
        do form }
      Width := Form.ClientWidth - 20;
    end;
    { Coloca o botão OK }
    with TBitBtn.Create(Form) do begin
      Parent := Form;
      { Posiciona de acordo com a largura do form }
      Left := Form.ClientWidth - (Width * 2) - 20;
      Top := 80;
      Kind := bkOK; { Botão Ok }
    end;
    { Coloca o botão Cancel }
    with TBitBtn.Create(Form) do begin
      Parent := Form;
      Left := Form.ClientWidth - Width - 10;
      Top := 80;
      Kind := bkCancel; { Botão Cancel }
    end;
    { Exibe o form e aguarda a ação do usuário. Se for OK... }
    if Form.ShowModal = mrOK then begin
      Nome := Edt.Text;
      Result := true;
    end;
  finally
    Form.Free;
  end;
end;

Para chamar esta função siga o exemplo abaixo:

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
begin
  if ObterNome(S) then
    Edit1.Text := S;
end;

Observações

Os componentes Label, Edit (var Edt) e BitBtn's (botões) não são destruídos explicitamente (Componente.Free). Isto não é necessário, pois ao criá-los informei como proprietário o Form (ex: TLabel.Create(Form)). Neste caso, estes componentes são destruídos automaticamente ao destruir o Form (Form.Free).

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Determinar se uma janela (form) está maximizada

Inclua na seção uses: Windows
if IsZoomed(Form1.Handle) then
  { Form1 está maximizado }
else
  { Form2 NÃO está maximizado }

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Determinar se o aplicativo está minimizado

Inclua na seção uses: Windows
if IsIconic(Application.Handle) then
  { Minimizado }
else
  { Não minimizado }


Observações

Pode-se verificar qualquer janela (form). Só um lembrete: quando clicamos no botão de minimizar do form principal, na verdade ele é oculto e o Application é que é minizado.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Maximizar um form de forma que cubra toda a tela, inclusive a barra de tarefas

{ É um "maximizar" com jeitinho brasileiro... mas funciona.
  No evento OnShow do form coloque o código abaixo: }

Top := 0;
Left := 0;
Width := Screen.Width;
Height := Screen.Height;

Observações

Nos testes que fiz, mesmo com a barra de tarefas marcada como "Sempre Visível", funcionou perfeitamente. Fiz os testes usando o Win95. Talvez em novas versões, possa apresentar problemas.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Posicionar Form's em relação ao Desktop do Windows

{ Quando usamos a propridade Position de um Form para
  centralizá-lo estamos sujeitos a um inconveniente:
  dependendo da posição/tamanho da barra de tarefas do
  Windows, o nosso Form poderá ficar parcialmente coberto
  por ela. Uma forma eficaz de resolver este problema é
  posicionar o form considerando apenas a área livre do
  Desktop. Vejamos este exemplo:

  - Crie um novo projeto;
  - Na seção implementation digite a procedure abaixo:
}

procedure FormPos(Form: TForm; const Horz, Vert: byte);
{ Horz: 1=esquerda, 2=centro, 3=direita
  Vert: 1=topo, 2=centro, 3=em baixo }
var
  R: TRect;
begin
  if not SystemParametersInfo(SPI_GETWORKAREA, 0, @R, 0) then
    R := Rect(0, 0, Screen.Width, Screen.Height);
  with Form do
    case Horz of
      1: Form.Left := 0;
      2: Form.Left := (R.Right - R.Left - Width) div 2;
      3: Form.Left := R.Right - Width;
    end;
  with Form do
    case Vert of
      1: Form.Top := 0;
      2: Form.Top := (R.Bottom - R.Top - Height) div 2;
      3: Form.Top := R.Bottom - Height;
    end;  
end;

{ - Coloque dois TEdit's: Edit1 e Edit2;
  - Coloque um TButton e altere o evento OnClick deste 
    conforme abaixo:
}

procedure TForm1.Button1Click(Sender: TObject);
begin
  FormPos(Form1, StrToInt(Edit1.Text), StrToInt(Edit2.Text));
end;

Observações

Para testar, execute este exemplo e experimente digitar números de 1 a 3 em ambos os Edit's e clique no Button para ver o resultado. O Edit1 indica a posição horizontal (esquerda, centro e direita) e o Edit2 indica a posição vertical (topo, centro e em baixo).

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Salvar/restaurar o tamanho e posição de Form's

{ Crie uma nova Unit conforme abaixo: }
unit uFormFunc;

interface
uses Forms, IniFiles, SysUtils, Messages, Windows;

procedure tbLoadFormStatus(Form: TForm; const Section: string);
procedure tbSaveFormStatus(Form: TForm; const Section: string);

implementation

procedure tbSaveFormStatus(Form: TForm; const Section: string);
var
  Ini: TIniFile;
  Maximized: boolean;
begin
  Ini := TIniFile.Create(ChangeFileExt(
    ExtractFileName(ParamStr(0)),'.INI'));
  try
    Maximized := Form.WindowState = wsMaximized;
    Ini.WriteBool(Section, 'Maximized', Maximized);
    if not Maximized then begin
      Ini.WriteInteger(Section, 'Left', Form.Left);
      Ini.WriteInteger(Section, 'Top', Form.Top);
      Ini.WriteInteger(Section, 'Width', Form.Width);
      Ini.WriteInteger(Section, 'Height', Form.Height);
    end;
  finally
    Ini.Free;
  end;
end;

procedure tbLoadFormStatus(Form: TForm; const Section: string);
var
  Ini: TIniFile;
  Maximized: boolean;
begin
  Maximized := false; { Evita msg do compilador }
  Ini := TIniFile.Create(ChangeFileExt(
    ExtractFileName(ParamStr(0)),'.INI'));
  try
    Maximized := Ini.ReadBool(Section, 'Maximized', Maximized);
    Form.Left := Ini.ReadInteger(Section, 'Left', Form.Left);
    Form.Top := Ini.ReadInteger(Section, 'Top', Form.Top);
    Form.Width := Ini.ReadInteger(Section, 'Width', Form.Width);
    Form.Height := Ini.ReadInteger(Section, 'Height', Form.Height);
    if Maximized then
      Form.Perform(WM_SIZE, SIZE_MAXIMIZED, 0);
      { A propriedade WindowState apresenta Bug.
        Por isto usei a mensagem WM_SIZE }
  finally
    Ini.Free;
  end;
end;

end.

{
  Em cada formulário que deseja salvar/restaurar:
  - Inclua na seção uses: uFormFunc
  - No evento OnShow digite: 
    tbLoadFormStatus(Self, Self.Name);
  - No evento OnClose digite:
    tbSaveFormStatus(Self, Self.Name);
}

Observações

O arquivo INI terá o nome do executável e extensão INI e será salvo no diretório do Windows. A palavra Self indica o Form relacionado com a unit em questão. Poderia ser, por exemplo, Form1, Form2, etc. Onde aparece Self.Name poderá ser colocado um nome a sua escolha. Este nome será usado como SectionName no arquivo INI e deve ser idêntico no evento OnShow e OnClose de um mesmo Form, porém para cada Form deverá ser usado um nome diferente.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Mostrar um Form de LogOn antes do Form principal

{
  * Crie um novo Projeto. Este certamente terá o Form1.
  * Adicione um novo Form (Form2).
  * Coloque no Form2 dois botões TBitBtn.
  * Mude a propriedade Kind do BitBtn1 para bkOK.
  * Mude a propriedade Kind do BitBtn2 para bkCancel.
  * Vá no menu "Project/Options" na aba "Forms" e passe o
    Form2 de "Auto-create Forms" para "Available Forms".
  * Abra o arquivo Project.dpr (menu Project/View Source).
  * Altere o conteúdo deste arquivo conforme abaixo:
}

program Project1;

uses
  Forms, Controls,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.RES}

var
  F: TForm2;

begin
  F := TForm2.Create(Application);
  try
    if F.ShowModal = mrOK then begin
      Application.Initialize;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end;
  finally
    F.Free;
  end;
end.

Observações

O Form2 do exemplo é o Form de LogOn. Este deverá ser preparado para que se possa escolher o usuário, digitar a senha, etc.

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Fazer a barra de título ficar intermitente (piscante)

Inclua na seção uses: Windows
{ Coloque um TTimer no Form desejado. Define a propriedade
  Interval do Timer para 1000 (1 segundo). Modifique
  o evento OnTimer do Timer conforme abaixo: }

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  FlashWindow(Handle, true);
  FlashWindow(Application.Handle, true);
end;

Autor: Daniel P. Guimarães
Home-page: www.tecnobyte.com.br

Início


Página atualizada em 18 de setembro de 2014
Todos os direitos reservados
www.tecnobyte.com.br