locked
Problem with LoadMDIState / SaveMDIState and more than one window open on the same document of a VS2010SP1 MFC MDI tabbed application

    Pergunta

  • Hello,
    I have a problem with the Load/SaveMDIState mechanism when I have two or more documents of a VS2010SP1 MFC MDI tabbed application open and have also two ore more windows open on the same document.
    This problem is reproduceable with the Microsoft Visual C++ 2008 Feature Pack sample "VisualStudioDemo".
    The window layout before closing and saving the application state is shown in picture 1:

    As you can see I have 3 documents a.cpp, b.cpp and c.cpp and b.cpp has two windows 1 and 2. I have also 2 horizontal tab groups.
    After closing the application and reopening the (now wrong) layout is as in the following picture:

    As you can see now document c.cpp apears twice and the second window of b.cpp is missing.
    In the registry

    HKCU\software\Microsoft\MFC\Samples\VisualStudioDemo\Settings\MDIClientArea-0\MDITabState

    the entries look corretly as I can evaluate.
    Can anyone help?
    Thanks,


    Andreas
    quarta-feira, 25 de janeiro de 2012 11:54

Todas as Respostas

  • Hi,

     

    Could you please provide more detail about your issue? Or can your provide a sample to us?

     

    In addition, here are the steps to load or save the state of MDI tabs and groups and the list of opened documents.

    1.       Call SaveMDIState when the main frame is being closed

    2.       Call CMDIFrameWndEx::LoadMDIState when the main frame is being created. The recommended location for this call is before the main frame is displayed for the first time.

    3.       Call CWinAppEx::EnableLoadWindowPlacement(FALSE); before pMainFrame->LoadFrame (IDR_MAINFRAME);

    4.       Call CWinAppEx::ReloadWindowPlacement(pMainFrame) after LoadMDIState to display the main frame at the position that was stored in the registry.

    5.       Override GetDocumentName in the CMDIChildWndEx- derived class if your application displays documents that are not stored as files. The returned string will be saved in the registry as a document identifier. For more information, see CMDIChildWndEx::GetDocumentName.

    6.       Override CMDIFrameWndEx::CreateDocumentWindow to correctly create documents when they are loaded from the registry. The parameter to CreateDocumentWindow is the string that GetDocumentName returned earlier.

    Best Regards,

    Rob

     

     


    Rob Pan [MSFT]
    MSDN Community Support | Feedback to us
    sexta-feira, 27 de janeiro de 2012 06:18
  • Hello, as I wrote in my first post, you can reproduce this problem with the Microsoft Visual C++ 2008 Feature Pack sample "VisualStudioDemo". You can domwnload it from here:
    http://archive.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=vcsamplesmfc&DownloadId=9828

    The only thing you must do is to compile and start the application. Than create 3 documents
    a.cpp, b.cpp c.cpp enter some text and save the documents.
    Create a new horizontal group by dragging document c.cpp to the bottom of a/b.cpp or using the context menu "New horizontal tab group".
    Than create a new window for b.cpp with the Window menu "New Window".
    Move this window with the context menu "Move to next tab group".
    Now you should have the situation as you can see in the first picture.

    Now close and reopen the application and you can see the problem (should be the same as in the second picture)
    The second window of b.cpp disappeared and the document c.cpp is shown twice which is wrong.

    When you look into the registry, the entries seem to be correct.
    Because I have the problem in a real application, I need a solution / workaround / patch for it.

    Best regards,


    Andreas
    sexta-feira, 27 de janeiro de 2012 08:36
  • Hello Rob,
    could you reproduce the problem with the steps and the VisualStudioDemo I submitted?
    I think the problem is that in CMDIFrameWndEx::CreateNewWindow, that is called from CMDICclientAreaWnd::SerializeTabGroup, MDIGetActive is called, because the active child window seems to be the last added "c.cpp". Now a new window of "c.cpp" is created instead of "b.cpp".
    Can you confirm this?

    Thanks for the help,


    Andreas

    terça-feira, 6 de março de 2012 09:55
  • Hello,
    I think I have a solution, it would be nice if someone could confirm this.
    I overwrite CreateNewWindow in CMainFrame and instead of calling the base class CMDIFrameWndEx::CreateNewWindow in case of AreMDITabs is true I have done the following trick:
      if (AreMDITabs())
      {
        // do not call OnWindowNew() here, because first we need 
        // to set the MDI active window to the correct corresponding child that has already loaded this document,
        // because OnWindowNew calls CreateNewFrame with the current MDI active window.
        //OnWindowNew();
    
        CDocument* pSearchDoc = NULL;
        // Find already loaded document, ...
        POSITION pos = theApp.GetFirstDocTemplatePosition();
        while ( NULL != pos )
        {
          CDocTemplate* pDocTemlate = theApp.GetNextDocTemplate( pos );
          POSITION posDoc = pDocTemlate->GetFirstDocPosition();
          while ( NULL != posDoc )
          {
            CDocument* pDoc = pDocTemlate->GetNextDoc( posDoc );
            const CString& strPathName = pDoc->GetPathName();
            if ( strPathName == lpcszDocName )
            {
              pSearchDoc = pDoc;
              posDoc = NULL;
              pos = NULL;
            }
          }
        }
    
        // ... we must find one here!
        _ASSERT( NULL != pSearchDoc );
        if ( NULL != pSearchDoc )
        {
          // From the first view we can now get the parent child window, that we can activate
          // so that the call to OnWindowNew call works.
          POSITION posView = pSearchDoc->GetFirstViewPosition();
          _ASSERT( NULL != posView );
          if ( NULL != posView )
          {
            CView* pView = pSearchDoc->GetNextView(posView);
            ASSERT_VALID( pView );
            CFrameWnd* pFrame = pView->GetParentFrame();
            ASSERT_VALID( pFrame );
            CMDIChildWnd* pActivateChild = DYNAMIC_DOWNCAST(CMDIChildWnd, pFrame);
            ASSERT_VALID( pActivateChild );
            MDIActivate( pActivateChild );
          }
        }
        
        // Now we can call OnWindowNew() here, because we activated the corresponding child window
        OnWindowNew();
    
        return DYNAMIC_DOWNCAST(CMDIChildWndEx, MDIGetActive());
      }
    
    Kind regards,

    Andreas

    quarta-feira, 7 de março de 2012 12:56
  • The same problem occurs when compiled with VS2012 + SP1 + Platform Toolset (v110).

    Andreas

    terça-feira, 4 de dezembro de 2012 11:51