Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle messages from dynamically created controls in an MFC app?

Imagine I have a CDialog which creates controls dynamically when the user clicks a button. It could be like this:

// We don't know which is the first id for the new buttons until runtime (!)
MyDialog::MyDialog(/*whatever parameters needed*/, first_id)
  : next_id_(first_id) 
{ /*...*/ }

BOOL MyDialog::OnSomeButtonClicked()
{
  CButton* new_button = new CButton;
  new_button->Create("Caption", WS_CHILD | WS_VISIBLE, this->new_button_rect_, 
                     this, this->next_id_++);
}

Then my question would be: How could I handle messages from this button? Is it possible to use the MFC message map facility?

The solution should work in both vs6 and vs2005.

Thank you!

like image 255
davidag Avatar asked May 13 '09 10:05

davidag


People also ask

How message map works in an MFC application?

MFC instead uses message maps to map direct messages to distinct class member functions. Message maps are more efficient than virtual functions for this purpose, and they allow messages to be handled by the most appropriate C++ object — application, document, view, and so on.

How do you make a list control dynamically in MFC?

To create the column(s) of a list control, you can use the CListCtrl::InsertColumn() method. One of its syntaxes is: int InsertColumn(int nCol, const LVCOLUMN* pColumn); The nCol argument is the index of the column that this call will create.


3 Answers

These are the solutions I've found so far in order of relevance:

  1. Use ON_COMMAND_RANGE if you can define the range of the control IDs you want to handle.

  2. Overload CWnd::PreTranslateMessage() and do whatever stuff you want with the messages received. NOTE: When dealing with buttons, take into account that the BN_CLICKED event is NOT sent to PreTranslateMessage but directly sent to the window procedure.

  3. Overload CWnd::WindowProc() and do whatever stuff you want with the messages received. NOTE that when dealing with buttons this is the ONLY WAY I've found to handle the BN_CLICKED event.

Interesting links:

  • Please help with PreTranslateMessage and user defined messages handling.
  • TN006: Message Maps

I hope this helps... thank you all for your contributions.

like image 75
davidag Avatar answered Oct 26 '22 22:10

davidag


Eventhough you dont know the exact values of the id, if you know the possible range of IDs then the following macro can be used.

BEGIN_MESSAGE_MAP(MyDialog, CDialog)
    ...
    ...
    ON_COMMAND_RANGE(1000, 5000, OnButtonDynamic)
END_MESSAGE_MAP()


void MyDialog::OnButtonDynamic(UINT nID)
{

}

This will work for ids in the range 1000 - 5000.

like image 24
Shino C G Avatar answered Oct 26 '22 22:10

Shino C G


I'm a few years late to this party, but the solution to this is to assign the same control id to each button (no need to 'reserve' id's in resource.h, and no artificial restrictions on the amount of controls that can be created), to save the window handle and to use GetCurrentMessage() in the handler for that button:

// resource.h
#define IDC_DYNAMIC_BUTTON 123

// In message map
ON_BN_CLICKED(IDC_DYNAMIC_BUTTON, OnDynamicButtonClicked)

// Store the window handles when creating them in a member:
std::map<HWND, SomeStruct> m_Buttons;
... fill this map when creating the buttons, presumably in OnInitDialog()

// Actual handler
void MyDialog::OnDynamicButtonClicked()
{
    const MSG* message = GetCurrentMessage();

    if (m_Buttons.find((HWND)message->lParam) != m_Buttons.end()) {
        // Do something with m_Buttons[(HWND)message->lParam]
    }
}
like image 34
Roel Avatar answered Oct 26 '22 22:10

Roel