Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Esta seção explica como executar as seguintes tarefas:
- Registrando classes de criança e janela de moldura
- Criando janelas de moldura e janelas filhas
- Escrevendo o loop de mensagem principal
- Escrevendo o procedimento da janela principal
- Escrevendo o procedimento da janela da criança
- Criando uma janela secundária
Para ilustrar essas tarefas, esta seção inclui exemplos do Multipad, um aplicativo típico de interface de vários documentos (MDI).
Registrando classes de criança e janela de moldura
Um aplicativo MDI típico deve registrar duas classes de janela: uma para sua janela de quadro e outra para suas janelas filhas. Se um aplicativo suportar mais de um tipo de documento (por exemplo, uma planilha e um gráfico), ele deve registrar uma classe de janela para cada tipo.
A estrutura de classe para a janela de quadro é semelhante à estrutura de classe para a janela principal em aplicativos não-MDI. A estrutura de classe para as janelas subordinadas MDI difere ligeiramente da estrutura para janelas subordinadas em aplicações que não utilizam MDI da seguinte maneira:
- A estrutura de classe deve ter um ícone, porque o utilizador pode minimizar uma janela MDI filha como se fosse uma janela de aplicação normal.
- O nome do menu deve ser NULL, porque uma janela filha MDI não pode ter o seu próprio menu.
- A estrutura da classe deve reservar espaço extra na estrutura da janela. Com este espaço, a aplicação pode associar dados, como um nome de ficheiro, a uma janela filha específica.
O exemplo seguinte mostra como o Multipad regista as suas classes de moldura e janelas filhas.
BOOL WINAPI InitializeApplication()
{
WNDCLASS wc;
// Register the frame window class.
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MPFrameWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, IDMULTIPAD);
wc.hCursor = LoadCursor((HANDLE) NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);
wc.lpszMenuName = IDMULTIPAD;
wc.lpszClassName = szFrame;
if (!RegisterClass (&wc) )
return FALSE;
// Register the MDI child window class.
wc.lpfnWndProc = (WNDPROC) MPMDIChildWndProc;
wc.hIcon = LoadIcon(hInst, IDNOTE);
wc.lpszMenuName = (LPCTSTR) NULL;
wc.cbWndExtra = CBWNDEXTRA;
wc.lpszClassName = szChild;
if (!RegisterClass(&wc))
return FALSE;
return TRUE;
}
Criando janelas de moldura e secundárias
Depois de registrar suas classes de janela, um aplicativo MDI pode criar suas janelas. Primeiro, ele cria a janela da moldura usando a função CreateWindow ou a função CreateWindowEx. Depois de criar sua janela de quadro, o aplicativo cria sua janela de cliente, novamente usando CreateWindow ou CreateWindowEx. O aplicativo deve especificar MDICLIENT como o nome da classe da janela do cliente; MDICLIENT é uma classe de janela pré-registrada definida pelo sistema. O parâmetro lpvParam de CreateWindow ou CreateWindowEx deve apontar para uma estrutura CLIENTCREATESTRUCT. Esta estrutura contém os membros descritos na tabela a seguir:
| Membro | Descrição |
|---|---|
| hWindowMenu | Manipule o menu da janela usado para controlar as janelas filho do MDI. À medida que as janelas filho são criadas, o aplicativo adiciona seus títulos ao menu da janela como itens de menu. O utilizador pode então ativar uma janela secundária clicando no seu título no menu de janelas. |
| idFirstChild | Especifica o identificador da primeira janela filha MDI. A primeira janela filho MDI criada recebe esse identificador. Janelas adicionais são criadas com identificadores de janela incrementados. Quando uma janela secundária é destruída, o sistema reatribui imediatamente os identificadores de janela para manter a continuidade do seu intervalo. |
Quando o título de uma janela filha é adicionado ao menu de janela, o sistema atribui um identificador à janela filha. Quando o usuário clica no título de uma janela filha, a janela de moldura recebe uma mensagem WM_COMMAND com o identificador no parâmetro wParam. Você deve especificar um valor para o membro idFirstChild que não entre em conflito com os identificadores de item de menu no menu da janela de moldura.
O procedimento da janela de moldura do Multipad cria a janela cliente MDI durante o processamento da mensagem WM_CREATE. O exemplo a seguir mostra como a janela do cliente é criada.
case WM_CREATE:
{
CLIENTCREATESTRUCT ccs;
// Retrieve the handle to the window menu and assign the
// first child window identifier.
ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), WINDOWMENU);
ccs.idFirstChild = IDM_WINDOWCHILD;
// Create the MDI client window.
hwndMDIClient = CreateWindow( "MDICLIENT", (LPCTSTR) NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
0, 0, 0, 0, hwnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs);
ShowWindow(hwndMDIClient, SW_SHOW);
}
break;
Os títulos das janelas secundárias são adicionados à parte inferior do menu da janela. Se o aplicativo adicionar cadeias de caracteres ao menu da janela usando a função AppendMenu, essas cadeias de caracteres poderão ser substituídas pelos títulos das janelas filho quando o menu da janela for repintado (sempre que uma janela filho for criada ou destruída). Uma aplicação MDI que adiciona strings ao seu menu de janela deve usar a função InsertMenu e verificar se os títulos das janelas filhas não substituíram essas novas strings.
Use o estilo WS_CLIPCHILDREN para criar a janela do cliente MDI para impedir que a janela pinte sobre suas janelas filhas.
Escrevendo o loop de mensagem principal
O loop de mensagem principal de um aplicativo MDI é semelhante ao de um aplicativo não-MDI manipulando chaves aceleradoras. A diferença é que o loop de mensagem MDI chama a função TranslateMDISysAccel antes de verificar se há teclas de atalho definidas pelo aplicativo ou antes de enviar a mensagem.
O exemplo a seguir mostra o loop de mensagem de um aplicativo MDI típico. Observe que GetMessage pode retornar -1 se houver um erro.
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
if (!TranslateMDISysAccel(hwndMDIClient, &msg) &&
!TranslateAccelerator(hwndFrame, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
A funçãoTranslateMDISysAccel converte mensagens WM_KEYDOWN em mensagens WM_SYSCOMMAND e as envia para a janela filha MDI ativa. Se a mensagem não for uma mensagem de acelerador MDI, a função retornará FALSE , caso em que o aplicativo usa a funçãoTranslateAccelerator para determinar se alguma das teclas de aceleração definidas pelo aplicativo foi pressionada. Caso contrário, o loop envia a mensagem para o procedimento de janela apropriado.
Escrevendo o procedimento da janela de quadro
O procedimento de janela para uma janela de quadro MDI é semelhante ao da janela principal de um aplicativo não-MDI. A diferença é que um procedimento de janela de quadro passa todas as mensagens que não manipula para a função DefFrameProc em vez de para a função DefWindowProc. Além disso, o procedimento da janela de quadro também deve passar algumas mensagens que ele manipula, incluindo as listadas na tabela a seguir.
| Mensagem | Resposta |
|---|---|
| WM_COMMAND | Ativa a janela filho MDI escolhida pelo usuário. Esta mensagem é enviada quando o utilizador escolhe uma janela filha MDI no menu de janela da janela de moldura MDI. O identificador de janela que acompanha esta mensagem identifica a janela filho MDI a ser ativada. |
| WM_MENUCHAR | Abre o menu da janela da janela secundária MDI ativa quando o utilizador pressiona a combinação de teclas ALT+ – (menos). |
| WM_SETFOCUS | Passa o foco do teclado para a janela do cliente MDI, que, por sua vez, o passa para a janela filho MDI ativa. |
| WM_SIZE | Redimensiona a janela do cliente MDI para caber na área do cliente da nova janela principal. Se o procedimento da janela de moldura redimensionar a janela do cliente MDI para um tamanho diferente, não deve passar a mensagem para a função DefWindowProc. |
O procedimento de janela de moldura no Multipad é chamado MPFrameWndProc. O tratamento de outras mensagens por MPFrameWndProc é semelhante ao de aplicativos não-MDI. As mensagens WM_COMMAND no Multipad são tratadas pela função localmente definida CommandHandler. Para as mensagens de comando que o Multipad não manipula, o CommandHandler chama a função DefFrameProc. Se o Multipad não usar DefFrameProc por padrão, o usuário não poderá ativar uma janela filho no menu da janela, porque a mensagem WM_COMMAND enviada clicando no item de menu da janela será perdida.
Escrever o procedimento da janela filha
Como no procedimento de janela de moldura, um procedimento de janela filho MDI usa uma função especial para processar mensagens por padrão. Todas as mensagens que o procedimento da janela filho não manipula devem ser passadas para a função DefMDIChildProc em vez de serem encaminhadas para a função DefWindowProc. Além disso, algumas mensagens de gerenciamento de janela devem ser passadas para DefMDIChildProc, mesmo que o aplicativo manipule a mensagem, para que o MDI funcione corretamente. A seguir estão as mensagens que o aplicativo deve passar para DefMDIChildProc.
| Mensagem | Resposta |
|---|---|
| WM_CHILDACTIVATE | Executa o processamento de ativação quando as janelas filho MDI são dimensionadas, movidas ou exibidas. Esta mensagem deve ser passada. |
| WM_GETMINMAXINFO | Calcula o tamanho de uma janela MDI filho maximizada, com base no tamanho atual da janela cliente MDI. |
| WM_MENUCHAR | Passa a mensagem para a janela de quadro MDI. |
| WM_MOVE | Recalcula as barras de rolagem do cliente MDI, se elas estiverem presentes. |
| WM_SETFOCUS | Ativa a janela filha, se esta não for a janela filha MDI ativa. |
| WM_SIZE | Executa as operações necessárias para alterar o tamanho de uma janela, especialmente para maximizar ou restaurar uma janela filha MDI. Falhar em passar esta mensagem para a função DefMDIChildProc produz resultados altamente indesejáveis. |
| WM_SYSCOMMAND | Manipula comandos de menu da janela (anteriormente conhecido como sistema): SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZEe SC_MAXIMIZE. |
Criando uma janela filha
Para criar uma janela MDI filha, uma aplicação pode chamar a função CreateMDIWindow ou enviar uma mensagem WM_MDICREATE para a janela do cliente MDI. (O aplicativo pode usar a função CreateWindowEx com o estilo WS_EX_MDICHILD para criar janelas filha MDI.) Uma aplicação MDI de thread único pode usar qualquer método para criar uma janela filha. Uma thread em um aplicativo MDI multithreaded deve usar a função CreateMDIWindow ou a função CreateWindowEx para criar uma janela filha num thread diferente.
O parâmetro lParam de uma mensagem WM_MDICREATE é um ponteiro longínquo para uma estrutura MDICREATESTRUCT. A estrutura inclui quatro membros de dimensão: x e y, que indicam as posições horizontais e verticais da janela, e cx e cy, que indicam as extensões horizontal e vertical da janela. Qualquer um desses membros pode ser atribuído explicitamente pelo aplicativo, ou eles podem ser definidos para CW_USEDEFAULT, caso em que o sistema seleciona uma posição, tamanho, ou ambos, de acordo com um algoritmo em cascata. Em qualquer caso, todos os quatro membros devem ser inicializados. Multipad usa CW_USEDEFAULT para todas as dimensões.
O último membro da estrutura MDICREATESTRUCT é o membro de estilo , que pode conter bits de estilo para a janela. Para criar uma janela filha MDI que possa ter qualquer combinação de estilos de janela, especifique o estilo de janela MDIS_ALLCHILDSTYLES. Quando este estilo não é especificado, uma janela filha MDI possui os estilos WS_MINIMIZE, WS_MAXIMIZE, WS_HSCROLLe WS_VSCROLL como configurações padrão.
O Multipad cria suas janelas filhas MDI usando sua função AddFile definida localmente (localizada no arquivo de origem MPFILE. C). A função AddFile define o título da janela filha, atribuindo o membro szTitle da estruturaMDICREATESTRUCTda janela ao nome do ficheiro que está a ser editado ou a "Sem título". O membro szClass é definido como o nome da classe de janela filha MDI registada na função InitializeApplication do Multipad. O membro hOwner é definido como o identificador de instância do aplicativo.
O exemplo a seguir mostra a função AddFile no Multipad.
HWND APIENTRY AddFile(pName)
TCHAR * pName;
{
HWND hwnd;
TCHAR sz[160];
MDICREATESTRUCT mcs;
if (!pName)
{
// If the pName parameter is NULL, load the "Untitled"
// string from the STRINGTABLE resource and set the szTitle
// member of MDICREATESTRUCT.
LoadString(hInst, IDS_UNTITLED, sz, sizeof(sz)/sizeof(TCHAR));
mcs.szTitle = (LPCTSTR) sz;
}
else
// Title the window with the full path and filename,
// obtained by calling the OpenFile function with the
// OF_PARSE flag, which is called before AddFile().
mcs.szTitle = of.szPathName;
mcs.szClass = szChild;
mcs.hOwner = hInst;
// Use the default size for the child window.
mcs.x = mcs.cx = CW_USEDEFAULT;
mcs.y = mcs.cy = CW_USEDEFAULT;
// Give the child window the default style. The styleDefault
// variable is defined in MULTIPAD.C.
mcs.style = styleDefault;
// Tell the MDI client window to create the child window.
hwnd = (HWND) SendMessage (hwndMDIClient, WM_MDICREATE, 0,
(LONG) (LPMDICREATESTRUCT) &mcs);
// If the file is found, read its contents into the child
// window's client area.
if (pName)
{
if (!LoadFile(hwnd, pName))
{
// Cannot load the file; close the window.
SendMessage(hwndMDIClient, WM_MDIDESTROY,
(DWORD) hwnd, 0L);
}
}
return hwnd;
}
O ponteiro que é passado no parâmetro lParam da mensagem WM_MDICREATE é transmitido para a função CreateWindow, aparecendo como o primeiro membro da estrutura CREATESTRUCT, que é passada na mensagem WM_CREATE. No Multipad, a janela filha inicializa-se durante o WM_CREATE processamento de mensagens ao inicializar variáveis do documento nos seus dados extras da janela filha e ao criar a janela filha do controlo de edição.