创建可滚动对话框

Posted

技术标签:

【中文标题】创建可滚动对话框【英文标题】:Creating a scrollable dialog 【发布时间】:2021-06-20 07:52:01 【问题描述】:

我想知道在 Windows API 中是否有公认的策略,可能使用 WTL 可滚动类(CScrollImplCScrollWindowImplCScrollContainer 等)。

我的一个想法是在外部父窗口中放置一个内部子对话框(其中包含实际控件),然后简单地移动子对话框窗口以响应滚动条消息。这样,至少在我看来,当滚动发生时,您不必移动每个单独的控件。您只需移动内部窗口,这将反过来移动控件。

但也许我在想这一切都错了。以前有人处理过这个问题吗?感谢您的帮助。

【问题讨论】:

这基本上就是 MFC 的 CFormView 所做的。不过,你的问题是什么? 谢谢,虽然我不在 MFC 项目中工作。我想我的问题是的典型方法是什么。一起移动所有控件?使用子对话方式? 系统不提供实现,因此您将不得不使用自定义解决方案。无论是否是典型的方法,遵循 MFC 肯定会确保您在数十年的过程中经过成百上千的应用程序的尝试和测试。 相信我,我不想重新发明***。我只是希望避免对 MFC 的依赖而坚持使用 ATL/WTL。 我并不是建议您使用 MFC。您是在询问特定的实施策略以及它是否合理。这个问题的答案是,是的,这是一种可能的方法,并且一个通用库成功地使用了它。如何将其转换为您选择的特定窗口库是您必须解决的问题。 【参考方案1】:

这是修改后的版本,可以在对话框中使用:

template <class T>
class CScroller

public:
    enum  uSCROLL_FLAGS = SW_INVALIDATE ;

    POINT m_ptOffset;
    SIZE m_sizeAll;
    SIZE m_sizeLine;
    SIZE m_sizePage;
    SIZE m_sizeClient;
    int m_zDelta;              // current wheel value
    int m_nWheelLines;         // number of lines to scroll on wheel
    int m_zHDelta;              // current horizontal wheel value
    int m_nHWheelChars;         // number of chars to scroll on horizontal wheel
    UINT m_uScrollFlags;
    DWORD m_dwExtendedStyle;   // scroll specific extended styles

