MFC CScrollView鼠标拖拽移动滚动条出现回滚问题
问题分析:逻辑视图较大的时候(10w),使用鼠标拖拽移动滚动条时(单击不会回滚),会在32000左右回滚到0。看到熟悉的32000,应该是在获取滚动条位置时,出现了短字节数据转换到长字节数据时出现的精度丢失问题,通过debug单步调试,发现函数调用层次如下:滚动条位置nPos数据,通过函数一层层的转换和计算后传递到SetScrollPos正式更新滚动条位置。
OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) (垂直方向移动滚动条时调用)
OnScroll(MAKEWORD(nSBCode, 0xff), nPos);
OnScrollBy(CSize(x - xOrig, y - yOrig), bDoScroll);
SetScrollPos(SB_HORZ, x);(此处更新滚动条位置)
通过打印nPos,当x为0后(回滚后),nPos是一个很大的数0xff280012,所以很有可能是OnHScroll上一层函数将nPos传递到OnHScroll出现问题。查找调用OnHScroll的函数,在wincore.cpp以下函数调用OnHScroll
case AfxSig_SCROLL_REFLECT:
{
// special case for WM_VSCROLL and WM_HSCROLL
ASSERT(message == WM_VSCROLL || message == WM_HSCROLL
|| message == WM_VSCROLL+WM_REFLECT_BASE
|| message == WM_HSCROLL+WM_REFLECT_BASE);
int nScrollCode = (short)LOWORD(wParam);
int nPos = (short)HIWORD(wParam); // nPos是short转换的,即不能大于32768
if (lpEntry->nSig == AfxSig_SCROLL)
(this->*mmf.pfn_v_u_u_W)(nScrollCode, nPos,
CWnd::FromHandle(reinterpret_cast<HWND>(lParam)));
else
(this->*mmf.pfn_v_u_u)(nScrollCode, nPos);
}
break;
可以看到,其中nPos,是short到int转换成的;因为某种原因,short溢出了,变成了负数,传输到OnHScroll后,再次转换成UINT,所以出现了一个很大的数0xff280012。
解决方式:抛开在wincore.cpp调用OnHScroll函数,直接获取滚动条的位置进行更新;在OnHScroll使用GetScrollInfo();函数直接获取滚动条位置,传递下去。
void MyScrollView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
SCROLLINFO si;
GetScrollInfo(SB_HORZ,&si);
CScrollView::OnHScroll(nSBCode, si.nTrackPos, pScrollBar);
}