Print Preview in MDI Frame

Between Visual C++ 4.2 and 5.0 the behavior of displaying the print preview in MDI applications has changed. This issue is covered by MSDN Article ID: Q166135

In 5.0 the preview is not longer displayed in the MDI-Frame, it's in the MDI-Child. This results in a small preview area which is even smaller, if there are docking toolbars, and the user interface is fully functioning during preview. I think that this is unusable for many applications.

The reason is the following determination of the parent frame:


   // VC 4.2
   BOOL CView::DoPrintPreview(UINT nIDResource, CView* pPrintView,
      CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState)
   {
      ...
      CFrameWnd* pParent = (CFrameWnd*)AfxGetThread()->m_pMainWnd;
      ASSERT_VALID(pParent);
      ASSERT_KINDOF(CFrameWnd, pParent);
      ...
   }

   // VC 5.0
   BOOL CView::DoPrintPreview(UINT nIDResource, CView* pPrintView,
      CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState)
   {
      ...
      CFrameWnd* pParent;
      CWnd* pNaturalParent = pPrintView->GetParentFrame();
      pParent = DYNAMIC_DOWNCAST(CFrameWnd, pNaturalParent);
      if (pParent == NULL || pParent->IsIconic())
         pParent = (CFrameWnd*)AfxGetThread()->m_pMainWnd;
      ...
   }

To get back the old behavior the following this must be done:

1. Write your own DoPrintPreview() in your MDI View class

This function is mostly a copy of CView::DoPrintPreview() with 2 changes.

   BOOL CMDIView::DoPrintPreview(UINT nIDResource, CView* pPrintView,
      CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState)
   {
      ...
      // 1.) Use the MDI Frame as the parent
      CFrameWnd* pParent = (CFrameWnd*)AfxGetThread()->m_pMainWnd;
      ASSERT_VALID(pParent);
      ASSERT_KINDOF(CFrameWnd, pParent);
      ...

      ...
      // 2.) Deactivate the MDIChild and display the desired menu
      CChildFrame *pChildFrame = DYNAMIC_DOWNCAST (CChildFrame, pParent->GetActiveFrame());

      if (pChildFrame)
      {
          // Save the original shared menu
          HMENU hMenuShared = pChildFrame->GetSharedMenu ();

          // Set the displayed menu during preview to NULL or to the desired menu
          pChildFrame->SetSharedMenu (NULL);

          // Deactivate the MDIChild
          pChildFrame->SendMessage (WM_MDIACTIVATE, 0, 0);

          // Restore the shared menu
          pChildFrame->SetSharedMenu (hMenuShared);
      }
      ...
   }

2. Make a derivation of CPreviewView

In CView::DoPrintPreview(), lots of protected members from CPreviewView are used. This isn't longer possible in your own DoPrintPreview().

The solution is to declare the CMDIView as a friend of CMyPreviewView.


    class CMyPreviewView : public CPreviewView
    {
    protected:
        CMyPreviewView();
        DECLARE_DYNCREATE(CMyPreviewView)
        ...

        friend class CMDIView;
    };

3. Write menu access function in the MDI Child Frame


    class CChildFrame : public CMDIChildWnd
    {
        DECLARE_DYNCREATE(CChildFrame)
    public:
        CChildFrame();

    // Operations
    public:
        HMENU GetSharedMenu () const { return m_hMenuShared; }
        void SetSharedMenu (HMENU hMenuShared) { m_hMenuShared = hMenuShared; }
    ...
    };

Download demo project - 20 KB