Qual é o problema da minha tentativa de subclassificar o CButton?
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.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
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 comoCButton
, 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).