1. 程式人生 > >MFC傻瓜式教程

MFC傻瓜式教程

本教程重操作,輕理論,為操作減負。需瞭解詳細原理的朋友可以自行看各種書籍。
  

MFC:Microsoft Foundation Class ,微軟基礎類庫。

對話方塊

對話方塊的建立和顯示

  1. 新建MFC AppWizard(exe)工程,單文件型別。工程名:Mybole。編譯執行。
    新建
  2. 點選幫助-關於Mybole。這是MFC自動建立的。
    關於

  3. 建立自己的對話方塊。點選Insert-Resource。選擇Dialog,點選New。VC++自動將其標識設定為IDD_DIALOG1,並自動新增到ResourceView-Dialog項中。Dialog項下還有一個對話方塊資源標識:IDD_ABOUTBOX,即上一步中的“關於”對話方塊。
    Insert Resource對話方塊

    新建的對話方塊資源

  4. 選中對話方塊本身,右鍵點選屬性。將Caption設定為“測試”。

  5. 選擇View-ClassWizard,點選create a new class,OK。出現下圖,並輸入下圖選項。
    New Class
  6. 在隨後出現的MFC ClassWizard對話方塊上點選OK。
    列表
    注意:看看左側類列表中是否新增好了CTestDlg,否則會影響後續操作。

接下來,我們希望在程式中顯示這個對話視窗。

  1. 點選右側選單Menu,選中IDR_MAINFRAME。點選幫助旁邊的虛線框。
    Menu

  2. 對虛線框右鍵屬性,修改為下圖。
    屬性

  3. 關閉屬性。點選View-ClassWizard(中文是建立類嚮導),選擇CMyboleView,用COMMAND命令訊息響應函式。如圖。
    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(); }

編譯執行,點選對話方塊。會發現若不確認該視窗,將無法點選其他視窗。
模態對話方塊1

模態對話方塊2

非模態對話方塊的建立

將上面的模態對話方塊程式碼註釋掉。

改為:

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);
}

注意:需要把之前執行的對話方塊關掉才能編譯成功。

然而,當它生命週期結束時,所儲存的記憶體地址就丟失了,那麼程式中也就無法再引用到它所指向的那塊記憶體。於是,我們這樣解決該問題。
MFC ClassWizard

注意: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按鈕時,在對話方塊中動態建立一個按鈕這一功能。

  1. 為CTestDlg類新增一個私有的CButton成員變數。
      點選ClassView標籤頁右鍵,如圖點選。
    ClassView

      填入資訊。
    新增成員變數

  2. 新增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出現非法操作,我們需要進行如下步驟。

  1. 為CTestDlg類增加一個私有的BOOL型別成員變數。
    變數型別:BOOL
    變數名稱:m_bIsCreated
    Access: private

  2. 在TestDlg.cpp中找到建構函式,將m_bIsCreated初始為FALSE。如圖所示。
    這裡寫圖片描述

      或者改為如下亦可。
    Static BOOL bIsCreated = FALSE;

  3. 回到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