The top project name here is d-trinet, and the document extension name is .dtnand .csv.
To enable the open/save dialog box to support multiple extensions, the simplest way is to modify the idr_dtrinettype field in the resource file:
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
The disadvantage is that files can have multiple extensions, but they are still divided into two categories: "D-Trinet Files" and "all files ".
To classify data in more detail, you need to rewrite related virtual functions. The specific method is not unique. I think the better one is to rewrite cdocmanager: dopromptfilename.
The text below is a bit messy, because its content is organized according to my exploration process.
First, consider opening the dialog box. The first step is to find out when the dialog box is opened (where?
By default, cdtrinetapp calls the cwinapp: onfileopen method to process fileopen events:
ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
Cwinapp: onfileopen and call cdocmanager: onfileopen to process fileopen events:
void CWinApp::OnFileOpen(){ ENSURE(m_pDocManager != NULL); m_pDocManager->OnFileOpen();}
The cdocmanager: onfileopen display dialog box interacts with the user, and then calls the cwinapp: opendocumentfile method:
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}
Obviously, one possible solution is to bypass cwinapp and cdocmanager, display the custom dialog box in the cdtrinetapp: onfileopen method, and then call the cwinapp: opendocumentfile method.
On_command (id_file_open, & cdtrinetapp: onfileopen) void cdtrinetapp: onfileopen () {lpctstr szfilter = l "dtrinet file (*. dtn) | *. dtn | CSV file (*. CSV) | *. CSV | all files (*. *) | *. * | "; cfiledialog ofiledlg (true, l ". dtn ", null, 4 | 2, szfilter); If (ofiledlg. domodal () = idok) opendocumentfile (ofiledlg. getfilename (); // cdtrinetapp does not need to rewrite cwinapp: opendocumentfile method}
Now consider saving the dialog box. The first step is to find out when (where) the Save dialog box pops up?
When sending a message, the cdocument: dofilesave virtual method is called:
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 calls cdocument: dosave, which is also a virtual method:
(Note: There is no dosaveas method. The lpszpathname parameter determines whether the cdocument: dosave function is "save" or "Save ".)
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}
The cdocument: dosave virtual method calls the cwinapp: dopromptfilename method to bring up the Save dialog box. The latter then calls the cdocmanager: dopromptfilename virtual method (the case is the same as when the file is opened ):
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 filterCString 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;}
It seems that the proportion of the override cdocmanager: dopromptfilename method to write the cdocument: dosave method should be easier:
Bool cdtrinetdocmgr: dopromptfilename (cstring & filename, uint nidstitle, DWORD lflags, bool bopenfiledialog, cdoctemplate * ptemplate) {lpctstr strfilter = l "dtrinet file (*. dtn) | *. dtn | CSV file (*. CSV) | *. CSV | all files (*. *) | *. * | "; 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 is a class derived from cdocmanager.
The next question is: how to create a cdtrinetdocmgr object (instead of a cdocmanager object) for cwinapp: m_pdocmanager )?
M_pdocmanager is created in the rewritten cwinapp: initinstance virtual method. Specifically, it is created in the cwinapp: adddoctemplate method. cwinapp :: the adddoctemplate method checks whether it is null before m_pdocmanager is created. The cdocmanager object is created only when m_pdocmanager is null. Therefore, you do not need to override the cwinapp: adddoctemplate method, instead, you only need to create the cdtrinetdocmgr object for m_pdocmanager In the rewritten cwinapp: initinstance method and before calling the cwinapp: adddoctemplate method.
Once the cdocmanager: dopromptfilename method is rewritten, the previously mentioned method for customizing the file extension of the open dialog box will be redundant, and the idr_dtrinettype field in the resource file will no longer need to be modified, because no matter whether the dialog box is opened or saved, it is finally executed by cdocmanager: dopromptfilename.