MFC MDI 通过覆盖 FWS_ADDTOTITLE 获取文件路径和文档切换的更新标题?

MFC MDI Getting file path and updated titles on document switch by overriding FWS_ADDTOTITLE?

TL;DR: 如何为每个文档标题添加 完整 路径?

我通过搜索 FWS_ADDTOTITLE 看到了很多话题。我正在更新的特定旧应用程序专门使用此:

lpwndMainFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW|FWS_ADDTOTITLE);

我的应用程序专门处理 MP4 文件和元数据标记,作为 MDI,它可以同时打开 1 个或多个 mp4 文件进行编辑,copying/cut/paste 元数据等...一切正常一切都好。

我真正想看到的是应用程序标题中 MP4 的完整路径 + 文件名,它根据 selected 的文档而变化。

我可以使用它作为硬编码方法来覆盖标题:

m_pMainWnd->SetWindowText(_T("My hardcoded title with no help to my topic example"));

所以我知道该机制可以替换焦点上的文档标题。

由于该应用程序已经可以很好地处理标题中的文件名在文档之间切换,我想要完全相同的功能,但标题中包含路径。

为什么?有时我可以拥有两个名称完全相同但文件夹不同的文件,如果我不知道我正在修改哪个文件,它会变得混乱。

所以我正在寻找应用程序的标题来显示如下内容:

MyApp - C:\MyHarddrive\Folder1\ThisIsMyFile1.mp4
MyApp - C:\MyHarddrive\Folder17\ThisIsMyFile2.mp4
MyApp - C:\MyHarddrive\FolderA\ThisIsMyFile1.mp4

因为我 select 文档 1 -3 分别。

编辑:

我正在添加 FWS_ADDTOTITLE 在我的应用程序中的位置。我拿起了几年前被遗弃的代码,所以如果不重写我不想做的整个事情,我就无法控制。下面的代码位于一个名为 application.cpp 的文件中。在我名为 MainFrame.cpp 或 ChildFrame.cpp 的文件中,它们没有对 FWS_ADDTOTITLE 的其他引用。我尝试了如下所述的覆盖,但它从粘贴后复制到覆盖函数中的 VC 源代码文件中提供了一个 &hookpointer 未知标识符。这有什么帮助吗?

BOOL CApplication::InitInstance()
{
    CWinApp::InitInstance();

    // set registry key
    SetRegistryKey(_T("AppNameC"));
    //CleanState(); 

    // create document template
    CMultiDocTemplate* m_pkDocTemplate;
    m_pkDocTemplate = new CMultiDocTemplate(
        IDR_MOVIE,
        RUNTIME_CLASS(CMovieDoc),
        RUNTIME_CLASS(CChildFrame),
        RUNTIME_CLASS(CMovieView));
    if (!m_pkDocTemplate)
        return FALSE;
    AddDocTemplate(m_pkDocTemplate);    

    // create main window
    CMainFrame *lpwndMainFrame = new CMainFrame();
    lpwndMainFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW|FWS_ADDTOTITLE);
    m_pMainWnd = lpwndMainFrame;
    m_pMainWnd->DragAcceptFiles(); // Added
    lpwndMainFrame->ShowWindow(m_nCmdShow); 
    //lpwndMainFrame->ShowWindow(SW_MAXIMIZE); // Added start full screen
    //m_pMainWnd->SetWindowText(_T("History database")); // Added Valid
    lpwndMainFrame->UpdateWindow();

    // parse command line
    return ParseCommandLine();
    
}

编辑 2021 年 3 月 24 日

所以我已经通过已经说明的帮助大部分工作了,但是出现了一个新问题并且不确定如何解决它。在我的 MainFrame.cpp 和 ChildFrame.cpp 中,它们都有完全相同的逐字覆盖:

BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: Add your specialized code here and/or call the base class
    
    cs.style &= ~(LONG)FWS_ADDTOTITLE;

    
    return CMDIChildWndEx::PreCreateWindow(cs);
}


void CChildFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
    // TODO: Add your specialized code here and/or call the base class


    CView* pView = GetActiveView();
    if (pView) {
        CDocument* pDoc = pView->GetDocument();
        if (pDoc) {
            CString title;

            title = pDoc->GetPathName();
            //title.Format("My App - %s", pDoc->GetPathName());
            AfxGetMainWnd()->SetWindowText(title.GetString());
        }
    }


    CMDIChildWndEx::OnUpdateFrameTitle(bAddToTitle);
}

如图所示,我有正确的路径和标题:

但是出现了一个新的工件:Windows Active(不确定它叫什么)现在缺少通常存在的文件名:

如果我注释掉 //cs.style &= ~(LONG)FWS_ADDTOTITLE;仅在 CHILDFRAME 中..然后它恢复我的 window 菜单标题,因为它以某种方式通过 FWS_ADDTOTITLE 使用 GetTitle()。我希望路径像上面那样显示,但在显示每个文件时仍保留菜单中的标题。 - [FIL-TITLE.MP4](在图像中以红色框显示)需要去......但不确定如何实现......一件事通过覆盖扰乱另一件事......任何想法关于如何纠正这最后一篇文章?除了 window 下拉菜单外几乎是 100%。有什么想法吗?

