由浅入深MFC学习摘记--第三部分
由淺入深MFC摘要
- 工欲善其事必先利其器
- debug工具
- 5章 Application Framework
- General Purpose classes
- Windows API classes
- Application framework classes
- High level abstractions
- Afx 全局函數
- MFC 宏
- MFC 數據類型
- 6章 MFC 程序的生命周期
- 層次結構
- 所需函數庫
- 所需頭文件
- MFC簡易程序
- MFC 程序的來龍去脈
- CWinApp 和 CFrameWnd
- CWinApp
- CFrameWnd
- Application object
- WinMain
- 第一步 AfxWinInit
- 第二步 CWinApp::InitApplication
- 第三部 CMyWinApp::InitInstance
- 第四步 CFrameWnd::Create
- 窗口類名稱
- 窗口顯示與更新
- 第五步 CWinApp::Run
- Message Map 機制
- 小結
- CallBack 函數
- 空閑時間(idle time)的處理:OnIdle
- Dialog 與 Control
- 通用對話框(Common Dialogs)
- 章節總結
- 7章 MFC主程序
- 牢記 MFC 類層級架構
- UI接口
- Document、View
- 文檔模板
- view
- 主窗口
- 工具欄、狀態欄
- 鼠標拖放
- 消息映射
- 標準菜單
- 關于對話框
- CEditView
讀書筆記,記錄要點
工欲善其事必先利其器
mfc開發流程:
debug工具
debug版本的 mfc 程序,可以使用 trace 輸出調試信息。
或者使用afxDump
try catch的使用,catch有兩個參數,第一個為錯誤類型,第二個是錯誤對象。
5章 Application Framework
簡而言之就是通過使用應用框架(Application Framework)快速開發。
General Purpose classes
CObject:
- RTTI(執行時期型別鑒識)
- Persistence(對象保存)
- Dynamic Creation(動態生成)
- Diagnostic(錯誤診斷)
數據處理類別(collection classes)
理解為一些容器類
其他類
- CRect
- CSize
- CPoint
- CTime 絕對時間
- CTimeSpan 以秒數表示時間
- CString
異常處理類
Windows API classes
CWinThread:MFC 程序中的一個執行線程
CWinApp:整個MFC 應用程序。派生于CWinThread
CWnd: 所有窗口,不論是主框窗口、子框窗口、對話框、控制組件、view 視窗,都有一個對應的C++ 類。這些C++ 類統統派生于CWnd,也就是說,派生于CWnd 的類才能收到WM_ 窗口消息(WM_COMMAND 除外)。CWnd 對象有一個成員變量m_hWnd,就放著對應的窗口handle。所以,只要有一個CWnd 對象或CWnd 對象指針,就可以輕易獲得其窗口handle:
HWND hWnd = pWnd->m_hWnd;CCmdTarget:- CWnd 的父類。繼承它的類才能夠處理命令消息WM_COMMAND
GDI 類
DC 類
Menu 類
Application framework classes
Document:文檔
View:界面
High level abstractions
視圖UI 對象,例如工具欄CToolBar、狀態欄CStatusBar、對話框CDialogBar。
Afx 全局函數
| AfxWinInit | 被WinMain調用的一個函數,用做MFC GUI程序初始化的一部份 |
| AfxBeginThread | 開始一個線程 |
| AfxEndThread | 結束一個線程 |
| AfxFormatString1 | 類似printf 將字符串格式化 |
| AfxFormatString2 | 類似printf 將字符串格式化 |
| AfxMessageBox | 類似Windows API 函數MessageBox |
| AfxOutputDebugString | 把調試信息輸出到編譯器的輸出窗口 |
| AfxGetApp | 取得application object(CWinApp 衍生對象)的指針 |
| AfxGetMainWnd | 取得程序主窗口的指針。 |
| AfxGetInstance | 取得程序的instance handle。 |
| AfxRegisterClass | 以自定的WNDCLASS 注冊窗口類別;用來注冊窗口類的函數,用已注冊的窗口類可以創建一個窗口 |
MFC 宏
RTTI:(Run-Time Type Identification,運行時類型識別)
Serialization:序列化
Dynamic object creation:動態的對象生成
| DECLARE_DYNAMIC | 執行時期類別信息 |
| IMPLEMENT_DYNAMIC | 執行時期類別信息 |
| DECLARE_DYNCREATE | 動態生成 |
| IMPLEMENT_DYNCREATE | 動態生成 |
| DECLARE_SERIAL | 對象內容的文件讀寫 |
| IMPLEMENT_SERIAL | 對象內容的文件讀寫 |
| DECLARE_OLECREATE OLE | 對象的動態生成 |
| IMPLEMENT_OLECREATE OLE | 對象的動態生成 |
Message Mapping:消息映射
Command Routing: 命令傳遞
| DECLARE_MESSAGE_MAP | 聲明消息映射表數據結構 |
| BEGIN_MESSAGE_MAP | 開始消息映射表的建立 |
| ON_COMMAND | 增加消息映射表中的項目 |
| ON_CONTROL | 增加消息映射表中的項目 |
| ON_MESSAGE | 增加消息映射表中的項目 |
| ON_OLECMD | 增加消息映射表中的項目 |
| ON_REGISTERED_MESSAGE | 增加消息映射表中的項目 |
| ON_REGISTERED_THREAD_MESSAGE | 增加消息映射表中的項目 |
| ON_THREAD_MESSAGE | 增加消息映射表中的項目 |
| ON_UPDATE_COMMAND_UI | 增加消息映射表中的項目 |
| END_MESSAGE_MAP | 結束消息映射表的建立 |
MFC 數據類型
和Win32 程序(SDK 程序)共同使用的數據類型
| BOOL | Boolean 值(布爾值,不是TRUE 就是FALSE) |
| BSTR | 32-bit 字符指針(LPWSTR) |
| BYTE | 8-bit 整數,無符號 |
| COLORREF | 32-bit 數值,代表一個顏色值 |
| DWORD | 32-bit 整數,無符號 |
| LONG | 32-bit 整數,帶符號 |
| LPARAM | 32-bit 數值,做為窗口函數或callback 函數的一個參數 |
| LPCSTR | 32-bit 指針,指向一個常數字符串 |
| LPSTR | 32-bit 指針,指向一個字符串 |
| LPCTSTR | 32-bit 指針,指向一個常數字符串。此字符串可移植到Unicode 和DBCS(雙字節字集) |
| LPTSTR | 32-bit 指針,指向一個字符串。此字符串可移植到Unicode 和DBCS(雙位組字集) |
| LPVOID | 32-bit 指針,指向一個未指定類型的資料 |
| LPRESULT | 32-bit 數值,做為窗口函數或callback 函數的回返值 |
| UINT | 在Win16 中是一個16-bit 無符號整數,在Win32 中是一個32-bit 無符號整數。 |
| WNDPROC | 32-bit 指針,指向一個窗口函數 |
| WORD | 16-bit 整數,無符號 |
| WPARAM | 窗口函數的callback 函數的一個參數。在Win16 中是16 bits,在Win32中是32 bits。 |
下面這些是MFC 獨特的數據類型:
| POSITION | 一個數值,代表collection 對象(例如數組或串行)中的元素位置。常使用于MFC collection classes。 |
| LPCRECT | 32-bit 指針,指向一個不變的RECT 結構。 |
常用的宏定義:
#define NULL 0 #define far //Win32 不再有far 或near memory model,而是使用所謂的flat model。pascall 函數調用習慣 #define near //也被stdcall 函數調用習慣取而代之。 #define pascal __stdcall //在Windows programming演化過程中曾經出現的PASCAL、CALLBACK、WINAPI、APIENTRY //現在都代表相同的意義,就是stdcall函數調用習慣。 #define cdecl _cdecl #define CDECL _cdecl #define CALLBACK __stdcall // #define WINAPI __stdcall // #define WINAPIV __cdecl // #define APIENTRY WINAPI // #define APIPRIVATE __stdcall #define PASCAL __stdcall #define FAR far #define NEAR near #define CONST const typedef unsigned long DWORD; typedef int BOOL; typedef unsigned char BYTE; typedef unsigned short WORD; typedef float FLOAT; typedef FLOAT *PFLOAT; typedef BOOL near *PBOOL; typedef BOOL far *LPBOOL; typedef BYTE near *PBYTE; typedef BYTE far *LPBYTE; typedef int near *PINT; typedef int far *LPINT; typedef WORD near *PWORD; typedef WORD far *LPWORD; typedef long far *LPLONG; typedef DWORD near *PDWORD; typedef DWORD far *LPDWORD; typedef void far *LPVOID; typedef CONST void far *LPCVOID; typedef int INT; typedef unsigned int UINT; typedef unsigned int *PUINT; /* Types use for passing & returning polymorphic values */ typedef UINT WPARAM; typedef LONG LPARAM; typedef LONG LRESULT; typedef DWORD COLORREF; typedef DWORD *LPCOLORREF; typedef struct tagRECT {LONG left;LONG top;LONG right;LONG bottom; } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT; typedef const RECT FAR* LPCRECT; typedef struct tagPOINT {LONG x;LONG y; } POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT; typedef struct tagSIZE {LONG cx;LONG cy; } SIZE, *PSIZE, *LPSIZE;6章 MFC 程序的生命周期
層次結構
所需函數庫
Windows C Runtime 函數庫
(runtime 是一個通用抽象的術語,指的是計算機程序運行的時候所需要的一切代碼庫,框架,平臺等)
| LIBC.LIB | C Runtime 函數庫的靜態聯結版本 |
| MSVCRT.LIB | C Runtime 函數庫的動態聯結版本 |
| MSVCRTD.LIB | ‘D’ 表示使用于Debug 模式 |
DLL Import 函數庫
| GDI32.LIB | for GDI32.DLL |
| USER32.LIB | for USER32.DLL |
| KERNEL32.LIB | for KERNEL32 DLL |
MFC 函數庫 (AFX 函數庫)
所需頭文件
WINDOWS.H:大合集。
STDAFX.H:預編譯頭文件,用于包含其他頭文件
AFXWIN.H:MFC程序必須包含的頭文件,內含所有MFC類。(其中包含了AFX.H,AFX.H又包含了AFXVER_.H,AFXVER_.H又包含了AFXV_W32.H,AFXV_W32.H又包含了WINDOWS.H)
AFXEXT.H:使用工具欄、狀態欄需要包含的頭文件
AFXDLGS.H:對話框
AFXCMN.H:win95通用組件
AFXCOLL.H:Collections Classes
AFXDLLX.H:extension DLL
AFXRES.H:MFC標準資源
預編譯頭 就是將.H 文件第一次編譯后的結果貯存起來,第二次再編譯時就可以直接從磁盤中取出來用。有效減少大型應用程序的編譯時間。
MFC簡易程序
新建空項目,增加如下文件:
resource.h
需要在最后面增加一個回車
hello.rc
#include "resource.h" #include "afxres.h"JJHouRIcon ICON DISCARDABLE "JJHOUR.ICO" AFX_IDI_STD_FRAME ICON DISCARDABLE "JJHOUR.ICO"MainMenu MENU DISCARDABLE {POPUP "&Help"{MENUITEM "&About HelloMFC...", IDM_ABOUT} }AboutBox DIALOG DISCARDABLE 34, 22, 147, 55 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Hello" {ICON "JJHouRIcon", IDC_STATIC, 11, 17, 18, 20LTEXT "Hello MFC 4.0", IDC_STATIC, 40, 10, 52, 8LTEXT "Copyright 1996 Top Studio", IDC_STATIC, 40, 25, 100, 8LTEXT "J.J.Hou", IDC_STATIC, 40, 40, 100, 8DEFPUSHBUTTON "OK", IDOK, 105, 7, 32, 14, WS_GROUP }stdafx.h
// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, // but are changed infrequently#include <afxwin.h> // MFC core and standard componentsstdafx.cpp
// stdafx.cpp : source file that includes just the standard includes // Hello.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information#include "stdafx.h"hello.h
class CMyWinApp : public CWinApp { public:BOOL InitInstance(); // };//--------------------------------------------------------------- class CMyFrameWnd : public CFrameWnd { public:CMyFrameWnd(); // constructorafx_msg void OnPaint(); // for WM_PAINTafx_msg void OnAbout(); // for WM_COMMAND (IDM_ABOUT)private:DECLARE_MESSAGE_MAP() // Declare Message Mapstatic VOID CALLBACK LineDDACallback(int, int, LPARAM);//注意: callback 函數必須是"static",才能去除隱藏的'this' 指針。};hello.cpp
#include "stdafx.h" #include "hello.h" #include "resource.h"CMyWinApp theApp; // application object//--------------------------------------------------------------- // CMyWinApp's member //--------------------------------------------------------------- BOOL CMyWinApp::InitInstance() {m_pMainWnd = new CMyFrameWnd();m_pMainWnd->ShowWindow(m_nCmdShow);m_pMainWnd->UpdateWindow();return TRUE; } //--------------------------------------------------------------- // CMyFrameWnd's member //--------------------------------------------------------------- CMyFrameWnd::CMyFrameWnd() {Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu"); // "MainMenu" 定義于 RC 檔 } //--------------------------------------------------------------- BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_COMMAND(IDM_ABOUT, OnAbout)ON_WM_PAINT() END_MESSAGE_MAP() //--------------------------------------------------------------- void CMyFrameWnd::OnPaint() {CPaintDC dc(this);CRect rect;GetClientRect(rect);dc.SetTextAlign(TA_BOTTOM | TA_CENTER);//沿著定義的起始點與結束點組成的直線,重復執行指定的LINEDDAPROC回調函數。在線條的每個坐標點(結束點除外)上執行一次回調函數。::LineDDA(rect.right / 2, 0, rect.right / 2, rect.bottom / 2,(LINEDDAPROC)LineDDACallback, (LPARAM)(LPVOID)&dc); } //--------------------------------------------------------------- VOID CALLBACK CMyFrameWnd::LineDDACallback(int x, int y, LPARAM lpdc) {static char szText[] = "Hello, MFC,textdraw";((CDC*)lpdc)->TextOut(x, y, szText, sizeof(szText) - 1);for (int i = 1; i < 5000000; i++); // 純粹是為了延遲下降速度,以利觀察 } //--------------------------------------------------------------- void CMyFrameWnd::OnAbout() {CDialog about("AboutBox", this); //"AboutBox" 定義于RC 文檔about.DoModal(); }需要在工程目錄中增加一個圖標文件JJHOUR.ICO。
工程配置修改如下:
預處理器的內容不增加也可以編譯通過
其中說明:
#define CALLBACK __stdcall // 一種函數調用習慣 #define afx_msg // intentional placeholder故意安排的一個空位置。也許以后版本會用到。MFC 程序的來龍去脈
CWinApp 和 CFrameWnd
CWinApp 代表程序本體,包含了WinMain。
CFrameWnd代表一個主框窗口(Frame Window),包含了WndProc。
CWinApp
CWinApp部分成員:
class CWinApp : public CWinThread { // Attributes// Startup args (do not change)HINSTANCE m_hInstance;HINSTANCE m_hPrevInstance;LPTSTR m_lpCmdLine;int m_nCmdShow;// Running args (can be changed in InitInstance)LPCTSTR m_pszAppName; // human readable nameLPCTSTR m_pszRegistryKey; // used for registry entries public: // set in constructor to override defaultLPCTSTR m_pszExeName; // executable name (no spaces)LPCTSTR m_pszHelpFilePath; // default based on module pathLPCTSTR m_pszProfileName; // default based on app name public:// hooks for your initialization codevirtual BOOL InitApplication();// overrides for implementationvirtual BOOL InitInstance();virtual int ExitInstance();virtual int Run();virtual BOOL OnIdle(LONG lCount); };傳統上SDK 程序的WinMain(windows程序入口) 所完成的工作現在由CWinApp的三個函數完成:
virtual BOOL InitApplication(); virtual BOOL InitInstance(); virtual int Run();CWinThread部分成員
class CWinThread : public CCmdTarget { // AttributesCWnd* m_pMainWnd; // main window (usually same AfxGetApp()->m_pMainWnd)CWnd* m_pActiveWnd; // active main window (may not be m_pMainWnd)// only valid while runningHANDLE m_hThread; // this thread's HANDLEDWORD m_nThreadID; // this thread's IDint GetThreadPriority();BOOL SetThreadPriority(int nPriority); // OperationsDWORD SuspendThread();//掛起線程DWORD ResumeThread();//喚醒線程 // Overridables// thread initializationvirtual BOOL InitInstance();// running and idle processingvirtual int Run();virtual BOOL PreTranslateMessage(MSG* pMsg);virtual BOOL PumpMessage(); // low level message pumpvirtual BOOL OnIdle(LONG lCount); // return TRUE if more idle processing public:// valid after constructionAFX_THREADPROC m_pfnThreadProc; ... };CFrameWnd
CFrameWnd實現窗口過程的函數如下:
class CMyFrameWnd : public CFrameWnd { public:CMyFrameWnd();afx_msg void OnPaint();//處理 WM_PAINTafx_msg void OnAbout();//處理 WM_COMMAND 的 IDM_ABOUTDECLARE_MESSAGE_MAP() };通過宏DECLARE_MESSAGE_MAP、 BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)、
END_MESSAGE_MAP()實現消息與對應函數的映射。
Application object
首先查看mfc的代碼WinMain.cpp
int AFXAPI AfxWinMain (...) {CWinApp* pApp = AfxGetApp();AfxWinInit(...);pApp->InitApplication();pApp->InitInstance();nReturnCode = pApp->Run();AfxWinTerm(); }然后是我們模擬的例子hello.cpp
CMyWinApp theApp; // application object BOOL CMyWinApp::InitInstance() {m_pMainWnd = new CMyFrameWnd();m_pMainWnd->ShowWindow(m_nCmdShow);m_pMainWnd->UpdateWindow();return TRUE; } CMyFrameWnd::CMyFrameWnd() {Create(NULL, "Hello MFC", ...,"MainMenu"); } void CMyFrameWnd::OnPaint() { ... } void CMyFrameWnd::OnAbout() { ... } BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_COMMAND(IDM_ABOUT, OnAbout)ON_WM_PAINT() END_MESSAGE_MAP()theApp就是一個應用程序對象(Application object)。每一個MFC 應用程序都有一個,而且也只有這么一個。當執行hello.cpp時,這個全局對象被創建,執行其構造函數,此構造函數在父類CWinApp中:
CWinApp::CWinApp(LPCTSTR lpszAppName) {m_pszAppName = lpszAppName;// initialize CWinThread stateAFX_MODULE_THREAD_STATE* pThreadState = AfxGetModuleThreadState();pThreadState->m_pCurrentWinThread = this;m_hThread = ::GetCurrentThread();m_nThreadID = ::GetCurrentThreadId();// initialize CWinApp stateAFX_MODULE_STATE* pModuleState = AfxGetModuleState();pModuleState->m_pCurrentWinApp = this;// in non-running state until WinMainm_hInstance = NULL;m_pszHelpFilePath = NULL;m_pszProfileName = NULL;m_pszRegistryKey = NULL;m_pszExeName = NULL;m_lpCmdLine = NULL;m_pCmdInfo = NULL;... }WinMain
theApp創建后就會調用WinMain函數。WinMain函數實際上是通過調用AfxWinMain函數來完成它的功能的。
_tWinMain 調用 AfxWinMain(APPMODUL.CPP)。
以上代碼簡化下就是:
int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow) {int nReturnCode = -1;CWinApp* pApp = AfxGetApp();AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);pApp->InitApplication();pApp->InitInstance();nReturnCode = pApp->Run();AfxWinTerm();return nReturnCode; }AfxGetApp 是一個全局函數,定義于(afxwin1.inl)中
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp(){ return afxCurrentWinApp; }afxCurrentWinApp 定義于(afxwin.h*)中
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinAppAfxGetApp 其實就是取得CMyWinApp 對象指針即theApp
所以afxwinmain就相當于:
也就是順序調用:
CMyWinApp::InitApplication(); CMyWinApp::InitInstance(); CMyWinApp::Run();考慮重寫就是:
CWinApp::InitApplication(); //因為 CMyWinApp 并沒有改寫InitApplication CMyWinApp::InitInstance(); //因為 CMyWinApp 改寫了 InitInstance CWinApp::Run(); //因為 CMyWinApp 并沒有改寫Run第一步 AfxWinInit
AfxWinInit 是winmain中首先調用的,代碼如下
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow) {ASSERT(hPrevInstance == NULL);// set resource handlesAFX_MODULE_STATE* pState = AfxGetModuleState();pState->m_hCurrentInstanceHandle = hInstance;pState->m_hCurrentResourceHandle = hInstance;// fill in the initial state for the applicationCWinApp* pApp = AfxGetApp();if (pApp != NULL){// Windows specific initialization (not done if no CWinApp)pApp->m_hInstance = hInstance;pApp->m_hPrevInstance = hPrevInstance;pApp->m_lpCmdLine = lpCmdLine;pApp->m_nCmdShow = nCmdShow;pApp->SetCurrentHandles();}// initialize thread specific data (for main thread)if (!afxContextIsDLL)AfxInitThread();return TRUE; }其中 AfxInitThread:
void AFXAPI AfxInitThread() {if (!afxContextIsDLL){// attempt to make the message queue biggerfor (int cMsg = 96; !SetMessageQueue(cMsg) && (cMsg -= 8); );// set message filter proc_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx(WH_MSGFILTER,_AfxMsgFilterHook, NULL, ::GetCurrentThreadId());// intialize CTL3D for this thread_AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;if (pCtl3dState->m_pfnAutoSubclass != NULL)(*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle());// allocate thread local _AFX_CTL3D_THREAD just for automatic termination_AFX_CTL3D_THREAD* pTemp = _afxCtl3dThread;} }第二步 CWinApp::InitApplication
BOOL CWinApp::InitApplication() {if (CDocManager::pStaticDocManager != NULL){if (m_pDocManager == NULL)m_pDocManager = CDocManager::pStaticDocManager;CDocManager::pStaticDocManager = NULL;}if (m_pDocManager != NULL)m_pDocManager->AddDocTemplate(NULL);elseCDocManager::bStaticInit = FALSE;return TRUE; }第三部 CMyWinApp::InitInstance
即調用hello.cpp中重寫的 InitInstance, 在該處創建主窗口。
應用程序一定要改寫虛擬函數InitInstance,因為它在CWinApp 中只是個空函數。
第四步 CFrameWnd::Create
new CMyFrameWnd 對象時,會進入構造函數:
CFrameWnd::Create 的參數如下
BOOL Create( LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle = WS_OVERLAPPEDWINDOW,const RECT& rect = rectDefault,CWnd* pParentWnd = NULL,LPCTSTR lpszMenuName = NULL,DWORD dwExStyle = 0,CCreateContext* pContext = NULL );參數說明:
其簡略實現如下:
//WINFRM.cpp BOOL CFrameWnd::Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT& rect,CWnd* pParentWnd,LPCTSTR lpszMenuName,DWORD dwExStyle,CCreateContext* pContext) {HMENU hMenu = NULL;if (lpszMenuName != NULL){// load in a menu that will get destroyed when window gets destroyedHINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);hMenu = ::LoadMenu(hInst, lpszMenuName);}m_strTitle = lpszWindowName; // save title for laterCreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext);return TRUE; }CFrameWnd 沒有 CreateEx 函數,所以調用繼承來的 CWnd::CreateEx;
//WINCORE.CPP BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,int x, int y, int nWidth, int nHeight,HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam) {// allow modification of several common create parametersCREATESTRUCT cs;cs.dwExStyle = dwExStyle;cs.lpszClass = lpszClassName;cs.lpszName = lpszWindowName;cs.style = dwStyle;cs.x = x;cs.y = y;cs.cx = nWidth;cs.cy = nHeight;cs.hwndParent = hWndParent;cs.hMenu = nIDorHMenu;cs.hInstance = AfxGetInstanceHandle();cs.lpCreateParams = lpParam;PreCreateWindow(cs);AfxHookWindowCreate(this); //HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);... }PreCreateWindow 是虛函數,CWnd 和CFrameWnd 之中都有定義,這里調用是CFrameWnd::PreCreateWindow
//WINFRM.CPP // CFrameWnd second phase creation BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background}... }宏中AfxDeferRegisterClass定義如下:
//AFXIMPL.H #define AfxDeferRegisterClass(fClass) \((afxRegisteredClasses & fClass) ? TRUE : AfxEndDeferRegisterClass(fClass))如果變量afxRegisteredClasses 的值顯示系統已經注冊了fClass 這種窗口類別,MFC 就啥也不做;否則就調用AfxEndDeferRegisterClass(fClass)注冊。
// in AFXWIN.H #define afxRegisteredClasses AfxGetModuleState()->m_fRegisteredClasses注冊函數如下:
BOOL AFXAPI AfxEndDeferRegisterClass(short fClass) {BOOL bResult = FALSE;// common initializationWNDCLASS wndcls;memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaultswndcls.lpfnWndProc = DefWindowProc;wndcls.hInstance = AfxGetInstanceHandle();wndcls.hCursor = afxData.hcurArrow;AFX_MODULE_STATE* pModuleState = AfxGetModuleState();if (fClass & AFX_WND_REG){// Child windows - no brush, no icon, safest default class styleswndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.lpszClassName = _afxWnd;bResult = AfxRegisterClass(&wndcls);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WND_REG;}else if (fClass & AFX_WNDOLECONTROL_REG){// OLE Control windows - use parent DC for speedwndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.lpszClassName = _afxWndOleControl;bResult = AfxRegisterClass(&wndcls);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDOLECONTROL_REG;}else if (fClass & AFX_WNDCONTROLBAR_REG){// Control bar windowswndcls.style = 0; // control bars don't handle double clickwndcls.lpszClassName = _afxWndControlBar;wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);bResult = AfxRegisterClass(&wndcls);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDCONTROLBAR_REG;}else if (fClass & AFX_WNDMDIFRAME_REG){// MDI Frame window (also used for splitter window)wndcls.style = CS_DBLCLKS;wndcls.hbrBackground = NULL;bResult = RegisterWithIcon(&wndcls, _afxWndMDIFrame,AFX_IDI_STD_MDIFRAME);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDMDIFRAME_REG;}else if (fClass & AFX_WNDFRAMEORVIEW_REG){// SDI Frame or MDI Child windows or views - normal colorswndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);bResult = RegisterWithIcon(&wndcls, _afxWndFrameOrView,AFX_IDI_STD_FRAME);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;}else if (fClass & AFX_WNDCOMMCTLS_REG){InitCommonControls();bResult = TRUE;pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG;}return bResult; }其中的的常數定義:
#define AFX_WNDCLASS(s) \ _T("Afx") _T(s) _T("42") _STATIC_SUFFIX _UNICODE_SUFFIX _DEBUG_SUFFIX #define AFX_WND AFX_WNDCLASS("Wnd") #define AFX_WNDCONTROLBAR AFX_WNDCLASS("ControlBar") #define AFX_WNDMDIFRAME AFX_WNDCLASS("MDIFrame") #define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView") #define AFX_WNDOLECONTROL AFX_WNDCLASS("OleControl")所以debug編譯時這5個窗口的名稱是
"AfxWnd42d" "AfxControlBar42d" "AfxMDIFrame42d" "AfxFrameOrView42d" "AfxOleControl42d"AfxEndDeferRegisterClass注冊中使用的函數:
static BOOL AFXAPI RegisterWithIcon(WNDCLASS* pWndCls,LPCTSTR lpszClassName, UINT nIDIcon) {pWndCls->lpszClassName = lpszClassName;HINSTANCE hInst = AfxFindResourceHandle(MAKEINTRESOURCE(nIDIcon), RT_GROUP_ICON);if ((pWndCls->hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(nIDIcon))) == NULL){// use default iconpWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);}return AfxRegisterClass(pWndCls); }BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass) {WNDCLASS wndcls;if (GetClassInfo(lpWndClass->hInstance,lpWndClass->lpszClassName, &wndcls)){// class already registeredreturn TRUE;}::RegisterClass(lpWndClass);...return TRUE; }不同類的PreCreateWindow 成員函數都是在窗口產生之前一刻被調用,準備用來注冊窗口類。如果我們指定的窗口類是NULL,那么就使用系統默認類。
// in WINCORE.CPP BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs) {{AfxDeferRegisterClass(AFX_WND_REG);...cs.lpszClass = _afxWnd; //(這表示CWnd 使用的窗口類別是_afxWnd)}return TRUE; }// in WINFRM.CPP BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);...cs.lpszClass = _afxWndFrameOrView; //(這表示CFrameWnd 使用的窗口類別是_afxWndFrameOrView)} ... }// in WINMDI.CPP BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG);//(這表示CMDIFrameWnd 使用的窗口類別是_afxWndMDIFrame)...cs.lpszClass = _afxWndMDIFrame; } return TRUE; }// in WINMDI.CPP BOOL CMDIChildWnd::PreCreateWindow(CREATESTRUCT& cs) {...return CFrameWnd::PreCreateWindow(cs); //(這表示CMDIChildWnd 使用的窗口類別是_afxWndFrameOrView) } // in VIEWCORE.CPP BOOL CView::PreCreateWindow(CREATESTRUCT & cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);//(這表示CView 使用的窗口類別是_afxWndFrameOrView)... cs.lpszClass = _afxWndFrameOrView; } ... }窗口類名稱
選擇vs自帶安裝工具:
選中并拖動 查找工具 圖標到指定窗口上,可以查看窗口的信息
但是這里沒有看到示例的Afx:x:y:z:w 格式窗口名稱。
此格式中:
這種情況如果修改窗口名稱,需要在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)中修改。
窗口顯示與更新
調用ShowWindow 顯示窗口
調用UpdateWindow向新窗口發送WM_PAINT消息
第五步 CWinApp::Run
窗口注冊→窗口產生并顯示→調用UpdateWindow→產生WM_PAINT 消息,等待被處理。
此時該執行pApp->run(),由于沒有重寫該方法(一般也不用重寫),所以相當于調用CMyWinApp::Run()
//APPCORE.CPP int CWinApp::Run() {if (m_pMainWnd == NULL && AfxOleGetUserCtrl()){// Not launched /Embedding or /Automation, but has no main window!TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quittingapplication.\n");AfxPostQuitMessage(0);}return CWinThread::Run(); } // THRDCORE.CPP int CWinThread::Run() {// for tracking the idle time stateBOOL bIdle = TRUE;LONG lIdleCount = 0;// acquire and dispatch messages until a WM_QUIT message is received.for (;;){// phase1: check to see if we can do idle workwhile (bIdle &&!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)){// call OnIdle while in bIdle stateif (!OnIdle(lIdleCount++))bIdle = FALSE; // assume "no idle" state}// phase2: pump messages while availabledo{// pump message, but quit on WM_QUITif (!PumpMessage())return ExitInstance();// reset "no idle" state after pumping "normal" messageif (IsIdleMessage(&m_msgCur)){bIdle = TRUE;lIdleCount = 0;}} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));}ASSERT(FALSE); // not reachable }BOOL CWinThread::PumpMessage() {if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)){return FALSE;}// process this messageif (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)){::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur);}return TRUE; }獲得的消息后SDK 程序的作法是調用DispatchMessage,把消息丟給窗口函數。而MFC在AfxEndDeferRegisterClass 源代碼中注冊四種窗口類之前已經指定窗口函數為:
wndcls.lpfnWndProc = DefWindowProc;
雖然窗口函數被指定為DefWindowProc 成員函數,但事實上消息是一個名為AfxWndProc 的全局函數處理。
WinMain 由MFC 提供,窗口類由MFC 注冊完成,窗口函數也由MFC提供。
Message Map 機制
mfc的消息映射步驟:
如上,就把WM_PAINT 導到OnPaint 函數, 把WM_COMMAND(IDM_ABOUT)導到OnAbout 函數去了。
Message Map 機制中對于消息與函數間的映射關系也規定以下三種:
- 標準Windows 消息(WM_xxx)的映射規則:
| ON_WM_CHAR | WM_CHAR | OnChar |
| ON_WM_CLOSE | WM_CLOSE | OnClose |
| ON_WM_CREATE | WM_CREATE | OnCreate |
| ON_WM_DESTROY | WM_DESTROY | OnDestroy |
| ON_WM_LBUTTONDOWN | WM_LBUTTONDOWN | OnLButtonDown |
| ON_WM_LBUTTONUP | WM_LBUTTONUP | OnLButtonUp |
| ON_WM_MOUSEMOVE | WM_MOUSEMOVE | OnMouseMove |
| ON_WM_PAINT | WM_PAINT | OnPaint |
-
命令消息(WM_COMMAND)的一般映射規則:
ON_COMMAND(<id>,<memberFxn>)
例如:
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_COMMAND(IDM_FILENEW, OnFileNew)
ON_COMMAND(IDM_FILEOPEN, OnFileOpen)
ON_COMMAND(IDM_FILESAVE, OnFileSave) -
Notification 消息(由控件產生,例如BN_xxx)的映射機制的宏分為幾種(因為控件本就分為幾種):
| Button | ON_BN_CLICKED(,) | memberFxn |
| ComboBox | ON_CBN_DBLCLK(,) | memberFxn |
| Edit | ON_EN_SETFOCUS(,) | memberFxn |
| ListBox | ON_LBN_DBLCLK(,) | memberFxn |
如果沒有指定消息映射函數,消息會在基類中處理,這個消息在基類流竄動作稱為「Message Routing」
小結
- 創建application object (即CMyWinApp theApp)
- 執行程序入口函數,調用Afx WinMain ,調用 AfxWinInit,又調用AfxInitThread
- Afx WinMain 調用 InitApplication。這是CWinApp 的虛函數,我們通常不改寫它。
- AfxWinMain 調用 InitInstance。這是CWinApp 的虛函數,我們必須改寫它。
- CMyWinApp::InitInstance new 一個 CMyFrameWnd 對象。
- CMyFrameWnd 構造中調用Create,產生主窗口。我們在Create 參數中指定的窗口類別是NULL, 于是MFC 根據窗口種類, 自行為我們注冊一個名為"AfxFrameOrView42d" 的窗口類。
- 回到 InitInstance 中繼續執行ShowWindow,顯示窗口
- 執行UpdateWindow,于是發出 WM_PAINT。
- 回到AfxWinMain,執行Run,進入消息循環
- 程序獲得WM_PAINT 消息(通過CWinApp::Run 中的::GetMessage 循環)
- WM_PAINT 經由::DispatchMessage 送到窗口函數CWnd::DefWindowProc 中。
- CWnd::DefWindowProc 將消息發送到消息映射表格(Message Map)
- 調用映射中對應的函數。此函數是應用程序利用BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP 之間的宏映射的
- 標準消息的處理函數有標準命名,例如WM_PAINT 必然由OnPaint 處理。
- 使用者選按【File/Close】,于是發出 WM_CLOSE。
- CMyFrameWnd 并沒有設置 WM_CLOSE 處理函數,于是交給默認處理函數
- 默認函數對于WM_CLOSE 的處理方式是調用::DestroyWindow, 并因而發出 WM_DESTROY
- 默認 WM_DESTROY 處理方式是調用::PostQuitMessage,因此發出WM_QUIT。
- CWinApp::Run 收到 WM_QUIT 后會結束其內部之消息循環, 然后調用ExitInstance,這是CWinApp 的一個虛函數。
- 如果 CMyWinApp 改寫了 ExitInstance , 那么 CWinApp::Run 所調用的就是CMyWinApp::ExitInstance,否則就是 CWinApp::ExitInstance。
- 最后回到 AfxWinMain,執行 AfxWinTerm,結束程序
CallBack 函數
LineDDA函數:
void WINAPI LineDDA(int, int, int, int, LINEDDAPROC, LPARAM);
利用前四個參數指定屏幕上任意兩點的(x,y) 座標,此函數將以Bresenham 算法計算出通過兩點之直線中的每一個屏幕圖素座標;每計算出一個坐標,就通知由LineDDA 第五個參數所指定的callback 函數。
LineDDA 的第六個(最后一個)參數可以視應用程序的需要傳遞一個32 位指針.
LineDDA 的 callback 函數:
typedef void (CALLBACK* LINEDDAPROC)(int, int, LPARAM);
LineDDA 并不屬于任何一個MFC 類別,因此調用它必須使用C++ 的"scope operator"(也就是::)
::LineDDA(rect.right/2, 0, rect.right/2, rect.bottom/2,(LINEDDAPROC) LineDDACallback, (LPARAM) (LPVOID) &dc);
其中LineDDACallback 是我們準備的callback 函數,必須在類中先有聲明:
class CMyFrameWnd : public CFrameWnd { ... private:static VOID CALLBACK LineDDACallback(int,int,LPARAM); };類的成員函數是一個callback 函數, 你必須聲明它為"static",才能把C++ 編譯器加到函數的一個隱藏參數this 去掉
空閑時間(idle time)的處理:OnIdle
CWinThreadd::OnIdle函數:
virtual BOOL OnIdle(LONG lCount);
lCount 是系統傳進來的一個值,表示自從上次有消息進來,到現在,OnIdle 已經被調用了多少次。
lCount 會持續累增,直到CWinThread::Run 的消息循環又獲得了一個消息,此值才重置為0
測試重寫onIdle
重寫app的OnIdle函數:
窗口類增加IdleTimeHandler函數:
app OnIdle函數的定義:
BOOL ChelloworldApp::OnIdle(LONG lCount) {// TODO: 在此添加專用代碼和/或調用基類ChelloworldDlg* pWnd = (ChelloworldDlg*)m_pMainWnd;pWnd->IdleTimeHandler(lCount);return TRUE;return CWinApp::OnIdle(lCount); }窗口類函數CMyFrameWnd::IdleTimeHandler的實現
void CMyFrameWnd::IdleTimeHandler(LONG lCount) {CString str;CRect rect(10,10,200,30);CDC* pDC = new CClientDC(this);str.Format(_T("%010d"), lCount);pDC->DrawText(str, &rect, DT_LEFT | DT_TOP); }運行CWinThread::Run,則需要pThread->InitInstance返回才行,但模態對話框程序有點特殊,會直接阻塞在DoMoDal函數中,然后運行自己的消息循環,CXXXApp::InitInstace在程序沒結束時,不會返回,故OnIdle不會得到運行;我用的是基于文檔的mfc向導。
Dialog 與 Control
“關于”對話框的創建:
當使用者按下【File/About 】菜單, 根據Message Map 的設定,WM_COMMAND(IDM_ABOUT)被送到OnAbout 函數去。我們首先在OnAbout 中產生一個CDialog 對象,名為about。接下來直接調用CDialog::DoModal,對話框就開始運行。
通用對話框(Common Dialogs)
| CCommonDialog | 以下各類別的父類別 |
| CFileDialog | File 對話框(Open 或Save As) |
| CPrintDialog | Print 對話框 |
| CFindReplaceDialog | Find and Replace 對話框 |
| CColorDialog | Color 對話框 |
| CFontDialog | Font 對話框 |
| CPageSetupDialog | Page Setup 對話框(MFC 4.0 新增) |
| COleDialog | Ole 相關對話框 |
如打開文件的操作:
第五個參數szFilters 指定使用者可以選擇的文件型態,
最后一個參數是父窗口。
當DoModal 回返,我們可以利用CFileDialog 的成員函數
GetPathName 取得完整的文件路徑
也可以使用另一個成員函數GetFileName 取其不含路徑的文件名稱
或GetFileTitle 取得既不含路徑亦不含擴展名的文件名稱。
章節總結
一開始是一個派生自CWinApp 的全局對象application object,然后是一個隱藏的WinMain 函數,調用application object 的InitInstance 函數,將程序初始化。初始化動作包括構造一個窗口對象(CFrameWnd 對象),而其構造函數又調用CFrameWnd::Create 產生真正的窗口(并在產生之前要求MFC注冊窗口類別)。窗口產生后WinMain 又調用Run 激活消息循環,將WM_COMMAND(IDM_ABOUT)和WM_PAINT 分別交給成員函數OnAbout 和OnPaint 處理。
7章 MFC主程序
牢記 MFC 類層級架構
UI接口
document窗口:如word、excel可以操作多份文件,每一個文件作為一個 document。
文件窗口
關于窗口
打印及預覽窗口
Document、View
Document 是數據
View 是數據的表現、視圖
文檔模板
View 本身雖然已經是一個窗口,其外部卻必須再包裝一個外框窗口做為舞臺。這樣是為了讓View 可以獨立地放置于「MDI Document Frame 窗口」或「SDI Document Frame 窗口」或「OLE Document Frame 窗口」等各種應用之中。也可以說,Document Frame 窗口是View 窗口的一個容器。
文檔內容、文檔視圖、文檔的窗口是一個組合,程序打開一個資料,就會產生對應的三個對象:
三個對象由 Document Template 對象來管理。
其中,構造函數:
CMultiDocTemplate::CMultiDocTemplate(UINT nIDResource,CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass );中,參數:
nIDResource:這是一個資源ID,表示此一文件類型(文件格式)所使用的資源。
pDocClass: 這是一個指針,指向Document 類
pFrameClass: 這是一個指針,指向Child Frame 類
pViewClass : 這是一個指針,指向View 類
可以獲取CDocTemplate文件類型用到的七個常量,如 filterExt:
CDocTemplate *pDoc1 = this->m_pDocument->GetDocTemplate(); CString strDefExt; pDoc1->GetDocString(strDefExt, CDocTemplate::filterExt);view
view中常用的相應函數:
OnDraw:畫面重繪
OnLButtonDown:鼠標左鍵按下(作為鼠標事件的舉例)
主窗口
創建過程:
由 CMainFrame 的 LoadFrame 到 OnCreate。
工具欄、狀態欄
工具欄相關 主要動作和設置:
LoadToolBar 知道如何把BITMAP 資源和TOOLBAR 資源搭配起來,完成工具欄的設定。如果不是使用資源工具來編輯工具欄,BITMAP 資源和TOOLBAR 資源就可能格數不符,那是不被允許的。
狀態欄相關的動作:
鼠標拖放
主要講述在initInstance中的應用的三個函數:
BOOL CScribbleApp::InitInstance() {// Enable drag/drop openm_pMainWnd->DragAcceptFiles();// Enable DDE Execute openEnableShellOpen();RegisterShellFileTypes(TRUE); }CWnd::DragAcceptFile(BOOL bAccept=TRUE); 參數TRUE 表示你的主窗口以及每一個子窗口(文件窗口)都愿意接受來自Shell 的拖放文件。CFrameWnd 內有一個OnDropFiles 成員函數,負責對WM_DROPFIELS 消息做出反應,它會通知 application 對象的OnOpenDocument,并夾帶被拖放的文件的名稱。
CWinApp::EnableShellOpen(); 當使用者在Shell 中對著本程序的文件文件快按兩下時,本程序能夠打開文件并讀內容。通常此函數后面跟隨著RegisterShellFileTypes。(我理解為文件默認打開的應用程序注冊。)
CWinApp::RegisterShellFileTypes(); 此函數將向Shell 注冊本程序的文件類型。有了這樣的注冊動作,使用者在Shell 的雙擊動作才有著力點。這個函數搜尋Document Template 串行中的每一種文件類型,然后把它加到系統所維護的registry中。
消息映射
每一個派生自CCmdTarget 的類別都可以有自己的Message Map 以處理消息。
需要在頭文件中聲明DECLARE_MESSAGE_MAP 宏,
定義中BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP 兩個宏 之間,聲明消息和對應的處理函數。
標準菜單
菜單消息及其映射函數
| File | ||
| New | ID_FILE_NEW | CWinApp::OnFileNew |
| Open | ID_FILE_OPEN | CWinApp::OnFileOpen |
| Close | ID_FILE_CLOSE | CDocument::OnFileClose |
| Save | ID_FILE_SAVE | CDocument::OnFileSave |
| Save As | ID_FILE_SAVEAS | CDocument::OnFileSaveAs |
| ID_FILE_PRINT | CView::OnFilePrint | |
| Pre&view | ID_FILE_PRINT_PREVIEW CView::OnFilePrintPreview | |
| Setup | ID_FILE_PRINT_SETUP CWinApp::OnFilePrintSetup | |
| “Recent File Name” | ID_FILE_MRU_FILE1~4 | CWinApp::OnOpenRecentFile |
| Exit | ID_APP_EXIT | CWinApp::OnFileExit |
| Edit | ||
| Undo | ID_EDIT_UNDO | None |
| Cut | ID_EDIT_CUT | None |
| Copy | ID_EDIT_COPY | None |
| Paste | ID_EDIT_PASTE | None |
| View | ||
| Toolbar | ID_VIEW_TOOLBAR | FrameWnd::OnBarCheck Yes |
| Status | Bar ID_VIEW_STATUS_BAR | FrameWnd::OnBarCheck Yes |
| Window(MDI only) | ||
| New | Window ID_WINDOW_NEW | MDIFrameWnd::OnWindowNew Yes |
| Cascade | ID_WINDOW_CASCADE | MDIFrameWnd::OnWindowCmd Yes |
| Tile | ID_WINDOW_TILE_HORZ | MDIFrameWnd::OnWindowCmd Yes |
| Arrange Icons | ID_WINDOW_ARRANGE | MDIFrameWnd::OnWindowCmd Yes |
| Help | ||
| About AppName | ID_APP_ABOUT | None |
關于對話框
響應函數
BEGIN_MESSAGE_MAP(CScribbleApp, CWinApp)ON_COMMAND(ID_APP_ABOUT, OnAppAbout) END_MESSAGE_MAP()CAboutDlg 是CDialog 的派生類
CEditView
替換基類從CView 改為CEditView
界面變為可以輸入。
總結
以上是生活随笔為你收集整理的由浅入深MFC学习摘记--第三部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PyQt5-在窗口上绘制文本 QPain
- 下一篇: 移动互联网商业契机