VS中MFC三大類應用程序開發起步(Dialog、SDI、MDI)

在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類似這種應用,每一個文檔是獨立的頂級窗口,而非在同一主窗口下的子窗口應用,這裏先暫時不介紹。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章