TUTORIAL DIALOG BOX
Silício ReversooicìliS ©
Assembly para Iniciantes
TUTORIAIS
Notice: Undefined variable: subtitulo in /home/numaboa.com.br/public_html/informatica/oiciliS/assembler/head.php on line 10

(ver 1.1 de 03.08.03)

Uma caixa de diálogo nada mais é do que uma janela normal que foi desenhada para trabalhar com controles do tipo caixas de edição e botões. Os controles, por sua vez, são apenas janelas-filha. A classe de janelas dialog box foi criada para diminuir o trabalho dos programadores com uma porção de facilidades tipo "prato pronto". O pessoal da microsoft chamou esta janela "especial" de CAIXA de diálogo (dialog box), mas eu prefiro o termo JANELA de diálogo. Neste tutorial usaremos uma janela de diálogo como nossa janela principal.

A JANELA DIALOG BOX
  1. Um pouco de teoria
  2. Escrever o recurso da janela de diálogo com caixa de edição, botões e menu
  3. Criar a janela de diálogo
  4. Gerenciar as mensagens recebidas
    1. Determinar o controle com o foco inicial
    2. Gerenciar os itens de menu
      1. "Pegar Texto"
      2. "Limpar Texto"
      3. "Sair"
    3. Gerenciando o fechamento da janela
    4. Gerenciar botões
    5. O valor de retorno do gerente de mensagens de uma janela de diálogo
  5. A "cara" do aplicativo
  6. Atélogo e download do tutorial
1. UM POUCO DE TEORIA

O Windows possui um "gerente de janelas de diálogo" que administra a maior parte da lógica do teclado, como acionar o botão default quando a tecla [Enter] for pressionada ou mudar o foco de entrada quando a tecla [Tab] for pressionada. As janelas de diálogo são utilizadas principalmente como dispositivos de entrada e saída. Como o gerenciamento fica por conta do Windows, elas funcionam como um espécie de caixa preta: não precisamos saber como uma janela de diálogo funciona internamente, basta saber como interagir com ela. Caso você não saiba, este é um dos princípios da programação orientada a objetos, denominado ocultação de informações. Se você acredita no gerente de diálogos... basta usar a dialog box.

Vamos às mordomias. Normalmente, se pusermos controles (janelas-filha) numa janela normal, precisamos produzir estas janelas-filha como subclasses da janela-mãe, ou seja, precisamos derivar as propriedades da janela-mãe. Além disso, toda a lógica do teclado fica por nossa conta. É muita mão de obra. É muito mais fácil e cômodo deixar o gerente do sistema trabalhar para a gente.

Janelas de diálogo são definidas como um recurso, da mesma maneira que menus. Podemos escrever um modelo com as características e os controles da janela de diálogo e depois compilar o código fonte do recurso com um editor de recursos. É claro que podemos produzir o código fonte "na unha", mas é um processo muito chato. Usando um editor de recursos podemos construir nossa janela de diálogo num ambiente visual - clicar e arrastar botões é mais fácil do que calcular suas coordenadas. Sugestões de editres de recurso você encontra no tutorial "Usando Recursos".

Existem dois tipos de janelas de diálogo: modal e não modal. Uma janela de diálogo não modal permite que o foco seja transferido para outra janela e as modais não permitem. Existem dois subtipos da janela de diálogo modal: modal de aplicativo e modal de sistema. Uma modal de aplicativo não permite que se mude o foco para outra janela do mesmo aplicativo, mas permite que se mude o foco para outra janela de OUTRO aplicativo. Uma modal de sistema nunca permite a mudança de foco - ela precisa ser fechada antes.

As funções da API para criar uma janela de diálogo e comunicar-se com ela são:

Função Observações
CreateDialogParam Não modal
DialogBoxParam Modal de aplicativo
________(estilo DS_SYSMODAL no modelo do recurso) Modal de sistema


SendDlgItemMessage Comunicação com controles
2. O RECURSO DA JANELA DE DIÁLOGO

O código fonte do arquivo de recursos do nosso exemplo contém o modelo de janela de diálogo e um pequeno menu:

#include "resource.h"

#define IDC_EDIT 3000
#define IDC_BUTTON 3001
#define IDC_EXIT 3002
#define MenuDaqui 3003

#define IDM_pegar 32000
#define IDM_limpar 32001
#define IDM_sair 32002

DLGNumaBoa DIALOG 10, 10, 205, 48
STYLE DS_MODALFRAME | 0x0004 | DS_CENTER | WS_CAPTION |
   WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED |
   WS_3DLOOK
