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

问题描述 投票:0回答:1

我在 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);
            }
        }
    }
c++ mfc
1个回答
2
投票

使用

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

如果指定了

SW_SCROLLCHILDREN
标志,则当子窗口的一部分滚动时,Windows 将无法正确更新屏幕。位于源矩形之外的滚动子窗口部分将不会被擦除,也不会在其新目标中正确重绘。使用
DeferWindowPos
Windows 函数移动未完全位于
lpRectScroll
矩形内的子窗口。

解决方案是手动移动子窗口。

DeferWindowPos
与针对多个窗口调用
SetWindowPos
具有相同的效果,但经过优化以在单次调用中执行布局。这有助于减少视觉伪影,即控件看起来相对于彼此移动,直到一切都解决。

DeferWindowPos
需要一个保存新窗口属性的结构。它是通过调用
BeginDeferWindowPos
创建的,然后通过调用
DeferWindowPos
为每个窗口进行更新,最后发送到系统以使用
EndDeferWindowPos
执行重新定位。以下代码假设一个数组包含数组中所有子控件的
CWnd*
,其中 cxcy 保存水平和垂直偏移。它的目的是取代对
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 );
© www.soinside.com 2019 - 2024. All rights reserved.