MFC框架中对最近文件列表的支持

来源:互联网 发布:上瘾网络剧花絮 编辑:程序博客网 时间:2024/04/29 07:11

MFC建立的标准框架程序中有记录最近操作文件的能力,这些最近文件的路径被记录到注册表,在程序运行时,又将添加到文件菜单中。 
在CWinApp中有个 CRecentFileList* m_pRecentFileList;指针管理这些信息。 
以下对此过程进行分析,采用类似的方法,可以保存其他一些固定条数的最近数据。 
1.CRecentFileList对象的建立,记录读入、记录保存、对象销毁。 
如果在CWinApp派生类中InitInstance()中调用了LoadStdProfileSettings,则CRecentFileList被建立,时程序具有管理最近文件列表的能力。 
同时,从注册表中读入以前的记录。 

void CWinApp::LoadStdProfileSettings(UINT nMaxMRU) //缺省为4 { ... if (nMaxMRU != 0) { //建立CRecentFileList对象,初始化为管理nMaxMRU条文件信息 m_pRecentFileList = new CRecentFileList(0, _afxFileSection, _afxFileEntry,nMaxMRU); //读入记录 m_pRecentFileList-> ReadList(); } ... } 
CRecentFileList对象中的主要数据成员 
CRecentFileList包含一个CString指针 CString* m_arrNames; ,它用来指向一个CString对象数组,正是这个数组记录了最近的文件名。 
另外,成员 int m_nSize 指出了记录的个数。 
在对象创建时,构造函数完成初始化,包括CString数组的建立等。 

2.记录的保存、CRecentFileList的销毁。 

