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

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.

Conceitual

Referência de Controle Geral

Personalizando a aparência de um controle usando o desenho personalizado

Mensagens de controle

Usando estilos visuais com controles desenhados pelo proprietário