MFC的組合框(ComboBox)控件切換下拉樣式

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

相關文章
相關標籤/搜索