400 likes | 644 Views
使用 MFC. 在視窗程式的執行過程中 , 使用者與程式 執行間的關係是一個交互影響的過程 . 整個視窗大致可分為兩個部份 , 一是視窗框架 , 二是客戶區. 如何撰寫視窗程式. 利用 AF(Application Frameworks) 簡稱應用軟體架構 因為 AF 本身就是一個很複雜的類別階層 , 而運用 AF 的方法就是利用類別的繼承. CObject. 應用程式類別. 繪圖類別. 容器類別. 其他類別. 視窗類別. 如何撰寫視窗程式. 應用程式類別 : 定義了建立應用程式可能需要利用的類
E N D
使用MFC 在視窗程式的執行過程中, 使用者與程式 執行間的關係是一個交互影響的過程. 整個視窗大致可分為兩個部份, 一是視窗框架, 二是客戶區
如何撰寫視窗程式 利用AF(Application Frameworks)簡稱應用軟體架構 因為AF本身就是一個很複雜的類別階層, 而運用AF的方法就是利用類別的繼承 CObject 應用程式類別 繪圖類別 容器類別 其他類別 視窗類別
如何撰寫視窗程式 應用程式類別: 定義了建立應用程式可能需要利用的類 別, 如:CWinApp(視窗應用程式類別), CDocTemplate(文件範本類別)等等. 視窗程式類別: 定義了建構視窗所需的類別如:CFrameWnd(視窗框架類別),CDialog(對話盒類別), CView(瀏覽類別)等等. 繪圖類別: 用於畫圖的類別, 如:CDC(裝置內文類別), Cpen(畫筆類別),Cbrush(畫刷類別)等等. 容器類別: Clist,Carry,Cmap等容器類別. 其他類別: ODBC資料庫類別.
如何撰寫視窗程式 要使用MFC類別時,必需要先載入afcwin的標頭檔 當我們要撰寫視窗程式時必需要建立兩種物件 一是繼承CWinApp的應用程式物件 二是繼承CFrameWnd的視窗框架物件
如何撰寫視窗程式 每一個程式都只有一個應用程式類別(CWinApp), 這個應用程式類別用於產生應用程式物件, 且是程式的進入點. 當建立了應用程式物件後, 將由該物件負責視窗框架物件的建立, 且應用程式類別將有一個屬性指向所使用的視窗框架物件. 應用程式物件 Pointer 視窗框架物件
MFC的Example #include <afxwin.h> //載入afxwin標頭檔 class MyApp : public CWinApp //繼承CWinApp { public: BOOL InitInstance() //程式進入點 { CFrameWnd *Frame = new CFrameWnd(); //建立CFrameWnd物件(產生) m_pMainWnd = Frame; //將m_pMainWnd設定為Frame Frame->Create(NULL,“Hello MFC”); //建立視窗(建立) Frame->ShowWindow(SW_SHOW); return true; } }; MyApp a_app; //建立應用程式物件
程式說明 要如何自定應用程式類別??? • 繼承CWinApp類別 • 重載CWinApp::IninInstance函數, 此函數為視窗程式的進入點, 回傳值的型態為BOOL class MyApp : public CWinApp //繼承CWinApp { public: BOOL InitInstance() //程式進入點 {…} };
程式說明 CWinApp::m_pMainWnd屬性, 這個屬性將指向應用程 式所使用的視窗框架物件. CWinApp::IninInstance函數, 在該函數中完成下列工作. • 產生視窗框架物件 • 將該視窗框架物件的指標,設定給CWinApp::m_pMainWnd屬性 • 在螢幕中建立視窗框架 • 顯示視窗框架物件
程式說明 BOOL InitInstance() //程式進入點 { CFrameWnd *Frame = new CFrameWnd(); //建立CFrameWnd物件 m_pMainWnd = Frame; //將m_pMainWnd設定為Frame Frame->Create(NULL,"Hello MFC"); //建立視窗 Frame->ShowWindow(SW_SHOW); return true;} 建立應用程式物件 MyApp a_app; //建立應用程式物件
建立自訂視窗 當您想要建立一個自定的視窗框架時, 您必須建立一個繼承於CFrameWnd類別的視窗框架類別, 然後將視窗組合出自定的視窗類別. 在此範例我們不直接利用CFrameWnd類別, 而是使用繼承的方式, 由CFrameWnd類別衍生出自定的MyFrame類別.
MyFrame程式範例 #include <afxwin.h> #include "MyFrame.h" //由資源編輯器所產生的標頭檔 class MyFrame : public CFrameWnd //繼承CFrameWnd類別 { private: CMenu *FMenu; public: MyFrame() { Create(NULL,"Hello MFC"); //建立視窗 FMenu = new CMenu; //產生選單 FMenu->LoadMenu(IDR_MENU1); //載入選單 SetMenu(FMenu); //設定視窗所使用的選單 } }; class MyApp : public CWinApp { public: BOOL InitInstance() { CFrameWnd *Frame = new MyFrame; //產生視窗 m_pMainWnd = Frame; //將視窗物件設定給應用程式 Frame->ShowWindow(SW_SHOW); //顯示視窗 return true; } }; MyApp a_app; //建立應用程式物件
MyFrame程式範例 class MyApp : public CWinApp { public: BOOL InitInstance() { CFrameWnd *Frame = new MyFrame; //產生視窗 m_pMainWnd = Frame; //將視窗物件設定給應用程式 Frame->ShowWindow(SW_SHOW); //顯示視窗 return true; } }; MyApp a_app; //建立應用程式物件
資源檔的設定與使用 • 在VC++中的File選New, 再點選Files中的Resource Template進行script的編輯. • 再點選Insert中的Resource進行資源檔的編輯. • 編輯完畢後, 再將script存成*.rc的檔案格式, 再到Resource Files中將剛才存檔的.rc import進來即可.
程式說明 我們將利用MyFrame程式範例介紹下列重點 • 視窗框架與資源檔 • 視窗元件的使用
程式說明 在上述程式中在MyFrame類別中, 亦有一個指向Cmenu物件的FMenu指標屬性,當您要操作該選單時(如: 新增, 刪除等等)可透過CMenu來完成. 而在第17行利用LoadMenu將定義在資源檔中的選單代號載入, 再利用SetMenu函數將Fmneu設定為視窗使用的選單
程式說明 在視窗框架類別中是負責產生應用程式物件所使用的視窗框架物件, 也就是應用程式的視窗介面, 所以資源檔中定義的資源大部份都是建立視窗框架時所用的元件 #include "MyFrame.h" //由資源編輯器所產生的標頭檔
程式說明 // Used by MyFrame.rc #define IDR_MENU1 101 #define ID_Exit 40001 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40002 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
視窗的訊息傳遞與處理 在視窗作業系統下, 作業系統將不斷由電腦系統的週邊裝置得到訊息, 就連您輕輕的移動了一下滑鼠, 作業系統也會收到滑鼠移動的訊息. 在多而繁雜的訊息中, 如何讓應用程式知道究竟哪些訊息要回應? 要如何回應呢? 這要透過訊息映射表的建立.
視窗訊息的種類 回應訊息與處理函數分為兩種 • 標準系統訊息 標準系統訊息是由作業系統所產生的訊息, 比如: 移動滑鼠或按下滑鼠左鍵…等. 而接收並處理這類標準系統訊息的類別, 必須衍生自CWnd類別. 定義的方式通常以ON_WM_XXX方式定義 BEGIN_MESSAGE_MAP(類別名稱,基礎類別名稱) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() END_MESSAGE_MAP()
視窗訊息的種類 而回應系統訊息名稱, MFC均已定義, 其宣告方式如下 afx_msg void OnLButtonDown(UINT nFlags, CPoint point) {……; }//當滑鼠左鍵按下後的回應函數,取得滑鼠訊息接收權 其中nFlags與point為傳入函數的參數.
視窗訊息的種類 • 命令訊息 命令訊息大致上是指由使用者自行定義的選單.工具列. 控制項…等視窗元件, 被選取時所產生的訊息. 回應這類訊息的類別必須衍生自CCmdTarget類別. 這類訊息回息回應項目的定義方式如下: BEGIN_MESSAGE_MAP類別名稱,基礎類別名稱) ON_COMMAND(訊息代號,回應函數) … END_MESSAGE_MAP()
視窗訊息的種類 命令訊息必須在建立該命令時, 即建立一個代號, 這個代號, 就是建立資源物件時設定的資源代號, 比如: 我們在定義File選單的Exit選項為ID_EXIT1. 而回應函數的名稱也必須由我們定義,比如: 我們在定義File選單的Exit函數名稱為ONExit. 而在宣告這類訊息處理函數時, 我們必須在該函數前加afx_msg, 定義方式如下: afx_msg void 回應函數名稱(參數…) ex:afx_msg void OnExit() //ID_EXIT1的回應函數 對於一些大部份視窗程式常用到的命令, MFC亦定義了代號(Standard Command Ids)
訊息映射表的建立 DECLARE_MESSAGE_MAP() //宣告訊息映射表 然後在類別外宣告所處理的訊息, 以及處理函數. BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd) //建立MyFrame類別的訊息映射表 訊息回應項目 … END_MESSAGE_MAP()
訊息映射表的建立 class MyFrame : public CFrameWnd { … DECLARE_MESSAGE_MAP()//宣告訊息映表 }; BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd) //建立MyFrame類別的訊息映射表 ON_COMMAND(ID_Exit1, OnExit) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() END_MESSAGE_MAP() 函數名稱 訊息代號
Mouse訊息傳入的參數 Mouse訊息產生時,幾個特殊按鍵利用nFlags傳入,而 Mouse的座標則經由point傳入
繪圖相關說明 • SetCapture(); //當滑鼠左鍵按下後的回應函數,取得滑鼠訊息接收權 • CClientDC aDC(this); //建立一個畫布 aDC.SetPixel(point, RGB(255, 0 ,0));//利用SetPixel在畫布上點出紅點 • GetCapture() //判斷滑鼠游標是否在視窗之上 • ReleaseCapture() //當滑鼠左鍵放開後的回應函數,釋放滑鼠訊息接收權
#include <afxwin.h> #include "Message.h" //載入資源檔所使用之標頭檔 class MyFrame : public CFrameWnd { private: CMenu *FMenu; public: MyFrame() //建構子 { Create(NULL,"Hello MFC"); FMenu = new CMenu; FMenu>LoadMenu(IDR_MENU1); SetMenu(FMenu); } ~MyFrame(){ delete FMenu;} //解構子 afx_msg void OnExit() //ID_EXIT1的回應函數 { MessageBox("Exit1"); DestroyWindow(); } afx_msg void OnLButtonDown(UINT nFlags, CPoint point) { SetCapture(); } //當滑鼠左鍵按下後的回應函數,取得滑鼠訊息接收權 afx_msg void OnLButtonUp(UINT nFlags, CPoint point) { ReleaseCapture(); } //當滑鼠左鍵放開後的回應函數,釋放滑鼠訊息接收權 Message程式範例
afx_msg void OnMouseMove(UINT nFlags, CPoint point) { //當滑鼠移動時的回應函數 if (this == GetCapture())//判 斷滑鼠游標是否在視窗之上 {CClientDC aDC(this); //建立一個畫布 aDC.SetPixel(point, RGB(255, 0 ,0));//利用SetPixel在畫布上點出紅點} } afx_msg void OnLButtonUp(UINT nFlags, CPoint point) { ReleaseCapture(); } //當滑鼠左鍵放開後的回應函數,釋放滑鼠訊息接收權 DECLARE_MESSAGE_MAP() //宣告訊息映射表}; class MyApp : public CWinApp //應用程式類別 { public: BOOL InitInstance() //程式進入點 { CFrameWnd *Frame = new MyFrame; m_pMainWnd = Frame; Frame>ShowWindow(SW_SHOW); //顯示視窗 return true; } } a_app; //宣告應用程式物件 Message程式範例
Document/View的基本架構 Document/view(文件/瀏覽)的架構, 就是用來管理視窗介面下, 資料的儲存與顯示, 在分工上, Document物件用於管理視窗程式的資料儲存, 而View物件負責將Document儲存的資料正確顯示在視窗上. 一個完整的視窗應用程式, 必須具備有應用程式類別(CWinApp).視窗框架類別(CFrameWnd).Document類別(文件類別).View類別(瀏覽類別).
Document/View的基本架構 視窗框架物件 應用程式物件 Document物件 View物件
重新整理 • 應用程式物件用於建立應用程式 • 視窗框架物件用於建立視窗介面 • Document物件用於運作.儲存資料 • View物件用於將Document物件顯示於視窗 • 從資料的角度來看, Document物件與View物件其實是一體的, 只是將資料的處理與顯示的工作分開
應用程式物件 Pointer 文件樣版物件物件 文件物件 Pointer Pointer 瀏覽物件 Pointer Document/View架構圖 1.產生 指向 視窗框架物件 3.產生 2.產生 Pointer 4.產生
#include <afxwin.h> #include "Doc_View.h" class MyDocument : public CDocument {DECLARE_DYNCREATE(MyDocument) //宣告run-time類別}; IMPLEMENT_DYNCREATE(MyDocument, CDocument) //宣告MyDocument為run-time類別 class MyView : public CView {public: void OnDraw(CDC * aDC) //必須過載的虛擬函數 { } DECLARE_DYNCREATE(MyView)//宣告run-time類別}; IMPLEMENT_DYNCREATE(MyView, CView) //宣告MyView為run-time類別 class MyFrame : public CFrameWnd { DECLARE_DYNCREATE(MyFrame) //宣告run-time類別 }; IMPLEMENT_DYNCREATE(MyFrame, CFrameWnd) //宣告MyFrame為run-time類別 Document/View程式範例
class MyApp : public CWinApp {public: BOOL InitInstance() {CDocument *doc; //宣告指向文件的指標 CSingleDocTemplate* DocTemplate; //宣告指向單文件樣版物件的指標 DocTemplate = new CSingleDocTemplate( //建立具有單文件樣版物件 IDR_MENU1, //用於單文件框架的資源代號 RUNTIME_CLASS(MyDocument), //單文件視窗的Document RUNTIME_CLASS(MyFrame), //單文件視窗的視窗框架 RUNTIME_CLASS(MyView)); //單文件視窗的View AddDocTemplate(DocTemplate);//將單文件樣版物件設定給MyApp doc = DocTemplate->CreateNewDocument(); //建立新的文件 m_pMainWnd = DocTemplate->CreateNewFrame( doc, NULL ); //建立一個視窗框架 DocTemplate->InitialUpdateFrame ( (CFrameWnd*)m_pMainWnd, doc );//起始化視窗框架中的View m_pMainWnd->ShowWindow ( SW_SHOW); //顯示視窗 return true;}} a_app;//建立應用程式物件 Document/View程式範例
程式說明 宣告Run-Time類別 因為CSignleDocument在無法得知視窗框架物件.Vie w類別.Document類別的情況下,等到執行時才決定運用哪幾個類別的應用程式. 宣告 DECLARE_DYNAMIC(類別名稱) 然後在該類別外做如下宣告 IMPLEMENT_DYNAMIC(類別名稱,衍生類別名稱)
#include <afxwin.h> #include "repaint.h" #include <afxtempl.h> //定義樣版類別的標頭檔 class MyDocument : public CDocument { public: CArray<CPoint, CPoint &> pArray; //容納滑鼠軌跡點的Array容器 void AddPoint(CPoint p) //將軌跡點加到容器內 { pArray.Add(p); } CPoint GetPoint(int i) //將軌跡點從容器中取出 { return pArray[i]; } int GetSize() { return pArray.GetSize(); } //取得容器的大小 DECLARE_DYNCREATE(MyDocument) //宣告為run-time類別 DECLARE_MESSAGE_MAP() //宣告訊息映射表 }; IMPLEMENT_DYNCREATE(MyDocument, CDocument) //建立run-time類別 BEGIN_MESSAGE_MAP(MyDocument, CDocument) END_MESSAGE_MAP() //建立訊息映射表 Document/View架構的應用視窗的重繪
class MyView : public CView {public: void OnDraw(CDC * aDC) //過載OnDraw虛擬函數 {MyDocument *doc = (MyDocument *)GetDocument(); //取得目前Document物件的指標 int num = doc->GetSize(); //取得目前儲存的軌跡點點數 int i; for(i = 0; i < num; ++i) //將Document中儲存的軌跡點重繪到視窗上 {CPoint point = doc->GetPoint(i); aDC->SetPixel(point, RGB(255, 0 ,0)); }} afx_msg void OnLButtonDown(UINT, CPoint point) { SetCapture(); } //取得滑鼠訊息的接收權 afx_msg void OnMouseMove(UINT, CPoint point) {if (this == GetCapture()) {CClientDC aDC(this);//建立畫布 aDC.SetPixel(point, RGB(255, 0 ,0)); //將點畫在畫布上 MyDocument *doc = (MyDocument *)GetDocument();//取得目前Document物件的指標 doc->AddPoint(point); //將軌跡點加入Document物件中 }} Document/View架構的應用視窗的重繪
afx_msg void OnLButtonUp(UINT, CPoint point){ ReleaseCapture(); } //釋放滑鼠訊息的接收權 DECLARE_DYNCREATE(MyView) //宣告為run-time類別 DECLARE_MESSAGE_MAP() //宣告訊息映射表}; IMPLEMENT_DYNCREATE(MyView, CView)//建立run-time類別 BEGIN_MESSAGE_MAP(MyView, CView) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() END_MESSAGE_MAP() //建立訊息映射表 class MyFrame : public CFrameWnd { public: DECLARE_DYNCREATE(MyFrame) //宣告為run-time類別 DECLARE_MESSAGE_MAP() //宣告訊息映射表 }; IMPLEMENT_DYNCREATE(MyFrame, CFrameWnd) //建立run-time類別 BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd) END_MESSAGE_MAP() //建立訊息映射表 Document/View架構的應用視窗的重繪
class MyApp : public CWinApp {public: BOOL InitInstance() {CDocument *doc; CSingleDocTemplate* DocTemplate; DocTemplate = new CSingleDocTemplate( //單文件樣版類別 IDR_MENU1, RUNTIME_CLASS(MyDocument), RUNTIME_CLASS(MyFrame), RUNTIME_CLASS(MyView)); AddDocTemplate(DocTemplate);//將文件樣版物件加入應用程式 doc = DocTemplate->CreateNewDocument(); //建立新文件 m_pMainWnd = DocTemplate->CreateNewFrame( doc, NULL ); //建立新的視窗框架 DocTemplate->InitialUpdateFrame ( (CFrameWnd*)m_pMainWnd, doc ); //起始化View物件 m_pMainWnd->ShowWindow ( SW_SHOW ); //顯示視窗 return true; } } a_app; Document/View架構的應用視窗的重繪
程式說明 • 利用MFC所提供的容器類別 Carray類別, 將滑鼠在視窗中移動時, 傳入的軌跡點存起來. • Carray的宣告方式: Carray<儲存的資料型別,讀取儲存資料的傳回值> 函數說明 • void AddPoint(Cpoint p)//將軌跡點加入容器中. • Cpoint GetPoint(int i)//從容器中取得軌跡點. • Int GetSize()//取得軌跡點的大小.