我如何说服MFC的CScrollView按行而不是按像素滚动?

时间:2016-03-15 23:41:22

标签: c++ winapi mfc hex scrollview

用尽! 如何说服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
}

提前感谢任何帮助!

托马斯

2 个答案:

答案 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中按行滚动内容的人。