微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

创建可滚动对话框

如何解决创建可滚动对话框

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

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

但也许我在想这一切都是错误的。有没有人处理过这个问题?感谢您的帮助。

解决方法

这是修改后的版本,适用于对话框:

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,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,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,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,SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
            }
        }

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

        if (bRedraw)
            pT->Invalidate();
    }

    void SetScrollSize(SIZE size,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,10);
        m_sizePage.cy = T::CalcLineOrPage(cyPage,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_sizePage.cy,m_sizeLine.cy);
    }

    void ScrollLineUp()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT,SB_LINEUP,m_sizeLine.cy);
    }

    void ScrollPageDown()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT,SB_PAGEDOWN,m_sizeLine.cy);
    }

    void ScrollPageUp()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT,SB_PAGEUP,m_sizeLine.cy);
    }

    void ScrollTop()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT,SB_TOP,m_sizeLine.cy);
    }

    void ScrollBottom()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT,SB_BOTTOM,m_sizeLine.cy);
    }

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

    void ScrollLineLeft()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ,m_sizeLine.cx);
    }

    void ScrollPageRight()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ,m_sizeLine.cx);
    }

    void ScrollPageLeft()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ,m_sizeLine.cx);
    }

    void ScrollAllLeft()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ,m_sizeLine.cx);
    }

    void ScrollAllRight()
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ,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,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,BOOL& /*bHandled*/)
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_VERT,(int)(short)LOWORD(wParam),m_sizeLine.cy);
        return 0;
    }

    LRESULT OnHScroll(UINT /*uMsg*/,BOOL& /*bHandled*/)
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));
        pT->DoScroll(SB_HORZ,m_sizeLine.cx);
        return 0;
    }

    LRESULT OnMouseWheel(UINT /*uMsg*/,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,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,m_sizeLine.cx);
                pT->UpdateWindow();
            }
        }
        m_zDelta %= WHEEL_DELTA;

        return 0;
    }

    LRESULT OnMouseHWheel(UINT /*uMsg*/,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,m_sizeLine.cx);
                pT->UpdateWindow();
            }
        }
        m_zHDelta %= WHEEL_DELTA;

        return 0;
    }

    LRESULT OnSettingChange(UINT /*uMsg*/,BOOL& /*bHandled*/)
    {
        GetSystemSettings();
        return 0;
    }

    LRESULT OnSize(UINT /*uMsg*/,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*/,BOOL& /*bHandled*/)
    {
        ScrollLineDown();
        return 0;
    }

    LRESULT OnScrollPageUp(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollPageUp();
        return 0;
    }

    LRESULT OnScrollPageDown(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollPageDown();
        return 0;
    }

    LRESULT OnScrollTop(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollTop();
        return 0;
    }

    LRESULT OnScrollBottom(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollBottom();
        return 0;
    }

    LRESULT OnScrollLeft(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollLineLeft();
        return 0;
    }

    LRESULT OnScrollRight(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollLineRight();
        return 0;
    }

    LRESULT OnScrollPageLeft(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollPageLeft();
        return 0;
    }

    LRESULT OnScrollPageRight(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollPageRight();
        return 0;
    }

    LRESULT OnScrollAllLeft(WORD /*wNotifyCode*/,BOOL& /*bHandled*/)
    {
        ScrollAllLeft();
        return 0;
    }

    LRESULT OnScrollAllRight(WORD /*wNotifyCode*/,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,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,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,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,&m_nWheelLines,0);

#ifndef SPI_GETWHEELSCROLLCHARS
        const UINT SPI_GETWHEELSCROLLCHARS = 0x006C;
#endif
        ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS,&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*/,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,::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*/,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*/,BOOL& /*bHandled*/)
    {
        CAboutDlg dlg;
        dlg.DoModal();
        return 0;
    }

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。