正如问题所说,我试图在MFC MDI应用程序中一次只允许一个文档。由于许多(无关)原因,我没有使用SDI文档模板。很多地方评论这样做,但我不知道如何。最接近的是此链接中的建议:http://computer-programming-forum.com/82-mfc/06d5cebffaeefeae.htm但它不能与CWinAppEx一起使用 - 文档已关闭,即使用户取消了“文件打开”对话框。此外,使用MRU列表或工具栏按钮可以绕过此建议。
非常感谢任何帮助!
BOOL CMyDoc::closeDocument()
{
if (!SaveModified())
{
// User has vetoed the close, return error
return TRUE;
}
else
{
// OK to close
OnCloseDocument();
return FALSE;
}
}
在CMyApp中:
void CMyApp::OnFileOpen()
{
CMyDoc* pDoc = CMyDoc::GetDoc();
if (pDoc != NULL && pDoc->closeDocument())
// user has vetoed the close - can't create new one
return;
// no document currently open, or we succesfully closed it
CWinAppEx::OnFileOpen();
}
void CMyApp::OnFileNew()
{
CMyDoc* pDoc = CVATDoc::GetDoc();
if (pDoc != NULL && pDoc->closeDocument())
// user has vetoed the close - can't create new one
return;
// no document currently open, or we succesfully closed it
CWinAppEx::OnFileOpen();
}
虽然这可能适用于旧版本的MFC,但现在似乎无法正常工作(VS2013)。在用户选择(或取消)新文档之前,文档已关闭。
答案 0 :(得分:2)
有一种更简单的方法,并不涉及挂钩所有这些功能。将一些ON_UPDATE_COMMAND_UI()消息映射处理程序添加到应用程序对象。具体来说,ID_FILE_NEW和ID_FILE_OPEN。如果有打开的文档,请禁用该命令。这是一些代码。不能保证工作,但应该让你到那里。
ON_UPDATE_COMMAND_UI(ID_FILE_NEW, SomeUpdateHandler)
ON_UPDATE_COMMAND_UI(ID_FILE_OPEN, SomeUpdateHandler)
void CMyApp::SomeUpdateHandler(CCmdUI* pCmdUI)
{
POSITION pos = GetFirstDocTemplatePosition();
CDocTemplate* pTemplate = GetNextDocTemplate(pos);
POSITION posDoc = pTemplate->GetFirstDocPosition();
pCmdUI->Enable(posDoc != NULL);
}
如果您有一个打开的文档,则将禁用File-New和File-Open。关闭文档后,您可以打开或创建一个新文档。
答案 1 :(得分:0)
所以我找到了一个解决方案,并认为解释它很有用。我希望这对某人有用。我已经花了很长时间来度过MFC(这是一件好事)并且现在看看为什么上面链接的解决方案不再是MFC9中的解决方案。
调用堆栈是这样的:
CWinApp::OnOpenRecentFile(UINT nID) // Non-virtual – cannot override
CWinApp::OpenDocumentFile(LPCTSTR lpszFileName) // Virtual – can override in CMyApp
CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)
CDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU)
CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bAddToMRU, BOOL bMakeVisible); // here is the actual creation of the new frame etc
所以唯一可以加入的地方是覆盖OpenDocumentFile
课程中的CMyApp
。
这更容易 - 有多个地方可以挂钩,但是在开始时挂钩不会允许用户显示文件打开对话框并取消它,同时保持现有文档打开。
CMyApp::OnFileOpen(); // override
CWinAppEx::OnFileOpen(); // call base class
CDocManager::OnFileOpen(); // This one is key – in here the File Open dialog is shown
CMyApp::OpenDocumentFile(LPCTSTR lpszFileName) // And here we can hook into the call stack
CWinAppEx::OpenDocumentFile(lpszFileName);
CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)
CDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU)
CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bAddToMRU, BOOL bMakeVisible) // here is the actual creation of the new frame etc
这个很棘手,因为一旦OnFileNew
被调用就没有钩子。
CMyApp::OnFileNew(); // virtual override
CWinAppEx::OnFileNew(); // base class
CDocManager::OnFileNew(); // this calls the doc template without another hook back into our derived classes
CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)
解决方案是派生我们自己的DocTemplate类并重写一些函数,但这是一项很重要的工作。 A' hackier'方法如下。
此挂钩适用于MRU和对话框命令 - 请参阅上面的调用堆栈。 “文件打开”对话框已显示并已选中文件。如果取消“文件打开”对话框,我们就无法到达此处。
CDocument* CMyApp::OpenDocumentFile(LPCTSTR lpszFileName)
{
CMyDoc* pDoc = CMyDoc::GetDoc(); // static func that gets pointer to MyDoc
if (pDoc != NULL && pDoc->closeDocument())
// user has vetoed the close - can't create new one
return NULL; // here we prevent opening by by-passing base class call
return CWinAppEx::OpenDocumentFile(lpszFileName);
}
由于没有要取消的对话框,我们可以在开始时直接挂钩,如果用户不允许关闭现有文档,则阻止创建新文件。
void CMyApp::OnFileNew()
{
CMyDoc* pDoc = CMyDoc::GetDoc();
if (pDoc != NULL && pDoc->closeDocument())
// user has vetoed the close - can't create new one
return;
// no document currently open, or we succesfully closed it
CWinAppEx::OnFileNew();
}
最后,closeDocument()
功能符合上述链接:
BOOL CMyDoc::closeDocument()
{
if (!SaveModified())
{
// User has vetoed the close, return error
return TRUE;
}
else
{
// OK to close
OnCloseDocument();
return FALSE;
}
}