Creating a Dialog with a Variable Number of Buttons | CodeGuru

Creating a Dialog with a Variable Number of Buttons

Click here for a larger image. Environment: VC6 I was faced with a requirement for a dialog to display text retrieved from a DB table. Rather than showing this in a list control, the user wanted a button for each DB record, of which there would be an unknown number at runtime. Creating buttons dynamically […]

Written By
CodeGuru Staff
CodeGuru Staff
May 1, 2003
1 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More



Click here for a larger image.

Environment: VC6

I was faced with a requirement for a dialog to display text retrieved from a DB table. Rather than showing this in a list control, the user wanted a button for each DB record, of which there would be an unknown number at runtime.

Creating buttons dynamically is straightforward, but to handle ON_BN_CLICKED in the dialog’s message map we would need to know each button’s control ID in advance. Therefore, I created a CButton-derived class (CMyButton) and handled the button click message there.

To avoid CMyButton needing to have knowledge of its parent dialog class, and to make it generic, registered window messages are used to inform the parent dialog of a button click event. The declaration of CMyButton is:

class CMyButton : public CButton
{
public:
  CMyButton();
  virtual ~CMyButton();
  int m_index;            // member used to identify this button
  UINT m_uBtnClickMsg;    // an app-defined message created with
                          // RegisterWindowMessage(LPCTSTR)
protected:
  afx_msg void OnClicked();
  DECLARE_MESSAGE_MAP()
};
void CMyButton::OnClicked()
{
  ::PostMessage(GetSafeOwner()->GetSafeHwnd(), m_uBtnClickMsg,
                                (WPARAM)m_index, 0);
}

The parent dialog (CVarButtonsDlg) handles messages using the ON_REGISTERED_MESSAGE macro:

BEGIN_MESSAGE_MAP(CVarButtonsDlg, CDialog)
  …
  ON_REGISTERED_MESSAGE(g_uBtnClickMsg, OnMsgBtnClick)
END_MESSAGE_MAP()
// declaration of OnMsgBtnClick :
LRESULT OnMsgBtnClick(WPARAM wParam, LPARAM lParam);

The buttons are constructed dynamically by the dialog class:

CVarButtonsDlg::OnInitDialog() {
  …
  g_uBtnClickMsg = RegisterWindowMessage(BTN_MSG_STRING);
  // BTN_MSG_STRING is a unique string; I used guidgen to
  // create it
  CString csTemp;
  CRect rBtn;
  // say we need 10 buttons…
  for (int i = 0; i<10; i++) {
    CMyButton* pBtn = new CMyButton();
    m_arrayBtns.Add(pBtn);       // CTypedPtrArray storing all
                                 // buttons dynamically created
    GetButtonRect(i, rBtn);      // will populate rBtn given
                                 // button number
    csTemp.Format(“Button%d”, i);
    pBtn->Create(csTemp, WS_CHILD|WS_VISIBLE|WS_TABSTOP, rBtn,
                 this, 9001+i);  // give a unique ID
                                 // (not strictly necessary)
    pBtn->m_index = i;
    pBtn->m_uBtnClickMsg = g_uBtnClickMsg;
  }
  // size the dialog box to the number of buttons
  int width = 680;
  int height = ((i/3)*60) + 100;    // on my form, the buttons
                                    // are in rows of three
  SetWindowPos(NULL,                // pWndAfter (z-order)
               0,                   // x pos
               0,                   // y pos
               width,
               height,
               SWP_NOZORDER|SWP_NOMOVE);
  …
}
CVarButtonsDlg::GetButtonRect(int i, CRect& r)
{
  // in this example, the buttons are arranged in three columns
  if (i%3 == 0) {         // first column
    r.left = 20;
    r.right = 220;
  }
  else if (i%3 == 1) {    // second column
    r.left = 240;
    r.right = 440;
  }
  else {                  // third column
    r.left = 460;
    r.right = 660;
  }
  // now get top & bottom positions
  int row = (i/3);
  r.top = 20 + (row*60);
  r.bottom = 60 + (row*60);
}
LRESULT CVarButtonsDlg::OnMsgBtnClick(WPARAM wParam,
                                      LPARAM lParam)
{
  CString csTemp;
  csTemp.Format(“You pressed button %d”, int(wParam));
  MessageBox(csTemp);
  return 0;
  // in a real app. I use wParam as the index for a data array
  // and do something more meaningful…
}

Why have I used registered window messages and not something like WM_APP+100? Because it is possible for another app to broadcast the same carefully chosen WM_APP value to our app. Registered messages are therefore safer.

Downloads


Download source – 12 Kb

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.