Windows编程与MFC # 3 基于对话框的应用程序(3)

Windows编程与MFC # 3 基于对话框的应用程序(3)_第1张图片

组合框ComboBox

组合框相当常见,例如,在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了。
Windows编程与MFC # 3 基于对话框的应用程序(3)_第2张图片
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,以取消排序显示。

Windows编程与MFC # 3 基于对话框的应用程序(3)_第3张图片
Windows编程与MFC # 3 基于对话框的应用程序(3)_第4张图片

  1. 单击组合框控件,然后鼠标移至三角形箭头上,鼠标变为上下箭头,然后左键单击,出现边框,调整边框高度。该高度就是程序运行时鼠标单击组合后下拉长度,如果过短,不能显示全部选项。

Windows编程与MFC # 3 基于对话框的应用程序(3)_第5张图片
5. 添加关联变量。用于存放列表框中的当前选项的索引值。
Windows编程与MFC # 3 基于对话框的应用程序(3)_第6张图片

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

Q&A

  1. Visual Studio 2010(VS2010)如何在解决方案中仅运行当前打开的项目?
    点击最上面的解决方案,选属性,通用配置里, 把启动项目改为当前启动项目。
    Ref: https://blog.csdn.net/viva_bupt/article/details/7460144
  2. LPCTSTR是什么意思
    L表示long指针 这是为了兼容Windows 3.1等16位操作系统遗留下来的,在win32中以及其他的32位操作系统中, long指针和near指针及far修饰符都是为了兼容的作用。没有实际意义。
    P表示这是一个指针
    C表示是一个常量
    T表示在Win32环境中, 有一个_T宏
    STR表示这个变量是一个字符串
    A 32-bit pointer to a constant character string that is portable for Unicode and DBCS.
    所以LPCTSTR就表示一个指向const对象的指针。
    CString 和 LPCTSTR 可以通用, 原因在于CString定义的自动类型转换(C++ operator 重载)。
    常量字符串ansi和unicode的区分是由宏_T来决定的。(LPCTSTR = const TCHAR *
    但是用_T(“abcd”)时, 字符串"abcd"就会在编译时根据是否是_UNICODE来决定是char* 还是 wchar_t*。 同样,TCHAR 也是相同目的字符宏。
    Ref: https://baike.baidu.com/item/LPCTSTR/6495202
  3. _T() 宏
    _T是一个宏,作用是让你的程序支持Unicode编码。Windows使用两种字符集ANSI和UNICODE,
    前者就是通常使用的单字节方式,但这种方式处理像中文这样的双字节字符不方便,容易出现半个汉字的情况。而后者是双字节方式,方便处理双字节字符。
    _T("")宏,定义于tchar.h下。
#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

  1. CString转int
    _ttoi()函数
  2. int转CString
    CString s;
    s.Format(_T("%d"),d)

作业中日期计算器的核心代码
Windows编程与MFC # 3 基于对话框的应用程序(3)_第7张图片


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(),请不要搞混淆。

Reference

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

你可能感兴趣的:(Windows编程)