基本知识:
1.
OnPaint
和
OnDraw
函数
View
的父类的
OnPaint
函数调用了
OnDraw
函数,若在子类为
WM_PAINT
消息添加响应函数
OnPaint
,
OnDraw
函数将不会被调用。
2.
CpaintDC
和
CclietnDC
CpaintDC
的构造函数中调用了
BeginPaint(),
析构函数中调用了
EndPaint()
;
CclietnDC
的构造函数中调用了
GetDC(),
析构函数中调用了
ReleaseDC()
。
而
BeginPaint()
,
EndPaint()
只能用于响应
WM-PAINT
消息,否则将会出错。
二、???????????
利用动态数组:
1.
定义结构体
LINE
,用于保存线的数据。
struct LINE
{
?????? CPoint m_pt1;
?????? CPoint m_pt2;
};
2.
在
View
中定义一个动态数组,保存每一根线的指针。
CPtrArray m_ptrLines;
定义两个
Cpoint
的成员变量,保存线的起点和终点:
?? CPoint m_ptOld;
?????? CPoint m_ptNew;
3.
在
View
中加入
WM_LBUTTONDOWN,WM_LBUTTONUP
的响应函数,在
OnLButtonDown
中为
m_ptNew
赋值,
??? m_ptOld=point;
4.
在
OnLButtonUp
中加入代码:
m_ptNew=point;
?????? CClientDC dc(this);
?????? ?????? ?????? dc.MoveTo(m_ptOld);
?????? dc.LineTo(point);
?????? ????????????? LINE *pLn=new LINE;
?????? pLn->m_pt1=m_ptOld;
?????? pLn->m_pt2=m_ptNew;
?????? m_ptrLines.Add(pLn);
5.
在
OnDraw()
中加入:
int sum= m_ptrLines.GetSize();
for(int i=0;i
{
?????? pDC->MoveTo(((Line *)m_ptrLines.GetAt(i))->m_pt1);
?????? pDC->LineTo(((Line *)m_ptrLines.GetAt(i))->m_pt2);
}
6.
加入滚动条:将
View
的
cpp
文件和
h
文件中的
Cview
全部替换成
CscrollView
。
7.
在
view
中加入虚函数
OnInitialUpdate()
,这个函数在
View
第一次刷新前被调用,在其中加入代码:
SetScrollSizes(MM_TEXT,CSize(1024,768));
?????????????
这个函数也可在
View
的构造函数中调用。
8.
在
OnLButtonUp
中生成
DC
后加入
OnPrepareDC(&dc);
?????? ?????? dc.DPtoLP(&m_ptOld);
?????? dc.DPtoLP(&m_ptNew);
三、???????????
利用
CmetaFileDC
重绘
1.
在
View
中定义成员变量:
?????? CMetaFileDC m_dcMetaFile;
2.
在
View
的
OnCreate
中加入代码:
m_dcMetaFile.Create();
3.
在
View
的
OnLButtonUp
中,注释有关数组的代码,加入:
m_dcMetaFile.MoveTo(m_ptOld);
m_dcMetaFile.LineTo(m_ptNew);
4.
在
OnDraw()
中
?????? HMETAFILE hmetafile;
?????? hmetafile=m_dcMetaFile.Close();
?????? pDC->PlayMetaFile(hmetafile);
?????? m_dcMetaFile.Create();
?????? m_dcMetaFile.PlayMetaFile(hmetafile);
?????? ::DeleteMetaFile(hmetafile);
5.
保存文件:
加入菜单响应函数,
OnFileSave,
加入代码:
?????? HMETAFILE hmetafile;
?????? hmetafile=m_dcMetaFile.Close();
?????? ::CopyMetaFile(hmetafile,"c://2.ddd");
?????? m_dcMetaFile.Create();
?????? m_dcMetaFile.PlayMetaFile(hmetafile);
?????? ::DeleteMetaFile(hmetafile);
6.
读出文件:
加入菜单响应函数,
OnFileLoad,
加入代码:
?????? ?????? HMETAFILE hmetafile;
?????? ?????? hmetafile=::GetMetaFile("c://2.ddd");
?????? ?????? m_dcMetaFile.PlayMetaFile(hmetafile);
?????? ?????? ::DeleteMetaFile(hmetafile);
?????? Invalidate();
四、???????????
利用兼容
DC
重绘:
1.
在
View
中定义成员变量:
CDC??????? m_dcCompa;???
2.
在
OnLButtonDown
中加入代码:
CClientDC dc(this);
?????? if(!m_dcCompa.m_hDC)
?????? {
????????????? m_dcCompa.CreateCompatibleDC(&dc);
????????????? CBitmap bmp;
????????????? CRect rect;
????????????? GetClientRect(&rect);
????????????? bmp.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
????????????? m_dcCompa.SelectObject(&bmp);
m_dcCompa.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);
?????? }
?????? m_dcCompa.MoveTo(m_ptOld);
?????? m_dcCompa.LineTo(m_ptNew);
3.
在
OnDraw
中加入:
CRect rect;
GetClientRect(&rect);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompa,0,0,SRCCOPY);
?
?
?
?
?
CWnd
对象在构造函数里成员被初始化为
0
。但
CPoint
、
CRect
等在构造函数里成员没有做这种操作。
MFC
写的类一般在构造函数里要将成员初始化,除非它做了特殊说明
API
函数发出的消息是
SendMessage
方式。
?
在
CxxView
里画线,当我们画出图形后,修改了窗口的尺寸、覆盖了窗口、最小化、最大化这些操作后,由于窗口要重新显示出来,所以用窗口的定义好的画刷将窗口刷出来。窗口重绘后报告一个
WM_PAINT
通知你,并默认用父类的
OnPaint
响应该消息,父类的
OnPaint
要调用一个函数
OnDraw
。如果我们要把线在
OnDraw
里也画一遍的话,即使窗口重绘了,线还能看到,但是当我们覆盖了父类的
OnPaint
,发现线没有了。究其原因
CxxView
的
OnPaint
没有为你调用
OnDraw
。我们可以跟踪进入查看父类的
OnPaint
的实现。其实我们也可以在子类的
OnPaint
里把线重画出来,但是最好是在
OnDraw
里重画,为什么呢?我们为
CxxView
覆盖一个虚函数
OnPrint(
安装一个打印机
)
,发现它的父类也调用
OnDraw
,
OnDraw
是一个虚函数,它的参数
DC
可以接受任何设备的
DC
(这里可以接受打印机的
DC
,窗口的
DC
等,
OnPaint
传窗口的
DC
,
OnPrint
传打印机的
DC
),你对这个
DC
的操作就是在相对应的设备上输出,如输出到窗口或者输出到打印机,这样就可以让在
OnDraw
所写图形输出既能输出到窗口上又能输出到打印机上,实现了操作的统一性(虚函数的原理)和代码的复用性。
在
OnPaint
里为你产生了一个
CPaintDC,
它和
CClientDC
有什么区别能?
查看
CPaintDC
的说明。
CClientDC
的封装与之不同。
在
WinMain
程序里的
WM_LBUTTONDOWN
和
WM_LBUTTONUP
用
GetDC
和
ReleaseDC
画线没问题,但用
BeginPaint
和
EndPaint
画线出错。同样在
WM_PAINT
消息里不能用
GetDC
和
ReleaseDC
画线,容易出错,所以
CPaintDC
只能在响应
WM_PAINT
消息的函数里使用,
CClientDC
只能在响应非
WM_PAINT
消息的函数里使用。
An application should not call BeginPaint except in response to a WM_PAINT message. Each call to BeginPaint must have a corresponding call to the EndPaint function.
?
?????? ?????? case WM_PAINT:
???????????????????? HDC dc1;
???????????????????? //PAINTSTRUCT ps1;
???????????????????? //dc1=::BeginPaint(hwnd,&ps1);
???????????????????? dc1=::GetDC(hwnd);
???????????????????? ::TextOut(dc1,100,100,"hello world",strlen("hello world"));
???????????????????? //::MoveToEx(dc,20,20,NULL);
???????????????????? //::LineTo(dc,100,100);
???????????????????? //dc=::GetDC(hwnd);
???????????????????? //::MoveToEx(dc,x1,y1,NULL);
???????????????????? //::LineTo(dc,x2,y2);
???????????????????? ::ReleaseDC(hwnd,dc1);
???????????????????? //::EndPaint(hwnd,&ps1);
???????????????????? break;
????????????? case WM_LBUTTONDOWN:
???????????????????? x1=LOWORD(lParam);
???????????????????? y1=HIWORD(lParam);
???????????????????? break;
????????????? case WM_LBUTTONUP:
???????????????????? x2=LOWORD(lParam);
???????????????????? y2=HIWORD(lParam);???????????????????
???????????????????? HDC dc;
???????????????????? PAINTSTRUCT ps;
???????????????????? dc=::BeginPaint(hwnd,&ps);
???????????????????? //dc=::GetDC(hwnd);
???????????????????? ::MoveToEx(dc,x1,y1,NULL);
????????????????????