现在的位置: 首页 > 综合 > 正文

自定义MFC打开保存对话框的扩展名

2012年08月19日 ⁄ 综合 ⁄ 共 5774字 ⁄ 字号 评论关闭
文章目录

自定义MFC打开/保存对话框的扩展名

这里的顶目名称是D-TriNet,文档扩展名是.dtn和.csv。

要让打开/保存对话框支持多个扩展名,最简单的方法是修改资源文件中的IDR_DTriNetTYPE字段:

STRINGTABLE 
BEGIN
    IDR_MAINFRAME           "D-TriNet"
    IDR_DTriNetTYPE         "\nDTriNet\nD-TriNet\nD-TriNet 
    Files(*.dtn;*.csv)\n.dtn;.csv\nDTriNet.Document\nD-TriNet.Document"
END

这样做的不足是,文件虽然可以有多个扩展名,但仍然只分为两类:"D-TriNet Files"和"All Files"。要想更细致地分类,需要重写相关的虚函数,具体做法不唯一,我觉得比较好的一种是重写CDocManager::DoPromptFileName。

下面的文字有些凌乱,因为它的内容是按照我的探索过程组织的。

首先考虑打开对话框。第一步是要弄清,打开对话框是什么时候(在哪)弹出来的?

默认情况下,CDTriNetApp调用CWinApp::OnFileOpen方法处理FileOpen事件:

ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)

CWinApp::OnFileOpen又调用CDocManager::OnFileOpen处理FileOpen事件:

void CWinApp::OnFileOpen()
{
	ENSURE(m_pDocManager != NULL);
	m_pDocManager->OnFileOpen();
}

CDocManager::OnFileOpen显示对话框与用户交互,然后调用CWinApp::OpenDocumentFile方法:

void CDocManager::OnFileOpen()
{
	// prompt the user (with all document templates)
	CString newName;
	if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,
	  OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))
		return; // open cancelled

	AfxGetApp()->OpenDocumentFile(newName);
		// if returns NULL, the user has already been alerted
}

显然,一种可能的解决办法是绕过CWinApp和CDocManager,在CDTriNetApp::OnFileOpen方法中显示自定义对话框,然后调用CWinApp::OpenDocumentFile方法。

ON_COMMAND(ID_FILE_OPEN, &CDTriNetApp::OnFileOpen)
void CDTriNetApp::OnFileOpen()
{
	LPCTSTR szFilter = L"DTriNet文件(*.dtn)|*.dtn|CSV文件(*.csv)|*.csv|所有文件(*.*)|*.*||";   
	CFileDialog oFileDlg(TRUE, L".dtn", NULL, 4|2, szFilter);

	if(oFileDlg.DoModal() == IDOK)
		OpenDocumentFile(oFileDlg.GetFileName()); 
		// CDTriNetApp不需要重写CWinApp::OpenDocumentFile方法
}

现在考虑保存对话框。第一步仍然是弄清,保存对话框是什么时候(在哪)弹出来的?

分发消息时,调用了CDocument::DoFileSave虚方法:

BOOL CDocument::DoFileSave()
{
	DWORD dwAttrib = GetFileAttributes(m_strPathName);
	if (dwAttrib & FILE_ATTRIBUTE_READONLY)
	{
		// we do not have read-write access or the file does not (now) exist
		if (!DoSave(NULL))
		{
			TRACE(traceAppMsg, 0, "Warning: File save with new name failed.\n");
			return FALSE;
		}
	}
	else
	{
		if (!DoSave(m_strPathName))
		{
			TRACE(traceAppMsg, 0, "Warning: File save failed.\n");
			return FALSE;
		}
	}
	return TRUE;
}

CDocument::DoFileSave调用CDocument::DoSave,也是一个虚方法:(注:没有DoSaveAs方法,lpszPathName参数决定了CDocument::DoSave是表现为“保存”还是“另存为”。)

