Controles personalizados
Esta seção contém informações sobre controles definidos pelo aplicativo ou personalizados.
Os tópicos a seguir são discutidos.
- Criando controles desenhados pelo proprietário
- Subclassificando a classe Window de um controle existente
- Implementando uma classe de janela definida pelo aplicativo
- Enviando notificações de um controle
- Acessibilidade
- Tópicos relacionados
Criando controles desenhados pelo proprietário
Botões, menus, controles de texto estático, caixas de listagem e caixas de combinação podem ser criados com um sinalizador de estilo desenhado pelo proprietário. Quando um controle tem o estilo desenhado pelo proprietário, o sistema lida com a interação do usuário com o controle como de costume, executando tarefas como detectar quando um usuário escolheu um botão e notificar o proprietário do botão sobre o evento. No entanto, como o controle é desenhado pelo proprietário, a janela pai do controle é responsável pela aparência visual do controle. A janela pai recebe uma mensagem sempre que o controle deve ser desenhado.
Para botões e controles de texto estáticos, o estilo desenhado pelo proprietário afeta como o sistema desenha todo o controle. Para caixas de listagem e caixas de combinação, a janela pai desenha os itens dentro do controle e o controle desenha seu próprio contorno. Por exemplo, um aplicativo pode personalizar uma caixa de listagem para que ela exiba um pequeno bitmap ao lado de cada item na lista.
O código de exemplo a seguir mostra como criar um controle de texto estático desenhado pelo proprietário. Suponha que Unicode é definido.
// g_myStatic is a global HWND variable.
g_myStatic = CreateWindowEx(0, L"STATIC", L"Some static text",
WS_CHILD | WS_VISIBLE | SS_OWNERDRAW,
25, 125, 150, 20, hDlg, 0, 0, 0);
No exemplo a seguir, no procedimento de janela para a caixa de diálogo que contém o controle criado no exemplo anterior, a mensagem WM_DRAWITEM é manipulada exibindo o texto em uma cor personalizada, usando a fonte padrão. Observe que você não precisa chamar BeginPaint e EndPaint ao manusear WM_DRAWITEM.
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
if (pDIS->hwndItem == g_myStatic)
{
SetTextColor(pDIS->hDC, RGB(100, 0, 100));
WCHAR staticText[99];
int len = SendMessage(myStatic, WM_GETTEXT,
ARRAYSIZE(staticText), (LPARAM)staticText);
TextOut(pDIS->hDC, pDIS->rcItem.left, pDIS->rcItem.top, staticText, len);
}
return TRUE;
}
Para obter mais informações sobre controles desenhados pelo proprietário, consulte Criando uma caixa de listagem desenhada pelo proprietário e caixas de combinação desenhadas pelo proprietário.
Subclassificando a classe Window de um controle existente
Subclassificar um controle existente é outra maneira de criar um controle personalizado. O procedimento de subclasse pode alterar comportamentos selecionados do controle processando as mensagens que afetam os comportamentos selecionados. Todas as outras mensagens passam para o procedimento de janela original para o controle. Por exemplo, um aplicativo pode exibir um pequeno bitmap ao lado do texto em um controle de edição de linha única somente leitura subclassificando o controle e processando a mensagem WM_PAINT. Para obter mais informações, consulte Sobre procedimentos de janela e controles de subclassificação.
Implementando uma classe de janela definida pelo aplicativo
Para criar um controle que não é explicitamente baseado em um controle existente, o aplicativo deve criar e registrar uma classe de janela. O processo para registrar uma classe de janela definida pelo aplicativo para um controle personalizado é o mesmo que registrar uma classe para uma janela comum. Para criar um controle personalizado, especifique o nome da classe de janela na função CreateWindowEx ou em um modelo de caixa de diálogo. Cada classe deve ter um nome exclusivo, um procedimento de janela correspondente e outras informações.
No mínimo, o procedimento de janela desenha o controle. Se um aplicativo usa o controle para permitir que o usuário digite informações, o procedimento de janela também processa mensagens de entrada do teclado e do mouse e envia mensagens de notificação para a janela pai. Além disso, se o controle oferecer suporte a mensagens de controle, o procedimento de janela processará as mensagens enviadas a ele pela janela pai ou por outras janelas. Por exemplo, os controles geralmente processam a mensagem WM_GETDLGCODE enviada por caixas de diálogo para direcionar uma caixa de diálogo para processar a entrada do teclado de uma determinada maneira.
O procedimento de janela para um controle definido pelo aplicativo deve processar qualquer mensagem de controle predefinida na tabela a seguir se a mensagem afetar a operação do controle.
Mensagem | Recomendação |
---|---|
WM_GETDLGCODE | Processe se o controle usar as teclas ENTER, ESC, TAB ou de seta. A função IsDialogMessage envia essa mensagem para controles em uma caixa de diálogo para determinar se as chaves devem ser processadas ou passadas para o controle. |
WM_GETFONT | Processe se a mensagem WM_SETFONT também for processada. |
WM_GETTEXT | Processe se o texto do controle não for o mesmo que o título especificado pela função CreateWindowEx. |
WM_GETTEXTLENGTH | Processe se o texto do controle não for o mesmo que o título especificado pela função CreateWindowEx. |
WM_KILLFOCUS | Processe se o controle exibir um acento circunflexo, um retângulo de foco ou outro item para indicar que ele tem o foco de entrada. |
WM_SETFOCUS | Processe se o controle exibir um acento circunflexo, um retângulo de foco ou outro item para indicar que ele tem o foco de entrada. |
WM_SETTEXT | Processe se o texto do controle não for o mesmo que o título especificado pela função CreateWindowEx. |
WM_SETFONT | Processe se o controle exibir texto. O sistema envia essa mensagem ao criar uma caixa de diálogo que tenha o estilo DS_SETFONT. |
As mensagens de controle definidas pelo aplicativo são específicas para o controle fornecido e devem ser enviadas explicitamente para o controle usando uma função SendMessage ou SendDlgItemMessage. O valor numérico para cada mensagem deve ser exclusivo e não deve entrar em conflito com os valores de outras mensagens de janela. Para garantir que os valores de mensagem definidos pelo aplicativo não entrem em conflito, um aplicativo deve criar cada valor adicionando um número exclusivo ao valor WM_USER.
Enviando notificações de um controle
Controles personalizados podem ser necessários para enviar notificações de eventos para a janela pai para que o aplicativo host possa responder a esses eventos. Por exemplo, um modo de exibição de lista personalizado pode enviar uma notificação quando o usuário seleciona um item e outra notificação quando um item é clicado duas vezes.
As notificações são enviadas como mensagens WM_COMMAND ou WM_NOTIFY. WM_NOTIFY mensagens carregam mais informações do que WM_COMMAND mensagens.
O identificador de controle é um número exclusivo que o aplicativo usa para identificar o controle que envia a mensagem. O aplicativo define o identificador para um controle quando ele cria o controle. O aplicativo especifica o identificador no parâmetro hMenu da função CreateWindowEx ou no membro id da estrutura DLGITEMTEMPLATEEX.
Como o controle em si não define o identificador de controle, o controle deve recuperar o identificador antes de poder enviar mensagens de notificação. Um controle deve usar a função GetDlgCtrlID para recuperar seu próprio identificador de controle. Embora o identificador de controle é especificado como o identificador de menu quando o controle é criado, a função GetMenu não pode ser usada para recuperar o identificador. Como alternativa, um controle pode recuperar o identificador do membro hMenu na estrutura CREATESTRUCT ao processar a mensagem WM_CREATE.
Os exemplos a seguir, em que hwndControl é o identificador da janela de controle e CN_VALUECHANGED é uma definição de notificação personalizada, mostram as duas maneiras de enviar uma notificação específica de controle.
// Send as WM_COMMAND.
SendMessage(GetParent(hwndControl),
WM_COMMAND,
MAKEWPARAM(GetDlgCtrlID(hwndControl), CN_VALUECHANGED),
(LPARAM)hwndControl);
// Send as WM_NOTIFY.
NMHDR nmh;
nmh.code = CN_VALUECHANGED;
nmh.idFrom = GetDlgCtrlID(hwndControl);
nmh.hwndFrom = hwndControl;
SendMessage(GetParent(hwndControl),
WM_NOTIFY,
(WPARAM)hwndControl,
(LPARAM)&nmh);
Observe que a estrutura NMHDR pode fazer parte de uma estrutura definida por controle maior que contém informações adicionais. No exemplo, os valores antigos e novos do controle podem estar contidos nessa estrutura. (Essas estruturas estendidas são usadas com muitas notificações padrão; por exemplo, veja LVN_INSERTITEM, que usa a estrutura NMLISTVIEW .)
Acessibilidade
Todos os controles comuns oferecem suporte ao Microsoft Active Accessibility (MSAA), que permite o acesso programático por aplicativos de tecnologia acessível, como leitores de tela. O MSAA também permite que a UI Automation, uma tecnologia mais recente, interaja com controles.
Os controles personalizados devem implementar a interface IAccessible (para oferecer suporte a MSAA) ou as interfaces de automação da interface do usuário, ou ambas. Caso contrário, os produtos de tecnologia acessível poderão obter apenas informações muito limitadas sobre a janela de controle, não terão acesso às propriedades do controle e não poderão disparar eventos no controle.
Para obter mais informações sobre como tornar seu controle acessível, consulte API de automação do Windows.
Tópicos relacionados
-
Conceitual
-
Personalizando a aparência de um controle usando o desenho personalizado
-
Usando estilos visuais com controles desenhados pelo proprietário