MFC傻瓜式教程
本教程重操作,輕理論,為操作減負。需瞭解詳細原理的朋友可以自行看各種書籍。
MFC:Microsoft Foundation Class ,微軟基礎類庫。
對話方塊
對話方塊的建立和顯示
- 新建MFC AppWizard(exe)工程,單文件型別。工程名:Mybole。編譯執行。
點選幫助-關於Mybole。這是MFC自動建立的。
建立自己的對話方塊。點選Insert-Resource。選擇Dialog,點選New。VC++自動將其標識設定為IDD_DIALOG1,並自動新增到ResourceView-Dialog項中。Dialog項下還有一個對話方塊資源標識:IDD_ABOUTBOX,即上一步中的“關於”對話方塊。
選中對話方塊本身,右鍵點選屬性。將Caption設定為“測試”。
- 選擇View-ClassWizard,點選create a new class,OK。出現下圖,並輸入下圖選項。
- 在隨後出現的MFC ClassWizard對話方塊上點選OK。
注意:看看左側類列表中是否新增好了CTestDlg,否則會影響後續操作。
接下來,我們希望在程式中顯示這個對話視窗。
點選右側選單Menu,選中IDR_MAINFRAME。點選幫助旁邊的虛線框。
對虛線框右鍵屬性,修改為下圖。
關閉屬性。點選View-ClassWizard(中文是建立類嚮導),選擇CMyboleView,用COMMAND命令訊息響應函式。如圖。
模態對話方塊的建立
需要呼叫CDialog類的成員函式:DoModal,它能建立並顯示一個模態對話方塊,其返回值將作為CDialog類的另一個成員函式:EndDialog的引數,後者功能是關閉模態對話方塊。
在FileView中選擇MyboleView.cpp,編寫程式。
記得在開頭新增標頭檔案 #include “testdlg.h” (標頭檔案大小寫問題,linux區分,windows不區分)
顯示模態對話方塊的具體實現程式碼:
void CMyboleView::OnDialog()
{
// TODO: Add your command handler code here
CTestDlg dlg;
dlg.DoModal();
}
編譯執行,點選對話方塊。會發現若不確認該視窗,將無法點選其他視窗。
非模態對話方塊的建立
將上面的模態對話方塊程式碼註釋掉。
改為:
void CMyboleView::OnDialog()
{
// TODO: Add your command handler code here
//CTestDlg dlg;
//dlg.DoModal();
CTestDlg *pDlg = new CTestDlg;
pDlg->Create(IDD_DIALOG1,this);
pDlg->ShowWindow(SW_SHOW);
}
注意:需要把之前執行的對話方塊關掉才能編譯成功。
然而,當它生命週期結束時,所儲存的記憶體地址就丟失了,那麼程式中也就無法再引用到它所指向的那塊記憶體。於是,我們這樣解決該問題。
注意:Message裡雙擊新增函式或者點選add Class…
void CTestDlg::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
delete this;
CDialog::PostNcDestroy();
}
區別:點選確定,對話方塊都會消失。但是,模態對話方塊視窗物件被銷燬了。對非模態對話方塊來說,只是隱藏起來了,並未被銷燬。
因此,若要銷燬對話方塊,若有一個ID為IDOK的按鈕,就必須重寫基類的OnOK這個虛擬函式,並在重寫的函式中呼叫DestroyWindow函式,完成銷燬。並不要再呼叫基類的OnOK函式。
同樣地,若有一個ID為IDCANCEL的按鈕,也必須重寫基類的OnCancel虛擬函式,並在重寫的函式中呼叫DestroyWindow函式,完成銷燬。並不要再呼叫基類的OnCancel函式。
動態建立按鈕
註釋掉非模態對話方塊程式碼,還原模態對話方塊程式碼。
點選ResourceView-IDD_DIALOG1,開啟資源,用滑鼠拖出控制元件面板上的Button按鈕控制元件,對按鈕右鍵,選擇屬性,設定如下。
接下來,我們實現當單擊Add按鈕時,在對話方塊中動態建立一個按鈕這一功能。
為CTestDlg類新增一個私有的CButton成員變數。
點選ClassView標籤頁右鍵,如圖點選。
填入資訊。
新增Add按鈕單擊訊息的響應函式。
按鈕點右鍵,選ClassWizard(建立類嚮導),如圖。
單擊Edit Code,即可定位到該函式定義處。
新增一下程式碼:
void CTestDlg::OnBtnAdd()
{
// TODO: Add your control notification handler code here
m_btn.Create("New",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
CRect(0,0,100,100),this,123);
}
為避免多次點選Add出現非法操作,我們需要進行如下步驟。
為CTestDlg類增加一個私有的BOOL型別成員變數。
變數型別:BOOL
變數名稱:m_bIsCreated
Access: private在TestDlg.cpp中找到建構函式,將m_bIsCreated初始為FALSE。如圖所示。
或者改為如下亦可。
Static BOOL bIsCreated = FALSE;回到Add,雙擊它,進入程式碼部分,改之。
void CTestDlg::OnBtnAdd()
{
// TODO: Add your control notification handler code here
if(m_bIsCreated==FALSE)
{
m_btn.Create("New",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
CRect(0,0,100,100),this,123);
m_bIsCreated = TRUE;
}
else
{
m_btn.DestroyWindow();
m_bIsCreated = FALSE;
}
}
或者以下亦能實現。
void CTestDlg::OnBtnAdd()
{
// TODO: Add your control notification handler code here
if(!m_btn.m_hWnd)
{
m_btn.Create("New",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
CRect(0,0,100,100),this,123);
}
else
{
m_btn.DestroyWindow();
}
}
效果:
點選Add出現New視窗,再點選就銷燬。
控制元件的訪問
控制元件的調整
用Layout-Align,Layout-Make Same Size,Layout-Space Evenly裡的選項進行調整。
靜態文字控制元件
檢視三個靜態文字框,它們ID相同。我們可以更改第一個靜態文字框ID為IDC_NUMBER1,再開啟ClassWizard,可以在ObjectIDs看到新ID。
對BN_CLICKED進行Add Function,並Edit Code:
此時執行程式點選第一個靜態文字框並沒有反應。這是因為:靜態文字控制元件在預設狀態下是不傳送通告訊息的。
為了該控制元件能向父視窗傳送滑鼠事件,我們對該文字框右鍵-屬性,切換到styles選項卡,勾上Notify。
現在可以顯示了:
點選就改變。
總結:為了使一個靜態文字控制元件能夠響應滑鼠單擊訊息,那麼需要進行兩個特殊的步驟:第一步,改變它的ID;第二步,在它的屬性對話方塊中選中Notify選項。
編輯框控制元件
利用上面的對話方塊實現這樣的功能:在前兩個編輯框中分別輸入一個數字,然後單擊Add按鈕,對前兩個編輯框中的數字求和,並將結果顯示在第三個編輯框中。
第一種方式
void CTestDlg::OnBtnAdd()
{
int num1, num2, num3;
char ch1[10], ch2[10], ch3[10];
GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10);
GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
itoa(num3,ch3,10);
GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
}
C語言轉換函式:atoi 將一個由數字組成的字串轉換為相應的數值
itoa 數值轉換為文字
itoa函式的第三個引數表示轉換的進位制,數字10表示十進位制。
效果:
第二種方式
程式碼如下:
void CTestDlg::OnBtnAdd()
{
int num1, num2, num3;
char ch1[10], ch2[10], ch3[10];
//GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10);
//GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10);
GetDlgItemText(IDC_EDIT1,ch1,10);
GetDlgItemText(IDC_EDIT2,ch2,10);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
itoa(num3,ch3,10);
//GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
SetDlgItemText(IDC_EDIT3,ch3);
}
GetDlgItemText 將返回對話方塊中指定ID的控制元件上的文字,相當於將上面的GetDlgItem和GetWindowText這兩個函式功能組合起來了。
與之對應的是SetDlgItemText,用來設定對話方塊中指定ID的控制元件上的文字。
第三種方式
void CTestDlg::OnBtnAdd()
{
int num1, num2, num3;
//char ch1[10], ch2[10], ch3[10];
//GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10);
//GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10);
//GetDlgItemText(IDC_EDIT1,ch1,10);
//GetDlgItemText(IDC_EDIT2,ch2,10);
num1 = GetDlgItemInt(IDC_EDIT1);
num2 = GetDlgItemInt(IDC_EDIT2);
//num1 = atoi(ch1);
//num2 = atoi(ch2);
num3 = num1 + num2;
//itoa(num3,ch3,10);
//GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
//SetDlgItemText(IDC_EDIT3,ch3);
SetDlgItemInt(IDC_EDIT3,num3);
}
第四種方式
將這三個編輯框分別與對話方塊類的三個成員變數相關聯,然後通過這些成員變數來檢索和設定編輯框的文字,這是最簡單的訪問控制元件的方式。
開啟ClassWizard對話方塊,切換到Member Variables選項卡,如圖。
首先為IDC_EDIT1編輯框新增一個關聯的成員變數,方法是在Control IDs列表中選中IDC_EDIT1,再單擊Add Variable按鈕,如圖。
同樣地,為IDC_EDIT2和IDC_EDIT3分別新增好成員變數。
接著修改程式碼:
void CTestDlg::OnBtnAdd()
{
UpdateData();
m_num3 = m_num1 + m_num2;
UpdateData(FALSE);
}
對編輯框控制元件中輸入的數值設定一個範圍:
開啟ClassWizard-Member Variable,選中IDC_EDIT1,下方輸入0和100。同樣為IDC_EDIT2也設定好。
第五種方式
將編輯框控制元件再與一個變數相關聯,代表控制元件本身。為IDC_EDIT1增加一個控制元件型別的變數:m_edit1,類別為Control。同樣地,也為IDC_EDIT2和IDC_EDIT3新增。
修改程式碼:
void CTestDlg::OnBtnAdd()
{
int num1, num2, num3;
char ch1[10], ch2[10], ch3[10];
m_edit1.GetWindowText(ch1,10);
m_edit2.GetWindowText(ch2,10);
//num1 = GetDlgItemInt(IDC_EDIT1);
//num2 = GetDlgItemInt(IDC_EDIT2);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
itoa(num3,ch3,10);
m_edit3.SetWindowText(ch3);
}
第六種方式
修改程式碼:
void CTestDlg::OnBtnAdd()
{
int num1, num2, num3;
char ch1[10], ch2[10], ch3[10];
::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd, WM_GETTEXT, 10, (LPARAM)ch1);
::SendMessage(m_edit2.m_hWnd, WM_GETTEXT, 10, (LPARAM)ch2);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
itoa(num3,ch3,10);
m_edit3.SendMessage(WM_SETTEXT, 0, (LPARAM)ch3);
}
第七種方式
修改程式碼:
void CTestDlg::OnBtnAdd()
{
int num1, num2, num3;
char ch1[10], ch2[10], ch3[10];
SendDlgItemMessage(IDC_EDIT1, WM_GETTEXT, 10, (LPARAM)ch1);
SendDlgItemMessage(IDC_EDIT2, WM_GETTEXT, 10, (LPARAM)ch2);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
itoa(num3,ch3,10);
SendDlgItemMessage(IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)ch3);
}
獲得編輯框複選的內容:
在上述程式碼最後新增:
SendDlgItemMessage(IDC_EDIT3, EM_SETSEL, 0, -1); //0,-1表示全選若1,3表示選中1-3位複選
m_edit3.SetFocus();
效果:
總結
1 GetDlgItem()->Get(Set)WindowTest()
2 GetDlgItemText()/SetDlgItemText()
3 GetDlgItemInt()/SetDlgItemInt()
4 將控制元件和整型變數相關聯
5 將控制元件和控制元件變數相關聯
6 SendMessage()
7 SendDlgItemMessage()
最常用是1、4、5。在利用MFC程式設計時,6、7用得少。
對話方塊伸縮功能的實現
對話方塊上再新增一個按鈕,Caption設定為“收縮<<”點選ClassWizard,新增一個命令相應函式(BN_CLICKED)。具體實現程式碼為:
void CTestDlg::OnButton1()
{
CString str;
if(GetDlgItemText(IDC_BUTTON1,str), str == "收縮<<")
{
SetDlgItemText(IDC_BUTTON1, "拓展>>");
}
else
{
SetDlgItemText(IDC_BUTTON1, "收縮<<");
}
}
拖動一個影象控制元件來劃分對話方塊中要動態切除的部分。
修改該控制元件ID為IDC_SEPATATOR,styles選項卡勾上Sunken選項。
修改程式碼:
void CTestDlg::OnButton1()
{
CString str;
if(GetDlgItemText(IDC_BUTTON1,str), str == "收縮<<")
{
SetDlgItemText(IDC_BUTTON1, "拓展>>");
}
else
{
SetDlgItemText(IDC_BUTTON1, "收縮<<");
}
static CRect rectLarge;
static CRect rectSmall;
CRect rect1(10,10,10,10);
CRect rect2(0,0,0,0);
if(rectLarge.IsRectNull())
{
CRect rectSeparator;
GetWindowRect(&rectLarge);
GetDlgItem(IDC_SEPARATOR)->GetWindowRect(&rectSeparator);
rectSmall.left=rectLarge.left;
rectSmall.top=rectLarge.top;
rectSmall.right=rectLarge.right;
rectSmall.bottom=rectSeparator.bottom;
}
if(str == "收縮<<")
{
SetWindowPos(NULL, 0, 0, rectSmall.Width(), rectSmall.Height(), SWP_NOMOVE | SWP_NOZORDER);
}
else
{
SetWindowPos(NULL, 0, 0, rectLarge.Width(), rectLarge.Height(), SWP_NOMOVE | SWP_NOZORDER);
}
}
效果:
點選“收縮<<”:
若希望隱藏分隔條,則設定屬性去掉“Visible”前的勾。
輸入焦點的傳遞
為了遮蔽掉預設的回車鍵關閉對話方塊這一功能,應該在對話方塊子類(此處是CTestDlg類)中重寫OK按鈕的訊息響應函式。
首先點選OK按鈕,新增滑鼠單擊訊息響應函式。註釋掉原有函式。
法一
在ClassView選項卡的CTestDlg類新增WM_INITDIALOG訊息的響應函式。對類右鍵,選擇Add Windows Message Handler,在彈出的框左側選擇WM_INITDIALOG,直接單擊Add and Edit,跳轉。
修改程式碼為:
void CTestDlg::OnOK()
{
// TODO: Add extra validation here
//CDialog::OnOK();
}
WNDPROC prevProc;
LRESULT CALLBACK NewEditProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
if(uMsg == WM_CHAR && wParam == 0x0d)
{
::SetFocus(GetNextWindow(hwnd,GW_HWNDNEXT));
return 1;
}
else
{
return prevProc(hwnd,uMsg,wParam,lParam);
}
}
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
prevProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,
GWL_WNDPROC, (LONG)NewEditProc);
return TRUE;
}
檢視第一個編輯框的屬性,開啟styles選項卡,勾上MultiLine(多行)。即可實現焦點的傳遞。
法二
只需要改變一行程式碼:
WNDPROC prevProc;
LRESULT CALLBACK NewEditProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
if(uMsg == WM_CHAR && wParam == 0x0d)
{
//::SetFocus(GetNextWindow(hwnd,GW_HWNDNEXT));
SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));
return 1;
}
else
{
return prevProc(hwnd,uMsg,wParam,lParam);
}
}
法三
編輯框屬性有一個WS_TABSTOP,如果勾選了,則在對話方塊中按下Tab鍵後,輸入焦點可以轉移到此控制元件上。
修改一行程式碼:
WNDPROC prevProc;
LRESULT CALLBACK NewEditProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
if(uMsg == WM_CHAR && wParam == 0x0d)
{
SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,FALSE));
//::SetFocus(GetNextWindow(hwnd,GW_HWNDNEXT));
//SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));
return 1;
}
else
{
return prevProc(hwnd,uMsg,wParam,lParam);
}
}
三種方法的缺點:只修改了第一個編輯框的視窗過程,因此從第二到第三個編輯框的焦點轉移無法實現,除非繼續修改第二個編輯視窗。
再介紹一種方法解決這個問題。
法四
在MFC中,預設情況下,當在對話方塊視窗中按下回車鍵時,會呼叫對話方塊的預設按鈕的響應函式,我們可以在此預設按鈕的響應函式中把焦點依次向下傳遞。
首先取消第一個編輯框的MultiLine。
接著修改OnOK函式為:
void CTestDlg::OnOK()
{
// TODO: Add extra validation here
//GetDlgItem(IDC_EDIT1)->GetNextWindow()->SetFocus();
//GetFocus()->GetNextWindow()->SetFocus();
//GetFocus()->GetWindow(GW_HWNDNEXT)->SetFocus();
GetNextDlgTabItem(GetFocus())->SetFocus();
//CDialog::OnOK();
}```
註釋掉的部分是各種失敗的嘗試,各有各的bug。現在程式是正常的。
**注意:然而該遮蔽回車鍵的方法並非是常規做法,應該在PreTranslateMessage中進行攔截。(return TRUE即攔截)**
具體做法:
現在Testdlg.h中新增:
```C++
class CTestDlg : public CDialog
{
protected:
virtual BOOL PreTranslateMessage(MSG* pMsg);
public:
virtual void OnOK();
……
<div class="se-preview-section-delimiter"></div>
接著:
CTestDlg::PreTranslateMessage(MSG* pMsg)
{
//遮蔽ESC關閉窗體
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
{
return TRUE;
}
//遮蔽回車關閉窗體,但會導致回車在窗體上失效.
/*
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN && pMsg->wParam)
{
return TRUE;
}
*/
else
{
return CDialog::PreTranslateMessage(pMsg);
}
}
void CTestDlg::OnOK()
{
// TODO: Add extra validation here
//CDialog::OnOK();
}
<div class="se-preview-section-delimiter"></div>
點選Layout-Tab order,這些序號就是各控制元件的Tab順序。順序可改變,依次點選希望的順序控制元件即可。
呼叫順序:當用戶按下回車鍵時,Windows將檢視對話方塊中是否存在指定的預設按鈕,如果有,就呼叫該預設按鈕單擊訊息的響應函式。如果沒有,就會呼叫虛擬的OnOK函式,即使對話方塊沒有包含預設的OK按鈕(這個預設OK按鈕的ID是IDOK)。
檔案和登錄檔操作
C語言對檔案操作的支援
新建單文件型別的MFC應用程式,工程名為File,併為主選單新增一個子選單,名稱為“檔案操作”,然後為其新增兩個選單項,並分別為它們新增相應的命令響應函式(通過COMMAND),讓CFileView類接收這些選單項的命令響應。
檔案的開啟和寫入
程式碼:
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("1.txt","w");
fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile);
}
<div class="se-preview-section-delimiter"></div>
編譯後可看到資料夾中生成了1.txt,開啟有一行網址。
檔案的關閉
增加一行程式碼:
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("1.txt","w");
fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile);
fclose(pFile);
}
<div class="se-preview-section-delimiter"></div>
檔案指標定位
程式碼:
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("1.txt","w");
fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile);
fwrite("歡迎訪問", 1, strlen("歡迎訪問"), pFile);
fclose(pFile);
}
<div class="se-preview-section-delimiter"></div>
將檔案指標移動到檔案的開始位置處:
程式碼:
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("1.txt","w");
fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile);
fseek(pFile, 0, SEEK_SET);
fwrite("ftp:", 1, strlen("ftp:"),pFile);
//fwrite("歡迎訪問", 1, strlen("歡迎訪問"), pFile);
fclose(pFile);
}
<div class="se-preview-section-delimiter"></div>
顯示:ftp:://www.sunxin.org
檔案的讀取
在OnFileRead函式中寫入程式碼:
void CFileView::OnFileRead()
{
FILE *pFile = fopen("1.txt","r");
char ch[100];
fread(ch, 1, 100, pFile);
fclose(pFile);
MessageBox(ch);
}
<div class="se-preview-section-delimiter"></div>
編譯執行:
原因:C語言以“\0”結束。
解決方法:
法一:
修改程式碼:
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("1.txt","w");
char buf[22] = "http://www.sunxin.org";
buf[21] = '\0';
fwrite(buf, 1, 22, pFile);
fclose(pFile);
}
<div class="se-preview-section-delimiter"></div>
先點選寫入檔案,再點選讀取檔案,就可以看到正確的內容。
缺點:增加了檔案大小。
法二:
void CFileView::OnFileRead()
{
FILE *pFile = fopen("1.txt","r");
char ch[100];
memset(ch, 0, 100);
fread(ch, 1, 100, pFile);
fclose(pFile);
MessageBox(ch);
}
<div class="se-preview-section-delimiter"></div>
法三:
讀取檔案時,不知道檔案大小時的做法。
void CFileView::OnFileRead()
{
FILE *pFile = fopen("1.txt","r");
char *pBuf;
fseek(pFile, 0, SEEK_END);
int len=ftell(pFile);
pBuf = new char[len+1];
rewind(pFile);
fread(pBuf, 1, len, pFile);
pBuf[len] = 0;
fclose(pFile);
MessageBox(pBuf);
}
<div class="se-preview-section-delimiter"></div>
二進位制檔案和文字檔案
程式碼:
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("2.txt", "w");
char ch[3];
ch[0] = 'a';
ch[1] = 10;
ch[2] = 'b';
fwrite(ch, 1, 3, pFile);
fclose(pFile);
}
void CFileView::OnFileRead()
{
FILE *pFile = fopen("2.txt","r");
char ch[100];
fread(ch, 1, 3, pFile);
ch[3] = 0;
fclose(pFile);
MessageBox(ch);
}
<div class="se-preview-section-delimiter"></div>
效果:
文字方式:10實際上是換行符的ASCII碼。
以文字方式和二進位制方式讀取檔案是有明顯的區別的。
文字方式和二進位制方式
二進位制方式:換行是由兩個字元組成的,即ASCII碼10(回車符)和13(換行符)。
寫入和讀取檔案時要保持一致。如果採用文字方式寫入,應採用文字方式讀取;如果採用二進位制方式寫入資料,在讀取時也應採用二進位制方式。
面試題:給你一個整數,如:98341,將這個整數儲存到檔案中,要求在以記事本程式開啟該檔案時,顯示的是:98341。
法一:
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("3.txt", "w");
char ch[5];
ch[0] = 9 + 48;
ch[1] = 8 + 48;
ch[2] = 3 + 48;
ch[3] = 4 + 48;
ch[4] = 1 + 48;
fwrite(ch, 1, 5, pFile);
fclose(pFile);
}
<div class="se-preview-section-delimiter"></div>
或
void CFileView::OnFileWrite()
{
FILE *pFile = fopen("3.txt", "w");
int i = 98341;
char ch[5];
itoa(i, ch, 10);
fwrite(ch, 1, 5, pFile);
fclose(pFile);
}
<div class="se-preview-section-delimiter"></div>
面試題:給定一個字串,其中既有數字字元,又有26個英文字母中的幾個字元,讓你判斷一下哪些是數字字元。
對這種問題,實際上就是判斷各字元的ASCII碼,對於數字字元來說,它們的ASCII碼大於等於48,小於等於57。
C++對檔案操作的支援
void CFileView::OnFileWrite()
{
ofstream ofs("4.txt");
ofs.write("http://www.sunxin.org",strlen("http://www.sunxin.org"));
ofs.close;
}
void CFileView::OnFileRead()
{
ifstream ifs("4.txt");
char ch[100];
memset(ch, 0, 100);
ifs.read(ch,100);
ifs.close();
MessageBox(ch);
}
<div class="se-preview-section-delimiter"></div>
Win32 API 對檔案操作的支援
檔案的建立、開啟和寫入
void CFileView::OnFileWrite()
{
//定義一個控制代碼變數
HANDLE hFile;
//建立檔案
hFile = CreateFile("5.txt", GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
//接收實際寫入的位元組數
DWORD dwWrites;
//寫入資料
WriteFile(hFile,"http://www.sunxin.org",strlen("http://www.sunxin.org"),
&dwWrites, NULL);
//關閉檔案控制代碼
CloseHandle(hFile);
}
<div class="se-preview-section-delimiter"></div>
檔案的讀取
void CFileView::OnFileRead()
{
HANDLE hFile;
//開啟檔案
hFile = CreateFile("5.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//接收實際收到的資料
char ch[100];
//接收實際讀取到的位元組數
DWORD dwReads;
//讀取資料
ReadFile(hFile, ch, 100, &dwReads, NULL);
//設定字串結束字元
ch[dwReads] = 0;
//關閉開啟的檔案物件的控制代碼
CloseHandle(hFile);
//顯示讀取到的資料
MessageBox(ch);
}
<div class="se-preview-section-delimiter"></div>
選單
選單命令響應函式
新建一個單文件的MFC AppWizard(exe)工程,工程名為Menu。Build執行。
左上角點選按鈕,可以讓屬性框始終顯示,不會因為點選對話方塊以外的地方就消失。
去掉Pop-up彈出前的勾,將ID改為ID_TEST。給Test新增響應函式在CMainFrame中,在函式中加入 MessageBox(“MainFrame Clicked”);
效果:
選單命令的路由
程式類對選單命令的響應順序
響應Test
選單項命令的順序依次是:視類、文件類、框架類,最後才是應用程式類。
Windows訊息的分類
凡是從CWnd派生的類,它們既可以接收標準訊息,也可以接收命令訊息和通告訊息。而對於那些從CCmdTarget派生的類,則只能接收命令訊息和通告訊息,不能接收標準訊息。
本例中的文件類(CMenuDoc)和應用程式類(CWinApp),因為它們都派生於CCmdTarget類,所以它們可以接收選單命令訊息。但它們不是從CWnd類派生的,所以不能接收標準訊息。
選單命令的路由
選單命令訊息路由的具體過程:當點選某個選單項時,最先接收到這個選單命令訊息的是框架類。框架類將把接收到的這個訊息交給它的子視窗,即視類,由視類首先進行處理。視類首先根據命令訊息對映機制查詢自身是否對此訊息進行了響應,如果響應了,就呼叫相應響應函式對這個訊息進行處理,訊息路由過程結束;如果視類沒有對此命令訊息做出響應,就交由文件類,文件類同樣查詢自身是否對這個選單命令進行了響應,如果響應了,就由文件類的命令訊息響應函式進行處理,路由過程結束。如果文件類也未做出響應,就把這個命令訊息交還給視類,後者又把該訊息交還給框架類。框架類檢視自己是否對這個命令訊息進行了響應,如果它也沒有做出響應,就把這個選單命令訊息交給應用程式類,由後者來進行處理。
基本選單操作
標記選單
執行剛才建立的Menu程式,點選檢視,前面都有一個對號,這種型別就是標記選單。
在CMainFrame類的OnCreate的return語句之前新增這句程式碼 GetMenu()->GetSubMenu(0)->CheckMenuItem(0, MF_BYPOSITION | MF_CHECKED); 或者GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW, MF_BYCOMMAND | MF_CHECKED);
Build並執行,可發現新建左邊已新增一個複選標記。
預設選單項
在剛才的程式碼下,新增 GetMenu()->GetSubMenu(0)->SetDefaultItem(1, TRUE); 或者GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN, FALSE); 編譯執行,會發現“開啟”變成了粗體。
注意:“列印”的索引是5,不是4。計算選單項索引時,一定要把分割欄選單項計算在內。並且,一個子菜單隻能有一個預設選單項。
圖形標記選單
Insert-Resource-Bitmap,建立一個位圖資源。如圖。
為CMainFrame類新增一個CBitmap型別的成員變數:m_bitmap。
接著新增程式碼:
CString str;
str.Format(“x=%d”,y=%d”, GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str);
m_bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bitmap, &m_bitmap);
禁用選單項
通常把MF_GRAYED和MF_DISABLED這兩個標誌放在一起使用。不過這麼做並不是必需的。
刪除之前的程式碼,寫入 GetMenu()->GetSubMenu(0)->EnableMenuItem(1, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
開啟“檔案”子選單,發現“開啟”選單欄變灰,點選不起作用。
移除和裝載選單
再新增一行程式碼: SetMenu(NULL); 此時選單欄被移除了。
再新增幾行程式碼:
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();
此時選單欄又裝載了。
CMenu menu;
menu.CreateMenu();
GetMenu()->AppendMenu(MF_POPUP, (UINT)menu.m_hMenu, "Test1");
menu.AppendMenu(MF_STRING, 111, "Hello");
menu.AppendMenu(MF_STRING, 112, "Bye");
menu.AppendMenu(MF_STRING, 113, "Mybole");
menu.Detach();
CMenu menu1;
menu1.CreateMenu();
GetMenu()->InsertMenu(2, MF_POPUP | MF_BYPOSITION, (UINT)menu1. m_hMenu,"Test");
menu1.Detach();
GetMenu()->GetSubMenu(2)->AppendMenu(MF_STRING, 118, "Welcome");
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING, 114, "Welcome");
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN, MF_BYCOMMAND | MF_STRING, 115, "VC程式設計");
<div