改变MFC默认的文档模板结构

在很多软件中添加新文件的时候都可以选择创建好几种,每种对应的视图都不一样,而MFC的MDI默认只是对应一种,就算添加模板也会在新建时弹出对话框让用户选择。这种选择甚是烦,现在要靠自定义方式来实现。

首先,描述下MDI的结构和创建文档的过程。
STEP1:在App的InitInstance函数中,程序创建文档模板。并调用App的AddDocTemplate方法来加入模板到模板列表里。

	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(IDR_FILE_HTML,//IDR_KHSTYPE,
		RUNTIME_CLASS(CKHSDoc),
		RUNTIME_CLASS(CChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CKHSView));
	if (!pDocTemplate)
		return FALSE;
	AddDocTemplate(pDocTemplate);
STEP2:当在菜单或者别的什么地方点击新建文档时会调用App底层的CWinApp::OnFileNew,这个函数的底层就是调用了一个叫m_pDocManager的OnFileNew()函数。
void CWinApp::OnFileNew()
{
	if (m_pDocManager != NULL)
		m_pDocManager->OnFileNew();
}
STEP3:这个m_pDocManager就很重要了,他是App中一个叫CDocManager类的实例。它管理文档模板的列表。他的m_templateList保存了所有的文档模板,在新建文档时会在这个列表中寻找合适的模板,然后调用模板的OpenDocumentFile函数创建文档。
void CDocManager::OnFileNew()
{
	if (m_templateList.IsEmpty())
	{
		TRACE(traceAppMsg, 0, "Error: no document templates registered with CWinApp.\n");
		AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
		return;
	}

	CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
	if (m_templateList.GetCount() > 1)
	{
		// more than one document template to choose from
		// bring up dialog prompting user
		CNewTypeDlg dlg(&m_templateList);
		INT_PTR nID = dlg.DoModal();
		if (nID == IDOK)
			pTemplate = dlg.m_pSelectedTemplate;
		else
			return;     // none - cancel operation
	}

	ASSERT(pTemplate != NULL);
	ASSERT_KINDOF(CDocTemplate, pTemplate);

	pTemplate->OpenDocumentFile(NULL);
		// if returns NULL, the user has already been alerted
}
在if判断文中,就是弹出选择文档模板的对话框,真是对他恨得咬牙啊。删除它们就需要重建这个类。下面是重建这个管理类的方法。
STEP1:新建一个继承自CObject的类,取名为CDocManagerEx,然后把CObject改为MFC的内部类CDocManager。
class CDocManagerEx : public CDocManager
{
public:
	CDocManagerEx();
	virtual ~CDocManagerEx();
};
STEP2:重载void OnFileNew()函数,可以把MFC的DocManager的OnFileNew函数拷贝过来。这个函数的内容已经看过了,不再累述。CDocManager在atlmfc\src\mfc的docmgr.cpp中。
STEP3:创建完成这个类后就需要替换原有的m_pDocManager,这个实例在App类的AddDocTemplate函数中创建,所以必须在这个函数被调用之前创建。
	m_pDocManager = new CDocManagerEx(this); //这就是替换DocManager的地方。

	// Register the application's document templates.  Document templates
	//  serve as the connection between documents, frame windows and views
	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(IDR_FILE_HTML,//IDR_KHSTYPE,
		RUNTIME_CLASS(CKHSDoc),
		RUNTIME_CLASS(CChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CKHSView));
	if (!pDocTemplate)
		return FALSE;
	AddDocTemplate(pDocTemplate);
这样之后文档管理类就被成功替换了,如果仅仅是想删除那个可恶的对话框那就到此结束了。但是我们的目标是在画面上点击不同的新建文档菜单可以生成不同的文档视图结构,所以还要改变默认的消息映射结构。默认的是ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew),如果想添加多个映射表。
BEGIN_MESSAGE_MAP(CKHSApp, CWinApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	// Standard file based document commands
	ON_COMMAND(ID_NEW_HTML, CxxxApp::OnHTMLFileNew)         //我们添加的内容
	ON_COMMAND(ID_NEW_TXT,  CxxxApp::OnTXTFileNew)          //我们添加的内容
	ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
	// Standard print setup command
	ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
当然,也要在自己的App类中添加对应的函数。
void CKHSApp::OnHTMLFileNew()
{
	m_strNewFileType = STR_NEW_FILETYPE_HTML;       //这里是一个标记,表明不同的文件类型,原因是m_pDocManager的OnFileNew并不能添加参数。
	if (m_pDocManager != NULL)                      //所以只能在外部用添加标记的方法来处理,今后有新的方法可以更新也行。
		m_pDocManager->OnFileNew();
}
这里标记添加好以后再看看,我们上面替换m_pDocManager时使用了一个this参数,CDocManagerEx可以通过这个指针获取App类中的m_strNewFileType在CDocManagerEx中使用。
void CDocManagerEx::OnFileNew()
{
	ASSERT(m_pKHSApp != NULL);   //这里的指针就是构造函数里得到的App类的指针。

	if (m_templateList.IsEmpty())
	{
		TRACE(traceAppMsg, 0, "Error: no document templates registered with CWinApp.\n");
		AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
		return;
	}

	CString strType = m_pKHSApp->getNewFileType();  //获取窗口上点击的菜单的类型

	CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
	if (m_templateList.GetCount() > 1)
	{
		POSITION pos = m_templateList.GetHeadPosition();
		while (pos != NULL)
		{
			CDocTemplate* pBestTemplate = (CDocTemplate*)m_templateList.GetNext(pos);
			ASSERT_KINDOF(CDocTemplate, pBestTemplate);
			CString strTypeName;
			if (pBestTemplate->GetDocString(strTypeName, CDocTemplate::fileNewName) &&   //通过这个函数调用GetDocString获取模板的新建文档
				!strTypeName.IsEmpty())                                              //的文件名,这个和传递进来的参数对比,如果一致就
			{                                                                            //设置当前的模板为匹配一致的模板。
				if (strTypeName == strType)
				{
					pTemplate = pBestTemplate;
				}
			}
		}
	}

	ASSERT(pTemplate != NULL);
	ASSERT_KINDOF(CDocTemplate, pTemplate);

	pTemplate->OpenDocumentFile(NULL);
}
但是,但是,但是,GetDocString获取的文件名在哪里设置呢?我们怎么知道他得到的内容是什么呢?看看创建模板的时候使用的参数吧。
CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(IDR_FILE_HTML,//IDR_KHSTYPE,
		RUNTIME_CLASS(CKHSDoc),
		RUNTIME_CLASS(CChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CKHSView));
第一个参数ID的内容看起来是一个ICON的ID,实际上它也是一个String类型的资源。打开rc文件可以发现这个ID他的内容是
\nHTML\nHTML\n\n\nHTML.Document\nHTML.Document
这里有很多个段他们对应的内容就是如下的内容
	enum DocStringIndex
	{
		windowTitle,        // default window title
		docName,            // user visible name for default document
		fileNewName,        // user visible name for FileNew
		// for file based documents:
		filterName,         // user visible name for FileOpen
		filterExt,          // user visible extension for FileOpen
		// for file based documents with Shell open support:
		regFileTypeId,      // REGEDIT visible registered file type identifier
		regFileTypeName,    // Shell visible registered file type name
	};
这个就是GetDocString第二个参数的内容,可以一个一个是尝试具体的内容。这样一来就完成了整个替换设计。慢慢尝试吧。











你可能感兴趣的:(改变MFC默认的文档模板结构)