Qual é o problema da minha tentativa de subclassificar o CButton?

Tentei criar um controlo subclassificado pela primeira vez, mas sinto que fiz algo de errado. O controle é um botão, que eu coloquei no designer. Esta é a sua classe:
class TTTField : public CButton
{
public:
    BEGIN_MSG_MAP_EX(TTTField)
        MSG_WM_INITDIALOG(OnInitDialog);
    END_MSG_MAP()

    TTTField operator=(const CWindow& btn);

private:

    const BOOL OnInitDialog(const CWindow wndFocus, const LPARAM lInitParam);

};
Até agora, nada de especial.

No entanto, não consigo receber mensagens do windows neste controlo. Isto é ruim, considerando a principal razão para tentar subclassar um controle foi o fato de que esta deve ser uma classe reutilizável com comportamento de pintura reutilizável e personalizada. I quero sobrepor certos manipuladores de mensagens, enquanto guardo esses que não pedi explicitamente para a rotina habitual de CButton.

Como podem ver, implementei um mapa de mensagens, mas as mensagens não estão a chegar.

Foi assim que tentei configurar a instância desta classe:

TTTField fld;

é uma variável membro da minha principal classe de diálogo. Nesta classe I adicionou o seguinte mapa DDX_:

BEGIN_DDX_MAP(TTTMainDialog)
    DDX_CONTROL_HANDLE(IDC_BTN, fld)
END_DDX_MAP()

Sendo o IDC_ BTN o id do botão do desenhador.

na sobrecarga do operador de atribuição para TTTField tenho o seguinte:

TTTField TTTField::operator=(const CWindow& btn)
{
    Attach(btn);
    return *this;
}

sinto que esta sobrecarga de operador pode ser a fonte dos meus problemas, mas não consigo encontrar um site que explique adequadamente todo o tópico sem usar um código que parece desactualizado há 20 anos.

O que estou a fazer de errado aqui? Estou mesmo perdido.

Author: Sossenbinder, 2016-09-10

2 answers

A classe de botões deve ser definida do seguinte modo:

class TTTField : public CWindowImpl<TTTField, CButton>
{
protected:
    BEGIN_MSG_MAP_EX(TTTField)
        MSG_WM_LBUTTONDOWN(OnLButtonDown)
    END_MSG_MAP()

protected:
    LRESULT OnLButtonDown(UINT, CPoint)
    {
        //Edit: this override is meant for testing the subclass only
        //it's insufficient for handling button clicks
        MessageBox(L"Testing override...");
        return 0;
    }
};

Sobrepor a janela de OnInitDialog, chamar SubclassWindow para subclassificar o botão:

class TTTMainDialog: public CDialogImpl<CMainDialog>
{
public:
    enum { IDD = IDD_MYDIALOG };
    BEGIN_MSG_MAP(TTTMainDialog)
        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
    END_MSG_MAP()

    TTTField fld;
    LRESULT OnInitDialog(UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        fld.SubclassWindow(GetDlgItem(IDC_BTN));
        return 0;
    }
};

Editar, para inicialização

class TTTField : public CWindowImpl<TTTField , CButton>
{
public:
    void Create(CWindow *wnd, int id)
    {
        SubclassWindow(wnd->GetDlgItem(id));
        //add initialization here
    }
    ...
}

Então para criar o botão:

//fld.SubclassWindow(GetDlgItem(IDC_BTN));
fld.Create(this, IDC_BTN); //<== use this instead
 6
Author: Barmak Shemirani, 2016-10-01 18:05:36

Talvez o melhor exemplo, ou pelo menos um de, de subclassar um botão esteja direito nas fontes de WTL, no topo de atlctrlx.h:

template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
{
public:
    DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
...

Também irá registar recursos externos nesta classe: usando o CBitmapButton do WTL .

Já para não falar do comentário da WTL sobre os controlos.
// These are wrapper classes for Windows standard and common controls.
// To implement a window based on a control, use following:
// Example: Implementing a window based on a list box
//
// class CMyListBox : CWindowImpl<CMyListBox, CListBox>
// {
// public:
//      BEGIN_MSG_MAP(CMyListBox)
//          // put your message handler entries here
//      END_MSG_MAP()
// };

Mais exemplos de controlos WTL personalizados simples e sofisticados podem ser encontrados em viksoe.dk.

Uma coisa confusa sobre o controlo WTL a extensão é que classes básicas como CButton, CComboBox são papéis finos sobre os controles padrão. Eles traduzem principalmente métodos em mensagens a serem enviadas. Muitas vezes você pode facilmente lançar instâncias de tais classes para HWND e para trás. Os próprios controlos-padrão oferecem um nível de personalização através do suporte das mensagens de notificação.

Quando subclama um controlo, está a adicionar funcionalidade do seu lado que de alguma forma precisa de interoperar com a implementação do stock, e controlar as aulas já não são papéis finos. Portanto, herdais de CWindowImpl e não CButton directamente. O próximo desafio é especificamente a subclasse: você precisa ter a janela original criada e depois disso, tendo uma pega HWND, você a modifica para encaminhar as mensagens através do seu mapa de mensagens. É aqui que precisas do método SubclassWindow. Ou seja, você tem o controle criado, você procura-o acima de seu punho, por exemplo com GetDlgItem e então você subclama a janela usando sua instância de classe SubclassWindow chamada. Ou, em alternativa, pode crie o controlo usando o seu novo método class Create, caso em que CreateWindow e a associação com o seu mapa de mensagens será feita por si.

Algumas implementações, mais complicadas, de controlos personalizados também irão querer que reflicta as mensagens de notificação da janela-mãe para os controlos, para que eles possam lidar com eles dentro da mesma classe de controlo personalizado. Isto irá normalmente requerer que adicione uma linha REFLECT_NOTIFICATIONS no seu mapa de mensagens da classe de diálogo (veja esta questão relacionada em presente).

 4
Author: Roman R., 2017-05-23 12:25:57