用尽! 如何说服MFC的CScrollView按行而不是按像素滚动?我非常绝望,我甚至采用了Jeff Procise"编程Windows与MFC"复制和粘贴那里发现的琐碎的例子。没有成功......!
我用一个相当简化的hexa编辑器扩展了一个更大的项目,我遇到了这个噩梦。通常情况下,我错过了一些非常小的东西让它起作用。
请参阅下面的代码以及我认为是问题根源的OnSize
处理程序的两个版本。在每一个中,我都是:(a)确定表示文件的行数(每行包含16个字节,因此例如500字节文件由32行表示),以及(b)设置垂直滚动条的参数。如果要显示32行,我将范围设置为0..32。不幸的是,MFC将此范围视为以像素为单位而不是" line",并且我无法说服它在我的OnDraw
方法中执行其他操作。
提到OnDraw
方法时,问题看起来似乎是通过调用myDC.SelectClipRgn(NULL);
无法删除裁剪区域。 Aka,我不能通过移动原始内容来吸引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
}
提前感谢任何帮助!
托马斯
答案 0 :(得分:3)
在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);
}
}
答案 1 :(得分:0)
感谢您提出的好建议,他们扩大了我的观点: - )
无论如何,现在回答我自己的问题,因为我决定拒绝通过CScrollView
的方式,并使用CEdit
做事。我会采用更为一般CWnd
(或上面建议的CView
)的方式,但由于某些奇怪的原因,如果{{1}为父级,这些不会接受输入焦点。 }。因此,在非常严格地探索CSplitterWnd
在不同滚动情况下的行为之后,我不再认为这将是一种简单(甚至可行!)的方式。主要原因是CScrollView
本身 - 它调用CScrollView
设置剪辑区域(客户区减去&#34; Windows知道如何绘制区域&#34;),我没有管理去影响。就像我说的,一个更好的方法是从ScrollWindow
(或CEdit
)驱逐东西,或者使用纯WinAPI创建东西 - 在任何一种情况下,我都可以在行中计算并且不要#39;需要进行往返像素计数的往返。我已经选择了MFC的CEditView
,因为我不想在复制,粘贴等方面打扰自己。如果有人有兴趣,here&到目前为止我所拥有的东西(目前看来非常不相干的观众)。
CEdit
方式错误的两个方面:(1)主要问题不在CScrollView
处理程序中 - 它在绘图程序中我基本上是在组合三个不同的想法(按行滚动,按像素滚动,以及Jeff Procise按像素滚动),这使得在调用OnSize
之后的绘图代码中不相干 - 这就是为什么我没有&能够在可见的&#34;画布中随处画画#34 ;; (2)CDC::GetClipBox
处理程序中缺少return
语句 - 返回随机结果(非零,表示失败),我必须手动显示滚动条。
希望这能帮助那些也希望在MFC中按行滚动内容的人。