最近的C++课程老师要求我们学习MFC,虽然我也觉得这是即将被微软抛弃的东西
但是既然既然老师布置下来了,就好好学吧,反正这都是基础,以后肯定要学新的,先入门吧。
我用的是vs03,
首先是弄清楚视图和文档的概念:
在MFC"文档/视图"架构中,CView类是所有视图类的基类,它提供了用户自定义视图类的公共接口。在"文档/视图"架构中,文档负责管理和维护数据;而视图类则负责如下工作:
(1) 从文档类中将文档中的数据取出后显示给用户;
(2) 接受用户对文档中数据的编辑和修改;
(3) 将修改的结果反馈给文档类,由文档类将修改后的内容保存到磁盘文件中。
而文档负责了数据真正在永久介质中的存储和读取工作,视图呈现只是将文档中的数据以某种形式向用户呈现,因此一个文档可对应多个视图。
我们是从一个利用视图类与鼠标键盘交互的例子开始的。
1.利用MFC生成向导,选择单文档模式,生成程序框架
从解决方案中可以看到产生了一些文件
我在很多程序刚开始都见过stdafx.h这个头文件,今天算是彻底把它弄明白了,afx曾经是微软一个专门的技术开发团队,而stdafx.h则是这个团队为了定义一些环境配置、参数设置等专门定义的,有了这个头文件,我们才能调用AfxGetApp()等这些api函数。打开stdafx.h可以看到它帮我们包含进来一些关键的库:
另外一些文件例如doc文件对应的是文档类
view对应视图类。
利用类视图,对view类添加成员,这种利用向导的方式在MFC比较常用,因为比较不会乱,MFC是一个框架,我们在被人定好的框架里折腾罢了,你不需要知道框架的具体细节,多利用向导就行了。
添加这么几个变量,并且在构造函数中初始化,记住还是在view类中:
BOOL m_bMouseDown; //标识鼠标左键是否按下
HCURSOR m_hCross; //十字型鼠标句柄
HCURSOR m_hArrow; //标准型鼠标句柄
CPoint m_ptOld; //临时点
CPoint m_ptStart; //画线的起始点
CDocViewView::CDocViewView()
{
m_bMouseDown = false;
m_hCross = AfxGetApp()->LoadStandardCursor(IDC_CROSS);
m_hArrow = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
}
AfxGetApp( )这个函数可以得到当前应用进程的指针,是CWinApp*类型的,通过这个指针可以访问到这个进程中的对象。
上图两句话是从当前应用进程中,就是我们创建的实例。LoadStandardCursor的注释为:// Loads a stock cursor resource; for for IDC_* values.简言之,就是载入windows标准的光标对象,标准对象有很多,具体请看另外一篇文章。
在view类的属性中,选择消息,添加OnLButtonDown函数,并且加入以下代码。
void CDocViewView:: OnLButtonDown (UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_bMouseDown = true; // 鼠标左键按下
m_ptStart = point; // 画线的起点
m_ptOld = point; // 临时点
SetCapture(); // 将鼠标消息发送到视窗口
CRect rect;
GetClientRect(&rect); // 得到客户窗口的大小
ClientToScreen(&rect); // 将当前窗口坐标转换成屏幕坐标
ClipCursor(&rect); // 把鼠标限定在其参数指定的矩形区域内
SetCursor(m_hCross); // 设置鼠标形状为十字形
CView::OnLButtonDown(nFlags, point);
}
setcapture(),该函数在属于当前线程的指定窗口里设置鼠标捕获。一旦窗口捕获了鼠标,所有鼠标输入都针对该窗口,无论光标是否在窗口的边界内。同一时刻只能有一个窗口捕获鼠标。如果鼠标光标在另一个线程创建的窗口上,只有当鼠标键按下时系统才将鼠标输入指向指定的窗口。
ClipCursor()
该函数把鼠标限制在
屏幕上的一个矩形区域内,如果调用SetCursor或用鼠标设置的一个随后的鼠标位置在该矩形区域的外面,则系统自动调整该位置以保持鼠标在矩形区域之内
同样利用消息创建OnMouseMove和OnLButtonUp
void CMyDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if( m_bMouseDown )
{
CClientDC dc(this);
dc.SetROP2( R2_NOT );
dc.MoveTo( m_ptStart ); //这两行代码擦除从起点(鼠标按下点)到
dc.LineTo( m_ptOld ); //上次鼠标移动到的位置之间的临时线
dc.MoveTo( m_ptStart ); //这两行代码从起点到鼠标当前位置画线
dc.LineTo( point ); //
m_ptOld = point; //鼠标当前位置在下一次鼠标移动事件看来就是"旧位置"
}
CView::OnMouseMove(nFlags, point);
}
CClientDC:(客户区
设备上下文
)用于客户区的输出,与特定窗口关联,可以让开发者访问目标窗口中客户区,其
构造函数
中包含了GetDC,
析构函数
中包含了ReleaseDC。
Windows API SetROP2(int nDrawMode)主要用于设定当前前景色的混合模式。R2_NOT就是取反的意思,即前景色为背景色的反色,经常用R2_NOT来画橡皮线,因为两次取反可以还原背景色。
LineTo
void CMyDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
if( m_bMouseDown )
{
m_bMouseDown = false;
ReleaseCapture();
ClipCursor( NULL );
CClientDC dc(this);
dc.SetROP2( R2_NOT );
dc.MoveTo( m_ptStart ); //这两行代码擦除从起点(鼠标按下点)到
dc.LineTo( m_ptOld ); //上次鼠标移动到的位置之间的临时线
dc.SetROP2( R2_COPYPEN );
dc.MoveTo( m_ptStart ); //这两行代码从起点到鼠标当前位置画线
dc.LineTo( point ); //
SetCursor(m_hArrow); //设置鼠标形状为标准箭头形
}
CView::OnLButtonUp(nFlags, point);
}