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. |
||||||||||||||||||
|
||||||||||||||||||
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:
|
||||||||||||||||||
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". |
||||||||||||||||||
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
|
||||||||||||||||||
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".
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
|
||||||||||||||||||
![]() |
||||||||||||||||||
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:
O arquivo tutNB11.zip (45 Kb) contém o tutorial completo para download, inclusive os textos de referência. |
| Localizador | ||
|
| 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. | ||