CAPTION "Testando o Gerente"
MENU MenuDaqui
BEGIN
   EDITTEXT IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
   DEFPUSHBUTTON "Diga &Oi", IDC_BUTTON, 141,10,52,13
   PUSHBUTTON "&Sair", IDC_EXIT, 141,26,52,13
END

MenuDaqui MENU
BEGIN
   POPUP "Testar Controles"
   BEGIN
      MENUITEM "Pegar Texto", mnID_pegar
      MENUITEM "Limpar Texto", mnID_limpar
      MENUITEM SEPARATOR
      MENUITEM "&Sair", mnID_sair
   END
END

Foi usado um #include referenciando o arquivo resource.h. Este arquivo contém as definições necessárias para dar suporte às constantes nominadas como WS_SYSMENU, WS_VISIBLE, etc. Em relação ao modelo da janela de diálogo, destacado em verde:

O nome da classe: DLGNumaBoa. Palavra chave que identifica o tipo da janela: DIALOG. Medidas em unidades dialog box (não são pixels): 10,10,205,48

Palavra chave para indicar o estilo: STYLE. Composição com OR (|) de todos os estilos desejados: DS_MODALFRAME | ...

Texto da barra de título: CAPTION "Testando o Gerente".

O menu da janela, definido no mesmo arquivo de recursos: MENU MenuDaqui

Início da definição dos controles: BEGIN.

Caixa de Edição: EDITTEXT Identificador, Medidas, Opções

Botão default: DEFPUSHBUTTON Texto no botão, Identificador, Medidas

Botão: PUSHBUTTON Texto no Botão, Identificador, Medidas

Fim da definição de controles: END

Não se esqueça de que o arquivo de recursos precisa ser compilado para poder ser incorporado ao executável! Note que o modelo da janela de diálogo possui um menu, o MenuDaqui, que também precisa ser definido no mesmo arquivo de recursos. Se tiver dúvidas quanto ao modelo do menu, reveja o tutorial "Menus".

3. CRIANDO A JANELA DIALOG BOX

Quando criamos a janela "tradicional", logo após obtermos o manipulador da instância, chamamos o gerente da Janela (o procedimento gerenteJanela) que, entre outras tarefas, se encarrega de criar a janela principal. Acontece que sabemos que o dialog box possui um gerente próprio que fica de plantão para nos atender. Basta requisitar seus serviços através de CreateDialogParam (se quisermos uma janela não modal) ou de DialogBoxParam (se quisermos uma janela modal). No caso, como queremos uma janela modal, só precisamos substituir a linha invoke gerenteJanela do modelo usado até agora por invoke DialogBoxParam:

...
.DATA
   NomeClasse db "DLGNumaBoa",0 // precisa ser o nome dado no recurso

.CODE
inicio:
   invoke GetModuleHandle, NULL
   mov mInstancia, eax
   invoke DialogBoxParam, mInstancia, ADDR NomeClasse, NULL,
      ADDR gerenteMensagem, NULL
   invoke ExitProcess,0
end inicio
4. GERENCIANDO AS MENSAGENS RECEBIDAS

Como não fomos nós que definimos a classe DLGNumaBoa, e sim o gerente de diálogos, precisamos apresentar-lhe nosso gerente de mensagens - que ele não conhece porque é "nosso funcionário". Nós indicamos gerenteMensagem na função DialogBoxParam e precisamos "entregar a ficha" do gerente de mensagens ao gerente de diálogos. Fazemos isto entregando o protótipo da função:

...
includelib \masm32\lib\kernel32.lib

gerenteMensagem proto :DWORD, :DWORD, :DWORD, :DWORD
...
4a. Determinando o controle com o foco inicial

Assim que nossa janela de diálogo for mostrada na tela, queremos que a caixa de edição tenha o foco. Nosso objetivo pode ser alcançado se interceptarmos a mensagem WM_INITDIALOG, que é a "prima próxima" da mensagem WM_CREATE da janela "comum". Como pretendemos alterar uma das propriedades da caixa de edição, é preciso fornecer ao gerente de mensagens o identificador (ID) da mesma. Este identificador, como todos os dos outros controles da janela de diálogo, é o definido no arquivo de recursos. Para obter o manipulador da caixa de edição basta chamar a função GetDlgItem com o identificador do controle e para dirigir o foco para um controle basta chamar a função SetFocus com o manipulador do controle:

