在VS中開發VC++應用程序 ,最常用就是MFC應用,它下在又分爲常規應用程序、ActiveX控件和DLL程序,那麼常規的應用程序根據應用模式又分爲Dialog對話框程序、SDI單文檔、MDI多文檔以及多頂級文檔幾類,下面我們分別介紹一下這幾類應用程序的區別。首先在VS的“文件”菜單中選擇“新建”-》“項目”,選擇VC++-》MFC,其主要界面如下:
在選擇應用類型的同時需要輸入工程項目的名稱和存儲路徑,如下圖所示:
在MFC應用程序類型選項這裏從下拉框中選擇相應的類型即可,其他參數設置跳過,直接點擊“完成”按鈕。
一、對話框程序
對話框程序是一些桌面應用程序開發最常用的程序模板,比如咱們比較熟悉的瑞星殺毒、360、QQ、網易音樂、電腦管家等程序屬於典型的對話框程序 ,當然也可以用其他方式來實現,但對話框程序是實現各種界面最簡單的方式。這種應用程序可以將MFC的各類控件直接拖放到界面上,具有所見即所得的效果,但也僅限於一般簡單的界面,如果需要漂亮的界面,則需要大量的自繪製功能,藉助一些圖片或者界面庫進行美化。對於初學者來說需要了解三個視圖:
1、解決方案資源管理器:這裏主要是管理工程項目裏的頭文件、源文件即CPP以及資源文件,如上圖的最左側的截圖;
2、類視圖:是項目中創建的所有類的列表,對話框程序默認創建了一個CAboutDlg類(關於)、APP類(整個應用程序類)、主對話框類;
3、資源視圖:這裏是資源文件的細瀏覽和管理,包括對話框資源(雙擊對話框就可以打開界面進入編輯,點擊右鍵選擇屬性可以修改基本信息屬性)、ICON(圖標)、String Table(字符串表,資源ID、編號及描述的對應描述表)、Version(版本信息)。
對於MFC創建的Windows應用程序咱們一定要了解清楚程序執行和初始化的順序,這對我們編程很重要,對於對話框程序程序執行流程如下圖:
其中APP類是整個應用程序啓動的入口,一些全局變化或者配置參數的初始化建議可放在APP的InitInstance() 函數中,在InitInstance() 函數中最後創建並顯示主對話框,而主對話框類的OnInitDialog()函數也是很重要一個函數,主要是用於一些初始化工作,在這個函數裏初始時對話框及所有的界面控件都已經構建完成。如果需要和對話框同步創建一些子控件則可添加OnCreate函數,在此函數中完成創建。幾個核心的類函數代碼如下:
以下是APP主應用程序類的初始化函數,在這裏主要添加相應的全局變化的初始化代碼。
BOOL CMFCAppTestApp::InitInstance()
{
// 如果一個運行在 Windows XP 上的應用程序清單指定要
// 使用 ComCtl32.dll 版本 6 或更高版本來啓用可視化方式,
//則需要 InitCommonControlsEx()。 否則,將無法創建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 將它設置爲包括所有要在應用程序中使用的
// 公共控件類。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
// 創建 shell 管理器,以防對話框包含
// 任何 shell 樹視圖控件或 shell 列表視圖控件。
CShellManager *pShellManager = new CShellManager;
// 激活“Windows Native”視覺管理器,以便在 MFC 控件中啓用主題
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
// 標準初始化
// 如果未使用這些功能並希望減小
// 最終可執行文件的大小,則應移除下列
// 不需要的特定初始化例程
// 更改用於存儲設置的註冊表項
// TODO: 應適當修改該字符串,
// 例如修改爲公司或組織名
SetRegistryKey(_T("應用程序嚮導生成的本地應用程序"));
CMFCAppTestDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置處理何時用
// “確定”來關閉對話框的代碼
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置處理何時用
// “取消”來關閉對話框的代碼
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "警告: 對話框創建失敗,應用程序將意外終止。\n");
TRACE(traceAppMsg, 0, "警告: 如果您在對話框上使用 MFC 控件,則無法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
}
// 刪除上面創建的 shell 管理器。
if (pShellManager != nullptr)
{
delete pShellManager;
}
#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)
ControlBarCleanUp();
#endif
// 由於對話框已關閉,所以將返回 FALSE 以便退出應用程序,
// 而不是啓動應用程序的消息泵。
return FALSE;
}
下面是主對話框的初始化函數,VS會自動生成初始化代碼,只需要在這個函數中添加你自己的初始化代碼即可。
BOOL CMFCAppTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 將“關於...”菜單項添加到系統菜單中。
// IDM_ABOUTBOX 必須在系統命令範圍內。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 設置此對話框的圖標。 當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
二、SDI單文檔程序
SDI單文檔應用程序也是常有的程序開發模板,比如我們常用的“記事本”、“畫圖”、“foxmail”等應用就屬於單文檔應用,這種程序的特點是帶有菜單欄、工具欄、狀態欄以及可能還包括屬性頁等,VS自動爲程序生成了相應的初始代碼,我們沒有做任何編寫工作就得到了一個“完整”的應用程序。但這一類程序同一時刻只能打開一個文檔,如果在同一應用裏再打開新文檔需要關閉現有文檔,VS2017默認生成的單文檔應用如下圖所示:
SDI裏咱們只要瞭解除最核心的幾個類即可即CXXApp類、CMainFrame、CXXXView、CXXXDoc,項目管理視圖中核心視圖如下:
在資源視圖中相對對話框程序增加了Bitmap、Menu、Acceletator、Toolbar等幾個文件夾。
Bitmap:主要是存放和管理應用管理用到的位圖資源;
Menu:應用程序用到的菜單資源;
Acceletator:菜單和工具條快捷方式;
Toolbar:工具欄資源,可編輯工具欄圖片按鈕以及通過ID與菜單關聯。
CxxxApp: 派生自CWinApp , 爲應用程序對象,負責應用程序的初始化和退出的清理工作。
CMainFrame: 派生自CFrameWnd類, 爲框架窗口對象,對應應用程序的主窗口。
CxxxView: 派生自視圖類CView , 爲視圖對象, 對應應用程序的客戶窗口,用來顯示文檔數據。
CxxxDoc: 派生自CDocument文檔類,爲文檔對象,儲存於應用程序相關的數據。可以視圖配合使用,爲視圖提供數據並進行顯示。
對於SDI程序的執行順序大概如下,瞭解這個過程很重要,就知道一些代碼應該加在哪裏。
BOOL CMFCStudyApp::InitInstance()
{
// 如果一個運行在 Windows XP 上的應用程序清單指定要
// 使用 ComCtl32.dll 版本 6 或更高版本來啓用可視化方式,
//則需要 InitCommonControlsEx()。 否則,將無法創建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 將它設置爲包括所有要在應用程序中使用的
// 公共控件類。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinAppEx::InitInstance();
// 初始化 OLE 庫
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
EnableTaskbarInteraction(FALSE);
// 使用 RichEdit 控件需要 AfxInitRichEdit2()
// AfxInitRichEdit2();
// 標準初始化
// 如果未使用這些功能並希望減小
// 最終可執行文件的大小,則應移除下列
// 不需要的特定初始化例程
// 更改用於存儲設置的註冊表項
// TODO: 應適當修改該字符串,
// 例如修改爲公司或組織名
SetRegistryKey(_T("應用程序嚮導生成的本地應用程序"));
LoadStdProfileSettings(4); // 加載標準 INI 文件選項(包括 MRU)
InitContextMenuManager();
InitKeyboardManager();
InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
// 註冊應用程序的文檔模板。 文檔模板
// 將用作文檔、框架窗口和視圖之間的連接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMFCStudyDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CMFCStudyView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 分析標準 shell 命令、DDE、打開文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 調度在命令行中指定的命令。 如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 啓動應用程序,則返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 唯一的一個窗口已初始化,因此顯示它並對其進行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
CMainFrame主框架類的OnCreate函數也很重要,VS默認生成的代碼如下,VS自動添加了註釋,我們就可以讀懂具體意義,並在相應位置添加自己的代碼。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
return -1;
BOOL bNameValid;
if (!m_wndMenuBar.Create(this))
{
TRACE0("未能創建菜單欄\n");
return -1; // 未能創建
}
m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
// 防止菜單欄在激活時獲得焦點
CMFCPopupMenu::SetForceMenuFocus(FALSE);
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME))
{
TRACE0("未能創建工具欄\n");
return -1; // 未能創建
}
CString strToolBarName;
bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_STANDARD);
ASSERT(bNameValid);
m_wndToolBar.SetWindowText(strToolBarName);
CString strCustomize;
bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);
ASSERT(bNameValid);
m_wndToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);
// 允許用戶定義的工具欄操作:
InitUserToolbars(nullptr, uiFirstUserToolBarId, uiLastUserToolBarId);
if (!m_wndStatusBar.Create(this))
{
TRACE0("未能創建狀態欄\n");
return -1; // 未能創建
}
m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));
// TODO: 如果您不希望工具欄和菜單欄可停靠,請刪除這五行
m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndMenuBar);
DockPane(&m_wndToolBar);
// 啓用 Visual Studio 2005 樣式停靠窗口行爲
CDockingManager::SetDockingMode(DT_SMART);
// 啓用 Visual Studio 2005 樣式停靠窗口自動隱藏行爲
EnableAutoHidePanes(CBRS_ALIGN_ANY);
// 加載菜單項圖像(不在任何標準工具欄上):
CMFCToolBar::AddToolBarForImageCollection(IDR_MENU_IMAGES, theApp.m_bHiColorIcons ? IDB_MENU_IMAGES_24 : 0);
// 創建停靠窗口
if (!CreateDockingWindows())
{
TRACE0("未能創建停靠窗口\n");
return -1;
}
m_wndFileView.EnableDocking(CBRS_ALIGN_ANY);
m_wndClassView.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndFileView);
CDockablePane* pTabbedBar = nullptr;
m_wndClassView.AttachToTabWnd(&m_wndFileView, DM_SHOW, TRUE, &pTabbedBar);
m_wndOutput.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndOutput);
m_wndProperties.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndProperties);
// 基於持久值設置視覺管理器和樣式
OnApplicationLook(theApp.m_nAppLook);
// 啓用工具欄和停靠窗口菜單替換
EnablePaneMenu(TRUE, ID_VIEW_CUSTOMIZE, strCustomize, ID_VIEW_TOOLBAR);
// 啓用快速(按住 Alt 拖動)工具欄自定義
CMFCToolBar::EnableQuickCustomization();
if (CMFCToolBar::GetUserImages() == nullptr)
{
// 加載用戶定義的工具欄圖像
if (m_UserImages.Load(_T(".\\UserImages.bmp")))
{
CMFCToolBar::SetUserImages(&m_UserImages);
}
}
// 啓用菜單個性化(最近使用的命令)
// TODO: 定義您自己的基本命令,確保每個下拉菜單至少有一個基本命令。
CList<UINT, UINT> lstBasicCommands;
lstBasicCommands.AddTail(ID_FILE_NEW);
lstBasicCommands.AddTail(ID_FILE_OPEN);
lstBasicCommands.AddTail(ID_FILE_SAVE);
lstBasicCommands.AddTail(ID_APP_EXIT);
lstBasicCommands.AddTail(ID_EDIT_CUT);
lstBasicCommands.AddTail(ID_EDIT_PASTE);
lstBasicCommands.AddTail(ID_EDIT_UNDO);
lstBasicCommands.AddTail(ID_APP_ABOUT);
lstBasicCommands.AddTail(ID_VIEW_STATUS_BAR);
lstBasicCommands.AddTail(ID_VIEW_TOOLBAR);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2003);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_VS_2005);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_BLUE);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_SILVER);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_BLACK);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_AQUA);
lstBasicCommands.AddTail(ID_VIEW_APPLOOK_WINDOWS_7);
lstBasicCommands.AddTail(ID_SORTING_SORTALPHABETIC);
lstBasicCommands.AddTail(ID_SORTING_SORTBYTYPE);
lstBasicCommands.AddTail(ID_SORTING_SORTBYACCESS);
lstBasicCommands.AddTail(ID_SORTING_GROUPBYTYPE);
CMFCToolBar::SetBasicCommands(lstBasicCommands);
return 0;
}
三、MDI多文檔程序
MDI多文檔程序可以同時打開多個文檔,在界面中可以在多個文件間切換(同時也就爲每個文件打開一個窗口),並通過切換活動窗口激活相應的文檔進行編輯。這一類應該也比較常見,比如WPS、谷歌瀏覽器、火狐瀏覽器(可打開多個網頁,在不同的Tab窗口中顯示)等屬於多文檔應用,它相對於SDI多了一個CChildFrame類,即子框架類,對應打開的多個文檔和視圖。
在這裏大家先只需要大概明白創建方式及各類之間的關係即可,後續我會專門寫這方面的例子來詳細介紹。
四、多頂級文檔
多頂級文檔是VS裏最新增加的功能,類似像Word、powerpoint類似這種應用,每一個文檔是獨立的頂級窗口,而非在同一主窗口下的子窗口應用,這裏先暫時不介紹。