好几天没写博客了。今天发现了一个需求,在对话框上有好多下拉框选项,一个屏幕显示不完,需要使用到滚动条。起初并不知晓有CScrollView这个类,便尝试了用滚动条,然后在滚动时更改屏幕上控件的坐标来实现。但是后来发现,不但经常会滚动得“不干不净”的,还有些在初始情况下是隐藏的下拉框如何再显示出来呢?一番纠结后,开始寻求更简洁的方案。

以前听说过内嵌子视图的说法,于是灵光一现,想到了创建一个带滚动条的子视图,也就自然而然地寻找到了CScrollView。

这个视图类的作用很简单,就是创建一个视图区域,也可以像向里面添加控件等,超出的部分就“看不见”了,通过滚动条来查看更多的区域。

无图无真相,来个小程序就知道是怎么回事了。 

建立一个基于对话框的MFC程序ScrollViewTest。然后在项目上单击右键,添加新的MFC类CMyScrollView,在选择基类的时候,选择CScrollView。

然后在ScrollViewTest.h中添加一个成员变量

 
  
  1. CMyScrollView * m_pMyView; 

接下来在CScrollViewTestDlg::OnInitDialog()中添加下面的代码吧。

 
  
  1. m_pMyView = (CMyScrollView*)(RUNTIME_CLASS(CMyScrollView)->CreateObject()); 
  2. CCreateContext context; 
  3. context.m_pCurrentDoc = NULL; 
  4.  
  5. m_pMyView->Create(NULL, 
  6.     NULL,  
  7.     WS_BORDER, 
  8.     CRect(0, 0, 100, 100), 
  9.     this,  
  10.     WM_USER + 1,  
  11.     &context); 
  12.  
  13. ((CView*)m_pMyView)->OnInitialUpdate(); 
  14. m_pMyView->ShowWindow(SW_SHOW); 

行啦,已经可以运行了,结果如下。

可以看到这东西就像一个控件一样嵌到对话框中了。

然后就可以为所欲为了。修改一下代码。

 
  
  1. m_pMyView->Create(NULL,  
  2.     NULL,   
  3.     WS_BORDER,  
  4.     CRect(0, 0, 200, 200),  
  5.     this,   
  6.     WM_USER + 1,   
  7.     &context);  

再运行,你会发现,咦,滚动条们呢?

这里需要注意一下,在Create之后,调用了CScrollView的一个虚函数OnInitialUpdate(),会设置一些基本的参数,其中一个就是“滚动区域”。很明显运行此方法完后的“滚动区域”要比CRect(0, 0, 200, 200)小,所以子视图认为“我没有必要滚动了,你完全可以看得到所有的东西”。为了验证一下,我们Debug并查看一下。如图

m_totalDev便是“滚动区域”,m_pageDev是“滚动一页”需要滚动的大小,m_lineDev是“滚动一行”需要滚动的大小。

解决这种情况也很简单,增大“滚动区域”。使用SetScrollSize()方法。

 
  
  1. void SetScrollSizes(int nMapMode, 
  2.             SIZE sizeTotal, 
  3.             const SIZE& sizePage = sizeDefault, 
  4.             const SIZE& sizeLine = sizeDefault); 

第一个参数就是“滚动区域”了,一会我们把它设为长宽各为500。

第二个参数是“滚动一页”时需要滚动的区域。

第三个参数是“滚动一行”时需要滚动的区域。

好,设置一下“滚动区域”。

 
  
  1. SIZE size; 
  2. size.cx = 500; 
  3. size.cy = 500; 
  4. m_pMyView->SetScrollSizes(MM_TEXT, size); 

注意,这几行代码必须要加在OnInitialUpdate()之后,否则不就前功尽弃了?

结果如下。

好,接下来在子视图上添加控件试试。添加一个成员变量:

 
  
  1. CMFCButton * m_button; 

在构造函数中初始化之:

 
  
  1. m_button = new CMFCButton; 

在CScrollViewTestDlg::OnInitDialog()中添加如下代码:

 
  
  1. button->Create(_T("Hello"), 
  2. WS_VISIBLE, 
  3. CRect(5, 5, 80, 250), 
  4. m_pMyView, 
  5. WM_USER + 1); 

好,只能通过拖动滚动条的方式来看到这个按钮的全貌了。

基本的功能介绍完成了,还有些其他的方法,比如

 
  
  1. void CheckScrollBars(BOOL& bHasHorzBar, BOOL& bHasVertBar) const

检查滚动条是否存在。

比如

 
  
  1. CPoint GetDeviceScrollPosition( ) const

返回当前滚动条的位置,一个坐标值。

再比如

 
  
  1. void SetScaleToFitSize(SIZE sizeTotal); 

可以根据视图的内容调整视图的大小。

诸如此类的方法,可以灵活地运用到程序中去。