65.9K
CodeProject 正在变化。 阅读更多。
Home

组合框初始化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (9投票s)

2000年5月17日

viewsIcon

173979

downloadIcon

1486

学习如何以编程方式初始化组合框。

  • 下载源文件 - 3 Kb

    MFC 的一些比较无用的功能是在资源编辑器中向组合框添加项。你可能会问“为什么?”显然,它简化了生活。好吧,事实并非如此。事实上,它可能会使生活变得不可能。例如,这有用的唯一条件是字符串是与语言无关的文本,并且整个组合框完全不受排序与否的影响。我见过这样的情况,资源编辑器添加了诸如

    Black
    Blue
    Red
    Green
    

    之类的项,并且代码如下所示:

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

    你可以立即看出这不可能维护;组合框资源的更改必须反映在某些未知且不可知的代码中。另一个解决方案是

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

    这仅仅是在一个坏主意上的语法阿斯巴甜(甚至不是语法糖);它一点也没有改变问题。另一个解决方案是执行以下操作:

    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);
    }
    

    等等。这至少具有您不依赖位置的优点;但是您依赖语言。考虑一个可以编辑资源文件并更改字符串的欧洲分销商

    Schwartz
    Blau
    Rot
    Grün
    

    代码失败,原因相同。如果组合框已排序,则顺序完全错误;如果使用字符串比较,则永远不会匹配颜色。使用组合框的应用程序应该完全独立于排序顺序和语言。相信我。我经历过,做过。你只会后悔。

    本质上,你绝不能编写一个组合框或列表框,其中对特定偏移量的含义有任何假设。从GetCurSel返回的整数除了检索控件的字符串数据或ItemData 之外,基本上毫无意义。它们没有任何其他意义,赋予它们其他意义是不良的编程实践。

    我有一个名为“CIDCombo”的类,我用于所有此类情况。这是在我第二次使用预加载组合框时发明的(注意:仅仅因为某些东西可用,并不意味着使用它是好主意!)CIDCombo所做的是允许我指定一对,字符串表中的字符串 ID 和相关的映射值,在一个表中。该表基本上是

    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
    };<
    

    核心循环基本上是

    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);
        }
    }

    因此,在我的OnInitDialog中发生的是

    BOOL CMyDialog::OnInitDialog()
    {
        ...
        c_Colors.load(colors); // Note: no GetDlgItem, ever!
        ...
    }
    

    这与在资源中初始化的方法相比,有很多优点

    1. 它为组合框中的所有值提供了一个定义点。
    2. 只有一个地方需要更改才能添加或删除项目
    3. 它本质上不受排序问题的影响
    4. 它是与语言无关的。

    如果字符串IDS_BLACK更改为“Noir”或“Schwartz”或其他内容,颜色值始终为RGB(0,0,0)。如果组合框已排序或未排序,则无关紧要;颜色名称始终与其颜色值正确匹配。或控制流设置。或数据位值。或任何东西。本质上,可以从资源初始化的组合框最好使用此方法。我从未发现例外。

    该类可在随附我们书籍的 CD-ROM 上找到(Win32 编程,Rector & Newcomer,Addison-Wesley,1997),并且可以从此网站免费下载其实例

    CIDCombo 的另一个很酷的功能是它会自动调整下拉列表的大小,以便如果可能的话,所有项目始终都会显示,而无需滚动条。不再需要“手动”调整下拉大小,以期一切都能容纳!你将始终看到所有内容,无滚动条,除非整个选择无法容纳在窗口中。下拉高度会动态调整以尽可能容纳更多项目,具体取决于组合框在屏幕上的位置(如果需要更多空间并且组合框位于屏幕下方,它将弹出到组合框上方)。

    真正好的地方在于,每当您需要实际值时,都可以简单地使用GetItemData来获取该值。

    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);
    }
    

    如何处理更复杂的信息?一种方法是为信息组定义一个struct,例如,一个有点愚蠢的例子是一个下拉列表,它描述了豆类类型及其包装商。(之所以很愚蠢,是因为这实际上是从数据库中完成的,但目的是创建一个简单的示例)

    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
    };
    

    要使用数据,您需要执行以下操作

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

    如何选择项目?好吧,你需要FindStringExact 的道德等价物。在这种情况下,选择基于 ItemData 比较。例如

    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;
        }
    }
    

    您可以通过单击文章顶部的链接来获取我的CIDCombo实现。


    这些文章中表达的观点是作者的观点,不代表,也不被微软认可。

    发送邮件至newcomer@flounder.com提出关于本文的问题或评论。
    版权所有 © 1999CompanyLongName保留所有权利。
    www.flounder.com/mvp_tips.htm
  • © . All rights reserved.