我在 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);
}
}
}
CWnd::ScrollWindowEx
使用 SW_SCROLLCHILDREN
标志移动子窗口是有问题的:
标志,则当子窗口的一部分滚动时,Windows 将无法正确更新屏幕。位于源矩形之外的滚动子窗口部分将不会被擦除,也不会在其新目标中正确重绘。使用SW_SCROLLCHILDREN
Windows 函数移动未完全位于DeferWindowPos
矩形内的子窗口。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 );