写在前面:以下问题是由于将SetScrollSizes函数写在OnDraw里导致的,最好的办法是将SetScrollSizes放在需要改变大小的位置而非OnDraw中。但是如果你一定要将SetScrollSizes写在OnDraw里请继续阅读以下篇幅:
1 在正式阐明之前先交代以下四种情况:
(1)如果一个客户窗口没有滚动条,此时希望的视图宽高比客户窗口的宽高大,此时SetScrollSizes后会调用OnSize函数,因为出现滚动条后,客户区窗口会减掉滚动条的相应宽高。
(2)如果一个客户窗口本身已有滚动条,希望的视图宽高比客户窗口的宽高大,此时SetScrollSizes后并不会调用OnSize函数。
(3)如果一个客户窗口已有滚动条,希望的视图宽高比客户窗口的不带滚动条时宽高小于或等于,此时SetScrollSizes后会调用OnSize函数,因为滚动条要消失,客户区窗口会增加滚动条相应的宽高。
(4)如果一个客户窗口没有滚动条,希望的视图宽高也不会造成滚动条的增加,此时SetScrollSizes后也不会调用OnSize函数。
2 SetScrollSizes函数的歧义区间
先上一段代码,以便叙述。新建一个基于CScrollView的单文档工程,OnDraw函数书写如下:
void CTestScrollviewView::OnDraw(CDC* /*pDC*/)
{
CTestScrollviewDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
//客户区窗口的初始宽高
CRect rect;
GetClientRect(rect);
int originWidth=rect.Width();
int originHeight=rect.Height();
//滚动条的尺寸
int vwidth=GetSystemMetrics(SM_CXVSCROLL);//垂直滚动条的宽度
int hheight=GetSystemMetrics(SM_CYHSCROLL);//水平滚动条的高度
//从无滚动条到有滚动条需设置的视图最小尺寸
int minw=originWidth+vwidth+2;
int minh=originHeight+hheight+2;
SetScrollSizes(MM_TEXT,CSize(minw,minh));
}(1)宽度的歧义区间为:(originWidth,originWidht+vwidth+2) ,在这个区间内可以认为是从没有滚动条到有滚动条,也可以认为是从有滚动条到没有滚动条,于是SetScrollSizes就不停的调用OnSize然后调用Ondraw,于是就陷入了死循环。
(2)高度的歧义区间为:(originHeight,originHeight+hheight+2) ,它与宽度的歧义区间同理。
3 问题归纳
2对情况1-1进行了演示和阐述,从中我们可以得知:应避免设置歧义区间的视图尺寸,否则很可能陷入重绘死循环。同理,情况1-3也存在歧义区间,其他两种情况不会出现问题。现在,对两种情况(1-1,1-3)下的问题进行归纳。
设纵轴横轴都没有滚动条的客户窗口的尺寸为:( originWidth1, originHeight1 ) ,设纵轴横轴都有滚动条的客户窗口尺寸为:( originWidth2, originHeight2 ) ,设垂直滚动条的宽度为:vwidth ,设水平滚动条的高度为:hheight ,歧义区间归纳如下:
(1)从没有水平滚动条到有水平滚动条的歧义区间为:(originWidth1 , originWidth1+vwidth+3)
(2)从没有垂直滚动条到有垂直滚动条的歧义区间为:(originHeight1 , originHeight1+hheight+3)
(3)从有水平滚动条到没有水平滚动条的歧义区间为:(originWidth2 , originWidth2+vwidth+3)
(4)从有垂直滚动条到没有垂直滚动条的歧义区间为:(originHeight2 , originHeight2+hheight+3)
4 解决方案
//成员变量
//视图的宽与高;它们会由于其他设置而发生改变。
int m_Width;
int m_Height;
//客户区窗口的宽高
int m_OriginWidth;
int m_OriginHeight;
//绘制函数
void CTestScrollviewView::OnDraw(CDC* pDC)
{
CTestScrollviewDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
//客户区窗口的宽高
CRect rect;
GetClientRect(rect);
if (rect.Width()!=m_OriginWidth||rect.Height()!=m_OriginHeight)
{
m_OriginHeight=rect.Height();
m_OriginWidth=rect.Width();
}
//当前视图的宽高
TRACE2("m_Width=%d,m_Height=%d\r\n",m_Width,m_Height);
//在SetScrollSizes设置滚动条之前对m_Width和m_Height做微调
int width=m_Width;
int height=m_Height;
int vwidth=GetSystemMetrics(SM_CXVSCROLL);//垂直滚动条的宽度
int hheight=GetSystemMetrics(SM_CYHSCROLL);//水平滚动条的高度
TRACE2("vwidth=%d,hheight=%d\r\n",vwidth,hheight);
//当前滚动条的有无
BOOL vbar,hbar;
CheckScrollBars(hbar,vbar);
TRACE2("hbar=%d,vbar=%d\r\n",hbar,vbar);
//讨论滚动条SetScrollSizes后将要的出现状态
if (!hbar&&(width>m_OriginWidth))//当前没有水平滚动条,SetScrollSizes后将有
{
TRACE0("WillHaveHbar\r\n");
if (width>m_OriginWidth&&width<m_OriginWidth+vwidth+3)
{
width=m_OriginWidth+vwidth+3;
TRACE1("width adjust up,%d\r\n",width);
}
}
if (!vbar&&height>m_OriginHeight)//当前没有垂直滚动条,SetScrollSizes后将有
{
TRACE0("WillHaveVbar\r\n");
if (height>m_OriginHeight&&height<m_OriginHeight+hheight+3)
{
height=m_OriginHeight+hheight+3;
TRACE1("height adjust up,%d\r\n",height);
}
}
if (hbar&&width<(m_OriginWidth+vwidth+3))//当前有水平滚动条,SetScrollSizes后将没有
{
TRACE0("WillNoHbar\r\n");
if (width>m_OriginWidth&&width<m_OriginWidth+vwidth+3)
{
width=m_OriginWidth-1;
TRACE1("width,adjust down,%d\r\n",width);
}
}
if (vbar&&height<(m_OriginHeight+hheight+3))//当前有垂直滚动条,SetScrollSizes后将没有
{
TRACE0("WillNoVbar\r\n");
if (height>m_OriginHeight&&height<m_OriginHeight+hheight+3)
{
height=m_OriginHeight-1;
TRACE1("height,adjust down,%d\r\n",height);
}
}
//
TRACE2("currentorigin=(%d,%d)\r\n",m_OriginWidth,m_OriginHeight);
TRACE2("setscrollsize=(%d,%d)\r\n",width,height);
//设置滚动
SetScrollSizes(MM_TEXT,CSize(width,height));
//当前的视图宽高更新
m_Width=width;
m_Height=height;
//在m_Width,m_Height定义的视图区做绘制...
}