您可以使用 CDocument class 的 GetPathName() 成员(如评论中所述)。然后,您可以通过覆盖 MDI 子 window class 中的 OnMDIActivate() 成员函数(ON_WM_MDIACTIVATE 的处理程序)手动设置主框架 window 的文本.

大致如下:

BEGIN_MESSAGE_MAP(MyChildFrame, CMDIChildWnd)
    ON_WM_MDIACTIVATE()
    //...
END_MESSAGE_MAP()

void MyChildFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
{
    CMDIChildWnd::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd ); // Call base class handler
    if (bActivate) { // If we're activating a view, get the path and use that...
        CView* pView = GetActiveView();
        if (pView) {
            CDocument* pDoc = pView->GetDocument();
            if (pDoc) {
                CString title;
                title.Format("My App - %s", pDoc->GetPathName());
                AfxGetMainWnd()->SetWindowText(title.GetString());
            }
        }
    }
}

为此我要做的是在 MFC 源中搜索 FWS_ADDTOTITLE 的使用。在 VS 中,您 select 编辑->查找和替换->在文件中查找,然后在“查找”组合中 select“Visual C++ 源目录”。

此搜索 returns 几个结果,但看起来相关的结果在 winfrm.cpp 和 winmdi.cpp 中,在这两种情况下都在 OnUpdateFrameTitle() 成员函数中。好处是这个成员是虚拟的并且 public,即可以覆盖。因此您可以覆盖它(转到 Class 查看并单击属性工具箱中的“覆盖”)。在函数实现中不要调用祖先实现,而是从 MFC 源代码中复制代码并将 UpdateFrameTitleForDocument(pDocument->GetTitle()) 替换为 UpdateFrameTitleForDocument(pDocument->GetPathName())。不确定你的代码使用了这两个(winfrm.cpp 或 winmdi.cpp)的哪个实现,但你可以调试它以找出(我猜它是 winmdi.cpp 中的那个,因为你的框架 window 派生自 CMDIFrameWnd 但最好检查一下)。

可能的问题,代码可能无法编译,如果它在其他地方使用私有成员或变量declared/defined,但我会说试一试。


编辑:

OnUpdateFrameTitle() 函数,顾名思义,似乎是最适合重写的函数,不是吗?

按照我上面的描述进行,并更改代码如下所示 - 您的 MFC 版本可能不同,因此从您的 MFC 源代码复制代码并根据需要进行修改,不要只复制您在此处看到的内容。它是从 winmdi.cpp、函数 CMDIFrameWnd::OnUpdateFrameTitle().

复制的
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
    if ((GetStyle() & FWS_ADDTOTITLE) == 0)
        return;     // leave it alone!

    // allow hook to set the title (used for OLE support)
    if (m_pNotifyHook != NULL && m_pNotifyHook->OnUpdateFrameTitle())
        return;

    CMDIChildWnd* pActiveChild = NULL;
    CDocument* pDocument = GetActiveDocument();
    if (bAddToTitle &&
        (pActiveChild = MDIGetActive()) != NULL &&
        (pActiveChild->GetStyle() & WS_MAXIMIZE) == 0 &&
        (pDocument != NULL ||
        (pDocument = pActiveChild->GetActiveDocument()) != NULL))
    // This was the original code: UpdateFrameTitleForDocument(pDocument->GetTitle());
    // This is the modified code, and THE ONLY CHANGE in the source
    {
        CString sDoc = pDocument->GetPathName();
        // GetPathName() returns an empty string for a newly-created document
        if (sDoc.IsEmpty()) sDoc = pDocument->GetTitle();
        UpdateFrameTitleForDocument(sDoc);
    }
    else
    {
        LPCTSTR lpstrTitle = NULL;
        CString strTitle;

        if (pActiveChild != NULL &&
            (pActiveChild->GetStyle() & WS_MAXIMIZE) == 0)
        {
            strTitle = pActiveChild->GetTitle();
            if (!strTitle.IsEmpty())
                lpstrTitle = strTitle;
        }
        UpdateFrameTitleForDocument(lpstrTitle);
    }
}

此代码无法编译,因为如您所述,编译器不知道 m_pNotifyHook 的类型。这可以解决,使用 VS 编辑器功能(例如 right-click 和 select 查找 Declaration/Definition)。这个变量的类型是COleFrameHook*,它在oleimpl2.h中声明——一个简单的file/text搜索就可以找到它。所以你需要做的是在你的来源中包含这个 header:

#include "oleimpl2.h"

由于某些奇怪的原因,这个文件在源代码中,而不是在包含目录中,因此 VS 找不到它,即 #include <oleimpl2.h> 不起作用。所以我使用双引号并将文件复制到我项目的源文件夹中 - 或者您可以在 #include "" 指令中指定 fully-qualified 路径。