BOOL CDocument::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
	// Save the document data to a file
	// lpszPathName = path name where to save document file
	// if lpszPathName is NULL then the user will be prompted (SaveAs)
	// note: lpszPathName can be different than 'm_strPathName'
	// if 'bReplace' is TRUE will change file name if successful (SaveAs)
	// if 'bReplace' is FALSE will not change path name (SaveCopyAs)
{
	CString newName = lpszPathName;
	if (newName.IsEmpty())
	{
		CDocTemplate* pTemplate = GetDocTemplate();
		ASSERT(pTemplate != NULL);

		newName = m_strPathName;
		if (bReplace && newName.IsEmpty())
		{
			newName = m_strTitle;
			// check for dubious filename
			int iBad = newName.FindOneOf(_T(":/\\"));
			if (iBad != -1)
				newName.ReleaseBuffer(iBad);

			// append the default suffix if there is one
			CString strExt;
			if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) &&
			  !strExt.IsEmpty())
			{
				ASSERT(strExt[0] == '.');
				int iStart = 0;
				newName += strExt.Tokenize(_T(";"), iStart);
			}
		}

		if (!AfxGetApp()->DoPromptFileName(newName,
		  bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY,
		  OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate))
			return FALSE;       // don't even attempt to save
	}

	CWaitCursor wait;

	if (!OnSaveDocument(newName))
	{
		if (lpszPathName == NULL)
		{
			// be sure to delete the file
			TRY
			{
				CFile::Remove(newName);
			}
			CATCH_ALL(e)
			{
				TRACE(traceAppMsg, 0, 
				"Warning: failed to delete file after failed SaveAs.\n");
				DELETE_EXCEPTION(e);
			}
			END_CATCH_ALL
		}
		return FALSE;
	}

	// reset the title and change the document name
	if (bReplace)
		SetPathName(newName);

	return TRUE;        // success
}

CDocument::DoSave虚方法调用CWinApp::DoPromptFileName方法弹出保存对话框,后者又调用CDocManager::DoPromptFileName虚方法(情形与打开文件时相同):

BOOL CDocManager::DoPromptFileName(CString& fileName, UINT nIDSTitle, 
	DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate)
{
	CFileDialog dlgFile(bOpenFileDialog, NULL, NULL, 
		OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, NULL, NULL, 0);

	CString title;
	VERIFY(title.LoadString(nIDSTitle));

	dlgFile.m_ofn.Flags |= lFlags;

	CString strFilter;
	CString strDefault;
	if (pTemplate != NULL)
	{
		ASSERT_VALID(pTemplate);
		_AfxAppendFilterSuffix(strFilter, dlgFile.m_ofn, pTemplate, &strDefault);
	}
	else
	{
		// do for all doc template
		POSITION pos = m_templateList.GetHeadPosition();
		BOOL bFirst = TRUE;
		while (pos != NULL)
		{
			pTemplate = (CDocTemplate*)m_templateList.GetNext(pos);
			_AfxAppendFilterSuffix(strFilter, dlgFile.m_ofn, pTemplate,
				bFirst ? &strDefault : NULL);
			bFirst = FALSE;
		}
	}

	// append the "*.*" all files filter
	CString allFilter;
	VERIFY(allFilter.LoadString(AFX_IDS_ALLFILTER));
	strFilter += allFilter;
	strFilter += (TCHAR)'\0';   // next string please
	strFilter += _T("*.*");
	strFilter += (TCHAR)'\0';   // last string
	dlgFile.m_ofn.nMaxCustFilter++;

	dlgFile.m_ofn.lpstrFilter = strFilter;
	dlgFile.m_ofn.lpstrTitle = title;
	dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH);

	INT_PTR nResult = dlgFile.DoModal();
	fileName.ReleaseBuffer();
	return nResult == IDOK;
}

看起来,重写CDocManager::DoPromptFileName方法比重写CDocument::DoSave方法要省事些:

BOOL CDTriNetDocMgr::DoPromptFileName(CString& fileName, UINT nIDSTitle,
			DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate)
{
	LPCTSTR strFilter = L"DTriNet文件(*.dtn)|*.dtn|CSV文件(*.csv)|*.csv|所有文件(*.*)|*.*||";
	CFileDialog dlgFile(bOpenFileDialog, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFilter, NULL, 0);
	
	CString title;
	VERIFY(title.LoadString(nIDSTitle));

	dlgFile.m_ofn.lpstrTitle = title;
	dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH);

	INT_PTR nResult = dlgFile.DoModal();
	fileName.ReleaseBuffer();
	return nResult == IDOK;
}

CDTriNetDocMgr是派生自CDocManager的类。下一步的问题是:怎么为CWinApp::m_pDocManager创建CDTriNetDocMgr对象(而非CDocManager对象)?m_pDocManager是在重写的CWinApp::InitInstance虚方法中创建的,具体来说,是在CWinApp::AddDocTemplate方法中创建的,CWinApp::AddDocTemplate方法在创建m_pDocManager之前会检查它是否为NULL,只有当m_pDocManager为NULL时才为它创建CDocManager对象,所以现在并不需要重写CWinApp::AddDocTemplate方法,而只需要在重写的CWinApp::InitInstance方法中、在调用CWinApp::AddDocTemplate方法之前为m_pDocManager创建CDTriNetDocMgr对象。

一旦重写了CDocManager::DoPromptFileName方法,之前提到的自定义打开对话框文件扩展名的方法也就多此一举了,也不再需要修改资源文件中的IDR_DTriNetTYPE字段,因为无论打开还是保存对话框,最终都是由CDocManager::DoPromptFileName执行的。

抱歉!评论已关闭.