C++ CScrollView仅滚动大区域的一部分

C++ CScrollView仅滚动大区域的一部分,c++,windows,mfc,ccscrollview,C++,Windows,Mfc,Ccscrollview,在CScrollView中滚动大区域时遇到问题。当从上到下缓慢移动滚动条时,行为如下:首先,滚动工作正常。在某些情况下,进一步滚动不会产生任何效果,而是会显示该区域的顶部。在某个点上,滚动再次从顶部开始 这里有一个小例子。我使用文档视图模型创建了一个新的MFC项目,并使用CScrollView作为视图类。我添加了以下代码以创建一个大区域并添加一些文本以显示当前显示的部分: void CScrollViewTest2View::OnInitialUpdate() { CScrollView

在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 = 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
。这是因为Windows中的16位限制。x/y位置超过此限制的所有控件都被推回此位置

OnScroll
函数也有类似的问题,但是可以通过使用
GetScrollInfo

ON\u WM\u VSCROLL
添加到消息映射中,并将以下
ON VSCROLL
覆盖添加到您的类中

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();
} 
void CScrollViewTest2View::OnVScroll(UINT nSBCode、UINT nPos、CScrollBar*pScrollBar)
{
CView::OnVScroll(nSBCode、NPO、pScrollBar);
滚动信息;
GetScrollInfo(SB_垂直,和info,SIF_全部);
int pos=info.nPos;
int save=pos;
交换机(nSBCode)
{
案例SB_左:pos=info.nMin;中断;
案例SB_RIGHT:pos=info.nMax;break;
案例SB_LINELEFT:位置--;中断;
case SB_LINERIGHT:pos++;break;
case SB_PAGELEFT:pos-=info.nPage;break;
case SB_PAGERIGHT:pos+=info.nPage;break;
案例SB_拇指位置:pos=info.nTrackPos;断开;
案例SB_拇指轨迹:pos=info.nTrackPos;中断;
}
//确保新位置在范围内
如果(pos最大值)位置=最大值;
OnScrollBy(CSize(0,pos-save),1);
//编辑:在OnScrollBy之后移动此行并添加条件
如果(info.nPos!=pos)
{
info.nPos=pos;
设置crollinfo(SB_VERT和info,FALSE);
}
UpdateWindow();
} 
这不会解决x/y位置大于32000的windows控件的问题,但至少会按预期滚动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);
    }
}
void CScrollViewTest2View::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal(0,100*1000);
设置CrollSizes(MM_文本,sizeTotal);
}
无效CScrollViewTest2View::OnDraw(CDC*pDC)
{
对于(int i=0;i<1000;i++)
{
int y=i*100;
csts;
s、 格式(L“%d”,y);
pDC->TEXTOUT(200,y,s);
}
}

CScrollView
使用
SetScrollRange
和其他具有16位限制的旧函数,它只升到‭<代码>32767‬。它应该使用
SetScrollInfo/GetScrollInfo
,但他们没有更新。您在创建坐标超出16位范围的静态控件时还有一个问题,我认为没有解决方案。因此,如果我使用“SetScrollInfo/GetScrollInfo”编写自己版本的“CScrollView”,我会绕过这个问题吗?你知道是否有人已经做了这件事,并提供了它?16位范围之外的静态控件和坐标到底有什么问题?谢谢你的帮助。我认为将窗口控件放置在32767范围之外是无能为力的。我为代码添加了一个修复程序> CSCRelVIEW ,它是针对常规绘图的,否则您可能需要考虑HTML对话框或其他解决方案。我必须切换<代码> OnScRoBys和<代码> SETSCROLLIOFF</代码>使其工作。否则,松开鼠标按钮后,拇指总是会移动一点。显然,
OnScrollBy
以某种方式更改了
SCROLLINFO
?这是正确的修复方法还是我在这样做时破坏了其他东西?而且绝对没有办法让
CStatic
以某种方式工作?在我的实际项目中,我画的不仅仅是数字,而且转换所有东西(很可能会引入新的bug)也需要一些工作。我希望其他人也能遇到这个限制,并以某种方式解决它——当窗口调整大小时,它会发送消息。宽度和高度存储在32位
lParam
中。较低的16位是宽度,较高的16位是高度。我认为没有办法超越这些限制。应使用不同的方法按需显示/隐藏窗口控件。您应该能够将控件创建为单独的窗口,以便它们显示在正确的滚动位置。然后在滚动主客户端时隐藏控件。如果可能的话,也可以使用HTML控件。
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);
    }
}