HWND GetDlgItem(
   HWND hDlg, // manipulador da janela de diálogo
   int nIDDlgItem // identificador do controle
);

...
.CONST
   IDC_EDIT   equ 3000
   IDC_BUTTON equ 3001
   IDC_EXIT   equ 3002
   IDM_pegar  equ 32000
   IDM_limpar equ 32001
   IDM_sair   equ 32002
...
   gerenteMensagem proc hWnd:HWND, uMsg:UINT,
      wParam:WPARAM, lParam:LPARAM
      .IF uMsg == WM_INITDIALOG
         invoke GetDlgItem, hWnd, IDC_EDIT
         invoke SetFocus, eax
      .ELSEIF ...
4b. Gerenciando os itens do menu

A mensagem WM_COMMAND é enviada quando o usuário seleciona um item de comando do menu, quando um controle envia uma mensagem de notificação para a janela-mãe ou quando uma tecla aceleradora é traduzida. O formato da mensagem WM_COMMAND é:

WM_COMMAND
   wNotifyCode = HIWORD(wParam); // código de notificação
   wID = LOWORD(wParam); // identificador do item, controle ou acelerador
   hwndCtl = (HWND) lParam; // manipulador do controle

O parâmetro wParam é um valor de 32 bits, ou seja, possui 4 bytes de 8 bits. Os dois bytes mais à esquerda formam o word mais significativo (high word) e os dois à direita formam o word menos significativo (low word). Tratando os words em separado, o parâmetro wParam contém dois valores: o código de notificação e o identificador do item, controle ou tecla aceleradora. Veja abaixo para entender melhor ou dê uma olhada no texto de apoio "Registradores".

31 <- 24
23 <- 16
15 <- 8
7 <- 0
    byte mais significativo byte menos significativo
word mais significativo
(código de notificação)
word menos significativo
(identificador do controle)
DWORD
  • wNotifyCode: word mais significativo de wParam. Contém o código de notificação se a mensagem for de um controle. É 1 se a mensagem for de um acelerador e 0 (zero) se a mensagem for de um menu.
  • wID: word menos significativo de wParam. Contém o identificador do item de menu, controle ou acelerador.
  • hwndCtl: valor de lParam. Identifica o controle remetente se a mensagem for de um controle. Caso contrário, o valor é NULL.

De posse destas informações, sabemos que uma mensagem veio de um item de menu somente quando lParam de uma mensagem WM_COMMAND for igual a NULL. Vai daí que... precisamos interceptar a mensagem WM_COMMAND, testar se o parâmetro lParam é NULL e testar cada um dos itens do menu. O identificador do item do menu se encontra no word menos significativo de EAX, ocupando os primeiros 16 bits, por isso usamos AX (e não EAX).

 
...
gerenteMensagem proc hWnd:HWND, uMsg:UINT,
   wParam:WPARAM, lParam:LPARAM

   .IF uMsg == WM_INITDIALOG
      invoke GetDlgItem, hWnd, IDC_EDIT
      invoke SetFocus, eax
   .ELSEIF uMsg == WM_COMMAND
      mov eax, wParam
      .IF lParam == NULL
         .IF ax == IDM_pegar
            ...
4b.1 Item do menu "Pegar Texto"

Quando o item selecionado for "Pegar Texto", cujo ID é IDM_pegar, queremos que o texto que esteja na caixa de edição seja mostrado numa caixa de mensagem. Para obter o texto da caixa de edição utilizamos a função GetDlgItemText, para mostrar o texto a função MessageBox. A última você já conhece do tutorial "Message Box".

UINT GetDlgItemText(
   HWND hDlg, // manipulador da janela de diálogo
   int nIDDlgItem, // identificador do controle
   LPTSTR lpString, // endereço do buffer para o texto
   int nMaxCount // tamanho máximo da string
);

int MessageBox(
   HWND hWnd, // manipulador da janela proprietária
   LPCTSTR lpText, // endereço do texto da message box
   LPCTSTR lpCaption, // endereço do título da message box
   UINT uType // estilo da message box
);

Note que vamos precisar de um buffer para armazenar o texto da caixa de edição, portanto, vamos declará-lo na seção de dados não inicializados. Também vamos precisar do título da message box. Usaremos TituloJanela e modificamos apenas o texto:

...
.DATA
   NomeClasse   db "DLGNumaBoa",0
   TituloJanela db "O que o gerente fez",0

.DATA?
   mInstancia HTINSTANCE ?
   buffer db 512 dip(?)
...

