通過編寫串口助手工具學習MFC過程
因為以前也做過幾次MFC的編程,每次都是項目完成時,MFC基本操作清楚了,但是過好長時間不再接觸MFC的項目,再次做MFC的項目時,又要從頭開始熟悉。這次通過做一個串口助手再次熟悉一下MFC,並做了一下記錄,以便方便以后查閱。做的過程中多是遇到問題直接百度和谷歌搜索來的,所以很多都是不求甚解,知其然不知其所以然。另外做此工具只是為了熟悉了解,許多功能還沒有完善!(開發工具VS2008)
(四)添加ComboBox組合框
ComboBox組合框有編輯框和下拉列表框的復合功能。通過該控件的Type屬性可以進行相關設置。ComboBOX控件Type屬性 :
Dropdown(默認)組合框,可下拉可編輯
Drop List 下拉列表框,只能下拉不能編輯
Drop Simple 編輯框,只能編輯不能下拉。
屬性:數據可設置下拉列表顯示的內容,數據間隔用英文的分號”;”。下拉列表顯示的內容也可以在程序中設置,如果這個下拉列表的內容可能會編輯更改,這里就不要設置,直接在程序在添加就可。
上圖中屬性Sort也很重要,將他設置成False就不會根據下拉列表的內容自動排列了,下拉列表顯示的順序就是自己想要的定義成的那樣。
添加控件、修改屬性ID和增加變量部分略,參見按按鈕部分。
1、ComboBox的基本功能:
//校驗位 無;奇校驗;偶校驗;
char verifiBuf[][7]={"無","奇校驗","偶校驗"};
for(int i=0;i<3;i++)
{
m_ComboCheckBit.AddString(A2T(verifiBuf[i]));
}
m_ComboCheckBit.SetCurSel(0);
上面幾行代碼出現在初始對話框時,默認顯示的。
m_ComboCheckBit.AddString(A2T(verifiBuf[i]));
在下拉列表增加字符串,不管原來是什么,直接增加到后面(Sort設置為False)。回值大於等於0時,是列表中項的下標。出錯時,返回CB_ERROR,沒有足夠的空間存放新的字符串時返回CB_ERRSPACE。
A2T()將字符串轉為寬字符WCHAR *。char verifiBuf[][7]定義成了二維數組來存儲字符串,當然也可以定義成字符串數組來存儲字符串。
m_ComboCheckBit.SetCurSel(0); 設置當前選項為第0項,如不設置,此組合框最初什么數值都沒有。本函數在組合框的列表框中選擇一個字符串,調用成功時返回選中的項的下標。如果nSelect大於列表中項的個數,則返回CB_ERR。如果nSelect為-1,則清除當前的選擇並返回CB_ERR。
m_ComboCheckBit.GetCurSel();本函數用於取得組合框中當前選中的項的下標。返回組合框中列表框中當前選中的項的下標。如果沒有選中項,則返回CB_ERR。
下面幾行代碼從組合框中獲取字符串
//波特率
int baudIndex = pstComCWnds->pComboBaudActual->GetCurSel(); //波特率序號
pstComCWnds->pComboBaudActual->GetLBText(baudIndex,m_BaudStr); //從組合框的列表框中取得一個字符串(下標,獲取字符串存入的cstring類)返回字符串長度
BaudRate = atoi(T2A((LPCTSTR)m_BaudStr)); //獲得波特率
這個函數做為獲得波特率在編輯自定義的波特率時要判斷輸入是否是數字,才能這么使用,否則不安全。
這個從組合框獲取字符串的函數還有另一個重載。
int GetLBText( int nIndex, LPTSTR lpszText ) const;
返回字符串的字節數,不包括終結符null。如果nIndex指定的值無效,則返回CB_ERR。
(2)實現特殊功能:下拉列表自動調整寬度
串口名字是通過枚舉獲得的,名字較長,點下拉框時,名字不能完全顯示。所以就讓下拉框下拉時寬度變寬,從而能顯示出完整的名字
當點擊ComboBOX的三角使下拉列表展開時,下拉部分自動根據文字長度設置寬度,不改變原大小。
//ComboSelseri 選擇串口組合框下拉動作響應將根據下拉選項最大的長度設定下拉框寬度
//控件添加 CbnDropdown 響應事件 下拉框下拉消息
void CserialtestDlg::OnCbnDropdownComboSelseri()
{
// TODO: 在此添加控件通知處理程序代碼
int i = 0;
int originalLen = m_ComboSelSeri.GetDroppedWidth(); //獲取原下拉框的寬度(像素)
int lenBuf = originalLen;
int lenTmp = 0;
//聲明標識符
USES_CONVERSION; //使用A2T()或T2A()寬字節和多字節轉換時使用的
//點開下拉框時,默認選擇原來的那個串口
char m_SeriouStr[16] = {0};
CDeviceInfo * pCDeviceInfo = &m_DeviceInfo; //串口設備名字
int m_SeriouCurSel = m_ComboSelSeri.GetCurSel();; //串口選擇按鈕,當前選擇的序號
memcpy(m_SeriouStr, pCDeviceInfo->stDeviceInfo[m_SeriouCurSel].sDeviceName, sizeof(m_SeriouStr));//串口名字
m_ComboSelSeri.ResetContent(); //下拉列表清除列表項
for(i = 0; i<m_ComboSelSeri.GetCount();i++) //獲取下拉列表項數
{
//獲取下拉列表每項顯示文字的字節數 並轉為像素寬度。因為設置下拉列表的寬度單位是像素。此轉換方式是用笨法測試出來的。
lenTmp = (m_ComboSelSeri.GetLBTextLen(i)+1)*CHAR_PIXEL_NUM;
if(lenTmp > lenBuf)
{
lenBuf = lenTmp;
}
}
if(lenBuf > originalLen)
m_ComboSelSeri.SetDroppedWidth(lenBuf); //設置下拉列表的寬度(像素)
}
#define CHAR_PIXEL_NUM 6 //每個字符多少像素 人為根據現象設定
相關函數說明:
CComboBox::GetDroppedWidth
int GetDroppedWidth( ) const;
返回值:
調用成功時,返回允許的最小寬度,否則返回CB_ERR。
說明:
本函數用於取得組合框中的列表框所允許的最小寬度(以像素為單位)。它只對風格為CBS_DROPDOWN或CBS_DROPDOWNLIST的組合框有效。
CComboBox::SetDroppedWidth
int SetDroppedWidth( UINT nWidrh );
返回值:
調用成功時,返回列表框的新寬度。否則返回CB_ERR。
參數:nWidth 組合框的列表框所允許的最小寬度(以像素為單位)。
說明:
本函數用於設置組合框中列表框所允許的最小寬度。只對風格為CBS_DROPD-OWN或CBS_DROPDOWNLIST的組合框有效。
顯示效果如下:
(3)實現特殊功能:只能下拉和只能編輯切換屬性
組合框只能下拉或只能做編輯功能好辦,只需要修改控件的type屬性為Drop List 或Drop Simple就能實現。但如果需要在程序運行過程中動態切換這兩種屬性如何做呢?
方法一:通過一個復選框實現切換功能
//復選框單擊事件
void CserialtestDlg::OnBnClickedCheck1()
{
// TODO: 在此添加控件通知處理程序代碼
long param ;
if(check1.GetCheck()) //獲取復選框1(check1.)是否選中
{
//如果復選框1(check1.)被選中
check2.EnableWindow(FALSE); //另一個實驗中 復選框禁止
//修改屬性,CBS_DROPDOWN 原屬性,CBS_DROPDOWNLIST新的屬性
if(TRUE == m_comboBox1.ModifyStyle(CBS_DROPDOWN,CBS_DROPDOWNLIST))
{
printf("1111\n");
}
else
{
printf("2222\n");
}
//m_comboBox1.DestroyWindow();
RecreateComboBox(&m_comboBox1,¶m); //修改屬性實現,執行此功能才能修改控件屬性。
}//m_LcConfig是你的ListCtrl控件變量
else
{
check2.EnableWindow(TRUE);
if(TRUE == m_comboBox1.ModifyStyle(CBS_DROPDOWNLIST,CBS_DROPDOWN))
{
printf("1111\n");
}
else
{
printf("2222\n");
}
//m_comboBox1.DestroyWindow();
RecreateComboBox(&m_comboBox1,¶m);
}
}
復選框的功能將在下一節介紹,此函數中有printf()調試時打印語句,在VC的控制台程序中會打印輸出,但MFC中默認不會打印輸出,以后會介紹如何在MFC中添另控制台用於打印輸出。
//RecreateComboBox函數引自://(http://www.codeproject.com/KB/combobox/recreatecombobox.aspx)
// recreate the combo box by copying styles etc, and list items
// and applying them to a newly created control
BOOL RecreateComboBox(CComboBox* pCombo, LPVOID lpParam/*=0*/)
{
if (pCombo == NULL)
return FALSE;
if (pCombo->GetSafeHwnd() == NULL)
return FALSE;
CWnd* pParent = pCombo->GetParent();
if (pParent == NULL)
return FALSE;
// get current attributes
DWORD dwStyle = pCombo->GetStyle();
DWORD dwStyleEx = pCombo->GetExStyle();
CRect rc;
pCombo->GetDroppedControlRect(&rc);
pParent->ScreenToClient(&rc); // map to client co-ords
UINT nID = pCombo->GetDlgCtrlID();
CFont* pFont = pCombo->GetFont();
CWnd* pWndAfter = pCombo->GetNextWindow(GW_HWNDPREV);
// get the currently selected text (and whether it is a valid list selection)
CString sCurText;
int nCurSel = pCombo->GetCurSel();
BOOL bItemSelValid = nCurSel != -1;
if (bItemSelValid)
pCombo->GetLBText(nCurSel, sCurText);
else
pCombo->GetWindowText(sCurText);
// copy the combo box items into a temp combobox (not sorted)
// along with each item's data
CComboBox comboNew;
if (! comboNew.CreateEx(dwStyleEx, _T("COMBOBOX"), _T(""),
dwStyle, rc, pParent, nID, lpParam))
return FALSE;
comboNew.SetFont(pFont);
int nNumItems = pCombo->GetCount();
for (int n = 0; n < nNumItems; n++)
{
CString sText;
pCombo->GetLBText(n, sText);
int nNewIndex = comboNew.AddString(sText);
comboNew.SetItemData(nNewIndex, pCombo->GetItemData(n));
}
// re-set selected text
if (bItemSelValid)
comboNew.SetCurSel(comboNew.FindStringExact(-1, sCurText));
else
comboNew.SetWindowText(sCurText);
// destroy the existing window, then attach the new one
pCombo->DestroyWindow();
HWND hwnd = comboNew.Detach();
pCombo->Attach(hwnd);
// position correctly in z-order
pCombo->SetWindowPos(pWndAfter == NULL ?
&CWnd::wndBottom :
pWndAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
return TRUE;
}
方法二:通過兩個組合列表框交替使用來實現
本例中使用了下面的方法,此方法是自己原創(也許不是,只是自己按方法一實現后想到的另一種實現方法而已)
通過兩個組合列表框交替使用來實現, 並將使用的賦給程序正常用時的指針變量。pComboBaudActual為程序中使用的指針變量,他保存兩個組合列表框中的正在使用的那個地址。
並將正在使用的組合列表框顯示,將不在使用的隱藏(這兩個組合框位置上重合,程序執行起來就像是同一個組合框一樣)。
把當前組合框選中的項,設定為新組合框的選項。通過pComboBaudActual指針實現。
//兩個組合框添加 CBN_SELCHANGE事件響應, 控件中的選定內容已更改事件。
//ComboBaud 波特率組合框選中切換某一項動作
void CserialtestDlg::OnCbnSelchangeComboBaud()
{
// TODO: 在此添加控件通知處理程序代碼
int i =0;
if((i = pComboBaudActual->GetCurSel()) == 0) //當列表框選擇第0項時
{
//切換組合框
pComboBaudActual ->ShowWindow(SW_HIDE); //原來使用的隱藏
pComboBaudActual = &m_ComboBaudEdit; //將另一個要使用賦值
pComboBaudActual ->SetCurSel(i); //並將選擇項賦給新列表
pComboBaudActual->ShowWindow(SW_SHOW); //將新列表框顯示
}
}
//ComboBaudedit 波特率組合框選中切換某一項動作
//這是另一個組合框選擇程序,功能與上個函數相同
void CserialtestDlg::OnCbnSelchangeComboBaudedit()
{
// TODO: 在此添加控件通知處理程序代碼
int i =0 ;
if((i = pComboBaudActual->GetCurSel()) != 0)
{
//切換組合框
pComboBaudActual ->ShowWindow(SW_HIDE);
pComboBaudActual = &m_ComboBaud;
pComboBaudActual ->SetCurSel(i);
pComboBaudActual->ShowWindow(SW_SHOW);
}
}
pComboBaudActual->ShowWindow(SW_SHOW); //將列表框顯示
pComboBaudActual ->ShowWindow(SW_HIDE); //將列表框隱藏