Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Event Handler for a Dynamically Created Control Embedded in Another Control

0.00/5 (No votes)
13 Jan 2006 1  
A simple way to trap a command message and send the message to any window.

Overview

Message Map Mechanism is the backbone of Event-Driven programming. In the MFC framework, DECLARE_MESSAGE_MAP(), BEGIN_MESSAGE_MAP(), and END_MESSAGE_MAP() are the three macros which create the road map of messages. Please refer to MSDN articles to get a better perception of what is happening inside these three macros. With this understanding, it would be pretty simple to write a custom message handler for a control that is into some other control.

I have chosen a dialog based application using MFC to demonstrate this. Inserting a MSHFlexGrid control, I have added buttons and checkboxes into the cells of the flex grid. The button clicked (BN_CLICKED) event has been trapped in my sample application but one could easily add other events also, following these steps.

In a dialog based MFC application, if you insert a Microsoft Hierarchical Flex Grid control, the framework will automatically add wrapper classes to invoke public methods of the control class. Now, if we wish to insert buttons in each row of column zero of the flex grid, which will appear at run-time, we have to insert the following code inside the OnInitDialog() function of the dialog class. Before doing so, add two pointers of CButton as private data members of the main dialog class, i.e., the CDynMsgMapDlg class in our example (CButton *m_pCheckBox, *m_pButtons).

OnInitDialog()

BOOL CDynMsgMapDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.


    // IDM_ABOUTBOX must be in the system command range.

    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    ..............................
    ..............................
    // TODO: Add extra initialization here


    // Get Number of Rows of FlexGrid

    int nRow = m_hflexgrid.GetRows();

    // Create Button and CheckBox objects

    m_pButtons  = new CButton[m_hflexgrid.GetRows()];
    m_pCheckBox = new CButton[m_hflexgrid.GetRows()];

    for(int iCnt = 1; iCnt<nRow; iCnt++)
    {
        // Get Coordinates and Width,Height of flexgrid cells

        int nY     = m_hflexgrid.GetRowPos(iCnt);
        int nH     = m_hflexgrid.GetRowHeight(iCnt);
        
        // Get X-Coordinate and Width of flexgrid column number ZERO

        int nXBt = m_hflexgrid.GetColPos(0);
        int nWBt = m_hflexgrid.GetColWidth(0,0);

        // Get X-Coordinate and Width of flexgrid column number TWO

        int nXCb = m_hflexgrid.GetColPos(2);
        int nWCb = m_hflexgrid.GetColWidth(2,0);

        
        // Get the Grid Rectangle 

        CRect rectButton(nXBt/15, nY/15, (nXBt+nWBt)/15, (nY+nH)/15);
        // Get the Grid Rectangle 

        CRect rectCheckBox(nXCb/15, nY/15, (nXCb+nWCb)/15, (nY+nH)/15);

        CString cstrTextBt;
        cstrTextBt.Format("Button %d ",iCnt);
        // Create Button Dynamically Into the rectangle (Row=iCnt,Col=0)

        m_pButtons[iCnt].Create(
                     cstrTextBt, WS_CHILD|BS_PUSHBUTTON|WS_VISIBLE, 
                     rectButton, GetDlgItem(IDC_MSHFLEXGRID_TEST), 
                     iCnt+IDC_BUTTON_FIRST
                     );

        CString cstrTextCb;
        cstrTextCb.Format("CheckBox %d ",iCnt);
        // Create Button Dynamically Into the rectangle (Row=iCnt,Col=2)

        m_pCheckBox[iCnt].Create(
                     cstrTextCb, WS_CHILD|BS_AUTOCHECKBOX|WS_VISIBLE, 
                     rectCheckBox, GetDlgItem(IDC_MSHFLEXGRID_TEST), 
                     iCnt+IDC_CHECKBOX_FIRST
                      );

    }
    
    return TRUE;  // return TRUE  unless you set the focus to a control

}

With this, you will be able to see a button and a checkbox array populated in the zero'th and the second column of the grid.

Now, we have to add a click event handler for the buttons we have already created. For this, you have to add a macro ON_CONTROL_RANGE() within the ClassWizard generated BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP(). Here is our code snippet:

BEGIN_MESSAGE_MAP(CDynMsgMapDlg, CDialog)
//{{AFX_MSG_MAP(CDynMsgMapDlg)

  ON_WM_SYSCOMMAND()
  ON_WM_PAINT()
  ON_WM_QUERYDRAGICON()
  ON_WM_DESTROY()