gerenteMensagem proc hWnd:HWND, uMsg:UINT,
   wParam:WPARAM, lParam:LPARAM

   .IF uMsg == WM_INITDIALOG
      invoke GetDlgItem, hWnd, IDC_EDIT
      invoke SetFocus, eax
   .ELSEIF uMsg == WM_COMMAND
      mov eax, wParam
      .IF lParam == NULL
         .IF ax == IDM_pegar
            invoke GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512
            invoke MessageBox, hWnd, ADDR buffer,
               ADDR TituloJanela, MB_OK
            ...
         .ENDIF
4b.2 Item do menu "Limpar Texto"

Quando o item selecionado for "Limpar Texto", cujo ID é IDM_limpar, queremos apenas eliminar qualquer texto que possa estar na caixa de edição. Se trocarmos o texto existente por NULL, limpamos a caixa de edição. Usamos a função SetDlgItemText:

BOOL SetDlgItemText(
   HWND hDlg, // manipulador da janela de diálogo
   int nIDDlgItem, // identificador do controle
   LPCTSTR lpString // texto a ser incluído
);
.IF lParam == NULL
   .IF ax == IDM_pegar
      invoke GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512
      invoke MessageBox, hWnd, ADDR buffer, ADDR TituloJanela, MB_OK
   .ELSEIF ax == IDM_pegar
      invoke SetDlgItemText, hWnd, IDC_EDIT, NULL
      ...
   .ENDIF
4b.3 Item do menu "Sair"

O identificador do item de menu "Sair" é IDM_sair. Quando for selecionado, queremos que uma caixa de mensagem diga "tchauzinho" e a janela de diálogo seja fechada. Precisamos inicializar a string do "tchauzinho" e utilizar a função EndDialog que destrói uma janela de diálogo modal, ou seja, dispensa o gerente de diálogo.

BOOL EndDialog(
   HWND hDlg, // manipulador da janela de diálogo
   int nResult // valor de retorno
);
...
.DATA
   NomeClasse db "DLGNumaBoa",0
   TituloJanela db "O que o gerente fez",0
   Adeus db "Tchauzinho",0

...
      .IF lParam == NULL
         .IF ax == IDM_pegar
            invoke GetDlgItemText, hWnd, IDC_EDIT,
               ADDR buffer, 512
            invoke MessageBox, hWnd, ADDR buffer,
               ADDR TituloJanela, MB_OK
         .ELSEIF ax == IDM_pegar
            invoke SetDlgItemText, hWnd, IDC_EDIT, NULL
         .ELSEIF ax == IDM_sair
            invoke MessageBox, hWnd, ADDR Adeus,
               ADDR TituloJanema, MB_OK
            invoke EndDialog, hWnd, NULL
         .ENDIF
      ...
4c. Gerenciando o fechamento da janela

Já que o fechamento da janela é possível de ser obtido através do item de manu "Sair", podemos aproveitar o código para fechar a janela quando o usuário clica no canto superior direito da janela no botãozinho [x]. Este botãozinho envia uma mensagem do tipo WM_CLOSE:

gerenteMensagem proc hWnd:HWND, uMsg:UINT,
   wParam:WPARAM, lParam:LPARAM

   .IF uMsg == WM_INITDIALOG
      invoke GetDlgItem, hWnd, IDC_EDIT
      invoke SetFocus, eax
      .IF uMsg == WM_CLOSE
         invoke SendMessage, hWnd, WM_COMMAND, IDM_sair, 0
      .ELSEIF uMsg == WM_COMMAND
         ...
4d. Gerenciando os botões

Vimos acima que o parâmetro lParam identifica o remetente se a mensagem for de um controle. Caso contrário, o valor é NULL. Nossos botões são controles, então, as mensagens enviadas por eles terão lParam diferente de NULL. Acontece que precisamos do código de notificação dos botões para testar o evento (clique, duplo clique, etc) e este código se encontra no word mais significativo de wParam. Já vimos que, para obter o word menos significativo, basta transferir o valor de 32 bits para um registrador (EAX, por exemplo) e trabalhar com os primeiros 16 bits usando "meio" registrador (AX, por exemplo). Para acessar os 16 bits mais significativos precisamos usar um expediente: deslocar os bits da esquerda para a direita em 16 posições. A instrução que produz este deslocamento é a SHR, para maiores detalhes leia "Instruções mais comuns". Usaremos o registrador EDX para efetuar o deslocamento e depois utilizaremos DX para obter o código de notificação (de 16 bits).

