Search code examples
c++winapiatlwtl

What's wrong with my attempt at subclassing CButton?


I tried to create a subclassed control for the first time, but I feel like I did something wrong. The control is a Button, which I placed in the designer. This is its class:

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);

};

Nothing fancy so far.

However, I can't really achieve to receive windows messages in this control. This is bad, considering the main reason for trying to subclass a control was the fact that this should be a reusable class with reusable, custom Paint behaviour. I want to overwrite certain message handlers, while keeping those I didn't explicitely ask for to the usual CButton routine.

As you can see, I implemented a message map, but the messages are just not coming in.

This is how I tried to setup the instance of this class:

TTTField fld;

is a member variable of my main dialog class. In this class I added the following DDX_MAP:

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

with IDC_BTN being the id of the button on the designer.

In the assignment operator overload for TTTField I have the following:

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

I feel like this operator overload might be the source of my problems, but I just can't manage to find a website which is properly explaining the whole topic without using code which seems outdated for like 20 years.

What am I doing wrong here? I am really lost right now.


Solution

  • The button class should be defined as follows:

    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;
        }
    };
    

    Override dialog box's OnInitDialog, call SubclassWindow to subclass the button:

    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;
        }
    };
    

    Edit, for initialization

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

    Then to create the button:

    //fld.SubclassWindow(GetDlgItem(IDC_BTN));
    fld.Create(this, IDC_BTN); //<== use this instead