TN014: controles personalizados

Esta observação descreve o Suporte do MFC para controles personalizados e autodesenhados. Ele também descreve subclasses dinâmicas e descreve a relação entre objetos CWnd e HWNDs.

O aplicativo de exemplo do MFC CTRLTEST ilustra como usar muitos controles personalizados. Consulte o código-fonte do exemplo geral do MFC CTRLTEST e ajuda online.

Controles/menus de desenho de proprietário

O Windows fornece suporte para controles e menus de desenho de proprietário usando mensagens do Windows. A janela pai de todo controle ou menu recebe essas mensagens e chama funções em resposta. Você pode substituir essas funções para personalizar a aparência visual e o comportamento do seu controle ou menu de desenho do proprietário.

O MFC dá suporte diretamente ao desenho de proprietário com as seguintes funções:

Você pode substituir essas funções em sua classe derivada CWnd para implementar o comportamento de desenho personalizado.

Essa abordagem não leva ao código reutilizável. Se você tiver dois controles semelhantes em duas classes diferentes CWnd, você pode implementar o comportamento de controle personalizado em dois locais. A arquitetura de controle autodesenhado com suporte do MFC resolve esse problema.

Controles e menus autodesenhados

O MFC fornece uma implementação padrão (nas classes CWnd e CMenu) para as mensagens padrão de desenho do proprietário. Essa implementação padrão decodificará os parâmetros de desenho do proprietário e delegará as mensagens de desenho do proprietário para os controles ou menu. Isso é chamado de autodesenho porque o código de desenho está na classe do controle ou menu, não na janela de proprietário.

Ao usar controles autodesenho, você pode criar classes de controle reutilizáveis que usam semântica de desenho de proprietário para exibir o controle. O código para desenhar o controle está na classe de controle, não em seu pai. Essa é uma abordagem orientada pelo objeto para programação de controle personalizado. Adicionar a seguinte lista de funções às classes autodesenhadas:

  • Para botões autodesenhados:

    CButton:DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw this button
    
  • Para menus autodesenhados:

    CMenu:MeasureItem(LPMEASUREITEMSTRUCT);
    // insert code to measure the size of an item in this menu
    CMenu:DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw an item in this menu
    
  • Para caixas de listagem autodesenhadas:

    CListBox:MeasureItem(LPMEASUREITEMSTRUCT);
    // insert code to measure the size of an item in this list box
    CListBox:DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw an item in this list box
    
    CListBox:CompareItem(LPCOMPAREITEMSTRUCT);
    // insert code to compare two items in this list box if LBS_SORT
    CListBox:DeleteItem(LPDELETEITEMSTRUCT);
    // insert code to delete an item from this list box
    
  • Para caixas de combinação autodesenhadas:

    CComboBox:MeasureItem(LPMEASUREITEMSTRUCT);
    // insert code to measure the size of an item in this combo box
    CComboBox:DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw an item in this combo box
    
    CComboBox:CompareItem(LPCOMPAREITEMSTRUCT);
    // insert code to compare two items in this combo box if CBS_SORT
    CComboBox:DeleteItem(LPDELETEITEMSTRUCT);
    // insert code to delete an item from this combo box
    

Para obter detalhes sobre as estruturas de desenho do proprietário (DRAWITEMSTRUCT, MEASUREITEMSTRUCT, COMPAREITEMSTRUCT e DELETEITEMSTRUCT), consulte a documentação do MFC para CWnd::OnDrawItem, CWnd::OnMeasureItem, CWnd::OnCompareItem e CWnd::OnDeleteItem respectivamente.

Usar controles e menus autodesenhados

Em menus auto-desenhados, você deve substituir os métodos OnMeasureItem e OnDrawItem.

Em caixas de listagem autodesenhadas e caixas de combinação, você deve substituir OnMeasureItem e OnDrawItem. Você deve especificar o estilo LBS_OWNERDRAWVARIABLE para caixas de listagem ou estilo CBS_OWNERDRAWVARIABLE para caixas de combinação no modelo de caixa de diálogo. O estilo OWNERDRAWFIXED não funcionará com itens de autodesenho porque a altura fixa do item é determinada antes que os controles autodesenhados sejam anexados à caixa de listagem. (Você pode usar os métodos CListBox::SetItemHeight e CComboBox::SetItemHeight para superar essa limitação.)

