【问题标题】:Allow only one CDocument to be opened in an MFC MDI app只允许在 MFC MDI 应用程序中打开一个 CDocument
【发布时间】:2015-04-17 10:59:04
【问题描述】:

正如问题所说,我试图在 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)。在用户选择(或取消)新文档之前关闭文档。

【问题讨论】:

  • 您能解释一下为什么 SDI 不适合您的“许多原因”吗?另外:请在向导生成的应用程序中显示您所做的更改。
  • 我基于一个文档显示了许多视图。这些是动态创建的,并且在给定 MDI 设置的情况下,与 CDockablePanes 等一起更容易管理。代码根据链接 - 我将编辑问题。

标签: c++ mfc


【解决方案1】:

有一种更简单的方法,并且不涉及挂钩所有这些功能。将一些 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 将被禁用。关闭文档后,您可以打开或创建一个新文档。

【讨论】:

  • 这是一个不错的解决方案,Joe,并且可能更好地在用户心中强制它是一个“工作区”类型的应用程序,类似于 VS2013 本身。
【解决方案2】:

所以我有一个可行的解决方案,并认为解释它很有用。我希望这对某人有用。我花了很长时间逐步了解 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

因此,唯一可以加入的地方是在 CMyApp 类中覆盖 OpenDocumentFile

来自对话框

这更容易 - 有多个地方可以挂接,但在开始时挂接将不允许用户显示文件打开对话框并取消它,同时保持现有文档处于打开状态。

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 类并重写一些函数,但这是一项艰巨的工作。以下是一种“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;
    }
}

【讨论】:

  • 如果没有异议,或者更好的建议,我会尽快接受这个答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-11
  • 2021-12-03
  • 1970-01-01
相关资源
最近更新 更多