標籤:ack 包含 etc ++ png set 之間 ide 基於
由於課題的需求需要做MFC串口程式,看了百度下載的串口助手的介面風格,發現這個設計很好
傳輸速率的組合框只給出了5個可選數值,然後第6個選項是Custom,即手動輸入。
實際上DCB結構的BaudRate可選數值太多了,做成下拉框會很長很長,這種做法就是選用最常見的幾個選項,不需要使用者手動輸入,也不需要在很長的列表中去選擇。
從VS的屬性框中可以看到,下拉式方塊控制項有3種樣式,也就是實現的功能是點擊Custom選項時從Drop List切換到Dropdown。
從MSDN可以看到兩者對應的宏分別為CBS_DROPDOWNLIST和CBS_DROPDOWN。
參考:https://msdn.microsoft.com/en-us/library/12h9x0ch.aspx
所以我最初在想用CWnd::Modify()方法來修改樣式,但是失敗了,搜尋的原因時,下拉樣式只能在建立的時候確定,建立之後就無法更改了。
因此合理的做法是手動Create一個一模一樣的控制項,然後用CWnd::ShowWindow()方法來自動切換。
-----------------------------------------------------------------------------------分割線----------------------------------------------------------------------------------
下面給出從0開始詳盡的實現方法,建立一個基於MFC對話方塊程式(設對話方塊類為CComboTestDlg),手動拖一個ComboBox控制項上去。設定Type和ID
在類嚮導裡給該控制項添加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類型。
MFC的組合框(ComboBox)控制項切換下拉樣式