使用SW_SCROLLCHILDREN调用ScrollWindowEx时,整个滚动条在CWnd中移动

时间:2015-12-08 08:08:29

标签: c++ mfc

我已将CScrollBar添加到名为CPanel的Cwnd中。但是当我滚动页面时,整个滚动条会移动。知道如何解决这个问题吗?将CPanel更改为CScrollView或CFormView可能不是一种选择。

CPanel::CPanel()
{
    CreateEx(WS_EX_CONTROLPARENT, _T("Static"), NULL, WS_CHILD | WS_TABSTOP | WS_BORDER, m_clRect, pwndParent, IDC_PANEL_FORM);
    ScrollBarInit();    
}

void CPanel::ScrollBarInit()
{

    //Before this i calculate size of scrollbar and size of scrollarea
    m_pclScrollBar = new CScrollBar();
    m_pclScrollBar->Create(WS_CHILD | WS_VISIBLE | SBS_VERT | SBS_RIGHTALIGN, clRectScrollbar, this, IDC_SCROLLBAR_FORM);
    m_pclScrollBar->SetScrollRange(VSCROLL_RANGE_MIN, VSCROLL_RANGE_MAX);
    //After this I add scrollbar info

}

void CPanel::OnVScroll(UINT iSBCode, UINT iPos, CScrollBar* pclScrollBar)
{
    switch(pclScrollBar->GetDlgCtrlID())
    {
        case IDC_SCROLLBAR_FORM:
            ScrollBarScroll(iSBCode, iPos, pclScrollBar);
            break;
    }
}

void CPanel::ScrollBarScroll(UINT iSBCode, UINT iPos, CScrollBar *pclScrollBar)
{
    int     iScrollPositionPrevious;
    int     iScrollPosition;
    int     iScrollPositionOriginal;

    iScrollPositionOriginal = m_pclScrollBar->GetScrollPos();
    iScrollPosition = iScrollPositionOriginal;

    if(m_pclScrollBar != NULL)
    {
        SCROLLINFO info = {sizeof( SCROLLINFO ), SIF_ALL};
        pclScrollBar->GetScrollInfo(&info, SB_CTL);

        pclScrollBar->GetScrollRange(&info.nMin, &info.nMax);
        info.nPos = pclScrollBar->GetScrollPos();

        iScrollPositionPrevious = info.nPos;

        switch(iSBCode)
        {
            case SB_TOP:            // Scroll to top
                iScrollPosition = VSCROLL_RANGE_MIN;
                break;

            case SB_BOTTOM:         // Scroll to bottom
                iScrollPosition = VSCROLL_RANGE_MAX;
                break;

            case SB_ENDSCROLL:      // End scroll
                break;

            case SB_LINEUP:         // Scroll one line up
                if(iScrollPosition - VSCROLL_LINE >= VSCROLL_RANGE_MIN)
                    iScrollPosition -= VSCROLL_LINE;
                else
                    iScrollPosition = VSCROLL_RANGE_MIN;
                break;

            case SB_LINEDOWN:       // Scroll one line down
                if(iScrollPosition + VSCROLL_LINE <= VSCROLL_RANGE_MAX)
                    iScrollPosition += VSCROLL_LINE;
                else
                    iScrollPosition = VSCROLL_RANGE_MAX;
                break;

            case SB_PAGEUP:         // Scroll one page up
            {
                // Get the page size
                SCROLLINFO   scrollInfo;
                m_pclScrollBar->GetScrollInfo(&scrollInfo, SIF_ALL);

                if(iScrollPosition > VSCROLL_RANGE_MIN)
                    iScrollPosition = max(VSCROLL_RANGE_MIN, iScrollPosition - VSCROLL_PAGE);
                break;
            }

            case SB_PAGEDOWN:       // Scroll one page down
            {
                // Get the page size
                SCROLLINFO   scrollInfo;
                m_pclScrollBar->GetScrollInfo(&scrollInfo, SIF_ALL);

                if(iScrollPosition < VSCROLL_RANGE_MAX)
                    iScrollPosition = min(VSCROLL_RANGE_MAX, iScrollPosition + VSCROLL_PAGE);
                break;
            }

            case SB_THUMBPOSITION:  // Scroll to the absolute position. The current position is provided in nPos
            case SB_THUMBTRACK:     // Drag scroll box to specified position. The current position is provided in nPos
                iScrollPosition = iPos;
                break;

            default:
                break;
        }

        if(iScrollPositionOriginal != iScrollPosition)
        {
            m_pclScrollBar->SetScrollPos(iScrollPosition);

            ScrollWindowEx(0, iScrollPositionOriginal - iScrollPosition, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE);
        }
    }
}

1 个答案:

答案 0 :(得分:2)

由于您在调用ScrollWindowEx时指定了SW_SCROLLCHILDREN(另请参阅ScrollWindowEx的Windows API文档;它通常比MFC更好),并请求整个客户区域为通过传递{em> lpRectScroll 参数的NULL滚动,系统就是这样做的。滚动条也是一个子窗口,因此它会像所有其他子控件一样移动。

该解决方案在SW_SCROLLCHILDREN的文档中暗示:

  

滚动与 lpRectScroll 指向的矩形相交的所有子窗口,其数量为dx和dy中指定的像素数。

要防止滚动条与其他子窗口一起移动,必须将其从作为 lpRectScroll 参数传递的矩形中排除。为此,请查询客户区,并减去滚动条占用的区域。假设滚动条位于右侧并覆盖整个高度,以下代码将解决您的问题:

if(iScrollPositionOriginal != iScrollPosition) {
    m_pclScrollBar->SetScrollPos(iScrollPosition);

    // Query the window's client area
    CRect clientArea;
    GetClientRect(clientArea);
    // Find the area occupied by the scrollbar
    CRect scrollbarArea;
    m_pclScrollBar->GetWindowRect(scrollbarArea);
    // Adjust the client area to exclude the scrollbar area
    CRect scrollArea(clientArea);
    scrollArea.DeflateRect(0, 0, scrollbarArea.Width(), 0);
    ScrollWindowEx(0, iScrollPositionOriginal - iScrollPosition, scrollArea, NULL,
                   NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE);
}

另请注意使用SW_SCROLLCHILDREN标志的注释:

  

如果指定了SW_SCROLLCHILDREN标志,如果滚动子窗口的一部分,Windows将无法正确更新屏幕。滚动子窗口中位于源矩形外部的部分将不会被删除,并且不会在其新目标中正确重绘。使用DeferWindowPos Windows功能移动不完全位于 lpRectScroll 矩形内的子窗口。

由于您无法控制用户将滚动的像素数量,因此会出现屏幕无法正确更新的情况。要解决此问题,请按照上面引用中列出的步骤实施解决方案:通过对ScrollWindowEx的一系列调用替换对DeferWindowPos的调用,并手动重新定位所有子窗口。