Search code examples
c++mfcoverridingcontrolsccombobox

Is there any way to add an object other than CString to a CComboBox in MFC?


I am trying to add an object that has a member variable CString to a CCombobox. I cannot just add the string because I am trying to interface with a tool that requires me to have another member variable other than just a string as a list item in the CComboBox. Below is what I am trying to do.

CComboBox::AddString(myOwnObject);

I only want the string of myOwnObject to display, but for the whole object to be in the listbox so that the other member variable can be reached by the other tool.


Solution

  • The CComboBox Class wraps a native Combo Box control. It is a fairly basic implementation that meets the most common use case: Displaying strings for selection by the user.

    If you need additional functionality, you can use a CComboBoxEx Class instead. It exposes the full set of operations of the underlying ComboBoxEx control. In particular, the items can be configured to retrieve a string representation for items at runtime, based on arbitrary information.

    The following is assuming, that your custom item data layout is as follows:

    struct CustomItemData {
        CStringW m_Name;
        int m_SomeInteger;
    };
    

    The item data can be arbitrarily complex, and hold any information you wish to store. Populating a CComboBoxEx with items requires calling CComboBoxEx::InsertItem, passing an appropriately filled COMBOBOXEXITEM structure:

    // CustomItemData's lifetime must exceed that of the CComboBoxEx; don't use a
    // stack-based (automatic) variable.
    CustomItemData* pcid = new CustomItemData( myName, myInteger );
    
    CCOMBOBOXEXITEM cbei = { 0 };
    cbei.mask = CBEIF_TEXT | CBEIF_LPARAM;
    cbei.iItem = currentIndex;  // The zero-based index of the item.
    cbei.pszText = LPSTR_TEXTCALLBACK;  // The control will request the information by using
                                        // the CBEN_GETDISPINFO notification codes.
    cbei.lParam = reinterpret_cast<LPARAM>( pcid );  // Assign custom data to item.
    myComboBox.InsertItem( &cbei );
    

    At this point, the ComboBox control is populated with items, and will request the display information from the application. The CBEN_GETDISPINFO is sent to the control parent, so the notification handler must be placed into the parent's window (usually a dialog) implementation. The handler is connected to the notification message using the ON_NOTIFY macro:

    // Inside the parent's message map:
    ON_NOTIFY( CBEN_GETDISPINFO, IDC_MY_COMBOBOX, GetCBDispString )
    
    // Message handler inside the parent's class
    void CMyDlg::GetCBDispString( NMHDR* pNMHDR, LRESULT* pResult ) {
        NMCOMBOBOXEX* pncbe = reinterpret_cast<NMCOMBOBOXEX*>( pNMHDR );
        COMBOBOXEXITEM& cbei = pncbe->ceItem;
        if ( cbei.mask & CBEIF_TEXT ) {
            // Text is requested -> fill the appropriate buffer.
            const CustomItemData& cd = *reinterpret_cast<const CustomItemData*>( cbei.lParam );
            wcscpy( cbei.pszText, cd.m_Name );
            // Prevent future callbacks for this item. This is an optional optimization
            // and can be used, if the m_Name member doesn't change.
            cbei |= CBEIF_DI_SETITEM;
        }
        // Mark notification as handled
        *pResult = 0;
    }
    


    Occasionally it is desired to put the CBEN_GETDISPINFO callback inside the custom ComboBox implementation. MFC provides the necessary infrastructure to implement message reflection (see TN062: Message Reflection for Windows Controls). This allows a parent window to reflect notification messages back to the respective child control for handling. It can be useful at times, but is not required to implement a solution to this question.


    If you do not need full control over constructing display strings at runtime, you can go with a simple CComboBox control, and attach additional information calling CComboBox::SetItemData or CComboBox::SetItemDataPtr, as illustrated in πάντα ῥεῖ's answer.