Click here to Skip to main content
15,881,757 members
Articles / Desktop Programming / MFC
Article

Combo Box Initialization

Rate me:
Please Sign up or sign in to vote.
4.60/5 (11 votes)
16 May 2000 171.9K   1.5K   30   12
Learn how to programmatically initialize a combo box.
  • Download source files - 3 Kb

    One of the more useless features of MFC is the ability to add items to a combo box in the resource editor. You might ask "Why?" Clearly it makes life easy. Well, it doesn't. In fact, it can make life impossible. For example, the only condition under which this is useful is if the strings are language-independent text and the whole ComboBox is completely insensitive to being sorted or unsorted. I've seen things like the resource editor adding items like

    Black
    Blue
    Red
    Green

    and having code that says

    switch(((CComboBox *)GetDlgItem(IDC_COLORS))->GetCurSel())
    {
        case 0: // black
            color = RGB(0, 0, 0);
            break;
        case 1: // blue
            color = RGB(0, 0, 255);
            break;
    }

    You can see immediately that this is impossible to maintain; a change in the combo box resource has to be reflected in some unknown and unknowable code. Well, another solution is

    #define COLOR_BLACK 0
    #define COLOR_BLUE  1
    ...
    switch(((CComboBox *)GetDlgItem(IDC_COLORS))->GetCurSel())
    {
        case COLOR_BLACK:
    }

    This is merely syntactic aspartame (not even syntactic sugar) on a bad idea; it changes the problem not one bit. Another solution is to do something like

    CString s;
    ((CComboBox *)GetDlgItem(IDC_COLORS))->GetLBText(s, 
                  ((CComboBox*)GetDlgItem(IDC_COLORS)->GetCurSel());
    if (s == CString("Black"))
    {
        color = RGB(0, 0, 0);
    }
    else if (s == CString("Blue"))
    {
        color = RGB(0, 0, 255);
    }

    etc. This has the advantage that at least you are not position-sensitive; but you are language-sensitive. Consider a European distributor who can edit the resource file, and change the strings:

    Schwartz
    Blau
    Rot
    Grün

    The code fails, for the same reason. If the combo box is sorted, the order is all wrong; if the string compare is used, no color ever matches. An application that uses a combo box should be completely independent of the sort order and the language. Trust me. Been there, done that. You will only regret it.

    Essentially, you must never write a ComboBox or a ListBox in which there is any assumption whatsoever about the meaning of a particular offset. The integers returned from GetCurSel are fundamentally meaningless except for retrieving the string data or ItemData of the control. They have no other significance, and to assign other significance to them is poor programming practice.

    I have a class called "CIDCombo" which I use in all such cases. This was invented after the second time I did myself in using the preloaded combo box (Note: just because something is available, it does not mean that it is a good idea to use it!) What CIDCombo does is allow me to specify a pair, a string ID from the string table and a relevant mapped value, in a table. The table is basically

    typedef struct IDData {
        UINT id;
        DWORD value;
    }; // defined in IDCombo.h file
    
    IDData colors [] = {
        {IDS_BLACK, RGB(  0,   0,   0)},
        {IDS_BLUE,  RGB(  0,   0, 255)},
        ...
        {0, 0} // end of table
    };<

    the core loop is essentially

    void IDCombo::load(IDData * data )
    {
        for(int i = 0; data[i].id != 0; i++)
        {
            CString s;
            s.LoadString(data[i].id);
            int index = AddString(s);
            SetItemData(index, data[i].value);
        }
    }

    So what happens in my OnInitDialog is:

    BOOL CMyDialog::OnInitDialog()
    {
        ...
        c_Colors.load(colors); // Note: <A href="http://www.codeproject.com/cpp/NoGetDlgItem.asp">no GetDlgItem</A>, ever!
        ...
    }

    This has numerous advantages over the initialize-in-resource method:

    1. It has a single point of definition for all values in the combo box.
    2. There is only one place a change needs to be made to add or delete items
    3. It is intrinsically insensitive to sorting issues
    4. It is language-independent.

    If the string IDS_BLACK is changed to "Noir" or "Schwartz" or something else, the color value is always RGB(0,0,0). And if the combo box was sorted, or not sorted, it doesn't matter; the color names are always properly matched to their color values. Or control flow settings. Or data bits values. Or whatever. Essentially, an combo box that could be initialized from the resource is better served by this method. I've never found an exception.

    The class is available on the CD-ROM that accompanies our book (Win32 Programming, Rector & Newcomer, Addison-Wesley, 1997), and an instance of it can be downloaded free from this Web site

    Another cool feature of CIDCombo is that it automatically resizes the dropdown list so that if at all possible, all the items always show without a scrollbar. No more silly resizing the dropdown "by hand" in the hopes that everything will fit! You'll always see everything, scrollbar-free, unless the whole selection won't fit in the window. The height of the dropdown is dynamically adjusted to fit as many items as possible in, given the position of the combo box on the screen (it will pop up above the combo box if it needs more space and the combo box is low on the screen).

    What makes this really nice is that whenever you want the actual value, you can simply use GetItemData to obtain the value.

    COLORREF CMyDialog::getColor()
    {
        int sel = c_Colors.GetCurSel();
        if(sel == CB_ERR)
            return RGB(0, 0, 0); // or other suitable default value
        return c_Colors.GetItemData(sel);
    }

    How do you handle more complex information? Well one way is to define a struct for the group of information, for example, a somewhat silly example is a dropdown list that describes bean types and their packers. (The reason it is silly is that this would actually be done from a database, but the idea is to make a simple sample)

    typedef struct {
        UINT weight;
        UINT company;
    } BeanDescriptor, *LPBeanDescriptor;
    
    BeanDescriptor kidney = { 16, IDS_REDPACK};
    LineDescriptor vegveg = { 12, IDS_HEINZ};
    LineDescriptor green =  { 14, IDS_GENERIC};
    
    IDData lines [] = {
        { IDS_KIDNEY, (DWORD)&kidney},
        { IDS_VEGETARIAN, (DWORD)&vegveg},
        { IDS_GREENBLOCKS, (DWORD)&green},
        { 0, 0} // EOT
    };

    To use the data, you need to do the following:

    LPBeanDescriptor getBean()
    {
        int sel = c_Beans.GetCurSel();
        if(sel == CB_ERR)
            return NULL;
        return (LPBeanDescriptor)c_Beans.GetItemDataPtr(sel);
    }

    How do you select an item? Well, you need the moral equivalent of FindStringExact. In this case, the selection is based on an ItemData comparison. For example:

    int CIDCombo::Select(DWORD value)
    {
        for(int i = 0; i < CComboBox::GetCount(); i++)
        { /* compare */
            DWORD v = CComboBox::GetItemData(i);
            if(value == v)
            { /* found it */
                CComboBox::SetCurSel(i);
                return i;
            } /* found it */
    
            CComboBox::SetCurSel(-1);
            return CB_ERR;
        }
    }

    You can get my implementation of CIDCombo by clicking the link at the top of the article.


    The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

    Send mail to newcomer@flounder.com with questions or comments about this article.
    Copyright © 1999 CompanyLongName All Rights Reserved.
    www.flounder.com/mvp_tips.htm
  • 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
    Retired
    United States United States
    PhD, Computer Science, Carnegie Mellon University, 1975
    Certificate in Forensic Science and the Law, Duquesne University, 2008

    Co-Author, [i]Win32 Programming[/i]

    Comments and Discussions

     
    QuestionAdd char* or CString as data instead of int Pin
    jens_weller21-Jan-19 1:45
    jens_weller21-Jan-19 1:45 
    GeneralThe author is wrong. Pin
    Jason.Williams12-Oct-05 21:33
    sussJason.Williams12-Oct-05 21:33 
    GeneralRe: The author is wrong. Pin
    Bob Flynn21-Apr-06 1:59
    Bob Flynn21-Apr-06 1:59 
    GeneralRe: The author is wrong. Pin
    Hamed Musavi3-Sep-07 3:42
    Hamed Musavi3-Sep-07 3:42 
    GeneralRe: The author is wrong. Pin
    Bob Flynn6-Sep-07 4:50
    Bob Flynn6-Sep-07 4:50 
    GeneralThe author is Right. Pin
    Hamed Musavi6-Sep-07 5:40
    Hamed Musavi6-Sep-07 5:40 
    Questionhow can i make a combo box invisible at run time ? Pin
    Member 136101822-Sep-04 3:22
    Member 136101822-Sep-04 3:22 
    AnswerRe: how can i make a combo box invisible at run time ? Pin
    Joseph M. Newcomer22-Sep-04 8:17
    Joseph M. Newcomer22-Sep-04 8:17 
    GeneralComboBox in ActiveXCtrl x fill with Items Pin
    TRZ17-Jul-03 19:46
    TRZ17-Jul-03 19:46 
    QuestionHow to get the items for ComboBox from Database (Access, MS SQL Server Pin
    Exceter27-Nov-02 21:24
    Exceter27-Nov-02 21:24 
    1. how to store the items of ComboBox from Database
    how to to it

    2. I have a problem with sending data from Combo to the Database
    There is no problem with Edit Boxes, the Text from this is sent to database
    but with Combo there is a problem
    I cant get the item from Combo and assign it to CString variable

    void CSDIView::OnRecordAddNew()
    {
    // TODO: Add your command handler code here
    if (dlg.DoModal() == IDOK)
    {
    m_pSet->AddNew();
    .
    .
    m_pSet->m_toDatabase = dlg.m_ComboItem; //ComboBox
    // (m_toDatabase is CString)
    m_pSet->m_Surname = dlg.m_AddSurname; //EditBox
    m_pSet->m_Name = dlg.m_AddName;
    m_pSet->m_Midname = dlg.m_AddMidname;
    m_pSet->m_Cafedra = dlg.m_Combo; //ComboBox
    .
    .
    }
    }

    Sleepy | :zzz:
    GeneralError in Select function Pin
    18-Sep-01 20:50
    suss18-Sep-01 20:50 
    Generalload Pin
    Igor Proskuriakov29-Sep-00 5:00
    Igor Proskuriakov29-Sep-00 5:00 

    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.