// Constructor
    CScroller() : m_zDelta(0), m_nWheelLines(3),
        m_zHDelta(0), m_nHWheelChars(3),
        m_uScrollFlags(0U), m_dwExtendedStyle(0)
    
        m_ptOffset.x = 0;
        m_ptOffset.y = 0;
        m_sizeAll.cx = 0;
        m_sizeAll.cy = 0;
        m_sizePage.cx = 0;
        m_sizePage.cy = 0;
        m_sizeLine.cx = 0;
        m_sizeLine.cy = 0;
        m_sizeClient.cx = 0;
        m_sizeClient.cy = 0;

        SetScrollExtendedStyle(SCRL_SCROLLCHILDREN | SCRL_ERASEBACKGROUND);
    

    // Attributes & Operations
    DWORD GetScrollExtendedStyle() const
    
        return m_dwExtendedStyle;
    

    DWORD SetScrollExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
    
        DWORD dwPrevStyle = m_dwExtendedStyle;
        if (dwMask == 0)
            m_dwExtendedStyle = dwExtendedStyle;
        else
            m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
        // cache scroll flags
        T* pT = static_cast<T*>(this);
        (void)pT;   // avoid level 4 warning
        m_uScrollFlags = pT->uSCROLL_FLAGS | (IsScrollingChildren() ? SW_SCROLLCHILDREN : 0) | (IsErasingBackground() ? SW_ERASE : 0);
        m_uScrollFlags |= (IsSmoothScroll() ? SW_SMOOTHSCROLL : 0);
        return dwPrevStyle;
    

    // offset operations
    void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        pT->AdjustScrollOffset(x, y);

        int dx = m_ptOffset.x - x;
        int dy = m_ptOffset.y - y;
        m_ptOffset.x = x;
        m_ptOffset.y = y;

        // block: set horizontal scroll bar
        
            SCROLLINFO si =  sizeof(SCROLLINFO) ;
            si.fMask = SIF_POS;
            if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
                si.fMask |= SIF_DISABLENOSCROLL;
            si.nPos = m_ptOffset.x;
            pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
        

        // block: set vertical scroll bar
        
            SCROLLINFO si =  sizeof(SCROLLINFO) ;
            si.fMask = SIF_POS;
            if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
                si.fMask |= SIF_DISABLENOSCROLL;
            si.nPos = m_ptOffset.y;
            pT->SetScrollInfo(SB_VERT, &si, bRedraw);
        

        // Move all children if needed
        if (IsScrollingChildren() && ((dx != 0) || (dy != 0)))
        
            for (HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
            
                RECT rect = ;
                ::GetWindowRect(hWndChild, &rect);
                ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
                ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
            
        

        if (bRedraw)
            pT->Invalidate();
    

    void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE)
    
        SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw);
    

    void GetScrollOffset(POINT& ptOffset) const
    
        ptOffset = m_ptOffset;
    

    // size operations
    void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        m_sizeAll.cx = cx;
        m_sizeAll.cy = cy;

        int x = 0;
        int y = 0;
        if (!bResetOffset)
        
            x = m_ptOffset.x;
            y = m_ptOffset.y;
            pT->AdjustScrollOffset(x, y);
        

        int dx = m_ptOffset.x - x;
        int dy = m_ptOffset.y - y;
        m_ptOffset.x = x;
        m_ptOffset.y = y;

        // block: set horizontal scroll bar
        
            SCROLLINFO si =  sizeof(SCROLLINFO) ;
            si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
            if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
                si.fMask |= SIF_DISABLENOSCROLL;
            si.nMin = 0;
            si.nMax = m_sizeAll.cx - 1;
            si.nPage = m_sizeClient.cx;
            si.nPos = m_ptOffset.x;
            pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
        

        // block: set vertical scroll bar
        
            SCROLLINFO si =  sizeof(SCROLLINFO) ;
            si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
            if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
                si.fMask |= SIF_DISABLENOSCROLL;
            si.nMin = 0;
            si.nMax = m_sizeAll.cy - 1;
            si.nPage = m_sizeClient.cy;
            si.nPos = m_ptOffset.y;
            pT->SetScrollInfo(SB_VERT, &si, bRedraw);
        

        // Move all children if needed
        if (IsScrollingChildren() && ((dx != 0) || (dy != 0)))
        
            for (HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
            
                RECT rect = ;
                ::GetWindowRect(hWndChild, &rect);
                ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
                ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
            
        

        SetScrollLine(0, 0);
        SetScrollPage(0, 0);

        if (bRedraw)
            pT->Invalidate();
    

    void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true)
    
        SetScrollSize(size.cx, size.cy, bRedraw, bResetOffset);
    

    void GetScrollSize(SIZE& sizeWnd) const
    
        sizeWnd = m_sizeAll;
    

    // line operations
    void SetScrollLine(int cxLine, int cyLine)
    
        ATLASSERT((cxLine >= 0) && (cyLine >= 0));
        ATLASSERT((m_sizeAll.cx != 0) && (m_sizeAll.cy != 0));

        m_sizeLine.cx = T::CalcLineOrPage(cxLine, m_sizeAll.cx, 100);
        m_sizeLine.cy = T::CalcLineOrPage(cyLine, m_sizeAll.cy, 100);
    

    void SetScrollLine(SIZE sizeLine)
    
        SetScrollLine(sizeLine.cx, sizeLine.cy);
    

    void GetScrollLine(SIZE& sizeLine) const
    
        sizeLine = m_sizeLine;
    

    // page operations
    void SetScrollPage(int cxPage, int cyPage)
    
        ATLASSERT((cxPage >= 0) && (cyPage >= 0));
        ATLASSERT((m_sizeAll.cx != 0) && (m_sizeAll.cy != 0));

        m_sizePage.cx = T::CalcLineOrPage(cxPage, m_sizeAll.cx, 10);
        m_sizePage.cy = T::CalcLineOrPage(cyPage, m_sizeAll.cy, 10);
    

    void SetScrollPage(SIZE sizePage)
    
        SetScrollPage(sizePage.cx, sizePage.cy);
    

    void GetScrollPage(SIZE& sizePage) const
    
        sizePage = m_sizePage;
    

    // commands
    void ScrollLineDown()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT, SB_LINEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
    

    void ScrollLineUp()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT, SB_LINEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
    

    void ScrollPageDown()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT, SB_PAGEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
    

    void ScrollPageUp()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT, SB_PAGEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
    

    void ScrollTop()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT, SB_TOP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
    

    void ScrollBottom()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT, SB_BOTTOM, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
    

    void ScrollLineRight()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ, SB_LINEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
    

    void ScrollLineLeft()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ, SB_LINEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
    

    void ScrollPageRight()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ, SB_PAGEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
    

    void ScrollPageLeft()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ, SB_PAGEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
    

    void ScrollAllLeft()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ, SB_TOP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
    

    void ScrollAllRight()
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ, SB_BOTTOM, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
    

    // scroll to make point/view/window visible
    void ScrollToView(POINT pt)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        RECT rect =  pt.x, pt.y, pt.x, pt.y ;
        pT->ScrollToView(rect);
    

    void ScrollToView(RECT& rect)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        RECT rcClient = ;
        pT->GetClientRect(&rcClient);

        int x = m_ptOffset.x;
        if (rect.left < m_ptOffset.x)
            x = rect.left;
        else if (rect.right > (m_ptOffset.x + rcClient.right))
            x = rect.right - rcClient.right;

        int y = m_ptOffset.y;
        if (rect.top < m_ptOffset.y)
            y = rect.top;
        else if (rect.bottom > (m_ptOffset.y + rcClient.bottom))
            y = rect.bottom - rcClient.bottom;

        SetScrollOffset(x, y);
    

    void ScrollToView(HWND hWnd)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        RECT rect = ;
        ::GetWindowRect(hWnd, &rect);
        ::OffsetRect(&rect, m_ptOffset.x, m_ptOffset.y);
        ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2);
        ScrollToView(rect);
    

    BEGIN_MSG_MAP(CScroller)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        MESSAGE_HANDLER(WM_VSCROLL, OnVScroll)
        MESSAGE_HANDLER(WM_HSCROLL, OnHScroll)
        MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
        MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnMouseHWheel)
        MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
        MESSAGE_HANDLER(WM_SIZE, OnSize)
        // standard scroll commands
        ALT_MSG_MAP(1)
        COMMAND_ID_HANDLER(ID_SCROLL_UP, OnScrollUp)
        COMMAND_ID_HANDLER(ID_SCROLL_DOWN, OnScrollDown)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, OnScrollPageUp)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, OnScrollPageDown)
        COMMAND_ID_HANDLER(ID_SCROLL_TOP, OnScrollTop)
        COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, OnScrollBottom)
        COMMAND_ID_HANDLER(ID_SCROLL_LEFT, OnScrollLeft)
        COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, OnScrollRight)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, OnScrollPageLeft)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, OnScrollPageRight)
        COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, OnScrollAllLeft)
        COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, OnScrollAllRight)
    END_MSG_MAP()

    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
    
        T* pT = static_cast<T*>(this);
        pT->GetSystemSettings();

        bHandled = FALSE;
        return 1;
    

    LRESULT OnVScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT, (int)(short)LOWORD(wParam), (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
        return 0;
    

    LRESULT OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ, (int)(short)LOWORD(wParam), (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
        return 0;
    

    LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
        int nScrollCode = (m_nWheelLines == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGEUP : SB_PAGEDOWN) : ((zDelta > 0) ? SB_LINEUP : SB_LINEDOWN);
        m_zDelta += zDelta;   // cumulative
        int zTotal = (m_nWheelLines == WHEEL_PAGESCROLL) ? abs(m_zDelta) : abs(m_zDelta) * m_nWheelLines;
        if (m_sizeAll.cy > m_sizeClient.cy)
        
            for (int i = 0; i < zTotal; i += WHEEL_DELTA)
            
                pT->DoScroll(SB_VERT, nScrollCode, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
                pT->UpdateWindow();
            
        
        else if (m_sizeAll.cx > m_sizeClient.cx)   // can't scroll vertically, scroll horizontally
        
            for (int i = 0; i < zTotal; i += WHEEL_DELTA)
            
                pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
                pT->UpdateWindow();
            
        
        m_zDelta %= WHEEL_DELTA;

        return 0;
    

    LRESULT OnMouseHWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
        int nScrollCode = (m_nHWheelChars == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGERIGHT : SB_PAGELEFT) : ((zDelta > 0) ? SB_LINERIGHT : SB_LINELEFT);
        m_zHDelta += zDelta;   // cumulative
        int zTotal = (m_nHWheelChars == WHEEL_PAGESCROLL) ? abs(m_zHDelta) : abs(m_zHDelta) * m_nHWheelChars;
        if (m_sizeAll.cx > m_sizeClient.cx)
        
            for (int i = 0; i < zTotal; i += WHEEL_DELTA)
            
                pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
                pT->UpdateWindow();
            
        
        m_zHDelta %= WHEEL_DELTA;

        return 0;
    

    LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        GetSystemSettings();
        return 0;
    

    LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
    
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        pT->DoSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));

        bHandled = FALSE;
        return 1;
    

    // scrolling handlers
    LRESULT OnScrollUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollLineUp();
        return 0;
    

    LRESULT OnScrollDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollLineDown();
        return 0;
    

    LRESULT OnScrollPageUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollPageUp();
        return 0;
    

    LRESULT OnScrollPageDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollPageDown();
        return 0;
    

    LRESULT OnScrollTop(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollTop();
        return 0;
    

    LRESULT OnScrollBottom(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollBottom();
        return 0;
    

    LRESULT OnScrollLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollLineLeft();
        return 0;
    

    LRESULT OnScrollRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollLineRight();
        return 0;
    

    LRESULT OnScrollPageLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollPageLeft();
        return 0;
    

    LRESULT OnScrollPageRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollPageRight();
        return 0;
    

    LRESULT OnScrollAllLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollAllLeft();
        return 0;
    

    LRESULT OnScrollAllRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        ScrollAllRight();
        return 0;
    

    // Overrideables
    void DoPaint(CDCHandle /*dc*/)
    
        // must be implemented in a derived class
        ATLASSERT(FALSE);
    

    // Implementation
    void DoSize(int cx, int cy)
    
        m_sizeClient.cx = cx;
        m_sizeClient.cy = cy;

        T* pT = static_cast<T*>(this);

        // block: set horizontal scroll bar
        
            SCROLLINFO si =  sizeof(SCROLLINFO) ;
            si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
            si.nMin = 0;
            si.nMax = m_sizeAll.cx - 1;
            if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
                si.fMask |= SIF_DISABLENOSCROLL;
            si.nPage = m_sizeClient.cx;
            si.nPos = m_ptOffset.x;
            pT->SetScrollInfo(SB_HORZ, &si, TRUE);
        

        // block: set vertical scroll bar
        
            SCROLLINFO si =  sizeof(SCROLLINFO) ;
            si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
            si.nMin = 0;
            si.nMax = m_sizeAll.cy - 1;
            if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
                si.fMask |= SIF_DISABLENOSCROLL;
            si.nPage = m_sizeClient.cy;
            si.nPos = m_ptOffset.y;
            pT->SetScrollInfo(SB_VERT, &si, TRUE);
        

        int x = m_ptOffset.x;
        int y = m_ptOffset.y;
        if (pT->AdjustScrollOffset(x, y))
        
            // Children will be moved in SetScrollOffset, if needed
            pT->ScrollWindowEx(m_ptOffset.x - x, m_ptOffset.y - y, (m_uScrollFlags & ~SCRL_SCROLLCHILDREN));
            SetScrollOffset(x, y, FALSE);
        
    

    void DoScroll(int nType, int nScrollCode, int& cxyOffset, int cxySizeAll, int cxySizePage, int cxySizeLine)
    
        T* pT = static_cast<T*>(this);
        RECT rect = ;
        pT->GetClientRect(&rect);
        int cxyClient = (nType == SB_VERT) ? rect.bottom : rect.right;
        int cxyMax = cxySizeAll - cxyClient;

        if (cxyMax < 0)   // can't scroll, client area is bigger
            return;

        bool bUpdate = true;
        int cxyScroll = 0;

        switch (nScrollCode)
        
        case SB_TOP:        // top or all left
            cxyScroll = cxyOffset;
            cxyOffset = 0;
            break;
        case SB_BOTTOM:     // bottom or all right
            cxyScroll = cxyOffset - cxyMax;
            cxyOffset = cxyMax;
            break;
        case SB_LINEUP:     // line up or line left
            if (cxyOffset >= cxySizeLine)
            
                cxyScroll = cxySizeLine;
                cxyOffset -= cxySizeLine;
            
            else
            
                cxyScroll = cxyOffset;
                cxyOffset = 0;
            
            break;
        case SB_LINEDOWN:   // line down or line right
            if (cxyOffset < cxyMax - cxySizeLine)
            
                cxyScroll = -cxySizeLine;
                cxyOffset += cxySizeLine;
            
            else
            
                cxyScroll = cxyOffset - cxyMax;
                cxyOffset = cxyMax;
            
            break;
        case SB_PAGEUP:     // page up or page left
            if (cxyOffset >= cxySizePage)
            
                cxyScroll = cxySizePage;
                cxyOffset -= cxySizePage;
            
            else
            
                cxyScroll = cxyOffset;
                cxyOffset = 0;
            
            break;
        case SB_PAGEDOWN:   // page down or page right
            if (cxyOffset < cxyMax - cxySizePage)
            
                cxyScroll = -cxySizePage;
                cxyOffset += cxySizePage;
            
            else
            
                cxyScroll = cxyOffset - cxyMax;
                cxyOffset = cxyMax;
            
            break;
        case SB_THUMBTRACK:
            if (IsNoThumbTracking())
                break;
            // else fall through
        case SB_THUMBPOSITION:
        
            SCROLLINFO si =  sizeof(SCROLLINFO), SIF_TRACKPOS ;
            if (pT->GetScrollInfo(nType, &si))
            
                cxyScroll = cxyOffset - si.nTrackPos;
                cxyOffset = si.nTrackPos;
            
        
        break;
        case SB_ENDSCROLL:
        default:
            bUpdate = false;
            break;
        

        if (bUpdate && (cxyScroll != 0))
        
            pT->SetScrollPos(nType, cxyOffset, TRUE);
            if (nType == SB_VERT)
                pT->ScrollWindowEx(0, cxyScroll, m_uScrollFlags);
            else
                pT->ScrollWindowEx(cxyScroll, 0, m_uScrollFlags);
        
    

    static int CalcLineOrPage(int nVal, int nMax, int nDiv)
    
        if (nVal == 0)
        
            nVal = nMax / nDiv;
            if (nVal < 1)
                nVal = 1;
        
        else if (nVal > nMax)
        
            nVal = nMax;
        

        return nVal;
    

    bool AdjustScrollOffset(int& x, int& y)
    
        int xOld = x;
        int yOld = y;

        int cxMax = m_sizeAll.cx - m_sizeClient.cx;
        if (x > cxMax)
            x = (cxMax >= 0) ? cxMax : 0;
        else if (x < 0)
            x = 0;

        int cyMax = m_sizeAll.cy - m_sizeClient.cy;
        if (y > cyMax)
            y = (cyMax >= 0) ? cyMax : 0;
        else if (y < 0)
            y = 0;

        return ((x != xOld) || (y != yOld));
    

    void GetSystemSettings()
    
        ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &m_nWheelLines, 0);

#ifndef SPI_GETWHEELSCROLLCHARS
        const UINT SPI_GETWHEELSCROLLCHARS = 0x006C;
#endif
        ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &m_nHWheelChars, 0);
    

    bool IsScrollingChildren() const
    
        return (m_dwExtendedStyle & SCRL_SCROLLCHILDREN) != 0;
    

    bool IsErasingBackground() const
    
        return (m_dwExtendedStyle & SCRL_ERASEBACKGROUND) != 0;
    

    bool IsNoThumbTracking() const
    
        return (m_dwExtendedStyle & SCRL_NOTHUMBTRACKING) != 0;
    

    bool IsSmoothScroll() const
    
        return (m_dwExtendedStyle & SCRL_SMOOTHSCROLL) != 0;
    
;

template <class T, class TBase = ATL::CWindow>
class ATL_NO_VTABLE CScrollDialogImpl : public ATL::CDialogImpl<T, TBase>, public CScroller< T >

public:
    void InitializeScroll(HWND hWnd)
    
        T* pT = static_cast<T*>(this);
        pT->GetSystemSettings();

        RECT rect = ;
        this->GetClientRect(&rect);
        pT->DoSize(rect.right, rect.bottom);
    

    BEGIN_MSG_MAP(CScrollDialogImpl)
        MESSAGE_HANDLER(WM_VSCROLL, CScroller< T >::OnVScroll)
        MESSAGE_HANDLER(WM_HSCROLL, CScroller< T >::OnHScroll)
        MESSAGE_HANDLER(WM_MOUSEWHEEL, CScroller< T >::OnMouseWheel)
        MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScroller< T >::OnMouseHWheel)
        MESSAGE_HANDLER(WM_SETTINGCHANGE, CScroller< T >::OnSettingChange)
        MESSAGE_HANDLER(WM_SIZE, CScroller< T >::OnSize)
        ALT_MSG_MAP(1)
        COMMAND_ID_HANDLER(ID_SCROLL_UP, CScroller< T >::OnScrollUp)
        COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScroller< T >::OnScrollDown)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScroller< T >::OnScrollPageUp)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScroller< T >::OnScrollPageDown)
        COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScroller< T >::OnScrollTop)
        COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScroller< T >::OnScrollBottom)
        COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScroller< T >::OnScrollLeft)
        COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScroller< T >::OnScrollRight)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScroller< T >::OnScrollPageLeft)
        COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScroller< T >::OnScrollPageRight)
        COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScroller< T >::OnScrollAllLeft)
        COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScroller< T >::OnScrollAllRight)
    END_MSG_MAP()
