11.3窗口滚动功能的实现

11.3.1CScrollView类

在创建向导的第六步可以把视类的基类选择为CScrollView,这样,视图窗口就有了滚动功能。如图。
在这里插入图片描述
在这里也可以手工将视图的基类由CView改为CStrollView:
先将视类头文件中将CView修改为CScrollView。
Edit->Replace->Find what输入CView,Repalce with输入CStrollView->选中Match whole word only,单击Repalce All。就可以将视类源文件中的也修改过来了。

但是运行会出现一个非法操作框,原因就是滚动窗口在创建时候要进行滚动窗口的大小、单击滚动条箭头时滚动条滚动的距离以及单击滚动栏时滚动条滚动的数值。在这里我们需要调用CStrollView类SetScrollSizes的函数

void SetScrollSizes( int nMapMode, SIZE sizeTotal, const SIZE& sizePage = sizeDefault, const SIZE& sizeLine = sizeDefault );

第一个参数是映射模式;第二个参数设置滚动视图窗口总的尺寸;第三个参数设置单击滚动条时滚动的量;第四个参数第三个参数设置单击滚动箭头时滚动的量;

应该是在视类窗口创建之后调用该函数。可以为视类重载一个虚函数:OnInitialUpdate。该函数在第一次调用OnDraw函数之前调用。

void CGraphicView::OnInitialUpdate() 
{
 CScrollView::OnInitialUpdate(); 
 // TODO: Add your specialized code here and/or call the base class
 SetScrollSizes(MM_TEXT,CSize(800,600));
}

运行效果如图。
在这里插入图片描述

1.3.2图形错位现象

增加滚动条后,窗口中显示的内容出现在原位置的上方。
当我们把垂直滚动条拖到窗口的最下端后,当窗口重绘后,在OnPrepareDC中调用了SetViewportOrg函数将视口原点坐标设置为了(0,150)。因此视口原点的改变导致了图形跑到原位置的上方了。

1、关于图形错位的说明
在调用OnDraw函数之前,在OnPaint函数中调用了OnPrepareDC函数,调整了显示上下文的属性。、
这时在OnDraw函数中在此绘制直线时,所得设备的纵坐标就减少了150,于是图形出现在原图形的上方。
2、关于解决方法的说明
首先在绘制图形之后,在保存坐标点之前应该调用OnPrepareDC函数,调整显示上下文的属性,将视口的原点设置为(0,-150)
在视类OnLButtonUp函数中添加:

OnPrepareDC(&dc);
dc.DPtoLP(&m_ptOrigin);
dc.DPtoLP(&point);

要注意每次窗口重绘时候,都会调用OnPrepareDC函数,而OnPrepareDC函数会随时根据滚动条窗口的位置调整视口的原点。也就是说原点不是一成不变的,它会随着滚动条的位置不同而变化。

11.4元文件

11.4.1元文件的使用

这时要用到元文件设备上下文:CMetaFileDC。CMetaFileDC类是由CDC类派生的。元文件并没有包含所绘图形的图形数据,它包含的是图形的绘制命令。
我们可以通过以下步骤来使用Windows元文件:
1、利用CMetaFileDC构造函数构造一个元文件DC对象,然后调用一个该类的Create成员函数创建一个Windows元文件上下文,并将其与已构造的CMetaFileDC对象关联起来。
BOOL Create( LPCTSTR lpszFilename = NULL );
参数是创建的元文件的文件名,如果此参数为NULL,创建的元文件就是一个内存元文件。
2、给已创建的元文件DC对象发送一系列GDI命令。如LineTo。
3、在给元文件DC对象发送需要的命令之后,调用Close成员函数关闭元文件设备上下文。
HMETAFILE Close();
4、利用CDC类的PlayMetaFile成员函数播放该元文件。
BOOL PlayMetaFile(HMETAFILE hMF);
5、播放完其中的图形绘制命令之后,可以通过调用DeleteMetaFile函数将其删除。
BOOL DeleteMetaFile(HMETAFILE hMF);

1、为视类添加一个CMetaFileDC类私有成员变量:m_dcMetaFile。然后在该类的构造函数中调用CMetaFile类的Create方法。
2、在视类的OnLButtonUp中,将函数中的CDC类型的对象dc都替换为CMetaFileDC类型对象:m_dcMetaFile。将图形对象保存到集合类对象的代码注释起来。
3、接下来就应该在OnDraw函数中播放元文件。将该函数中其他代码注释起来。

void CGraphicView::OnDraw(CDC* pDC)
{
 CGraphicDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 HMETAFILE hmetaFile;
 hmetaFile=m_dcMetaFile.Close();
 pDC->PlayMetaFile(hmetaFile);
 m_dcMetaFile.Create();
 DeleteMetaFile(hmetaFile);
}

