使用ScrollWindowEx在Cwnd中滚动后控件消失

时间:2015-12-08 11:17:09

标签: c++ mfc

我在Cwnd中实现了CScrollBar,但滚动后窗口上的控件消失了。我听说过我可以使用DeferWindowPos,但我不知道怎么做。有什么想法吗?

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);

               CRect clientArea;
               GetClientRect(clientArea);

               CRect scrollbarArea;
               m_pclScrollBar->GetWindowRect(scrollbarArea);

               CRect scrollArea(clientArea);
               scrollArea.DeflateRect(0, 0, scrollbarArea.Width(), 0);
               ScrollWindowEx(0, iScrollPositionOriginal - iScrollPosition, scrollArea, NULL,
                       NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE);
            }
        }
    }

1 个答案:

答案 0 :(得分:2)

使用SW_SCROLLCHILDREN标记使用CWnd::ScrollWindowEx移动子窗口是有问题的:

  

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

解决方案是手动移动子窗口。 DeferWindowPos与为多个窗口调用SetWindowPos具有相同的效果,但优化后可在单个调用中执行布局。这有助于减少视觉伪像,其中控件似乎相对于彼此移动,直到一切都结束。

DeferWindowPos需要一个包含新窗口属性的结构。它创建时调用BeginDeferWindowPos,然后通过调用DeferWindowPos为每个窗口更新,最后发送到系统以使用EndDeferWindowPos执行重新定位。以下代码假定一个数组包含数组中所有子控件的CWnd*个,其中 cx cy 保持水平和垂直偏移。它旨在取代对ScrollWindowEx

的调用
CWnd* controls[] = { m_pEdit, m_pButton, ... };

HDWP hDwp = ::BeginDeferWindowPos( ARRAYSIZE( controls ) );

for ( size_t index = 0; index < ARRAYSIZE( controls ); ++index ) {
    // Find the current window position
    CRect wndRect;
    controls[index]->GetWindowRect( wndRect );
    // DeferWindowPos requires client coordinates, so we need to convert from screen coords
    ScreenToClient( wndRect );
    // Set the control's new position
    hDwp = ::DeferWindowPos( hDwp, *controls[index], NULL,
                             wndRect.left + cx, wndRect.top + cy, 0, 0,
                             SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE |
                             SWP_NOZORDER );
}

// All new control positions have been recorded. Now perform the operation
::EndDeferWindowPos( hDwp );