Controles de subclassificação

Se um controle faz quase tudo o que você deseja, mas você precisa de mais alguns recursos, você pode alterar ou adicionar recursos ao controle original subclassificando-o. Uma subclasse pode ter todos os recursos de uma classe existente, bem como quaisquer recursos adicionais que você queira dar-lhe.

Este documento discute como as subclasses são criadas e inclui os tópicos a seguir.

Controles de subclassificação anteriores à ComCtl32.dll versão 6

Você pode colocar um controle em uma subclasse e armazenar dados do usuário dentro de um controle. Você faz isso quando você usa versões do ComCtl32.dll anteriores à versão 6. Há algumas desvantagens na criação de subclasses com versões anteriores do ComCtl32.dll.

Para criar um novo controle, é melhor começar com um dos controles comuns do Windows e estendê-lo para atender a uma necessidade específica. Para estender um controle, crie um controle e substitua seu procedimento de janela existente por um novo. O novo procedimento intercepta as mensagens do controle e age sobre elas ou as passa para o procedimento original para processamento padrão. Use a função SetWindowLong ou SetWindowLongPtr para substituir o WNDPROC do controle. O exemplo de código a seguir mostra como substituir um WNDPROC.

OldWndProc = (WNDPROC)SetWindowLongPtr (hButton,
GWLP_WNDPROC, (LONG_PTR)NewWndProc);

Armazenando dados do usuário

Talvez você queira armazenar dados do usuário com uma janela individual. Esses dados podem ser usados pelo novo procedimento de janela para determinar como desenhar o controle ou para onde enviar determinadas mensagens. Por exemplo, você pode usar dados para armazenar um ponteiro de classe C++ para a classe que representa o controle. O exemplo de código a seguir mostra como usar SetProp para armazenar dados com uma janela.

SetProp (hwnd, TEXT("MyData"), (HANDLE)pMyData);

Desvantagens da antiga abordagem de subclassificação

A lista a seguir aponta algumas das desvantagens de usar a abordagem descrita anteriormente para subclassificar um controle.

  • O procedimento de janela só pode ser substituído uma vez.
  • É difícil remover uma subclasse depois que ela é criada.
  • Associar dados privados a uma janela é ineficiente.
  • Para chamar o próximo procedimento em uma cadeia de subclasses, você não pode converter o procedimento de janela antigo e chamá-lo, você deve chamá-lo usando a função CallWindowProc.

Controles de subclassificação usando ComCtl32.dll versão 6

Observação

ComCtl32.dll versão 6 é apenas Unicode. Os controles comuns suportados pelo ComCtl32.dll versão 6 não devem ser subclassificados (ou superclassificados) com procedimentos de janela ANSI.

 

ComCtl32.dll versão 6 contém quatro funções que facilitam a criação de subclasses e eliminam as desvantagens discutidas anteriormente. As novas funções encapsulam o gerenciamento envolvido com vários conjuntos de dados de referência, portanto, o desenvolvedor pode se concentrar em recursos de programação e não no gerenciamento de subclasses. As funções de subclassificação são:

SetWindowSubclass

Essa função é usada para inicialmente subclassificar uma janela. Cada subclasse é identificada exclusivamente pelo endereço da pfnSubclass e sua uIdSubclass. Ambos são parâmetros da função SetWindowSubclass. Várias subclasses podem compartilhar o mesmo procedimento de subclasse e o ID pode identificar cada chamada. Para alterar os dados de referência, você pode fazer chamadas subsequentes para SetWindowSubclass. A vantagem importante é que cada instância de subclasse tem seus próprios dados de referência.

A declaração de um procedimento de subclasse é ligeiramente diferente de um procedimento de janela normal porque tem duas partes adicionais de dados: o ID da subclasse e os dados de referência. Os dois últimos parâmetros da declaração de função a seguir mostram isso.

LRESULT CALLBACK MyWndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass,
DWORD_PTR dwRefData);

Toda vez que uma mensagem é recebida pelo novo procedimento de janela, um ID de subclasse e dados de referência são incluídos.

Observação

Todas as cadeias de caracteres passadas para o procedimento são cadeias de caracteres Unicode, mesmo se Unicode não for especificado como uma definição de pré-processador.

 

O exemplo a seguir mostra uma implementação de esqueleto de um procedimento de janela para um controle subclassificado.

LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
                               LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        .
        .
        .
        return TRUE;
    // Other cases...
    } 
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

O procedimento de janela pode ser anexado ao controle no manipulador de WM_INITDIALOG do procedimento de diálogo, conforme mostrado no exemplo a seguir.

case WM_INITDIALOG:
{
    HWND button = GetDlgItem(hDlg, IDC_OWNERDRAWBUTTON);
    SetWindowSubclass(button, OwnerDrawButtonProc, 0, 0);
    return TRUE;
}

GetWindowSubclass

Essa função recupera informações sobre uma subclasse. Por exemplo, você pode usar GetWindowSubclass para acessar os dados de referência.

RemoveWindowSubclass

Essa função remove subclasses. RemoveWindowSubclass em combinação com SetWindowSubclass permite que você adicione e remova subclasses dinamicamente.

DefSubclassProc

A função DefSubclassProc chama o próximo manipulador na cadeia de subclasses. A função também recupera o ID adequado e os dados de referência e passa as informações para o procedimento da próxima janela.