Click here to Skip to main content
15,888,201 members
Articles / Programming Languages / C++
Article

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

Rate me:
Please Sign up or sign in to vote.
4.09/5 (17 votes)
13 Jan 20062 min read 65K   1.2K   29   6
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.

Image 1

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:

C++
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.

Image 2

Image 3

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


Written By
Web Developer
United States United States
Rajib is one of the many altruist guy working with Cognizant Technology Solution ... Smile | :)

Comments and Discussions

 
QuestionIs there a way to achieve same thing without altering Pin
yash358-Dec-17 1:44
yash358-Dec-17 1:44 
GeneralVery Good article Pin
Koushik Biswas31-Aug-09 6:04
Koushik Biswas31-Aug-09 6:04 
Questionthis nice article for flexgrid Pin
yogesh shrikhande11-Mar-07 22:18
yogesh shrikhande11-Mar-07 22:18 
QuestionExcellent and useful article..... Can you ( or anyone ) help with CEdit box problem ? Pin
cfilorux16-Jul-06 13:49
cfilorux16-Jul-06 13:49 
Hi there, firstly many thanks for this article as its helped me alot with my own project.

But I've got a ( bit of a weird ) problem when creating a CEdit Box control on the Grid.
Using your template I can create and use CEdit boxes on the grid and it all behaves as it should (Scroll fine etc)..... Except in a very specific circumstance which causes the application to freeze


It should be fairly simple to recreate the problem using your code just create CEdit boxes instead of CButtons.

Click on one of the editboxes to give it focus and a flashing cursor(caret) will appear in the box. Then switch focus from the application to say this article ( or anything that causes the application to lose focus ) and the application freezes up !!!???

Can you or anyone help with clues as to the cause of this problem ?

I have overlaid both buttons and edit boxes on my grid and all is fine if a button or cell has focus before the application loses focus, it only happens when the edit box has the flashing cursor(caret).

I've tried dynamically creating an Edit box with the Dialog as the parent window and again all is fine, its got something to do with creating it with the grid as the parent window.....

I don't know alot about 'carets' ( nothing at all until I had this problem ), but it seems that it must be destoyed when an application loses focus as there can only be one in existance for the whole PC. I've tried trapping the Dialogs WM_NCACTIVATE( bActive == FALSE ) (This is fired just prior to the application losing focus ) message and destroying the caret at this point and it hasn't cured the problem so maybe the caret has got nothing to do with the cause, but on the other hand it only happens when the edit box has a caret.


I'm a bit stuck here and would really appreciate any help or pointers

Many thanks in advance.

Kind regards











GeneralNice one Pin
world.wild.web16-Jan-06 18:26
world.wild.web16-Jan-06 18:26 
GeneralFantastic Pin
Mainak Saha15-Jan-06 22:34
Mainak Saha15-Jan-06 22:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.