Usando caixas de diálogo
Use caixas de diálogo para exibir informações e solicitar a entrada do usuário. Seu aplicativo carrega e inicializa a caixa de diálogo, processa a entrada do usuário e destrói a caixa de diálogo quando o usuário conclui a tarefa. O processo de manipulação de caixas de diálogo varia, dependendo se a caixa de diálogo é modal ou sem formação. Uma caixa de diálogo modal exige que o usuário feche a caixa de diálogo antes de ativar outra janela no aplicativo. No entanto, o usuário pode ativar janelas em aplicativos diferentes. Uma caixa de diálogo sem formatação não requer uma resposta imediata do usuário. É semelhante a uma janela main que contém controles.
As seções a seguir discutem como usar os dois tipos de caixas de diálogo.
- Exibindo uma caixa de mensagem
- Criando uma caixa de diálogo modal
- Criando uma caixa de diálogo sem formatar
- Inicializando uma caixa de diálogo
- Criando um modelo na memória
Exibindo uma caixa de mensagem
A forma mais simples da caixa de diálogo modal é a caixa de mensagem. A maioria dos aplicativos usa caixas de mensagem para avisar o usuário sobre erros e solicitar instruções sobre como proceder após um erro. Você cria uma caixa de mensagem usando a função MessageBox ou MessageBoxEx , especificando a mensagem e o número e o tipo de botões a serem exibidos. O sistema cria uma caixa de diálogo modal, fornecendo seu próprio modelo e procedimento de caixa de diálogo. Depois que o usuário fecha a caixa de mensagem, MessageBox ou MessageBoxEx retorna um valor que identifica o botão escolhido pelo usuário para fechar a caixa de mensagem.
No exemplo a seguir, o aplicativo exibe uma caixa de mensagem que solicita ao usuário uma ação após a ocorrência de uma condição de erro. A caixa de mensagem exibe a mensagem que descreve a condição de erro e como resolve-la. O estilo MB_YESNO direciona MessageBox para fornecer dois botões com os quais o usuário pode escolher como proceder:
int DisplayConfirmSaveAsMessageBox()
{
int msgboxID = MessageBox(
NULL,
L"temp.txt already exists.\nDo you want to replace it?",
L"Confirm Save As",
MB_ICONEXCLAMATION | MB_YESNO
);
if (msgboxID == IDYES)
{
// TODO: add code
}
return msgboxID;
}
A imagem a seguir mostra a saída do exemplo de código anterior:
Criando uma caixa de diálogo modal
Você cria uma caixa de diálogo modal usando a função DialogBox . Você deve especificar o identificador ou o nome de um recurso de modelo de caixa de diálogo e um ponteiro para o procedimento da caixa de diálogo. A função DialogBox carrega o modelo, exibe a caixa de diálogo e processa toda a entrada do usuário até que o usuário feche a caixa de diálogo.
No exemplo a seguir, o aplicativo exibe uma caixa de diálogo modal quando o usuário clica em Excluir Item de um menu de aplicativo. A caixa de diálogo contém um controle de edição (no qual o usuário insere o nome de um item) e os botões OK e Cancelar . Os identificadores de controle para esses controles são ID_ITEMNAME, IDOK e IDCANCEL, respectivamente.
A primeira parte do exemplo consiste nas instruções que criam a caixa de diálogo modal. Essas instruções, no procedimento de janela da janela main do aplicativo, criam a caixa de diálogo quando o sistema recebe uma mensagem WM_COMMAND com o identificador de menu IDM_DELETEITEM. A segunda parte do exemplo é o procedimento da caixa de diálogo, que recupera o conteúdo do controle de edição e fecha a caixa de diálogo ao receber uma mensagem de WM_COMMAND .
As instruções a seguir criam a caixa de diálogo modal. O modelo da caixa de diálogo é um recurso no arquivo executável do aplicativo e tem o identificador de recurso DLG_DELETEITEM.
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_DELETEITEM:
if (DialogBox(hinst,
MAKEINTRESOURCE(DLG_DELETEITEM),
hwnd,
(DLGPROC)DeleteItemProc)==IDOK)
{
// Complete the command; szItemName contains the
// name of the item to delete.
}
else
{
// Cancel the command.
}
break;
}
return 0L;
Neste exemplo, o aplicativo especifica sua janela main como a janela de proprietário da caixa de diálogo. Quando o sistema exibe inicialmente a caixa de diálogo, sua posição é relativa ao canto superior esquerdo da área de cliente da janela do proprietário. O aplicativo usa o valor retornado de DialogBox para determinar se deve prosseguir com a operação ou cancelá-la. As instruções a seguir definem o procedimento da caixa de diálogo.
char szItemName[80]; // receives name of item to delete.
BOOL CALLBACK DeleteItemProc(HWND hwndDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (!GetDlgItemText(hwndDlg, ID_ITEMNAME, szItemName, 80))
*szItemName=0;
// Fall through.
case IDCANCEL:
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
return FALSE;
}
Neste exemplo, o procedimento usa GetDlgItemText para recuperar o texto atual do controle de edição identificado por ID_ITEMNAME. Em seguida, o procedimento chama a função EndDialog para definir o valor retornado da caixa de diálogo como IDOK ou IDCANCEL, dependendo da mensagem recebida, e para iniciar o processo de fechamento da caixa de diálogo. Os identificadores IDOK e IDCANCEL correspondem aos botões OK e Cancelar . Depois que o procedimento chama EndDialog, o sistema envia mensagens adicionais para o procedimento para destruir a caixa de diálogo e retorna o valor retornado da caixa de diálogo de volta para a função que criou a caixa de diálogo.
Criando uma caixa de diálogo sem formatar
Crie uma caixa de diálogo sem formatar usando a função CreateDialog , especificando o identificador ou o nome de um recurso de modelo de caixa de diálogo e um ponteiro para o procedimento da caixa de diálogo. CreateDialog carrega o modelo, cria a caixa de diálogo e, opcionalmente, exibe-o. Seu aplicativo é responsável por recuperar e expedir mensagens de entrada do usuário para o procedimento da caixa de diálogo.
No exemplo a seguir, o aplicativo exibe uma caixa de diálogo sem formação — se ainda não estiver exibida — quando o usuário clica em Ir para em um menu de aplicativo. A caixa de diálogo contém um controle de edição, uma caixa de marcar e os botões OK e Cancelar. O modelo da caixa de diálogo é um recurso no arquivo executável do aplicativo e tem o identificador de recurso DLG_GOTO. O usuário insere um número de linha no controle de edição e verifica a caixa marcar para especificar que o número de linha é relativo à linha atual. Os identificadores de controle são ID_LINE, ID_ABSREL, IDOK e IDCANCEL.
As instruções na primeira parte do exemplo criam a caixa de diálogo sem formatar. Essas instruções, no procedimento de janela da janela main do aplicativo, criam a caixa de diálogo quando o procedimento de janela recebe uma mensagem WM_COMMAND com o identificador de menu IDM_GOTO, mas somente se a variável global ainda não contiver um identificador válido. A segunda parte do exemplo é o loop de mensagem main do aplicativo. O loop inclui a função IsDialogMessage para garantir que o usuário possa usar a interface de teclado da caixa de diálogo nesta caixa de diálogo sem formatar. A terceira parte do exemplo é o procedimento da caixa de diálogo. O procedimento recupera o conteúdo do controle de edição e da caixa de marcar quando o usuário clica no botão OK. O procedimento destrói a caixa de diálogo quando o usuário clica no botão Cancelar .
HWND hwndGoto = NULL; // Window handle of dialog box
...
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_GOTO:
if (!IsWindow(hwndGoto))
{
hwndGoto = CreateDialog(hinst,
MAKEINTRESOURCE(DLG_GOTO),
hwnd,
(DLGPROC)GoToProc);
ShowWindow(hwndGoto, SW_SHOW);
}
break;
}
return 0L;
Nas instruções anteriores, CreateDialog será chamado somente se hwndGoto
não contiver um identificador de janela válido. Isso garante que o aplicativo não exiba duas caixas de diálogo ao mesmo tempo. Para dar suporte a esse método de verificação, o procedimento de caixa de diálogo deve definir como NULL quando destruir a caixa de diálogo.
O loop de mensagem para um aplicativo consiste nas instruções a seguir.
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// Handle the error and possibly exit
}
else if (!IsWindow(hwndGoto) || !IsDialogMessage(hwndGoto, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
O loop verifica a validade do identificador de janela para a caixa de diálogo e chama apenas a função IsDialogMessage se o identificador for válido. IsDialogMessage só processará a mensagem se ela pertencer à caixa de diálogo. Caso contrário, ele retornará FALSE e o loop enviará a mensagem para a janela apropriada.
As instruções a seguir definem o procedimento da caixa de diálogo.
int iLine; // Receives line number.
BOOL fRelative; // Receives check box status.
BOOL CALLBACK GoToProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
BOOL fError;
switch (message)
{
case WM_INITDIALOG:
CheckDlgButton(hwndDlg, ID_ABSREL, fRelative);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
fRelative = IsDlgButtonChecked(hwndDlg, ID_ABSREL);
iLine = GetDlgItemInt(hwndDlg, ID_LINE, &fError, fRelative);
if (fError)
{
MessageBox(hwndDlg, SZINVALIDNUMBER, SZGOTOERR, MB_OK);
SendDlgItemMessage(hwndDlg, ID_LINE, EM_SETSEL, 0, -1L);
}
else
// Notify the owner window to carry out the task.
return TRUE;
case IDCANCEL:
DestroyWindow(hwndDlg);
hwndGoto = NULL;
return TRUE;
}
}
return FALSE;
}
Nas instruções anteriores, o procedimento processa as mensagens WM_INITDIALOG e WM_COMMAND . Durante WM_INITDIALOG processamento, o procedimento inicializa a caixa marcar passando o valor atual da variável global para CheckDlgButton. Em seguida, o procedimento retorna TRUE para direcionar o sistema para definir o foco de entrada padrão.
Durante WM_COMMAND processamento, o procedimento fechará a caixa de diálogo somente se o usuário clicar no botão Cancelar , ou seja, o botão com o identificador IDCANCEL. O procedimento deve chamar DestroyWindow para fechar uma caixa de diálogo sem formatada. Observe que o procedimento também define a variável como NULL para garantir que outras instruções que dependem dessa variável operem corretamente.
Se o usuário clicar no botão OK, o procedimento recuperará o estado atual da caixa marcar e o atribuirá à variável fRelative. Em seguida, ele usa a variável para recuperar o número de linha do controle de edição. GetDlgItemInt converte o texto no controle de edição em um inteiro. O valor de fRelative determina se a função interpreta o número como um valor assinado ou não assinado. Se o texto do controle de edição não for um número válido, GetDlgItemInt definirá o valor da variável fError como diferente de zero. O procedimento verifica esse valor para determinar se é necessário exibir uma mensagem de erro ou realizar a tarefa. No caso de um erro, o procedimento da caixa de diálogo envia uma mensagem para o controle de edição, direcionando-a para selecionar o texto no controle para que o usuário possa substituí-lo facilmente. Se GetDlgItemInt não retornar um erro, o procedimento poderá executar a tarefa solicitada em si ou enviar uma mensagem para a janela do proprietário, direcionando-a para executar a operação.
Inicializando uma caixa de diálogo
Você inicializa a caixa de diálogo e seu conteúdo durante o processamento da mensagem de WM_INITDIALOG . A tarefa mais comum é inicializar os controles para refletir as configurações atuais da caixa de diálogo. Outra tarefa comum é centralizar uma caixa de diálogo na tela ou na janela do proprietário. Uma tarefa útil para algumas caixas de diálogo é definir o foco de entrada para um controle especificado em vez de aceitar o foco de entrada padrão.
No exemplo a seguir, o procedimento da caixa de diálogo centraliza a caixa de diálogo e define o foco de entrada ao processar a mensagem WM_INITDIALOG . Para centralizar a caixa de diálogo, o procedimento recupera os retângulos da janela da caixa de diálogo e da janela do proprietário e calcula uma nova posição para a caixa de diálogo. Para definir o foco de entrada, o procedimento verifica o parâmetro wParam para determinar o identificador do foco de entrada padrão.
HWND hwndOwner;
RECT rc, rcDlg, rcOwner;
....
case WM_INITDIALOG:
// Get the owner window and dialog box rectangles.
if ((hwndOwner = GetParent(hwndDlg)) == NULL)
{
hwndOwner = GetDesktopWindow();
}
GetWindowRect(hwndOwner, &rcOwner);
GetWindowRect(hwndDlg, &rcDlg);
CopyRect(&rc, &rcOwner);
// Offset the owner and dialog box rectangles so that right and bottom
// values represent the width and height, and then offset the owner again
// to discard space taken up by the dialog box.
OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
OffsetRect(&rc, -rc.left, -rc.top);
OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
// The new position is the sum of half the remaining space and the owner's
// original position.
SetWindowPos(hwndDlg,
HWND_TOP,
rcOwner.left + (rc.right / 2),
rcOwner.top + (rc.bottom / 2),
0, 0, // Ignores size arguments.
SWP_NOSIZE);
if (GetDlgCtrlID((HWND) wParam) != ID_ITEMNAME)
{
SetFocus(GetDlgItem(hwndDlg, ID_ITEMNAME));
return FALSE;
}
return TRUE;
Nas instruções anteriores, o procedimento usa a função GetParent para recuperar o identificador de janela do proprietário em uma caixa de diálogo. A função retorna o identificador de janela do proprietário para caixas de diálogo e o identificador da janela pai para janelas filho. Como um aplicativo pode criar uma caixa de diálogo sem proprietário, o procedimento verifica o identificador retornado e usa a função GetDesktopWindow para recuperar o identificador da janela da área de trabalho, se necessário. Depois de calcular a nova posição, o procedimento usa a função SetWindowPos para mover a caixa de diálogo, especificando o valor HWND_TOP para garantir que a caixa de diálogo permaneça na parte superior da janela do proprietário.
Antes de definir o foco de entrada, o procedimento verifica o identificador de controle do foco de entrada padrão. O sistema passa o identificador de janela do foco de entrada padrão no parâmetro wParam . A função GetDlgCtrlID retorna o identificador do controle identificado pelo identificador de janela. Se o identificador não corresponder ao identificador correto, o procedimento usará a função SetFocus para definir o foco de entrada. A função GetDlgItem é necessária para recuperar o identificador de janela do controle desejado.
Criando um modelo na memória
Às vezes, os aplicativos adaptam ou modificam o conteúdo das caixas de diálogo, dependendo do estado atual dos dados que estão sendo processados. Nesses casos, não é prático fornecer todos os modelos de caixa de diálogo possíveis como recursos no arquivo executável do aplicativo. Mas a criação de modelos na memória dá ao aplicativo mais flexibilidade para se adaptar a qualquer circunstância.
No exemplo a seguir, o aplicativo cria um modelo na memória para uma caixa de diálogo modal que contém uma mensagem e botões OK e Ajuda .
Em um modelo de caixa de diálogo, todas as cadeias de caracteres, como a caixa de diálogo e os títulos de botão, devem ser cadeias de caracteres Unicode. Este exemplo usa a função MultiByteToWideChar para gerar essas cadeias de caracteres Unicode.
As estruturas DLGITEMTEMPLATE em um modelo de caixa de diálogo devem ser alinhadas nos limites do DWORD . Para alinhar essas estruturas, este exemplo usa uma rotina auxiliar que usa um ponteiro de entrada e retorna o ponteiro mais próximo alinhado em um limite DWORD .
#define ID_HELP 150
#define ID_TEXT 200
LPWORD lpwAlign(LPWORD lpIn)
{
ULONG ul;
ul = (ULONG)lpIn;
ul ++;
ul >>=1;
ul <<=1;
return (LPWORD)ul;
}
LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, LPSTR lpszMessage)
{
HGLOBAL hgbl;
LPDLGTEMPLATE lpdt;
LPDLGITEMTEMPLATE lpdit;
LPWORD lpw;
LPWSTR lpwsz;
LRESULT ret;
int nchar;
hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
if (!hgbl)
return -1;
lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
// Define a dialog box.
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
lpdt->cdit = 3; // Number of controls
lpdt->x = 10; lpdt->y = 10;
lpdt->cx = 100; lpdt->cy = 100;
lpw = (LPWORD)(lpdt + 1);
*lpw++ = 0; // No menu
*lpw++ = 0; // Predefined dialog box class (by default)
lpwsz = (LPWSTR)lpw;
nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "My Dialog", -1, lpwsz, 50);
lpw += nchar;
//-----------------------
// Define an OK button.
//-----------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10; lpdit->y = 70;
lpdit->cx = 80; lpdit->cy = 20;
lpdit->id = IDOK; // OK button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // Button class
lpwsz = (LPWSTR)lpw;
nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "OK", -1, lpwsz, 50);
lpw += nchar;
*lpw++ = 0; // No creation data
//-----------------------
// Define a Help button.
//-----------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 55; lpdit->y = 10;
lpdit->cx = 40; lpdit->cy = 20;
lpdit->id = ID_HELP; // Help button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // Button class atom
lpwsz = (LPWSTR)lpw;
nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Help", -1, lpwsz, 50);
lpw += nchar;
*lpw++ = 0; // No creation data
//-----------------------
// Define a static text control.
//-----------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10; lpdit->y = 10;
lpdit->cx = 40; lpdit->cy = 20;
lpdit->id = ID_TEXT; // Text identifier
lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0082; // Static class
for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*lpszMessage++;);
lpw = (LPWORD)lpwsz;
*lpw++ = 0; // No creation data
GlobalUnlock(hgbl);
ret = DialogBoxIndirect(hinst,
(LPDLGTEMPLATE)hgbl,
hwndOwner,
(DLGPROC)DialogProc);
GlobalFree(hgbl);
return ret;
}