;

然后您可以如下使用它(启用垂直和水平滚动条的无模式对话框)

class CMainDlg : public CScrollDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
        public CMessageFilter, public CIdleHandler

public:
    enum  IDD = IDD_MAINDLG ;

    virtual BOOL PreTranslateMessage(MSG* pMsg)
    
        return CWindow::IsDialogMessage(pMsg);
    

    virtual BOOL OnIdle()
    
        UIUpdateChildWindows();
        return FALSE;
    

    BEGIN_UPDATE_UI_MAP(CMainDlg)
    END_UPDATE_UI_MAP()

    BEGIN_MSG_MAP(CMainDlg)
        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
        COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
        COMMAND_ID_HANDLER(IDOK, OnOK)
        COMMAND_ID_HANDLER(IDCANCEL, OnCancel)

        CHAIN_MSG_MAP(CScrollDialogImpl<CMainDlg>);
    END_MSG_MAP()

    LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        this->InitializeScroll(m_hWnd);

        RECT rect = ;
        this->GetClientRect(&rect);

        SetScrollSize(rect.right + 200, rect.bottom + 200);

        // center the dialog on the screen
        CenterWindow();

        // set icons
        HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, 
            ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
        SetIcon(hIcon, TRUE);
        HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, 
            ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
        SetIcon(hIconSmall, FALSE);

        // register object for message filtering and idle updates
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        ATLASSERT(pLoop != NULL);
        pLoop->AddMessageFilter(this);
        pLoop->AddIdleHandler(this);

        UIAddChildWindowContainer(m_hWnd);

        return TRUE;
    

    LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        // unregister message filtering and idle updates
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        ATLASSERT(pLoop != NULL);
        pLoop->RemoveMessageFilter(this);
        pLoop->RemoveIdleHandler(this);

        return 0;
    

    LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        CAboutDlg dlg;
        dlg.DoModal();
        return 0;
    

    LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        // TODO: Add validation code 
        CloseDialog(wID);
        return 0;
    

    LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    
        CloseDialog(wID);
        return 0;
    

    void CloseDialog(int nVal)
    
        DestroyWindow();
        ::PostQuitMessage(nVal);
    
;

【讨论】:

以上是关于创建可滚动对话框的主要内容,如果未能解决你的问题,请参考以下文章

将一个对话框拖放到另一个框中添加不必要的可滚动条

关于带有可滚动文本和网页按钮的对话框

纸质对话框可滚动在阴暗的dom中工作吗?

如何在具有绝对位置的滚动窗口中居中模式对话框?

颤振选择对话框 - 将过滤器字段滚动到视图中

Creating Dialogbased Win32 Application / 创建基于对话框的Win32应用程序Edit Control的应用Unicode转ANSI自动滚动 /