在改进cnbook时发现MFC的一个小问题。MFC框架支持最近文件列表。我们不用写一行代码就可以实现文件列表功能。
在资源文件的菜单资源中,ID_FILE_MRU_FILE1表示最近文件列表:
MENUITEM "最近文件", ID_FILE_MRU_FILE1,GRAYED MENUITEM SEPARATOR MENUITEM "退出(&X)", ID_APP_EXIT
MFC会在这个位置插入文件列表。默认情况下,最近文件列表是文件菜单的第一级菜单项。在最近文件比较多时,会造成文件菜单过长,不美观。所以,有时我们希望把最近文件列表放在子菜单项中,例如:
POPUP "最近文件(&F)" BEGIN MENUITEM "最近文件", ID_FILE_MRU_FILE1,GRAYED END MENUITEM SEPARATOR MENUITEM "退出(&X)", ID_APP_EXIT
但运行结果却不是我们所期待的:
通过查看MFC的源代码,我们可以找到负责文件列表显示的函数CRecentFileList::UpdateMenu。大概看一下这个函数,就会发现这个函数没有考虑最近文件列表放在子菜单项中的情况。它总是在前一个菜单项的后面插入最近文件的菜单项,即使前一个菜单项在上一级。这就造成将ID_FILE_MRU_FILE1作为子菜单的第一项时会发生错误。
Todd C. Gleason在1998年使用VC5时就发现了这个问题并提出了解决办法。 不过,在VC8中这个问题依然存在。Todd C. Gleason的解决办法就是:
protected: void LoadStdProfileSettings(UINT nMaxMRU);
在.c中首先包含VIRecentFileList.h:
#include "VIRecentFileList.h"
然后实现LoadStdProfileSettings函数:
static const TCHAR _afxFileSection[] = _T("Recent File List"); static const TCHAR _afxFileEntry[] = _T("File%d"); static const TCHAR _afxPreviewSection[] = _T("Settings"); static const TCHAR _afxPreviewEntry[] = _T("PreviewPages"); void CMruApp::LoadStdProfileSettings(UINT nMaxMRU) { ASSERT_VALID(this); ASSERT(m_pRecentFileList == NULL); if (nMaxMRU != 0) { // create file MRU since nMaxMRU not zero // Here's the important part--overriding CRecentFileList m_pRecentFileList = new VIRecentFileList(0, _afxFileSection, _afxFileEntry, nMaxMRU); m_pRecentFileList->ReadList(); } // 0 by default means not set m_nNumPreviewPages = GetProfileInt(_afxPreviewSection, _afxPreviewEntry, 0); }
好了,看看结果是不是我们想要的:
Todd的代码是用于VC5环境的,在VC6或VC8使用要做一些细微改动。另外在清空文件列表后:
void CTextProApp::OnClrMru() { int i; for (i = 0; i < m_pRecentFileList->m_nSize; i++) { m_pRecentFileList->m_arrNames[i].Empty(); } }
MFC和Todd的UpdateMenu函数都不能正确显示。我也做了些修改。 完整的例子可以从这里下载。