Alternar para um estilo OWNERDRAWVARIABLE forçará o sistema a aplicar o estilo NOINTEGRALHEIGHT ao controle. Como o controle não pode calcular uma altura integral com itens de tamanho variável, o estilo padrão de INTEGRALHEIGHT é ignorado e o controle é sempre NOINTEGRALHEIGHT. Se seus itens forem de altura fixa, você poderá impedir que itens parciais sejam desenhados especificando o tamanho do controle para ser um multiplicador inteiro do tamanho do item.

Para caixas de listagem e caixas de combinação autodesenhadas com o estilo LBS_SORT ou CBS_SORT, você deve substituir o método OnCompareItem.

Para caixas de listagem e caixas de combinação autodesenhadas, OnDeleteItem geralmente não é substituído. Você pode substituir OnDeleteItem se desejar executar um processamento especial. Um caso em que isso seria aplicável é quando memória adicional ou outros recursos são armazenados com cada item de caixa de listagem ou caixa de combinação.

Exemplos de controles e menus autodesenhados

O exemplo geral do MFC CTRLTEST fornece exemplos de um menu autodesenhado e uma caixa de listagem autodesenhada.

O exemplo mais comum de um botão autodesenhado é um botão de bitmap. Um botão de bitmap é um botão que mostra uma, duas ou três imagens de bitmap para os diversos estados. Um exemplo disso é fornecido na classe MFC CBitmapButton.

Subclasses dinâmicas

Ocasionalmente, você desejará alterar a funcionalidade de um objeto que já existe. Os exemplos anteriores exigiam que você personalizasse os controles antes de eles serem criados. As subclasses dinâmicas permitem personalizar um controle que já foi criado.

Subclasses é o termo do Windows para substituir a WndProc de uma janela por um WndProc personalizado e chamar o antigo WndProc para funcionalidade padrão.

Isso não deve ser confundido com a derivação de classe C++. Para esclarecimento, a classe base de termos C++ e a classe derivada são análogas à superclasse e à subclasse no modelo de objeto do Windows. A derivação C++ com subclasses do MFC e do Windows são funcionalmente semelhantes, a exceção é que a C++ não dá suporte às subclasses dinâmicas.

A classe CWnd fornece a conexão entre um objeto C++ (derivado de CWnd) e um objeto de janela do Windows (conhecido como um HWND).

Elas estão relacionadas de três maneiras comuns:

  • CWnd cria a HWND. Você pode modificar o comportamento em uma classe derivada criando uma classe derivada de CWnd. O HWND é criado quando seu aplicativo chama CWnd::Create.

  • O aplicativo anexa um CWnd a um HWND existente. O comportamento da janela existente não é modificado. Esse é um caso de delegação e foi possibilitado ao chamar CWnd::Attach como alias de um HWND existente para um objeto CWnd.

  • CWnd é anexado a um HWND existente e você pode modificar o comportamento em uma classe derivada. Isso é chamado de subclasses dinâmicas porque estamos alterando o comportamento e, portanto, a classe de um objeto do Windows no tempo de execução.

Você pode aplicar as subclasses dinâmicas usando os métodos CWnd::SubclassWindow e CWnd::SubclassDlgItem.

As duas rotinas anexam um objeto CWnd a um HWND existente. SubclassWindow obtém o HWND diretamente. SubclassDlgItem é uma função auxiliar que usa uma ID de controle e a janela pai. SubclassDlgItem foi criada para anexar objetos C++ aos controles de caixa de diálogo criados a partir de um modelo de caixa de diálogo.

Consulte o exemplo CTRLTEST para obter vários exemplos de quando usar SubclassWindow e SubclassDlgItem.

Confira também

Observações técnicas por número
Observações técnicas por categoria