因为课题的需求须要作MFC串口程序,看了百度下载的串口助手的界面风格,发现这个设计很好java
波特率的组合框只给出了5个可选数值,而后第6个选项是Custom,即手动输入。python
实际上DCB结构的BaudRate可选数值太多了,作成下拉框会很长很长,这种作法就是选用最多见的几个选项,不须要用户手动输入,也不须要在很长的列表中去选择。数组
从VS的属性框中能够看到,组合框控件有3种样式,也就是实现的功能是点击Custom选项时从Drop List切换到Dropdown。框架
从MSDN能够看到二者对应的宏分别为CBS_DROPDOWNLIST和CBS_DROPDOWN。函数
参考:https://msdn.microsoft.com/en-us/library/12h9x0ch.aspx字体
因此我最初在想用CWnd::Modify()方法来修改样式,可是失败了,搜寻的缘由时,下拉样式只能在建立的时候肯定,建立以后就没法更改了。this
所以合理的作法是手动Create一个如出一辙的控件,而后用CWnd::ShowWindow()方法来自动切换。spa
-----------------------------------------------------------------------------------分割线----------------------------------------------------------------------------------设计
下面给出从0开始详尽的实现方法,新建一个基于MFC对话框程序(设对话框类为CComboTestDlg),手动拖一个ComboBox控件上去。设置Type和ID3d
在类向导里给该控件添加CComboBox类型的关联变量,或者像下面同样手动添加:
1. 在ComboTestDlg.h中,CXXXDlg类中定义public变量
CComboBox m_cmbOld;
2. 在ComboTestDlg.cpp中,CXXXDlg::DoDataExchange()方法中添加一行关联语句
DDX_Control(pDX, IDC_COMBO_OLD, m_cmbOld);
如今在CComboTestDlg类中定义public变量m_cmbNew,可是不要修改DoDataExchange()方法,由于等下要手动建立下拉框
CComboBox m_cmbNew;
修改CComboTestDlg::OnInitDialog()方法,添加下述代码
// 获取原来的下拉框的位置 CRect rect; m_cmbOld.GetWindowRect(&rect); this->ScreenToClient(&rect); // 获取原来的下拉框的字体 CFont* pFont = m_cmbOld.GetFont(); // 获取原来的下拉框的样式(把Drop list改为Dropdown) DWORD dwStyle = m_cmbOld.GetStyle(); dwStyle ^= CBS_DROPDOWNLIST; dwStyle |= CBS_DROPDOWN; // 建立如出一辙的新下拉框 m_cmbNew.Create(dwStyle, rect, this, IDC_COMBO_NEW); // 设置相同的字体 m_cmbNew.SetFont(pFont); // 默认隐藏新下拉框 m_cmbNew.ShowWindow(SW_HIDE);
上述代码里有两点要注意(也就是我踩过的坑……)
1. 坐标转换ScreenToClient,由于GetWindowRect取得的是相对整个父控件(包含标题栏)的位置,而Create须要的是相对客户区(不包括标题栏)的位置,因此须要转换;
2. GetFont和SetFont设置字体,没有这一步的话,Create建立的下拉框的字体可能会和自动建立的下拉框字体不同。
PS:宏IDC_COMBO_NEW须要手动在resource.h中添加,注意不要和其余的宏相同,以避免冲突。
到此为止就只须要实现切换功能了,假设个人下拉框包含4项:cpp, java, python, custom,点击custom则切换到手动输入模式。
在ComboTestDlg.cpp中添加全局变量(列表初始化是C++11的语法)以便以后直接经过下标访问
#include <vector> std::vector<CString> g_strText = { _T("cpp"), _T("java"), _T("python"), _T("custom") };
在CComboTestDlg::OnInitDialog()中刚才添加的代码后面继续添加初始化代码
for (size_t i = 0; i < g_strText.size(); i++) { // C++11中能够用<type_traits>的std::extent<decltype(text)>::value取得text数组大小 m_cmbOld.AddString(g_strText[i]); m_cmbNew.AddString(g_strText[i]); } m_cmbOld.SetCurSel(0); // 默认选择第一项("cpp")
而后分别给2个控件添加响应事件,对于默认控件m_cmbOld,能够用类向导添加相应方法
实际上作了这几件事:
1. 在ComboTestDlg.h中,在CComboTestDlg类中添加成员函数的声明
afx_msg void OnCbnSelchangeComboOld();
2. 在ComboTestDlg.cpp中,在消息映射宏BEGIN_MESSAGE_MAP(CComboTestDlg, CDialog)和END_MESSAGE_MAP()之间添加
ON_CBN_SELCHANGE(IDC_COMBO_OLD, &CComboTestDlg::OnCbnSelchangeComboOld)
3. 在CComboTestDlg.cpp中,添加成员函数的具体定义
void CComboTestDlg::OnCbnSelchangeComboOld() { // TODO: 在此添加控件通知处理程序代码 }
知道了这几点后,照葫芦画瓢,在上述3步一样的位置添加相应的代码,若是选择已有项,则会显示原来的下拉框。
afx_msg void OnCbnSelChangeComboNew();
ON_CBN_SELCHANGE(IDC_COMBO_NEW, &CComboTestDlg::OnCbnSelChangeComboNew)
void CComboTestDlg::OnCbnSelChangeComboNew() { }
而输入自定义数据时,则须要响应回车消息,用类向导(以下图,点击添加函数)给CComboTestDlg重载虚函数PreTranslateMessage()
这一步实际作了这几件事:
1. 在ComboTestDlg.h中,在CComboTestDlg类中添加虚函数的声明
virtual BOOL PreTranslateMessage(MSG* pMsg);
2. 在ComboTestDlg.cpp中,添加虚函数的定义
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 return CDialog::PreTranslateMessage(pMsg); }
至此,框架已经搭好,如今只须要在添加的几个函数中中添加具体切换逻辑
void CComboTestDlg::OnCbnSelchangeComboOld() { CString text; m_cmbOld.GetWindowText(text); if (text == _T("custom")) { // 切换到手动编辑下拉框 m_cmbOld.ShowWindow(SW_HIDE); m_cmbNew.ShowWindow(SW_SHOW); m_cmbNew.SetFocus(); } else { MessageBox(text); } }
void CComboTestDlg::OnCbnSelChangeComboNew() { CString text; m_cmbNew.GetWindowText(text); if (text == _T("custom")) { // 从新输入 m_cmbNew.SetWindowText(_T("")); } else { // 切换到原来的下拉框 m_cmbNew.ShowWindow(SW_HIDE); m_cmbOld.ShowWindow(SW_SHOW); m_cmbOld.SetCurSel(m_cmbOld.FindString(0, text)); MessageBox(text); } }
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->message == WM_KEYDOWN) { if (pMsg->wParam == VK_RETURN) // 回车键 { if (m_cmbNew.GetFocus()) { // 添加控件信息到列表上 CString text; m_cmbNew.GetWindowText(text); int nSelect = m_cmbNew.FindString(0, text); if (nSelect == -1) { // 若列表项中不存在则添加控件信息到下拉框中 g_strText.push_back(text); m_cmbOld.AddString(text); m_cmbNew.AddString(text); } else { // 若已存在则显示原来的下拉框并定位到该列表项下 m_cmbNew.ShowWindow(SW_HIDE); m_cmbOld.ShowWindow(SW_SHOW); m_cmbOld.SetCurSel(nSelect); } MessageBox(text); } } } return CDialog::PreTranslateMessage(pMsg); }
至此功能完成,能够根据实际需求把一些代码进行封装,毕竟MFC对API封装得都很浅。
功能具体描述以下
1. 初始对话框,显示的是Drop List类型的组合框。
2. 选中custom之外的列表项时,会弹出窗口显示该列表项的文本。
3. 选中custom时,会进入编辑模式,下拉框变成Dropdown类型。
4. 编辑完后按回车,若是文本已经在列表项中,则会弹出窗口显示该文本而后组合框变回Drop List类型,不然把输入文本添加到列表项末尾。
5. 组合框是Dropdown类型时,若选择了custom以外的列表项,组合框会便会Drop List类型。