通過編寫串口助手工具學習MFC過程
因為以前也做過幾次MFC的編程,每次都是項目完成時,MFC基本操作清楚了,但是過好長時間不再接觸MFC的項目,再次做MFC的項目時,又要從頭開始熟悉。這次通過做一個串口助手再次熟悉一下MFC,並做了一下記錄,以便方便以后查閱。做的過程中多是遇到問題直接百度和谷歌搜索來的,所以很多都是不求甚解,知其然不知其所以然。另外做此工具只是為了熟悉了解,許多功能還沒有完善!(開發工具VS2008)
(二)通過“打開串口”按鈕了解基本操作
需要什么控件直接從這個對話框編輯器直接添加即可。
1、添加一個按鈕用於打開串口
2、設置按鈕控件的屬性
調出屬性對話框,設置ID
Caption是靜態選項,程序中可以動態設置按鈕顯示的文字,這里修改名字的好處是,方便編寫代碼是查看這個按鈕是什么。
設置這個控件的ID名稱,這樣MFC就會對這個名稱起一個對應的數字真正的ID,可以在工程中Resource.h 查看。
工程中,右鍵.rc文件->選擇”查看代碼”,下圖是部分截圖,就會看對按鈕的尺寸和位置和顯示內容、ID名字。
3、添加按鈕變量
再將這個按鈕向對話框對應的類添加變量,在按鈕上右鍵->選擇添加變量->在變量名的文本框里填入變量名。如下圖:
添加后工程的變化,在對話框的頭文件“serial testDlg.h : 頭文件”,CserialtestDlg類中增加對象成員變量(頭文件名和類名是建立工程時起的)
“serial testDlg.cpp : 實現文件”中
像注釋說明的那樣,按鈕變量m_ComBoComContrl和控件IDC_BUTTON_OPENCOM關聯起來了,以后對此變量操作也就是對該按鈕控件進行操作。
另外一種操作按鈕的方法是CWnd* GetDlgItem(ID)函數,返回窗口中指定參數ID的子元素的句柄,可以通過返回的句柄對相應的控件操作。並且這個操作可以在任何地方使用,按鈕變量只能在對話框類中使用同,相應比獲取句柄方式有局限性,也不夠方便。(個人理解。)
CWnd* GetDlgItem(ID)的應用后面有涉及到。
4、添加按鈕控件響應事件
方法一:在對話框里,雙擊按鈕。
直接跳轉到“serial testDlg.cpp : 實現文件”自動生成如下函數
//串口打開關閉按鈕
void CserialtestDlg::OnBnClickedButtonOpencom()
{
}
大部分的控件通過雙擊都會生成相應的響應事件函數。
方法二:右擊控件按鈕->“添加事件處理函數”
可以修改消息類型,不同的控件里的消息類型不同,此處選擇點擊消息,BN_CLICKED;
函數處理程序名稱設置好,類列表中可以選擇生成在哪個類中,按默認的生成,即在對話框類中生成。
在“serial testDlg.cpp : 實現文件”生成的 消息增加此消息,包括消息類型、控件ID、響應事件的函數指針。
通過以上信息,如果不使用向導增加變量和消息事件,也可以自己添加。
//串口打開關閉按鈕
/*
這個函數主要實現功能是打開串口時,如果沒有創建串口,先創建串口,根據串口原來的狀態,實現串口打開或關閉。同時設置串口按鈕的文字內容“打開或關閉串口”,設置串口顯示的圖片。
將串口參數設置的幾個ComboBox組合框實現使能和失能的控制*/
void CserialtestDlg::OnBnClickedButtonOpencom()
{
// TODO: 在此添加控件通知處理程序代碼
//組織用於CComSerial類指針實例化時的數據,將對話框類的控件變量的地址獲取給CComSerial
// 類,CComSerial類從這些控件獲取相應數據,對串口參數進行設置
struct StruComCWnds stComCWnds;
stComCWnds.pComboBaudActual = this->pComboBaudActual;
stComCWnds.pComboSelSeri = &this->m_ComboSelSeri;
stComCWnds.pComboCheckBit = &this->m_ComboCheckBit;
stComCWnds.pComboDataBit = &this->m_ComboDataBit;
stComCWnds.pComboStopBit = &this->m_ComboStopBit;
if(NULL == pCComSer) //第一次點按鈕時pCComSer為Null
{
pCComSer = new CComSerial(&stComCWnds);
if(NULL == pCComSer)
{
return;
}
}
//判斷點擊前串口是什么狀態,原來連接狀態,現在關閉;原來關閉,現在打開
if(pCComSer->GetConStatus())
{
//原來連接,現在要關閉
pCComSer->CloseComm(); //串口關閉操作
((CButton *)GetDlgItem(IDC_BUTTON_OPENCOM))->SetBitmap(hBitmapComOFF); //設置按鈕圖片
SetDlgItemText(IDC_BUTTON_OPENCOM, _T("打開串口")); //設置按鈕顯示文字內容
//串口參數設置的組合框使能,可以進行設置
m_ComboSelSeri.EnableWindow(TRUE);
m_ComboBaud.EnableWindow(TRUE);
m_ComboCheckBit.EnableWindow(TRUE);
m_ComboStopBit.EnableWindow(TRUE);
m_ComboDataBit.EnableWindow(TRUE);
m_ComboBaudEdit.EnableWindow(TRUE);
}
else
{
//原來未連接現在要連接
if(pCComSer->OpenComm()) //串口打開操作
{
//打開串口成功,設置按鈕圖片和顯示文字內容
((CButton *)GetDlgItem(IDC_BUTTON_OPENCOM))->SetBitmap(hBitmapComON);
SetDlgItemText(IDC_BUTTON_OPENCOM, _T("關閉串口"));
//串口參數設置的組合框失能,打開串口后不可以再進行設置
m_ComboSelSeri.EnableWindow(FALSE);
m_ComboBaud.EnableWindow(FALSE);
m_ComboCheckBit.EnableWindow(FALSE);
m_ComboStopBit.EnableWindow(FALSE);
m_ComboDataBit.EnableWindow(FALSE);
m_ComboBaudEdit.EnableWindow(FALSE);
}
}
}
此處注意,在這里動態分配了內存,pCComSer = new CComSerial(&stComCWnds);
所以在銷毀對話框時也要釋放此內存,可在對話框類的析構函數里增加對動態內存分配的釋放。因為這個類是MFC自動生成的,在WINDOWS的銷毀工作里會自動調用系統自動生成類的析構函數,所以把釋放功能增加到這個函數里。
CserialtestDlg::~CserialtestDlg()
{
//pCComSer->CloseComm();
if(pCComSer != NULL)
{
delete pCComSer;
pCComSer = NULL;
}
}
因為C++還在學習過程中,對類的設置不好。函數中實現了幾個功能,下面介紹一下:
(1)控件的使能和失能
控件的EnableWindow(TRUE)和EnableWindow(FALSE)實現控件的使能和失能。函數執行效果。
(2)按鈕顯示文字
SetDlgItemText(IDC_BUTTON_OPENCOM, _T("關閉串口"));
從函數和名字中可以看出SetDlgItemText();設置控件的文本顯示,第一個參數就是控件的ID號,第二個參數是文本信息。下面這幾行代碼也可以實現同樣的功能。
CString str ;
str.Format(_T("串口打開"));
SetDlgItemText(IDC_BUTTON_OPENCOM,str);
百度這個函數:Windows API宏,在WinUser.h中根據是否已定義Unicode被分別定義為SetWindowTextW和SetWindowTextA,這兩個函數改變指定窗口的標題欄的文本內容(如果窗口有標題欄)。如果指定窗口是一個控件,則改變控件的文本內容。特別需要引起重視的是,【SetWindowText函數不改變在其他應用程序中的控件的文本內容,如果需要可以用另外一個SendMessage函數發送一條WM_SETTEXT消息】
查看定義,都是三個定義的參數,沒有發現有兩個參數的重載,不知原因。
_T("關閉串口")作用將字符串轉為當前環境的字符寬度。如本工程設置的是Unicode寬字符。
和此功能類似的函數GetWindowText(),從函數名字可以看出此功能是獲取控件文本內容的。從文本編輯框edit控件中讀取數據就是用的這個方法。介紹文本編輯框時再詳細介紹。
CString str;
GetDlgItem(IDC_EDIT_DATASEND)->GetWindowText(str); //從發送框讀數據
(3)按鈕顯示圖片
往上查找說有三種方法,本例中用其中之一,
a、將圖片導入到資源管理器中,ID設置為IDB_BITMAP_COMON和IDB_BITMAP_COMOFF。
如何導入,右鍵工程->添加->資源->選擇”BITMAP”。注意BITMAP文件必須確實是轉換成此格式的,不能只改后綴名,那樣圖片格式不對是不能實現功能的。
b、在顯示圖片前加載圖片,下面兩行代碼。
//按鈕開關
hBitmapComON = LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_COMON)); // IDB_BITMAP_TEST為資源圖片ID
hBitmapComOFF = LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_COMOFF)); // IDB_BITMAP_TEST為資源圖
hBitmapComON變量和 hBitmapComOFF變量在CserialtestDlg中定義,如下圖所示。這兩個變量就是這兩個圖片的句柄。
c、然后獲得按鈕句柄並調用SetBitmap即可。
((CButton *)GetDlgItem(IDC_BUTTON_OPENCOM))->SetBitmap(hBitmapComON);
此處再次使用GetDlgItem()函數來獲取按鈕控件的句柄,相應的改成變量也能實現相同功能。
m_ComboComContrl.SetBitmap(hBitmapComON);
此句與上句功能相同,一個獲得控件句柄來操作,一個用控件變量來操作。
(4)按鈕Bitmap屬性對顯示圖片效果的影響
我的工程在unicode字符集下上面添加圖片的方法,可行,但是在使用多字節字符集就不行了。為此我選擇了使用Unicode字符集(寬字符)。
VC++6.0 默認是多字節字符集,VS2005以后默認是使用Unicode字符集。
使用unicode字符集編譯出的軟件 就像windows vista之后的軟件,使用多字節字符集編譯出的就像是XP時代的軟件,但看到有人用VS2013|的多字節字符集編譯的效果也是像vista之后的效果。
Unicode字符集和多字節字符集中的字符寬度不同,前者是寬字符,一個字符兩個字節,后者是一個字節。操作過程中需要多注意。