在 MFC DLL 中的 CDialog 派生对象中使用 CScrollView 派生对象

Posted

技术标签:

【中文标题】在 MFC DLL 中的 CDialog 派生对象中使用 CScrollView 派生对象【英文标题】:Using CScrollView derived object in a CDialog derived object in MFC DLL 【发布时间】:2014-01-26 14:07:58 【问题描述】:

我希望有人向我解释为什么以下在 CDialog 派生对象中使用 CScrollView 派生对象有效,以及这种方法是否存在隐藏问题。

我担心将 CDialog 转换为 CFrameWnd,以便使用 CFrameWnd 类的 CreateView() 方法为 CDialog 对象中的 CScrollView 对象创建 Document/View 对。

我正在构建一个 MFC DLL,它将提供一系列 GUI 函数来显示和允许在一次显示一个页面的旧应用程序中编辑某些类型的信息。

其中一个是提供一种在对话框中显示报表的方法,该对话框以可滚动组件的形式呈现报表,允许用户查看整个报表。

研究在 CDialog 派生的 GUI 对象中使用 CSrollView 派生控件时,我发现了这篇文章,Creating a View in Dialog (An easy way),因为我在使用 CScrollView 时遇到了许多关于 CDialog 关闭异常的问题。我还在“警告:创建没有 CDocument 的窗格”的调试输出窗口中看到警告。我不再看到了。

使用文章中的基本概念,我的代码似乎在 Windows XP 中的 Visual Studio 2005 调试器中运行良好。

我在 CDialog 派生类中用于在OnInitDialog() 中初始化的代码如下。我首先创建文档并将文本行放入内存区域,然后为 CDialog 派生对象的构造函数提供文档地址m_pDocument,然后在OnInitDialog() 函数中使用该地址。

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;

【问题讨论】:

“CDialog 和 CFrameWnd 都是从 CWnd 派生的,所以我们可以摆脱这个” - 哦,不,你不能。您只能将层次结构转换为基类,而CFrameWnd 不是 CDialog 的基类。 @RogerRowland,因为它是一种 C 风格的转换,可用于将内存区域或对象从任何东西解释为其他任何东西,转换工作但问题是它是否相当安全。我找到了CFrameWnd::CreateView() at this web location 的 MFC 源代码的副本,看起来该方法实际上只是创建了视图窗口,并且没有使用任何 CFrameWnd 数据。它使用this 指针作为父级指向创建的窗口。 我相信,在查看了 CFrameWnd::CreateView() 的 MFC 源代码后,虽然这可行,但它并不安全,因为它依赖于不修改方法的人。如果有人要修改方法以使用 CFrameWnd 中的某些数据,而这些数据不在 CWnd 的基类中,那么这个构造就会中断。 是否有另一种方法可以使用 CScrollView 和关联的 CDocument 通过使用 CDialog 以外的其他方式来提供对话框类型的界面?在网上看这个使用对话框呈现 CScrollView 的问题已经被问过很多次了。似乎需要在由许多人表达的模态对话框内的可滚动视图中呈现文档、文本或图形。这就是我面临的问题,需要在可滚动区域中以模式对话框的形式呈现简短报告。 【参考方案1】:

查看 CFrameWnd::CreateView() 的 MFC 源代码不依赖于任何 CFrameWnd 数据。但是,该方法的实现可能会在以后发生变化。

CFrameWnd::CreateView() 的 MFC 源版本使用 MFC 动态对象创建来创建视图的实例。然后,它为视图创建实际窗口作为具有特定 MFC 文档/视图窗口标识符的子窗口。

我们可以实现自己的版本,而不是依赖CFrameWnd::CreateView() 来保持相同的实现。

该函数的修改部分是用CreateView()实现的具体代码如下替换将CDialog对象强制转换为CFrameWnd对象,以便访问CreateView()方法。

#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

编辑1月28日

遇到的一个问题是有时对话框会显示在全屏应用程序下方。结果是带有 CScrollView 的对话框不可见。

有关使用 SetWindowPos() 函数的示例,请参阅 *** Always in Front Dialogs。我在返回之前在OnInitDialog() 方法中使用它。

【讨论】:

以上是关于在 MFC DLL 中的 CDialog 派生对象中使用 CScrollView 派生对象的主要内容,如果未能解决你的问题,请参考以下文章

从 CDialog 派生的对话框在 DoModal (MFC/C++) 之后返回 -1

在 Visual C++ DLL 中包含 MFC 资源(CDialog-Form)

MFC DLL的GUI工具?

从 .NET C# DLL 更新 MFC 对话框中的进度条

在 MFC 应用程序中动态创建控件

如何知道 CDialog 派生类中的焦点何时更改