Quando o botão com "Diga Oi" for acionado, queremos que o texto da caixa de edição seja preenchido com "Viu só? O gerente funcionou" que, claro, precisa ser declarada e inicializada na seção .DATA. Quando o botão "Sair" for acionado, aparece a caixa de mensagem com "Tchauzinho" e a janela de diálogo é fechada.

...
.DATA
   NomeClasse db "DLGNumaBoa",0
   TituloJanela db "O que o gerente fez",0
   Adeus db "Tchauzinho",0
   StringTeste db "Viu só? O gerente funcionou",0
...
      .ELSEIF uMsg == WM_COMMAND
         mov eax, wParam
         .IF lParam == NULL
            .IF ax == IDM_pegar
               invoke GetDlgItemText, hWnd, IDC_EDIT,
                  ADDR buffer, 512
               invoke MessageBox, hWnd, ADDR buffer,
                  ADDR TituloJanela, MB_OK
            .ELSEIF ax == IDM_pegar
               invoke SetDlgItemText, hWnd, IDC_EDIT, NULL
            .ELSEIF ax == IDM_sair
               invoke MessageBox, hWnd, ADDR Adeus,
                  ADDR TituloJanema, MB_OK
               invoke EndDialog, hWnd, NULL
            .ENDIF
         .ELSE
            mov edx, wParam
            shr edx, 16
            .IF dx == BN_CLICKED // botão clicado?
               .IF ax == IDC_BUTTON
                  invoke SetDlgItemText, hWnd, IDC_EDIT,
                     ADDR StringTeste
               .ELSEIF ax == IDC_EXIT
                  invoke SendMessage, hWnd, WM_COMMAND,
                     IDM_sair, 0
               .ENDIF
            .ENDIF
         .ENDIF
      ...
4e. O valor de retorno do gerente de mensagens de uma janela de diálogo

Você viu na rotina do gerente de mensagens de uma janela "normal", quando nenhuma mensagem é interceptada, é preciso redirigir a mensagem para o procedimento padrão do sistema com invoke DefWindowProc, hWnd, uMsg, wParam, lParam. Numa janela de diálogo, em princípio, é preciso fazer a mesma coisa - chamar o procedimento padrão. A diferença é a seguinte: não substituímos o gerente de diálogos, apenas o contratamos. Ele está "de serviço" e não precisamos chamá-lo através de uma função. É suficiente indicar para ele se nós já processamos a mensagem ou não, e isso é possível fazer com TRUE (verdadeiro) ou FALSE (false). Também sabemos que o registrador oficial para valores de retorno é o EAX. Então EAX = TRUE significa que processamos a mensagem; EAX = FALSE significa que a mensagem não foi processada e o gerente precisa tomar as devidas providências.

gerenteMensagem proc hWnd:HWND, uMsg:UINT,
   wParam:WPARAM, lParam:LPARAM

   .IF uMsg == WM_INITDIALOG
      ...
   .ELSEIF uMsg == WM_CLOSE
      ...
   .ELSEIF uMsg == WM_COMMAND
      ...
   .ELSE
      mov eax, FALSE
      ret
   .ENDIF
   mov eax, TRUE
   ret
gerenteMensagem endp
5. A CARA DO APLICATIVO



ATÉLOGO

Mais um tutorial do tamanho de um bonde! Mas é assim mesmo. Toda vez que se discute um assunto em detalhes, a conversa fica longa. OS principais conceitos abordados foram:

  1. Dialog Box: uma janela especial.
  2. Arquivo de recursos: escrevendo o modelo de uma dialog box.
  3. DialogBoxParam: criando uma dialog box modal.
  4. Mensagens da dialog box: o gerente de mensagens.
  5. Trabalhando com words: extraindo um word de um dword.
  6. Valor de retorno: o valor de retorno de uma dialog box.

O arquivo tutNB11.zip (45 Kb) contém o tutorial completo para download, inclusive os textos de referência.



Localizador

| AAAA | Página Inicial | Mapa do Site | Novidades | Busca | Indique esta página | Mestre da Teia | Voltar |
| Localizador || @ Info NumaBoa > oicìliS > Assembly > Dialog Box
Créditos: vovó Vicki

webdesign sobMedida by vickiSoft - /informatica/oiciliS/assembler/tutDlgbox/dlgbox.php (10.02.02) versão 1.1 de 03.08.03
Licença Creative Commons 1998-2006 Aldeia NumaBoa
Exceto onde especificamente declarado, todo material deste site é disponibilizado de acordo com a Licença Creative Commons.