Chinaunix首页 | 论坛 | 博客
  • 博客访问: 639949
  • 博文数量: 263
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 2555
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-26 11:20
文章分类

全部博文(263)

文章存档

2011年(10)

2010年(19)

2009年(170)

2008年(64)

我的朋友

分类: 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()函数必须要调用缺省处理,其它的就随便了   
   

如何重载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"时,你的文档框架将会被激活.   
阅读(708) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~