组合框相当常见,例如,在Windows系统的控制面板上设置语言或位置时,有很多选项,用来进行选择的控件就是组合框控件。
组合框其实就是把一个编辑框和一个列表框组合到了一起,分为三种:简易(Simple)组合框、下拉式(Dropdown)组合框和下拉列表式(Drop List)组合框。
简易组合框中的列表框是一直显示的;
下拉式组合框默认不显示列表框,只有在点击了编辑框右侧的下拉箭头才会弹出列表框;
下拉列表式组合框的编辑框是不能编辑的,只能由用户在下拉列表框中选择了某项后,在编辑框中显示其文本。
最常用的当属下拉式组合框和下拉列表式组合框了,它们在很多时候能使程序看起来更专业,更简洁,让用户在进行选择操作时更方便。
CBN_CLOSEUP:组合框的列表框组件被关闭,简易组合框不会发送该通知消息。CBN_DBLCLK:用户在某列表项上双击鼠标,只有简易组合框才会发送该通知消息。
CBN_DROPDOWN:组合框的列表框组件下拉,简易式组合框不会发送该通知消息。CBN_EDITUPDATE:在编辑框准备显示改变了的正文时发送该消息,下拉列表式组合框不会发送该消息。
CBN_EDITCHANGE:编辑框的内容被用户改变了,与CBN_EDITUPDATE不同,该消息是在编辑框显示的正文被刷新后才发出的,下拉列表式组合框不会发送该消息。
CBN_ERRSPACE:组合框无法申请足够的内存来容纳列表项。
CBN_SELENDCANCEL:表明用户的选择应该取消,当用户在列表框中选择了一项,然后又在组合框控件外单击鼠标时就会导致该消息的发送。
CBN_SELENDOK:用户选择了一项,然后按了回车键或单击了下滚箭头,该消息表明用户确认了自己所作的选择。
CBN_KILLFOCUS:组合框失去了输入焦点。
CBN_SELCHANGE:用户通过单击或移动箭头键改变了列表的选择。
CBN_SETFOCUS:组合框获得了输入焦点。
MFC将组合框控件的所有操作都封装到了CComboBox类中。
在对话框中加入组合框时,可以往对话框模板中拖入Combo Box控件,而后添加CComboBox类型的控件变量使用,但如果想在程序中动态创建的话,就要使用CComboBox类的成员函数Create了。
CBS_AUTOHSCROLL:使编辑框组件具有水平滚动的风格
CBS_DISABLENOSCROLL:使列表框在不需要滚动时显示一个禁止的垂直滚动条CBS_DROPDOWN:指定一个下拉式组合框CBS_DROPDOWNLIST:指定一个下拉列表式组合框
CBS_HASSTRINGS:指定一个含有字符串的自绘式组合框
CBS_LOWERCASE:将编辑框和列表框中的所有文本都自动转换为小写字符
CBS_NOINTEGRALHEIGHT:组合框的尺寸由应用程序而不是Windows 指定,通常,由Windows指定尺寸会使列表项的某些部分隐藏起来
CBS_OEMCONVERT:使编辑框组件中的正文可以在ANSI 字符集和OEM字符集之间相互转换。这在编辑框中包含文件名时是很有用的
CBS_OWNERDRAWFIXED:指定自绘式组合框,即由父窗口负责绘制列表框的内容,并且列表项有相同的高度
CBS_OWNERDRAWVARIABLE:指定自绘式组合框,并且列表项有不同的高度
CBS_SIIMPLE:指定一个简易组合框CBS_SORT:自动对列表框组件中的项进行排序
CBS_UPPERCASE:将编辑框和列表框中的所有文本都自动转换为大写字符
int GetCount( ) const; 获取组合框控件的列表框中列表项的数量。
int GetCurSel( ) const; 获取组合框控件的列表框中选中项的索引,如果没有选中任何项,该函数返回CB_ERR。
int SetCurSel(int nSelect); 在组合框控件的列表框中选择某项。nSelect参数指定了要选择的列表项的索引,如果为-1则列表框中当前选择项被取消选中,编辑框也被清空。
int AddString(LPCTSTR lpszString); 为组合框控件中的列表框添加新的列表项。lpszString参数是指向要添加的字符串的指针。该函数的返回值如果大于等于0,那么它就是新列表项的索引,而如果有错误发生则会返回CB_ERR,如果没有足够的内存存放新字符串则返回CB_ERRSPACE。
int DeleteString(UINT nIndex); 删除组合框中某指定位置的列表项。nIndex参数指定了要删除的列表项的索引。该函数的返回值如果大于等于0,那么它就是组合框中剩余列表项的数量。如果nIndex指定的索引超出了列表项的数量则返回CB_ERR。
int InsertString(int nIndex,LPCTSTR lpszString); 向组合框控件的列表框中插入一个列表项。nIndex参数指定了要插入列表项的位置,lpszString参数则指定了要插入的字符串。该函数返回字符串被插入的位置,如果有错误发生则会返回CB_ERR,如果没有足够的内存存放新字符串则返回CB_ERRSPACE。
DWORD GetEditSel( ) const; 获取组合框控件的编辑框中当前选择范围的起始和终止字符的位置。该函数返回一个32位数,低16位存放起始位置,高16位存放选择范围后第一个非选择字符的位置。如果该函数用于下拉列表式组合框时,会返回CB_ERR。
BOOL SetEditSel(int nStartChar,int nEndChar); 用于在组合框控件的编辑框中选择字符。nStartChar参数指定起始位置,nEndChar参数指定终止位置。
DWORD_PTR GetItemData(int nIndex) const; 获取组合框中指定项所关联的32位数据。nIndex参数指定组合框控件的列表框某项的索引(从0开始)。
int SetItemData(int nIndex,DWORD_PTR dwItemData); 为某个指定的组合框列表项设置一个关联的32位数。nIndex参数指定要进行设置的列表项索引。dwItemData参数指定要关联的新值。
void GetLBText(int nIndex,CString& rString) const; 从组合框控件的列表框中获取某项的字符串。nIndex参数指定要获取字符串的列表项的索引,CString参数用于接收取到的字符串。
int GetLBTextLen(int nIndex) const; 获取组合框控件的列表框中某项的字符串长度。nIndex参数指定要获取字符串长度的列表项的索引。
int GetTopIndex( ) const; 获取组合框控件的列表框中第一个可见项的索引。
int SetTopIndex(int nIndex); 将组合框控件的列表框中某个指定项设置为可见的。nIndex参数指定了该列表项的索引。该函数成功则返回0,有错误发生则返回CB_ERR。
BOOL LimitText(int nMaxChars); 用于限制用户在组合框控件的编辑框中能够输入的最大字节长度。nMaxChars参数指定了用户能够输入文字的最大字节长度,如果为0则长度被限制为65535个字节。
改造上一节实现的加法计算器
具体步骤:
打开“加法计算器”的sln文件,打开主对话框。删除“+”号的静态文本控件。
添加组合框控件,修改ID为IDC_OPERATOR,调整其大小,并对齐各控件。
右键单击组合框,选择Properties命令,在Data选项卡中输入如图所示内容。Type属性设为Drop List,为下拉列表式组合框,编辑框不允许用户输入,Sort属性设为False,以取消排序显示。
UpdateData(TRUE);
if(m_nOperator == -1)//判断是否选择了运算符
{
AfxMessageBox(_T("请选择运算符"));
return;
}
switch(m_nOperator)
{
case 0://加法
m_dResult = m_dOperand1 + m_dOperand2;break;
case 1://减法
m_dResult = m_dOperand1 - m_dOperand2;break;
case 2://乘法
m_dResult = m_dOperand1 * m_dOperand2;break;
case 3://除法
if(fabs(m_dOperand2)>1.0e-10)//判断除数是否为0
m_dResult = m_dOperand1 / m_dOperand2;
else
AfxMessageBox(_T("除数不能为零!"));//给出错误提示
break;
}
UpdateData(FALSE);
使用组合框的关联变量
为组合框IDC_OPERATOR 添加CComboBox类型的控件变量m_Operator。
在对话框初始化时,将四个算术运算符号加入到组合框中,并默认选择第一项,那么需要修改CEx_AdditionDlg::OnInitDialog()函数为:
m_Operator.AddString(_T(“+"));
m_Operator.AddString(_T(“-"));
m_Operator.AddString(_T(“*"));
m_Operator.AddString(_T(“/"));
// 默认选择第一项
m_Operator.SetCurSel(0);
如果要置空(未选中)改成-1
批量添加选项(如日期)
for(int i=2000;i<=2020;i++){
CString str;
str.Format(_T("%d"),i);
this->m_combo_year.AddString(str);
}
for(int i=1;i<=12;i++){
CString str;
str.Format(_T("%d"),i);
this->m_combo_month.AddString(str);
}
for(int i=1;i<=31;i++){
CString str;
str.Format(_T("%d"),i);
this->m_combo_day.AddString(str);
}
LPCTSTR = const TCHAR *
)char*
还是 wchar_t*
。 同样,TCHAR 也是相同目的字符宏。#define __T(x) L ## x
#define _T(x) __T(x)
如果你编译一个程序为ANSI方式,_T实际不起任何作用。如果编译一个程序为UNICODE方式,则编译器会把"Hello"字符串以UNICODE方式保存。_T和_L的区别在于,_L不管你是以什么方式编译,一律以UNICODE方式保存。
LPSTR:32bit指针指向一个字符串,每个字符占1字节
LPCSTR: 32-bit指针指向一个常字符串,每个字符占1字节
LPCTSTR: 32-bit指针指向一个常字符串,每字符可能占1字节或2字节,取决于Unicode是否定义
LPTSTR: 32-bit指针指向一个字符串,每字符可能占1字节或2字节,取决于Unicode是否定义
L是表示字符串资源为Unicode的。
Ref: https://baike.baidu.com/item/_T/8040470
bool isLeepYear(int y){
return y%4==0 && y%100==0 || y%400==0;
}
const int MONTH[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
bool checkDateExists(int year,int month,int day){
if(day <= 0 || month <= 0 || month > 12) return false;
if ( month == 2 ) {
return isLeepYear(year)?day<=29:day<=28;
} else {
return month<=MONTH[month-1];
}
}
void CXXXXXXXDlg::OnClickedButtonCalc()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
CString year,month,day;
int nChoose = this->m_combo_year.GetCurSel();
this->m_combo_year.GetLBText(nChoose,year);
nChoose = this->m_combo_month.GetCurSel();
this->m_combo_month.GetLBText(nChoose,month);
nChoose = this->m_combo_day.GetCurSel();
this->m_combo_day.GetLBText(nChoose,day);
int y = _ttoi(year),m = _ttoi(month), d = _ttoi(day);
if(!checkDateExists(y,m,d)){
this->m_sResult = _T("设置的日期不存在");
} else {
int date = d;
for(int i=0;i<m-1;i++) date += MONTH[i];
if(isLeepYear(y)&&m>2)date++;
this->m_sResult.Format(_T("该天是一年中的第%d天"),date);
}
UpdateData(FALSE);
}
但我们希望优化一下:选择年月后,自动生成日期,这样可以让用户选择正确的日期
上面已经写过了批量添加的方法,所以这里还需ComboBox的清空方法
这个类有个方法叫clear()它的作用其实是把ComboBox的文本框清空并复制出来的。
真正的清空选项是ResetContent(),请不要搞混淆。
https://blog.csdn.net/mlj318/article/details/6719022/ MFC ComboBox 获取值
优化版日期计算
(但是未处理combobox输入事件,我把combobox都改成了droplist,如果有同学做了处理combobox输入事件的改进版也请把代码与我分享一下呀~就当阅读我辛苦整理并撰写的文章的回报了hhhh)
bool isLeepYear(int y){
return y%4==0 && y%100==0 || y%400==0;
}
const int MONTH[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
bool checkDateExists(int year,int month,int day){
if(day <= 0 || month <= 0 || month > 12) return false;
if ( month == 2 ) {
return isLeepYear(year)?day<=29:day<=28;
} else {
return month<=MONTH[month-1];
}
}
void CXXXXXXXXXDlg::OnClickedButtonCalc()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
CString year,month,day;
int nChoose = this->m_combo_year.GetCurSel();
if(nChoose == CB_ERR){
AfxMessageBox(_T("please choose year"));
return;
}
this->m_combo_year.GetLBText(nChoose,year);
nChoose = this->m_combo_month.GetCurSel();
this->m_combo_month.GetLBText(nChoose,month);
nChoose = this->m_combo_day.GetCurSel();
this->m_combo_day.GetLBText(nChoose,day);
int y = _ttoi(year),m = _ttoi(month), d = _ttoi(day);
if(!checkDateExists(y,m,d)){
this->m_sResult = _T("设置的日期不存在");
} else {
int date = d;
for(int i=0;i<m-1;i++) date += MONTH[i];
if(isLeepYear(y)&&m>2)date++;
this->m_sResult.Format(_T("该天是一年中的第%d天"),date);
}
UpdateData(FALSE);
}
void CXXXXXXXXXDlg::OnSelchangeComboMonth()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
CString text;
int y,m,d;
int nChoose = this->m_combo_month.GetCurSel();
this->m_combo_month.GetLBText(nChoose,text);
m = _ttoi(text);
d = MONTH[m-1];
nChoose = this->m_combo_year.GetCurSel();
if(nChoose == CB_ERR){
AfxMessageBox(_T("please choose year first"));
return;
}
if(m == 2){
this->m_combo_year.GetLBText(nChoose,text);
y = _ttoi(text);
if(isLeepYear(y))d++;
}
this->m_combo_day.ResetContent();
for(int i=1;i<=d;i++){
CString str;
str.Format(_T("%d"),i);
this->m_combo_day.AddString(str);
}
this->m_combo_day.SetCurSel(0);
UpdateData(FALSE);
}
void CXXXXXXXXXDlg::OnSelchangeComboYear()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
CString text;
int y,m,d;
int nChoose = this->m_combo_month.GetCurSel();
this->m_combo_month.GetLBText(nChoose,text);
m = _ttoi(text);
d = MONTH[m-1];
nChoose = this->m_combo_year.GetCurSel();
this->m_combo_year.GetLBText(nChoose,text);
y = _ttoi(text);
if(m == 2){
if(isLeepYear(y))d++;
this->m_combo_day.ResetContent();
for(int i=1;i<=d;i++){
CString str;
str.Format(_T("%d"),i);
this->m_combo_day.AddString(str);
}
this->m_combo_day.SetCurSel(0);
}
UpdateData(FALSE);
}