ExitInstance()中将调用SaveStdProfileSettings(),SaveStdProfileSettings()中有m_pRecentFileList->WriteList();操作,完成记录的保存。 
在CWinApp析构时将delete m_pRecentFileList;销毁对象。 
CRecentFileList::CRecentFileList(UINT nStart, LPCTSTR lpszSection, LPCTSTR lpszEntryFormat, int nSize, int nMaxDispLen) { ASSERT(nSize != 0); m_arrNames = new CString[nSize]; //建立CString数组。 m_nSize = nSize; m_nStart = nStart; m_strSectionName = lpszSection; m_strEntryFormat = lpszEntryFormat; m_nMaxDisplayLength = nMaxDispLen; } 

3.记录的添加 

文档保存时,将调用SetPathName(..),SetPathName(..)中将调用应用程序类中的AddToRecentFileList 
AddToRecentFileList中执行m_pRecentFileList->Add(lpszPathName)将文件名添加到字符串数组 
void CRecentFileList::Add(LPCTSTR lpszPathName) { ASSERT(m_arrNames != NULL); ASSERT(lpszPathName != NULL); ASSERT(AfxIsValidString(lpszPathName)); // fully qualify the path name TCHAR szTemp[_MAX_PATH]; AfxFullPath(szTemp, lpszPathName); //得到文件全路径 // 查找,看是否已经有此文件名 for (int iMRU = 0; iMRU < m_nSize-1; iMRU++) { if (AfxComparePath(m_arrNames[iMRU], szTemp)) break; // iMRU will point to matching entry } // 其前面的各项后移 for (; iMRU > 0; iMRU--) { ASSERT(iMRU > 0); ASSERT(iMRU < m_nSize); m_arrNames[iMRU] = m_arrNames[iMRU-1]; } //添加到起始位置 m_arrNames[0] = szTemp; } 

4.记录的删除 

如果用户从菜单中选择打开某记录对应的文件,单该文件已经不存在,则将删除该无效记录。 
void CRecentFileList::Remove(int nIndex) { ASSERT(nIndex >= 0); ASSERT(nIndex < m_nSize); m_arrNames[nIndex].Empty(); for (int iMRU = nIndex; iMRU < m_nSize-1; iMRU++) m_arrNames[iMRU] = m_arrNames[iMRU+1]; //其后各项前移 ASSERT(iMRU < m_nSize); m_arrNames[iMRU].Empty(); } 

5.记录数据的保存 

void CRecentFileList::WriteList() { ASSERT(m_arrNames != NULL); ASSERT(!m_strSectionName.IsEmpty()); // m_strSectionName : _T("Recent File List") ASSERT(!m_strEntryFormat.IsEmpty()); // m_strEntryFormat : _T("File%d") LPTSTR pszEntry = new TCHAR[m_strEntryFormat.GetLength()+5]; CWinApp* pApp = AfxGetApp(); pApp-> WriteProfileString(m_strSectionName, NULL, NULL); //写入Recent File List键 for (int iMRU = 0; iMRU < m_nSize; iMRU++) { wsprintf(pszEntry, m_strEntryFormat, iMRU + 1); //得到号吗字符串 if (!m_arrNames[iMRU].IsEmpty()) { pApp-> WriteProfileString(m_strSectionName, pszEntry, //在写值名pszEntry,对应值为文件名。 m_arrNames[iMRU]); } } delete[] pszEntry; } 

6.记录数据的读取 

void CRecentFileList::ReadList() { ASSERT(m_arrNames != NULL); ASSERT(!m_strSectionName.IsEmpty()); ASSERT(!m_strEntryFormat.IsEmpty()); LPTSTR pszEntry = new TCHAR[m_strEntryFormat.GetLength()+5]; CWinApp* pApp = AfxGetApp(); for (int iMRU = 0; iMRU < m_nSize; iMRU++) { wsprintf(pszEntry, m_strEntryFormat, iMRU + 1); //得到值名字符串 m_arrNames[iMRU] = pApp-> GetProfileString( //取值名下的值,此即个记录,若值不存在,则为NULL m_strSectionName, pszEntry, &afxChNil); } delete[] pszEntry; } 

7.将记录添加到菜单项 

菜单资源中文件菜单下有ID为ID_FILE_MRU_FILE1的菜单项,用于在此处添加最近文件菜单项。 
命令更新机制根据ON_UPDATE_COMMAND_UI(ID_FILE_MRU_FILE1, OnUpdateRecentFileMenu)将经常调用到 
CWinApp::OnUpdateRecentFileMenu(..) OnUpdateRecentFileMenu中调用void CRecentFileList::UpdateMenu(CCmdUI* pCmdUI) void CRecentFileList::UpdateMenu(CCmdUI* pCmdUI) { ASSERT(m_arrNames != NULL); CMenu* pMenu = pCmdUI-> m_pMenu; //由pCmdUI直接找到菜单 if (m_strOriginal.IsEmpty() && pMenu != NULL) pMenu-> GetMenuString(pCmdUI-> m_nID, m_strOriginal, MF_BYCOMMAND); if (m_arrNames[0].IsEmpty()) { // no MRU files if (!m_strOriginal.IsEmpty()) pCmdUI-> SetText(m_strOriginal); pCmdUI-> Enable(FALSE); return; } if (pCmdUI-> m_pMenu == NULL) return; for (int iMRU = 0; iMRU < m_nSize; iMRU++) //删除所有最新文件菜单项 pCmdUI-> m_pMenu-> DeleteMenu(pCmdUI-> m_nID + iMRU, MF_BYCOMMAND); TCHAR szCurDir[_MAX_PATH]; GetCurrentDirectory(_MAX_PATH, szCurDir); int nCurDir = lstrlen(szCurDir); ASSERT(nCurDir >= 0); szCurDir[nCurDir] = '\\'; szCurDir[++nCurDir] = '\0'; CString strName; CString strTemp; for (iMRU = 0; iMRU < m_nSize; iMRU++) { if (!GetDisplayName(strName, iMRU, szCurDir, nCurDir)) break; // double up any '&' characters so they are not underlined LPCTSTR lpszSrc = strName; LPTSTR lpszDest = strTemp.GetBuffer(strName.GetLength()*2); while (*lpszSrc != 0) { if (*lpszSrc == '&') *lpszDest++ = '&'; if (_istlead(*lpszSrc)) *lpszDest++ = *lpszSrc++; *lpszDest++ = *lpszSrc++; } *lpszDest = 0; strTemp.ReleaseBuffer(); // insert mnemonic + the file name TCHAR buf[10]; wsprintf(buf, _T("&%d "), (iMRU+1+m_nStart) % 10); pCmdUI-> m_pMenu-> InsertMenu(pCmdUI-> m_nIndex++, MF_STRING | MF_BYPOSITION, pCmdUI-> m_nID++, CString(buf) + strTemp); //添加菜单项 } // update end menu count pCmdUI-> m_nIndex--; // point to last menu added pCmdUI-> m_nIndexMax = pCmdUI-> m_pMenu-> GetMenuItemCount(); pCmdUI-> m_bEnableChanged = TRUE; // all the added items are enabled } 

8.对最近文件菜单项的相应 

系统通过消息映射 ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE16, OnOpenRecentFile) 
调用OnOpenRecentFile,命令ID作为参数传入 
BOOL CWinApp::OnOpenRecentFile(UINT nID) { ASSERT_VALID(this); ASSERT(m_pRecentFileList != NULL); ASSERT(nID >= ID_FILE_MRU_FILE1); ASSERT(nID < ID_FILE_MRU_FILE1 + (UINT)m_pRecentFileList-> GetSize()); int nIndex = nID - ID_FILE_MRU_FILE1; ASSERT((*m_pRecentFileList)[nIndex].GetLength() != 0); TRACE2("MRU: open file (%d) '%s'.\n", (nIndex) + 1, (LPCTSTR)(*m_pRecentFileList)[nIndex]); if (OpenDocumentFile((*m_pRecentFileList)[nIndex]) == NULL) m_pRecentFileList-> Remove(nIndex); return TRUE;   
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

MRU(最近使用的)文件 
问:我想将最近使用过的几个文件名(以及路径)显示出来,该怎么办? 
答:一个由向导生成器生成的文档视图结图已经包含MRU列表操作.

具体是放在注 册表的 HKEY_CURRENT_USER/Software/Your Company Name/ Your App Name/Recent File List/File1, File2, File3, etc. 
1)你所要做的是用程序中的GetProfileString()读入第一个MRU文件的位置, 
剩下的由 CRecentFileList::ReadList()完成所有的工作.CRecentFileList类 
包含了所有操作 的代码象分析路径,管理MRU文件等,用不着为此费心. 

2)在AFXWIN.H中定义了一个变量CRecentFileList* m_pRecentFileList; 
你可以通过使用CRecentFileList的成员变量m_arrNames[n]来取得某个MRU文件. 
比如: 

#include // needed for access to MRU list Cfile mruFile; if ( !mruFile.Open( m_pRecentFileList->m_arrNames[index], Cfile::modeRead ).... 

3)你不需要建立一个CRecentFileList类,它已经由CWinApp基类完成了.

你 只要保证你在 initinstance中调用LoadStdProfileSettings() 

函数就可以了. CRecentFileList类中有一个CWinApp保护成员变量 
(如m_pRecentFileList) 所以你应该在你的继承类中处理它.下面的代码在 
我这儿工作很正常 
void CMdiApp::OnFileMruFile1() { // TODO: Add your command handler code here Cstring vl_name ; ASSERT( m_pRecentFileList->GetSize() > 0); vl_name = (*m_pRecentFileList)[0]; CWinApp::OnOpenRecentFile(ID_FILE_MRU_FILE1); } 
OnOpenRecentFile()函数必须要调用缺省处理,其它的就随便了   
   

如何重载MRU文件? 
我创建了一个应用程序可以载入图象文件,但当我点击FILE菜单下MRU文件列表 
时,却不能从磁盘载入以前曾经打开过的文件. 
下面是我所能想到的解决方案: 

(1)在文档类中定义一个成员函数(例如:CMyDoc::Reopen)来处理重新打开这个问 
题,指明参数和返回值. 
(2)产生一个CMultiDocTemplate的继承类(如CMyDocTemplate),
定义一个构造函 
数,取和基类相同的参数,不做任何事,只是调用基类的构造函数. 
(3)重载MatchDocType: 

CMyDocTemplate::Confidence CMyDocTemplate::MatchDocType( LPCTSTR lpszPath, CDocument *&rpDocMatch ) { Confidence match = CMultiDocTemplate::MatchDocType(lpszPath, rpDocMatch); if(yesAlreadyOpen == match) // clear enough { ASSERT_KINDOF(CMyDoc, rpDocMatch); ((CMyDoc *) rpDocMatch)->Reopen(/* your parameters */); // you can take any other actions here... } return match; } 
当这个函数返回"yesAlreadyOpen"时,你的文档框架将会被激活.