Как убедить CScrollView MFC прокрутить по целым строкам, а не только по пикселям?

Измученный! Как убедить CScrollView MFC прокрутить по целым строкам, а не только по пикселям? Я настолько отчаялся, что даже взял «Программирование Windows с помощью MFC» Джеффа Проциса для копирования & вставьте найденный там тривиальный пример. Безуспешно…!

Я расширяю большой проект с помощью довольно упрощенного гекса-редактора, и я столкнулся с этим кошмаром. Как это обычно бывает, я упускаю что-то ужасно маленькое, чтобы это работало.

Смотрите код ниже и две версии OnSize обработчик, который, как я предполагаю, является источником проблем. В каждом из них я: (а) определяю количество строк, представляющих файл (каждая строка с 16 байтами, например, файл размером 500 байт представлен 32 строками), и (б) устанавливаю параметры вертикальная полоса прокрутки. Если нужно показать 32 строки, я устанавливаю диапазон на 0..32. К сожалению, MFC воспринимает этот диапазон так, как если бы он был в пикселях, а не в «строках», и я не могу убедить его сделать иначе в моем OnDraw метод.

Упоминание OnDraw метод, проблема визуально кажется, что существует область отсечения, которая не может быть удалена путем вызова myDC.SelectClipRgn(NULL);, Ака, я не могу рисовать в той области, в которой Windows умела рисовать сама, перемещая исходный контент. Проблема, однако, вполне определенно будет лежать где-то еще …

// MFC message map set correctly

afx_msg int CHexaEditor::OnCreate(LPCREATESTRUCT lpcs){
// window created; instantiating the window manually
if (CScrollView::OnCreate(lpcs)==-1) return -1;
m_nMapMode=MM_TEXT;   // reason - see OnSize version 2
m_lineDev=CSize(0,1); // reason - see OnSize version 2
}
void CHexaEditor::PostNcDestroy(){
// window ready to be destroyed
//nop (I'll do the job)
}

// OnSize handler version 1 (making use of MFC methods)
afx_msg void CHexaEditor::OnSize(UINT nType,int cx,int cy){
// window size changed
nLinesTotal=...;
SetScrollSizes( MM_TEXT,
CSize(0,nLinesTotal),
...page size...,
CSize(0,1) // scroll up & down by one line
);
ShowScrollBar(SB_VERT,TRUE); // force visible
}

// OnSize handler version 2 (making use of API functions)
afx_msg void CHexaEditor::OnSize(UINT nType,int cx,int cy){
// window size changed
nLinesTotal=...;
SCROLLINFO si={ sizeof(si), SIF_RANGE|SIF_PAGE, ... };
SetScrollInfo( SB_VERT, &si, FALSE );
ShowScrollBar(SB_VERT,TRUE); // force visible
}

Любая помощь приветствуется, спасибо заранее!

Tomas

4

Решение

В CMyScrollView::OnCreate добавьте следующий код

int res = CScrollView::OnCreate(lpcs);
SIZE  sizeTotal = { 0, line_height * line_total };
SIZE  sizeLine = { 0, line_height };
SetScrollSizes(MM_TEXT, sizeTotal, sizeTotal, sizeLine);
return res;

line_height высота каждой строки в пикселях. Например 20 пикселей.

line_total это общее количество строк (не общее количество видимых строк на каждой странице, а общее количество строк от начала до конца, включая строки, которые не видны)

Смотрите также: CScrollView :: SetScrollSizes

Просто распечатайте весь контент. Например:

void CMyView::OnDraw(CDC* pdc)
{
CMyDoc* pDoc = GetDocument();
if (!pDoc) return;

CString s;
for (int i = 0; i < line_total; i++)
{
s.Format(L"line %d", i);
pdc->TextOut(0, i * line_height, s);
}
}

Не нужно ничего менять в CMyScrollView::OnSize, за исключением настройки размера страницы.



Чтобы внести изменения в SCROLLINFOне езжай из CScrollView, Езды от CViewвместо. добавлять ON_WM_VSCROLL к карте сообщений для обработки сообщений прокрутки.

void CMyView::OnInitialUpdate()
{
//you can also overload OnCreate to setup scroller
CView::OnInitialUpdate();

line_height = 18;
line_total = 0xffff;

SCROLLINFO info = { sizeof(SCROLLINFO) };
info.nMin = 0;
info.nMax = line_total;
info.nPage = 1;
info.fMask = SIF_ALL;
SetScrollInfo(SB_VERT, &info, TRUE);
}

void CMyView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CView::OnVScroll(nSBCode, nPos, pScrollBar);

SCROLLINFO info = { sizeof(SCROLLINFO) };
GetScrollInfo(SB_VERT, &info, SIF_ALL);

int pos = info.nPos;
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;

info.cbSize = sizeof(SCROLLINFO);
info.nPos = pos;
SetScrollInfo(SB_VERT, &info, FALSE);

Invalidate(FALSE);
}

void CMyView::OnDraw(CDC* pdc)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;

CRect rc;
GetClientRect(&rc);

SCROLLINFO info = { sizeof(SCROLLINFO) };
info.fMask = SIF_ALL;
GetScrollInfo(SB_VERT, &info, SIF_POS);

//find the start and end posion, print the visible portion
int start = info.nPos;
int end = start + rc.Height() / line_height;
if (end > line_total) end = line_total;
for (int i = start, y = 0; i < end; i++, y += line_height)
{
CString s;
s.Format(_T("line %i"), i);
pdc->TextOut(0, y, s);
}
}
3

Другие решения

Спасибо за хорошие предложения, они расширили мое мнение 🙂

Во всяком случае, отвечая теперь на мой собственный вопрос, так как я решил отклонить путь через CScrollView и делать вещи, используя CEdit, Я бы пошел по пути еще более общего CWnd (или же CView как предложено выше), но по какой-то странной причине они не принимают фокус ввода, если их CSplitterWnd, Так что после довольно пристального изучения поведения CScrollView в разных ситуациях прокрутки я больше не думаю, что это будет легкий (или даже выполнимый!) способ. Основная причина CScrollView сам — это вызывает ScrollWindow это устанавливает область отсечения (область клиента минус «область, которую Windows умела рисовать»), на которую мне не удалось повлиять. Как я уже сказал, гораздо лучший способ — извлечь из CEdit (или же CEditView), или для создания вещи с использованием чистого WinAPI — в любом случае я могу рассчитывать прямо в строках и не нужно совершать обходы до и из числа пикселей. Я выбрал МФЦ CEdit поскольку я не хочу беспокоиться о COM, когда дело доходит до копирования, вставки и т. д. Если кому-то интересно, ВотЭто то, что у меня так далеко (на данный момент неинтерактивный зритель).

Тем не менее, два места о том, что было не так в CScrollView способ: (1) главная проблема не была в OnSize обработчик — это было в процедуре рисования, где я комбинировал по существу три разные идеи (прокрутка по линиям, прокрутка по пикселям и прокрутка Джеффа Проциса по пикселям), которые позволяли объединяться в коде рисования, который был один раз CDC::GetClipBox меня называли — вот почему я не мог рисовать повсюду на видимом «холсте»; и (2) отсутствует return заявление в OnCreate Обработчик — возвращая случайный результат (не ноль, указывающий на сбой), мне пришлось вручную показывать полосу прокрутки.

Надеюсь, это поможет кому-то, кто также хочет прокручивать вещи по целым строкам в MFC.

0

По вопросам рекламы [email protected]