今天想做一个可以显示图标的CComboBox,一查资料发现MFC本来就有这个东西,叫CComboBoxEx,感觉捡到宝了。
于是按照网上教程:
在对话框的初始化函数OnInitDialog()
1.创建图像列表:
static CImageList img;
img.Create(16, 16, ILC_COLOR32, 0, 4);
img.Add(AfxGetApp()->LoadIcon(IDR_MAINFRAME));
img.Add(AfxGetApp()->LoadIcon(IDI_ICON1)); //资源视图里导入的6个图标
img.Add(AfxGetApp()->LoadIcon(IDI_ICON2));
img.Add(AfxGetApp()->LoadIcon(IDI_ICON3));
img.Add(AfxGetApp()->LoadIcon(IDI_ICON4));
img.Add(AfxGetApp()->LoadIcon(IDI_ICON5));
img.Add(AfxGetApp()->LoadIcon(IDI_ICON6));
2.将图像列表关联进Combo控件
cbe.SetImageList(&img); //cbe就是CComboBoxEx
3.插入列表项
COMBOBOXEXITEM cbi = { 0 };
cbi.mask = CBEIF_IMAGE | CBEIF_OVERLAY | CBEIF_SELECTEDIMAGE | CBEIF_TEXT;
cbi.iItem = -1; //-1表示总是插入到列表的最后
for (int i = 0; i < 7; i++)
{
TCHAR buf[100];
_stprintf_s(buf, 100, _T("ComboBoxItem_%d"), i);
cbi.pszText = buf;
//不是很明确下面这3个图标的含义,但3个图标设成统一值最好,否则显示鼠标移过时总是很乱
cbi.iImage = i; //普通状态图标
cbi.iSelectedImage = i; //选中图标
cbi.iOverlay = i; //可能是鼠标移过时显示的图标
cbe.InsertItem(&cbi);
}
结果一测试,发现不仅没图标,甚至根本就没有将列表项插入到组合框。反复测试不知道为什么,网上搜资料也没有答案。后来偶然发现在VC工具箱里有个控件叫:“Extended Combo Box”,它不和Combo Box排一起,在工具箱比较靠后的位置。将它拖到对话框,绑定变量,测试,成功!
另外测试发现img.Add(hico)立即::DestroyIcon(hico)并不会影响控件显示图标,可能是图像已经被复制到CImageList内部了。这点对于那种通过动态载入外部图标的方式非常好。
Extended Combo Box
继续测试:
发现CComboBoxEx有个很不方便的地方:它不可以让下拉列表随插入的表项增加而扩张,而之前的CComboBox有这个功能是因为在属性编辑里有一个“无整数高度”(VS2019是这样一个难以理解的属性名,vs属性窗口“文本对齐”的下边),将这个属性的值设置为false就可以让下拉列表随插入增长。可最大的问题是CComboBoxEx没有这个。找来找去没发现哪个函数可以控制这个MoveWindow()修改大小不行,SetWindowLong()修改控件样式也不行,看来只能是在编辑对话框时将它拉大一点。
SetWindowLong()测试:
发现CComboBox与CComboBoxEx在窗口组成上有区别,前者是2个窗口组合的,而后者有3个窗口分3个层次,如下图:
调用SetWindowLong()时要针对类名为ComboBox那个窗口而不是类名为ComboBoxEx32那个,可以使用GetWindow(hCtrl, GW_CHILD)获得这个窗口句柄,但是SetWindowLong去掉“无整数高度”这个属性(对应值:CBS_NOINTEGRALHEIGHT=0x400),除了让控件显示不正常没啥卵用。
继续测试MoveWindow修改列表大小:
同样必须针对中间层这个窗口句柄MoveWindow()。乍一看完美解决,但后来发现选定后的项目不刷新图标了。如下列图:
控件原本的样子:
MoveWindow()过后如下(注意看选中的文本框前面的图标是错误的,是之前选的另一个的图标):
遮挡一下窗口甚至变成下面的样子:
这应该是窗口不刷新这个区域导致的,此时将鼠标点一下别的控件或者TAB键将焦点移走就会显示正确的图标。
至此不继续测试,感觉也测不出什么。初次接触CComboBoxEx类,只搞这么多了。