组合框初始化






4.60/5 (9投票s)
2000年5月17日

173979

1486
学习如何以编程方式初始化组合框。
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!
...
}
这与在资源中初始化的方法相比,有很多优点
- 它为组合框中的所有值提供了一个定义点。
- 只有一个地方需要更改才能添加或删除项目
- 它本质上不受排序问题的影响
- 它是与语言无关的。
如果字符串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
实现。
这些文章中表达的观点是作者的观点,不代表,也不被微软认可。