全部博文(263)
分类: WINDOWS
2009-11-15 18:51:43
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数组的建立等。
②记录的保存、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.
你所要做的是用程序中的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()函数必须要调用缺省处理,其它的就随便了