Usando o Windows
Os exemplos nesta seção descrevem como executar as seguintes tarefas:
- Criando uma janela principal
- Criando, enumerando e dimensionando janelas filho
- Destruindo uma janela
- Usando janelas em camadas
Criando uma janela principal
A primeira janela que um aplicativo cria normalmente é a janela principal. Você cria a janela principal usando a função CreateWindowEx , especificando a classe da janela, o nome da janela, os estilos de janela, o tamanho, a posição, o identificador de menu, o identificador de instância e os dados de criação. Uma janela principal pertence a uma classe de janela definida pelo aplicativo, portanto, você deve registrar a classe de janela e fornecer um procedimento de janela para a classe antes de criar a janela principal.
A maioria dos aplicativos normalmente usa o estilo WS_OVERLAPPEDWINDOW para criar a janela principal. Esse estilo dá à janela uma barra de título, um menu de janela, uma borda de dimensionamento e botões de minimizar e maximizar. A função CreateWindowEx retorna um identificador que identifica exclusivamente a janela.
O exemplo a seguir cria uma janela principal pertencente a uma classe de janela definida pelo aplicativo. O nome da janela, Janela Principal, aparecerá na barra de título da janela. Ao combinar os estilos WS_VSCROLL e WS_HSCROLL com o estilo WS_OVERLAPPEDWINDOW , o aplicativo cria uma janela principal com barras de rolagem horizontais e verticais, além dos componentes fornecidos pelo estilo WS_OVERLAPPEDWINDOW . As quatro ocorrências da constante CW_USEDEFAULT definem o tamanho inicial e a posição da janela para os valores padrão definidos pelo sistema. Ao especificar NULL em vez de um identificador de menu, a janela terá o menu definido para a classe de janela.
HINSTANCE hinst;
HWND hwndMain;
// Create the main window.
hwndMain = CreateWindowEx(
0, // no extended styles
"MainWClass", // class name
"Main Window", // window name
WS_OVERLAPPEDWINDOW | // overlapped window
WS_HSCROLL | // horizontal scroll bar
WS_VSCROLL, // vertical scroll bar
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
CW_USEDEFAULT, // default width
CW_USEDEFAULT, // default height
(HWND) NULL, // no parent or owner window
(HMENU) NULL, // class menu used
hinst, // instance handle
NULL); // no window creation data
if (!hwndMain)
return FALSE;
// Show the window using the flag specified by the program
// that started the application, and send the application
// a WM_PAINT message.
ShowWindow(hwndMain, SW_SHOWDEFAULT);
UpdateWindow(hwndMain);
Observe que o exemplo anterior chama a função ShowWindow depois de criar a janela principal. Isso é feito porque o sistema não exibe automaticamente a janela principal depois de criá-la. Ao passar o sinalizador SW_SHOWDEFAULT para ShowWindow, o aplicativo permite que o programa que iniciou o aplicativo defina o estado de exibição inicial da janela principal. A função UpdateWindow envia à janela sua primeira mensagem WM_PAINT.
Criando, enumerando e dimensionando janelas filho
Você pode dividir a área do cliente de uma janela em diferentes áreas funcionais usando janelas filhas. Criar uma janela filho é como criar uma janela principal — você usa a função CreateWindowEx. Para criar uma janela de uma classe de janela definida pelo aplicativo, você deve registrar a classe de janela e fornecer um procedimento de janela antes de criar a janela filho. Você deve dar à janela filho o estilo WS_CHILD e especificar uma janela pai para a janela filho ao criá-la.
O exemplo a seguir divide a área do cliente da janela principal de um aplicativo em três áreas funcionais, criando três janelas filhas de tamanho igual. Cada janela filho tem a mesma altura que a área do cliente da janela principal, mas cada uma tem um terço de sua largura. A janela principal cria as janelas filho em resposta à mensagem WM_CREATE, que a janela principal recebe durante seu próprio processo de criação de janela. Como cada janela filho tem o estilo WS_BORDER , cada uma tem uma borda de linha fina. Além disso, como o estilo WS_VISIBLE não é especificado, cada janela filho é inicialmente ocultada. Observe também que a cada janela filho é atribuído um identificador de janela filha.
A janela principal dimensiona e posiciona as janelas filhas em resposta à mensagem WM_SIZE , que a janela principal recebe quando seu tamanho muda. Em resposta a WM_SIZE, a janela principal recupera as dimensões de sua área de cliente usando a função GetClientRect e, em seguida, passa as dimensões para a função EnumChildWindows . EnumChildWindows passa o identificador para cada janela filho, por sua vez, para a função de retorno de chamada EnumChildProc definida pelo aplicativo. Essa função dimensiona e posiciona cada janela filho chamando a função MoveWindow , o tamanho e a posição são baseados nas dimensões da área do cliente da janela principal e no identificador da janela filho. Depois, EnumChildProc chama a função ShowWindow para tornar a janela visível.
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT rcClient;
int i;
switch(uMsg)
{
case WM_CREATE: // creating main window
// Create three invisible child windows.
for (i = 0; i < 3; i++)
{
CreateWindowEx(0,
"ChildWClass",
(LPCTSTR) NULL,
WS_CHILD | WS_BORDER,
0,0,0,0,
hwnd,
(HMENU) (int) (ID_FIRSTCHILD + i),
hinst,
NULL);
}
return 0;
case WM_SIZE: // main window changed size
// Get the dimensions of the main window's client
// area, and enumerate the child windows. Pass the
// dimensions to the child windows during enumeration.
GetClientRect(hwnd, &rcClient);
EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient);
return 0;
// Process other messages.
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
LPRECT rcParent;
int i, idChild;
// Retrieve the child-window identifier. Use it to set the
// position of the child window.
idChild = GetWindowLong(hwndChild, GWL_ID);
if (idChild == ID_FIRSTCHILD)
i = 0;
else if (idChild == ID_SECONDCHILD)
i = 1;
else
i = 2;
// Size and position the child window.
rcParent = (LPRECT) lParam;
MoveWindow(hwndChild,
(rcParent->right / 3) * i,
0,
rcParent->right / 3,
rcParent->bottom,
TRUE);
// Make sure the child window is visible.
ShowWindow(hwndChild, SW_SHOW);
return TRUE;
}
Destruindo uma janela
Você pode usar a função DestroyWindow para destruir uma janela. Normalmente, um aplicativo envia a mensagem WM_CLOSE antes de destruir uma janela, dando à janela a oportunidade de solicitar ao usuário a confirmação antes que a janela seja destruída. Uma janela que inclui um menu de janela recebe automaticamente a mensagem WM_CLOSE quando o usuário clica em Fechar no menu da janela. Se o usuário confirmar que a janela deve ser destruída, o aplicativo chama DestroyWindow. O sistema envia a mensagem de WM_DESTROY para a janela depois de removê-la da tela. Em resposta a WM_DESTROY, a janela salva seus dados e libera todos os recursos alocados. Uma janela principal conclui seu processamento de WM_DESTROY chamando a função PostQuitMessage para sair do aplicativo.
O exemplo a seguir mostra como solicitar a confirmação do usuário antes de destruir uma janela. Em resposta a WM_CLOSE, é exibida uma caixa de diálogo que contém os botões Sim, Não e Cancelar . Se o usuário clicar em Sim, DestroyWindow é chamado, caso contrário, a janela não será destruída. Como o aplicativo está manipulando a mensagem WM_CLOSE, 0
é retornado em todos os casos. Como a janela que está sendo destruída é uma janela principal, o exemplo chama PostQuitMessage em resposta a WM_DESTROY.
case WM_CLOSE:
// Create the message box. If the user clicks
// the Yes button, destroy the main window.
if (MessageBox(hwnd, szConfirm, szAppName, MB_YESNOCANCEL) == IDYES)
DestroyWindow(hwndMain);
return 0;
case WM_DESTROY:
// Post the WM_QUIT message to
// quit the application terminate.
PostQuitMessage(0);
return 0;
Usando janelas em camadas
Para que uma caixa de diálogo apareça como uma janela translúcida, primeiro crie a caixa de diálogo como de costume. Em seguida, em WM_INITDIALOG, defina o bit em camadas do estilo estendido da janela e chame SetLayeredWindowAttributes com o valor alfa desejado. O código pode ter esta aparência:
// Set WS_EX_LAYERED on this window
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
// Make this window 70% alpha
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);
Observe que o terceiro parâmetro de SetLayeredWindowAttributes é um valor que varia de 0 a 255, com 0 tornando a janela completamente transparente e 255 tornando-a completamente opaca. Este parâmetro imita a função BLENDFUNCTION mais versátil da função AlphaBlend.
Para tornar essa janela completamente opaca novamente, remova o bit WS_EX_LAYERED chamando SetWindowLong e, em seguida, peça à janela para pintar novamente. A remoção do bit é desejada para que o sistema saiba que ele pode liberar alguma memória associada a camadas e redirecionamento. O código pode ter esta aparência:
// Remove WS_EX_LAYERED from this window styles
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// Ask the window and its children to repaint
RedrawWindow(hwnd,
NULL,
NULL,
RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
Para usar janelas filho em camadas, o aplicativo precisa se declarar compatível com o Windows 8 no manifesto.
Para o Windows 10/11, pode-se incluir este trecho de compatibilidade em seu app.manifest
para torná-lo compatível com o Windows 10:
...
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 GUID -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
...
Mais informações sobre como modificar o manifesto do aplicativo podem ser lidas aqui: Manifestos do aplicativo