Я столкнулся с проблемой прокрутки больших областей в CScrollView. При медленном перемещении полосы прокрутки сверху вниз поведение выглядит следующим образом: сначала прокрутка работает нормально. В какой-то момент прокрутка дальше ничего не делает, вместо этого отображается верхняя часть области. В какой-то момент прокрутка начинается снова сверху.
Вот небольшой пример. Я создал новый проект MFC с использованием модели документа-представления и использовал CScrollView в качестве класса представления. Я добавил следующий код, чтобы создать большую область и добавить текст, чтобы показать, какая часть отображается в данный момент:
void CScrollViewTest2View::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal;
// TODO: calculate the total size of this view
sizeTotal.cx = sizeTotal.cy = 100*1000;
SetScrollSizes(MM_TEXT, sizeTotal);
for(int i = 0; i < 1000; i++)
{
CStatic* label = new CStatic();
label->Create(NULL, WS_CHILD | WS_VISIBLE, CRect(10,10 + i*100,100,30 + i*100), this);
CString text;
text.Format(L"%d",i);
label->SetWindowText(text);
}
}
Если я добавлю следующий код, то увижу, что во время прокрутки значение nPos, похоже, оборачивается. Это объяснило бы поведение. Но я не знаю, как обойти это.
BOOL CScrollViewTest2View::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll )
{
CString msg;
msg.Format(L"nPos = %u\n",nPos);
TRACE(msg);
return CScrollView::OnScroll(nScrollCode, nPos, bDoScroll);
}
и вот вывод, когда он прекращает прокрутку:
nPos = 27826
nPos = 29190
nPos = 30281
nPos = 31372
nPos = 31645
nPos = 32464
nPos = 4294938588
nPos = 4294939134
nPos = 4294939407
nPos = 4294939680
nPos = 4294940225
nPos = 4294940771
Так есть ли способ использовать CScrollView, чтобы полностью прокрутить большую область?
Код примера проекта можно найти Вот.
Статические элементы управления расположены не там, где вы ожидаете. Чтобы увидеть проблему, запустите следующий код:
static CStatic test;
CRect r(0, 0, 100, 30);
r.MoveToY(40000);
test.Create(0, WS_CHILD | WS_VISIBLE, r, this);
test.GetWindowRect(r);
TRACE("%d\n", r.top);
r.top
должно быть 32767
, Это из-за 16-битных ограничений в Windows. Все элементы управления, чья позиция x / y превышает этот предел, возвращаются в эту позицию.
OnScroll
Функция страдает от аналогичной проблемы, однако это можно исправить с помощью GetScrollInfo
добавлять ON_WM_VSCROLL
на карту сообщений и следующее OnVScroll
переопределить в свой класс
void CScrollViewTest2View::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CView::OnVScroll(nSBCode, nPos, pScrollBar);
SCROLLINFO info;
GetScrollInfo(SB_VERT, &info, SIF_ALL);
int pos = info.nPos;
int save = pos;
switch (nSBCode)
{
case SB_LEFT: pos = info.nMin; break;
case SB_RIGHT: pos = info.nMax; break;
case SB_LINELEFT: pos--; break;
case SB_LINERIGHT: pos++; break;
case SB_PAGELEFT: pos -= info.nPage; break;
case SB_PAGERIGHT: pos += info.nPage; break;
case SB_THUMBPOSITION: pos = info.nTrackPos; break;
case SB_THUMBTRACK: pos = info.nTrackPos; break;
}
//make sure the new position is within range
if (pos < info.nMin) pos = info.nMin;
int max = info.nMax - info.nPage + 1;
if (pos > max) pos = max;
OnScrollBy(CSize(0, pos - save), 1);
//EDIT: moved this line after OnScrollBy and added condition
if (info.nPos != pos)
{
info.nPos = pos;
SetScrollInfo(SB_VERT, &info, FALSE);
}
UpdateWindow();
}
Это не решит проблему с элементами управления windows, чья позиция x / y больше 32000, но, по крайней мере, будет прокручивать DC, как и ожидалось.
Для тестирования удалите статические элементы управления. Вы можете использовать функцию рисования ниже, чтобы проверить картину:
void CScrollViewTest2View::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal(0, 100 * 1000);
SetScrollSizes(MM_TEXT, sizeTotal);
}
void CScrollViewTest2View::OnDraw(CDC* pDC)
{
for (int i = 0; i < 1000; i++)
{
int y = i * 100;
CString s;
s.Format(L"%d ", y);
pDC->TextOutW(200, y, s);
}
}
Других решений пока нет …