因爲課題的需求須要作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類型。