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 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
  virtual ~CMyButton();

  int m_index;            // member used to identify this button
  UINT m_uBtnClickMsg;    // an app-defined message created with
                          // RegisterWindowMessage(LPCTSTR)

  afx_msg void OnClicked();


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)

// 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

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


Download source - 12 Kb


  • Dialog Control Limitations

    Posted by Legacy on 05/06/2003 12:00am

    Originally posted by: Blake Miller

    I hope there will not be more than 256 records.
    I seem to recall a documented limitation of 256 controls in each window.
    It would have seemed a virtual listbox might have been better, or a grid control or something, at least for your specific application.


    Posted by Legacy on 05/02/2003 12:00am

    Originally posted by: Peter Mares

    Why didn't you just use a ON_COMMAND_RANGE() macro?

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 23, 2015 The cloud is not just about a runtime platform for your projects – now, you can do your development in the cloud, too. Check out this webcast to learn how the cloud improves your development experience and team collaboration. Join Dana Singleterry, Principal Product Manager for Oracle Dev Tools, as he discusses how to simplify every aspect of the development lifecycle, including requirements gathering, version management, code reviews, build automation, and …

  • Moving from an on-premises environment to Office 365 does not remove the need to plan for disruptions or reduce the business risk requirements for protecting email services. If anything, some risks increase with a move to the cloud. Read how to ease the transition every business faces if considering or already migrating to cloud email. This white paper discusses: Setting expectations when migrating to Office 365 Understanding the implications of relying solely on Exchange Online security Necessary archiving …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date