Измученный! Как убедить 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
В 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);
}
}
Спасибо за хорошие предложения, они расширили мое мнение 🙂
Во всяком случае, отвечая теперь на мой собственный вопрос, так как я решил отклонить путь через CScrollView
и делать вещи, используя CEdit
, Я бы пошел по пути еще более общего CWnd
(или же CView
как предложено выше), но по какой-то странной причине они не принимают фокус ввода, если их CSplitterWnd
, Так что после довольно пристального изучения поведения CScrollView
в разных ситуациях прокрутки я больше не думаю, что это будет легкий (или даже выполнимый!) способ. Основная причина CScrollView
сам — это вызывает ScrollWindow
это устанавливает область отсечения (область клиента минус «область, которую Windows умела рисовать»), на которую мне не удалось повлиять. Как я уже сказал, гораздо лучший способ — извлечь из CEdit
(или же CEditView
), или для создания вещи с использованием чистого WinAPI — в любом случае я могу рассчитывать прямо в строках и не нужно совершать обходы до и из числа пикселей. Я выбрал МФЦ CEdit
поскольку я не хочу беспокоиться о COM, когда дело доходит до копирования, вставки и т. д. Если кому-то интересно, ВотЭто то, что у меня так далеко (на данный момент неинтерактивный зритель).
Тем не менее, два места о том, что было не так в CScrollView
способ: (1) главная проблема не была в OnSize
обработчик — это было в процедуре рисования, где я комбинировал по существу три разные идеи (прокрутка по линиям, прокрутка по пикселям и прокрутка Джеффа Проциса по пикселям), которые позволяли объединяться в коде рисования, который был один раз CDC::GetClipBox
меня называли — вот почему я не мог рисовать повсюду на видимом «холсте»; и (2) отсутствует return
заявление в OnCreate
Обработчик — возвращая случайный результат (не ноль, указывающий на сбой), мне пришлось вручную показывать полосу прокрутки.
Надеюсь, это поможет кому-то, кто также хочет прокручивать вещи по целым строкам в MFC.