因为在窗口重绘之后,用户可以希望继续绘制图形,而我们仍然采用元文件的方式来实现图形的绘制。所以在播放元文件之后创建一个元文件上下文对象,以便用户再次绘制图形使用。
如果想保存先前绘制的图形,那么在创建新的元文件DC之后,应该用新的元文件DC的PlayMetaFile函数去播放先前的元文件。在m_dcMetaFile.Create();后面添加:m_dcMetaFile.PlayMetaFile(hmetaFile);
运行后,进行绘图操作,先是没显示出来,但是当窗口重绘后,先前绘制的图形也能显示出来。

11.4.2元文件的保存与打开

为视类添加文件子菜单下的打开与保存菜单项的命令消息响应函数。在各自的响应函数中实现元文件的保存和打开。
为保存元文件,可以利用CopyMetaFile函数,该函数的作用就是将Windows元文件的内容复制到指定文件中。

HMETAFILE CopyMetaFile(
  HMETAFILE hmfSrc,  // handle to a Windows-format metafile
  LPCTSTR lpszFile   // pointer to a filename string
);

第一个参数是要复制的Windows元文件的句柄;
第二个参数是复制的目标文件的名称。

void CGraphicView::OnFileSave() 
{
 // TODO: Add your command handler code here
 HMETAFILE hmetaFile;
 hmetaFile=m_dcMetaFile.Close();
 CopyMetaFile(hmetaFile,"meta.wmf");
 m_dcMetaFile.Create();
 DeleteMetaFile(hmetaFile);
}

对于元文件来说,当把它的内容保存为一个磁盘文件时,通常将其扩展名为wmf。
运行程序,发现Graphic目录下面多了一个meta.wmf文件。

接下来实现打开刚保存的元文件的功能,这是需要GetMetaFile函数得到指定元文件的句柄:

void CGraphicView::OnFileOpen() 
{
 // TODO: Add your command handler code here
 HMETAFILE hmetaFile;
 hmetaFile=GetMetaFile("meta.wmf");
 m_dcMetaFile.PlayMetaFile(hmetaFile);
 DeleteMetaFile(hmetaFile);
 Invalidate();
}

播放元文件之后,保存了先前绘制的图形,但是不显示出元文件。这时元文件句柄hmetaFile也就没用了,就要删除。最后调用Invalidate()函数,将调用OnDraw函数中当前窗口设备上下文(pDC)的PlayMetaFile函数在当前窗口中播放元文件,这时才显示元文件。
注意元文件的保存并不是图形数据,而是图形绘制命令。

11.5兼容设备描述表

下面介绍另一种图形保存和重绘方式:利用兼容DC的知识。
首先为视类创建一个兼容DC对象,为其增加一个CDC类型的私有成员变量:m_dcCompatible。然后在该类的OnLButtonUp函数中,利用兼容DC实现图形的保存。

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) 
{
 // TODO: Add your message handler code here and/or call default
 CClientDC dc(this);
 CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
 if(!m_dcCompatible.m_hDC)
 {
  m_dcCompatible.CreateCompatibleDC(&dc);
  CRect rect;
  GetClientRect(&rect);
  CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
  m_dcCompatible.SelectObject(&bitmap);
  m_dcCompatible.SelectObject(pBrush);
 }
 switch(m_nDrawType)
 {
 case 1:
  m_dcCompatible.SetPixel(point,m_clr);
  break;
 case 2:
  m_dcCompatible.MoveTo(m_ptOrigin);
  m_dcCompatible.LineTo(point);
  break;
 case 3:
  m_dcCompatible.Rectangle(CRect(m_ptOrigin,point));
  break;
 case 4:
  m_dcCompatible.Ellipse(CRect(m_ptOrigin,point));
  break;
    }
 CScrollView::OnLButtonUp(nFlags, point);
}

当程序运行时,OnLButtonUp函数会多次调用,所以得进行判断,不能重复创建这个兼容DC,只有第一次没有创建时,才创建兼容DC。
CBitmap类成员函数CreateCompatibleBitmap可以通过指定的宽和高创建一副与指定DC相兼容的位图。
有了兼容位图后,就可以把兼容位图选入兼容DC中,从而确定兼容DC显示表面的大小。
然后将透明画刷选入兼容DC中,并将该函数中之前的元文件DC全部替换成新创建的兼容DC。

因为兼容DC实际上是一块内存,所以利用它绘制的图形在窗口中是看不到的。因此需要在视类OnDraw函数中利用已创建的兼容DC对象,将该DC中的内容复制到目的DC中,从而实现图形的显示。

void CGraphicView::OnDraw(CDC* pDC)
{
 CGraphicDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here
 CRect rect;
 GetClientRect(&rect);
 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
}

运行,进行绘图操作,但是改变窗口尺寸之后,程序就会出现问题。
因为CreateCompatibleDC函数返回的位图对象没有包含位图的颜色表和像素数据块。因此在调用SelectObject操作将兼容位图选入兼容DC之后,还需要调用BitBlt函数将原始设备描述表的颜色表以及像素数据块复制到兼容设备描述表中。因此m_dcCompatible.SelectObject(&bitmap);之后添加:
m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

身影王座

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值