c++comboboxmfc

ComboBox - add user value to dropdown list


I have CComboBox - dropdown type. I add there any predefined values from my own list in OnInitDilaog() method. User can either choose value from list or add his own value to editbox. How can I add new user value to droplist? Ideally when he press Enter?

 BOOL CMFCtestDlg::OnInitDialog()
   {
       m_cCombo.AddString(L"Volba 1");
       m_cCombo.AddString(L"Volba 2");

       return TRUE;  
   }

Solution

  • The problem with the Win32 Combo-Box is that it doesn't automatically add the edit-box contents to the list, neither it sends some notification when the Enter key is pressed. So we have to capture the key event, that is process the its messages. In MFC we can use the PreTranslateMessage() function. It's often used as a "last resort" solution, but in this case it comes in really handy. The handle of the edit-box can be obtained by calling the Win32/MFC GetComboBoxInfo() function (or sending the CB_GETCOMBOBOXINFO message).

    Take the following steps:

    In the dialog class add a HWND member to hold the handle of the edit-box:

    private:
        HWND hWCBEdit = NULL; // Combo-box's edit-control
    

    Add some few code lines in the dialog's OnInitDialog() member:

    BOOL CMFCtestDlg::OnInitDialog()
    {
        // Do not delete this, it binds member variables to controls, among others!
        CDialogEx::OnInitDialog();
    
        // Get and store CB's edit-control
        COMBOBOXINFO cbi { sizeof(COMBOBOXINFO) };
        m_cCombo.GetComboBoxInfo(&cbi);
        hWCBEdit = cbi.hwndItem;
    
        m_cCombo.AddString(L"Volba 1");
        m_cCombo.AddString(L"Volba 2");
    
        return TRUE;
    }
    

    Override the dialog's PreTranslateMessage() member as shown below:

    BOOL CMFCtestDlg::PreTranslateMessage(MSG* pMsg)
    {
        // Capture [Enter] key for the CB Edit-Box
        if (pMsg->message == WM_KEYDOWN && pMsg->hwnd == hWCBEdit && pMsg->wParam == VK_RETURN)
        {
            TCHAR sCBEdit[100];
            if (::GetWindowText(hWCBEdit, sCBEdit, 100) > 0) // CB Edit-Box non-empty
            {
                if (m_cCombo.FindStringExact(-1, sCBEdit) < 0) // String not in the drop-list
                    m_cCombo.AddString(sCBEdit);
                // Maybe Add some more actions here
            }
            return TRUE; // Message processed, no further processing is needed
        }
    
        return CDialogEx::PreTranslateMessage(pMsg);
    }
    

    This adds the edit-control contents to the list if the Enter key is pressed. The dialog will NOT close if Enter is pressed while the edit-control is focused.

    An alternative solution, not using PreTranslateMessage(), could be to subclass the edit-control. This is done by changing its window procedure (check if Enter was pressed, otherwise call the original one).