我已将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);
}
}
}
答案 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
的调用,并手动重新定位所有子窗口。