我在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);
}
}
}
答案 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 );