-2

I would like for someone to explain to me why the following use of a CScrollView derived object within a CDialog derived object works and whether this approach has hidden problems.

My concern is the casting of a CDialog to a CFrameWnd in order to use the CreateView() method of the CFrameWnd class to create a Document/View pair for the CScrollView object in the CDialog object.

I am building an MFC DLL that will provide a series of GUI functions to display and allow the edit of some types of information in a legacy application which displays a page at a time.

One of the first pieces is to provide a way to display reports in a dialog that presents the report in a scrollable component allowing the user to view the entire report.

Looking into using a CSrollView derived control in a CDialog derived GUI object, I found this article, Creating a View in Dialog (An easy way), as I was running into a number of problems with exceptions on CDialog closing when using a CScrollView. I was also seeing a warning in the debug output window of "Warning: Creating a pane with no CDocument." which I am no longer seeing.

Using the basic concept from the article I have code which seems to be working fine with Visual Studio 2005 in Windows XP in the debugger.

The code I am using in the CDialog derived class for initializing in the OnInitDialog() is as follows. I am creating the document first and putting lines of text into a memory area and the constructor of the CDialog derived object is given the address of the document, m_pDocument, which is then used in the OnInitDialog() function.

BOOL CScrollReportDialog::OnInitDialog ()
{
    // Get the client area size of the dialog we are putting the
    // CScrollView into and pull the right edge in sufficient to
    // clear buttons on the right hand side of the dialog.
    RECT  rectSize;
    GetClientRect (&rectSize);
    rectSize.right -= 120;

    // allocate and set up the view document context linking the view
    // to a particular document, in our case a CScrollDocument.
    CCreateContext pContext;
    pContext.m_pCurrentDoc = m_pDocument;
    pContext.m_pNewViewClass = RUNTIME_CLASS(CScrollReport);

    // Cast the pointer to this dialog into a CFrameWnd pointer allowing
    // us to access the CFrameWnd methods.  Both CDialog and CFrameWnd are
    // derived from CWnd so we can get away with this.
    CFrameWnd* pFrameWnd = (CFrameWnd *) ((CWnd *)this);
    CScrollReport *pView = (CScrollReport *)pFrameWnd->CreateView(&pContext);
    ASSERT(pView);

    // Set an initial scroll size for the CScrollView which will be
    // modified in the OnDraw () later when presenting the actual view
    // and we have the complete document and can calculate the document's
    // scrollable size properly.
    CSize sizeTotal;
    sizeTotal.cx = rectSize.right;
    sizeTotal.cy = 1 * rectSize.bottom;
    pView->SetScrollSizes(MM_TEXT, sizeTotal);

    pView->ShowWindow(SW_NORMAL);

    /**
    * After a view is created, resize window area of the view to fit into the
    * dialog.  Since this is a CScrollView, set an initial size for the
    * size of the object being scrolled.
    */
    pView->MoveWindow(&rectSize);
    return TRUE;
}
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • 1
    *"Both CDialog and CFrameWnd are derived from CWnd so we can get away with this"* - Oh no you can't. You can only cast up the hierarchy to a base class and `CFrameWnd` is *not* a base of `CDialog`. – Roger Rowland Jan 26 '14 at 14:43
  • @RogerRowland, since it is a C style cast which can be used to interpret a memory area or object from anything to anything else, the cast works however the question is whether it is reasonably safe or not. I found a copy of the MFC source for [`CFrameWnd::CreateView()` at this web location](http://software.msu.montana.edu/free/Microsoft/Updates/Visual_Studio/sp5/vc98/mfc/src/winfrm.cpp) and it looks like the method is doing nothing more than actually creating the view window and is not using any CFrameWnd data. It is using the `this` pointer for the parent to the created window. – Richard Chambers Jan 26 '14 at 14:58
  • I believe that after reviewing the MFC source for `CFrameWnd::CreateView()` that while this works, it is not really safe since it depends on someone not modifying the method. If someone were to modify the method to use some data within CFrameWnd which is not in the base class of CWnd then this construct would break. – Richard Chambers Jan 26 '14 at 15:01
  • is there another way of providing a dialog type of interface using a CScrollView with associated CDocument by using something other than a CDialog? Looking on the web this question of presenting a CScrollView using a dialog has been asked a number of times. There appears to be a need for presenting a document, text or graphical, in a scrollable view within a modal dialog expressed by a number of people. This is what I am facing, the need to present a short report in a scrollable area as a modal dialog. – Richard Chambers Jan 27 '14 at 03:51

1 Answers1

0

Reviewing the MFC source for CFrameWnd::CreateView() there is no dependency on any CFrameWnd data. However the implementation of the method could change at a later time.

The version of the MFC source for CFrameWnd::CreateView() uses the MFC dynamic object creation to create an instance of the view. It then creates the actual window for the view as a child window with a specific MFC document/view window identifier.

Rather than depending on CFrameWnd::CreateView() to remain the same implementation, we can implement our own version.

The modified part of the function is to replace casting the CDialog object to a CFrameWnd object in order to access the CreateView() method with the specific code for the CreateView() implementation as follows.

#if 0
    // Cast the pointer to this dialog into a CFrameWnd pointer allowing
    // us to access the CFrameWnd methods.  Both CDialog and CFrameWnd are
    // derived from CWnd so we can get away with this.
    CFrameWnd* pFrameWnd = (CFrameWnd *) ((CWnd *)this);
    CScrollReport *pView = (CScrollReport *)pFrameWnd->CreateView(&pContext);
    ASSERT(pView);
#else
    // Use the approach from CFrameWnd::CreateView() to create a view and
    // link the view with the document.  We use the dynamic CreateObject()
    // functionality to create a CScrollReport view object.  We use the
    // standard child window id for the first view of an instance of the
    // MFC document/view architecture.  We are basing this on a copy of
    // the CFrameWnd::CreateView () method from the MFC source.
    int  nID = AFX_IDW_PANE_FIRST;
    CScrollReport *pView = (CScrollReport *)pContext.m_pNewViewClass->CreateObject();
    ASSERT(pView);
    ASSERT_KINDOF(CWnd, pView);

    if (pView) {
        if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, &pContext)) {
            TRACE0("Warning: could not create view for dialog.\n");
            return FALSE;
        }
    } else {
        TRACE0("Warning: dynamic create of CScrollView for dialog failed.\n");
        return FALSE;
    }
#endif

EDIT Jan28

One problem run into is sometimes the dialog will display beneath what is a full screen application. The result is that the dialog with the CScrollView is not visible.

See stackoverflow Always in Front Dialogs for an example of using the SetWindowPos() function. I am using it in the OnInitDialog() method just before returning.

Community
  • 1
  • 1
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106