//}}AFX_MSG_MAP

ON_CONTROL_RANGE(BN_CLICKED,IDC_BUTTON_FIRST, 
            IDC_BUTTON_LAST, OnButtonClicked)
END_MESSAGE_MAP()

Now, add the OnButtonClicked() prototype within the class declaration.

afx_msg void OnButtonClicked(UINT nIDbutton);

The implementation of OnButtonClicked() may be like:

void CDynMsgMapDlg::OnButtonClicked(UINT nIDbutton)
{
     MessageBox("BN_CLICKED Event trapped within  'MessageMap' Dialog");
}

After doing the steps up to this, if we try to run our application, we will see that a message box is not coming with the click event of the dynamically created buttons. The reason behind it is the dynamic creation of the buttons. As buttons and checkboxes have taken the MSHFlexGrid as their parent, the BN_CLICKED message has to be trapped within the wrapper class of MSHFlexGrid. So, add DECLARE_MESSAGE_MAP(), BEGIN_MESSAGE_MAP(), and END_MESSAGE_MAP() to trap the BN_CLICKED event as we did earlier. Now, the last thing is we have to send a WM_COMMAND message to the parent window of the MSHFlexGrid, i.e., the main dialog window with the same ID.

BEGIN_MESSAGE_MAP(CMSHFlexGrid, CWnd)
    //{{AFX_MSG_MAP(CMSHFlexGrid)

    //}}AFX_MSG_MAP

    ON_CONTROL_RANGE(BN_CLICKED,IDC_BUTTON_FIRST, 
                IDC_BUTTON_LAST, OnButtonClicked)
END_MESSAGE_MAP()

// Button Click Event handler : Post this message as WM_COMMAND message

// to the parent window, i.e. the Dialog containing flexgrid

void CMSHFlexGrid::OnButtonClicked(UINT nID)
{
    GetParent()->SendMessage(
                                WM_COMMAND,
                                MAKELONG(nID,BN_CLICKED),
                                (LPARAM)(GetDlgItem(nID)->m_hWnd)
                            );
}

Finally, add the OnScroll() event of the MSHFlexGrid using the ClassWizard, and insert the following code into it:

void CDynMsgMapDlg::OnScrollMshflexgridTest() 
{
    // TODO: Add your control notification handler code here

    int nRow = m_hflexgrid.GetRows();
    int nTopRow = m_hflexgrid.GetTopRow();
    for(int iCnt = m_hflexgrid.GetFixedRows(); iCnt<nRow; iCnt++)
    {
        if(iCnt<nTopRow)
        {
            m_pButtons[iCnt].ShowWindow(SW_HIDE);
            m_pCheckBox[iCnt].ShowWindow(SW_HIDE);
        }
        else
        {
            // Get Coordinates and Width,Height of flexgrid cells

            int nY     = m_hflexgrid.GetRowPos(iCnt);
            int nH     = m_hflexgrid.GetRowHeight(iCnt);
            
            // Get X-Coordinate and Width of flexgrid column number ZERO

            int nXBt = m_hflexgrid.GetColPos(0);
            int nWBt = m_hflexgrid.GetColWidth(0,0);
            
            // Get X-Coordinate and Width of flexgrid column number TWO

            int nXCb = m_hflexgrid.GetColPos(2);
            int nWCb = m_hflexgrid.GetColWidth(2,0);
            
            
            // Get the Grid Rectangle 

            CRect rectButton(nXBt/15, nY/15, (nXBt+nWBt)/15, (nY+nH)/15);
            // Get the Grid Rectangle 

            CRect rectCheckBox(nXCb/15, nY/15, (nXCb+nWCb)/15, (nY+nH)/15);
            
            // Move Buttons and CheckBoxes along with the ScrollBar

            m_pButtons[iCnt].MoveWindow(rectButton);
            m_pButtons[iCnt].ShowWindow(SW_SHOW);
            m_pCheckBox[iCnt].MoveWindow(rectCheckBox);
            m_pCheckBox[iCnt].ShowWindow(SW_SHOW);
            
        }
    }    
}

Now, run the application, click a button inside the grid, and you will see what you want.

We need to be careful about this:

  • The button and checkbox IDs should be unique.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here