MFC交互绘图基础_第1页
MFC交互绘图基础_第2页
MFC交互绘图基础_第3页
MFC交互绘图基础_第4页
MFC交互绘图基础_第5页
已阅读5页,还剩117页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第一章第二章 MFC交互绘图基础在上一章我们所创建的应用程序中,通过添加的菜单项实现了简单的用户和应用程序的交互。用户可以通过选择菜单项,定义使用的画笔和画刷,并通过选择菜单项执行相应的绘图代码来看绘制的图形。但是该应用程序有很多缺点,比如绘制的图形有限,想要绘制新的图形必须修改代码;通过菜单处理函数执行的绘图代码因为没有将图形的信息存储起来,导致图形在窗口进行视图重画时不能够正确显示等等。通常情况下,用户需要使用更灵活的方式来绘制图形。比如像Windows中的“画图”程序一样,用户使用鼠标绘制图形,可以更灵活方便的设置绘图使用的画笔和画刷的类型,并且希望绘制完的图形可以保存起来,以后可以再次

2、打开以前所绘制的图形并进行编辑。本章将以编写一个简单的绘图应用程序为例,介绍如何在MFC中实现鼠标绘图,如何定义图元的结构以保证应用程序可以正确的重画用户绘制的图形,如何选择和编辑已有的图形,如何保存图形到永久存储介质中等等的编程方法。这个简单的绘图应用程序将实现以下基本功能:用户使用鼠标绘制图形;通过对话框设置绘制图形使用的线型和颜色以及填充封闭区域的模式和颜色;用户可以选择已经绘制的图形,并可以对该图形进行编辑;可以保存绘制完的图形到永久存储介质(这里是硬盘)中,以便以后可以读取以前绘制的图形,并再次进行编辑。122.1 创建工具条创建一个新的MFC项目,项目名称为DrawMap。创建该项

3、目时各步的设置与上一章中创建DrawTest项目时相同,只是在“MFC AppWizard Step 4 of 6”对话框中不选择Printing and print preview复选框。在上一章的应用程序中,用户需要通过选择菜单项来选择要执行的功能。当菜单项的层数比较多的时候,用户需要点击的次数较多。对于一些常用的功能,用户会希望能够更容易的选择到,此时就可以使用工具条。对于本章中要创建的绘图应用程序来说,绘图功能是常用功能,所以可以将这些功能的选择做成工具条。用户通过点击工具条按钮,就能选择要绘制的图形的类型,然后用鼠标进行绘图。2.1.1 添加新工具条我们创建应用程序项目时,在“MFC

4、 AppWizard Step 4 of 6”对话框中选择了Docking toolbar复选框,此时系统会在应用程序中创建一个默认的初始工具条。该工具条的样式如图2.1所示。我们可以修改此工具条,在该工具条中添加新的按钮来对应绘图功能。不过,通常情况下,因为一个应用程序窗口可以有多个工具条,为了把相类似的功能放在同一个工具条中,我们准备在绘图应用程序中添加一个新的工具条,把绘图功能按钮放在该工具条中。在已有的工具条中添加新的按钮和在新建的工具条中添加按钮是一样的,所以读者只需要学会如何添加新的工具条,也就学会了如何修改已有的工具条。选择资源面板,用鼠标右键点击“Toolbar”节点,弹出快捷

5、菜单,如图2.2所示。在快捷菜单中选择“Insert”,出现“Insert Resource”对话框,如图2.3所示。该对话框用于在项目中添加各种资源。对话框左边的列表框中列出了可添加的资源种类。选择“Toolbar”,添加一个新的工具条资源,然后单击“New”(新建)按钮,系统会在项目中添加一个新的工具条。也可以在图2.2的快捷菜单中选择“Insert Toolbar”直接插入一个工具条。此时,在资源面板的“Toolbar”节点下我们会看到两个节点。一个是“IDR_MAINFRAME”,该工具条是默认的初始工具条。另一个是“IDR_TOOLBAR1”,它是我们新添加的工具条,名称是系统起的默

6、认名称。用鼠标右键点击该节点。在弹出的快捷菜单中(图2.2所示快捷菜单)选择“Properties”,会出现“Toolbar Properties”(工具条属性对话框),如图2.4所示。在“ID”下拉框中,我们可以修改当前工具条的ID,该ID用于标识工具条。此处我们将此ID修改为IDR_DRAW。添加新工具条完毕,现在需要在工具条中添加工具条按钮。在资源面板中选中“IDR_DRAW”节点,我们可以在右侧的工具条编辑区中编辑此工具条,如图2.5所示。在编辑区的上端是完成后工具条的样式,现在工具条中只有一个空白的按钮,是系统自动添加的。下部的左侧是选中的工具条按钮的样式预览。中间是按钮的绘制区,用

7、户在该区域中绘制工具条按钮的图形样式。右侧是绘图工具条,该工具条提供给用户简单的绘图工具,可以用于绘制工具条按钮。现在我们来绘制工具条按钮。在此之前需要确定该工具条中有几个按钮,每个按钮都是什么功能。我们现在要绘制的是用于选择绘图类型的工具条按钮。在本章要创建的绘图应用程序中准备让用户可以绘制四种类型的图形:直线段,椭圆,椭圆区域,矩形区域。其中椭圆指只有边界线的椭圆,而椭圆区域除了边界线之外,还要对内部进行填充。因为本应用程序只是用来学习如何用MFC进行交互绘图,所以没有提供更多的绘图类型。工具条按钮的图形样式最好能够直观的表现出该按钮的功能。在工具条编辑区的绘图工具条中选择绘制直线,然后在

8、中间的绘图区中画一条直线段,如图2.6所示。此工具条按钮可以直观的表明该按钮用于绘制直线段。同时系统在该工具条按钮右侧自动添加一个空白按钮。用鼠标左键双击我们刚刚绘制的工具条按钮,会出现“Toolbar Button Properties”(工具条按钮属性)对话框,如图2.7所示。在“ID”下拉框中输入该工具条按钮的ID为ID_DRAWLINE。在“Prompt”输入框中输入说明“绘制直线段”,该说明为按钮的提示。按照相同的方法可以绘制其他三个工具条按钮,并设置相应的属性,具体数据如下表所示:工具条按钮IDPromptID_DRAWLINE绘制直线段ID_DRAWELLIPSE绘制椭圆ID_D

9、RAWELLIPSEREGION绘制椭圆区域ID_DRAWRECTANGLE绘制矩形区域绘制完的工具条如图2.8所示。2.1.2 在应用程序中显示工具条新的工具条创建完毕,此时如果我们运行应用程序,会发现该工具条并没有显示出来,这是因为我们还没有编写代码将该工具条加入到应用程序窗口中。下面我们来看一下如何将工具条加入到应用程序窗口中。首先,选择类面板,双击CMainFrame节点,在右侧的编辑区中将打开CMainFrame类的头文件。在头文件中我们可以找到如下代码:protected: / control bar embedded membersCStatusBar m_wndStatusBa

10、r;CToolBar m_wndToolBar;这里声明了一个CStatusBar类对象变量m_wndStatusBar和一个CToolBar类对象变量m_wndToolBar。它们分别对应了系统自动添加的默认状态栏和默认的初始工具条。CStatusBar是MFC封装的一个状态栏类,而CToolBar类是一个工具条类。想要操作工具条就必须首先声明一个工具条的对象。这里我们添加如下代码:CToolBarm_DrawToolBar;/绘图工具条对象该对象将用于与绘图工具条对应。在类面板中双击CMainFrame节点下的OnCreate节点,在编辑区打开CMainFrame类的CPP文件,并定位到该

11、类的OnCreate成员函数处。该成员函数在主窗口创建的时候调用,在此函数中可以给主窗口添加工具条和状态栏。此时该成员函数的代码如下:int CMainFrame:OnCreate(LPCREATESTRUCT lpCreateStruct)if (CFrameWnd:OnCreate(lpCreateStruct) = -1)return -1;/创建默认初始工具条if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP| CBRS_GRIPPER | CBRS_TOOLTIPS | CBR

12、S_FLYBY | CBRS_SIZE_DYNAMIC) |!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)TRACE0("Failed to create toolbarn");return -1; / fail to create/创建默认状态栏if (!m_wndStatusBar.Create(this) |!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)TRACE0("Failed to create status barn&

13、quot;);return -1; / fail to create/ TODO: Delete these three lines if you don't want the toolbar to/ be dockablem_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m_wndToolBar);return 0;看一下此函数中创建默认初始工具条的代码,会发现分别调用了工具条类CToolBar的CreateEx函数和LoadToolBar函数来生成

14、和初始化工具条。u LoadToolBar函数,用于加载指定的工具条资源,其函数声明如下:BOOL LoadToolBar(LPCTSTR lpszResourceName);BOOL LoadToolBar(UINT nIDResource);其中第一个函数的参数lpszResourceName为指向要加载的工具条资源名称的指针,第二个函数的参数nIDResource是要加载的工具条资源的ID,通常都使用第二个函数来加载工具条。在当前函数中就是通过默认初始工具条的ID(IDR_MAINFRAME)来加载的。如果加载成功,函数返回TRUE,否则返回FALSE。u CreateEx函数,用于初始

15、化工具条,其函数声明如下:BOOL CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle = TBSTYLE_FLAT, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP, CRect rcBorders = CRect(0, 0, 0, 0), UINT nID = AFX_IDW_TOOLBAR);其中参数pParentWnd为指向包含工具条的父窗口的指针。参数dwCtrlStyle指定了工具条的附加风格,值TBSTYLE_FLAT指定了工具条为一个水平风格的工具条;参数dwStyle指定了工具

16、条所具有的各种风格,该参数可以设为多个可选值的组合值,各值之间用“|”连接。WS_CHILD指定工具条为一个子工具条,WS_VISIBLE指定工具条可见,CBRS_TOP指定工具条在窗口的顶端出现,CBRS_GRIPPER指定工具条最左端有一凸起的竖条并且使工具条可移动,CBRS_TOOLTIPS使工具条按钮具有提示特性,CBRS_FLYBY使光标在工具条按钮上时显示按钮提示(如果没有此风格,则只有在实际按下鼠标键时才显示提示),CBRS_SIZE_DYNAMIC指定了工具条大小为动态的。参数rcBorders指定了工具条的边框,默认的值为没有边框。参数nID为工具条的子窗口ID。通常后两个参

17、数使用默认值即可,在调用函数时不用传入。如果工具条初始化成功,函数返回TRUE,否则返回FALSE。我们在创建默认初始工具条的代码下添加如下代码:/创建绘图工具条if (!m_DrawToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) | !m_DrawToolBar.LoadToolBar(IDR_DRAW)TRACE0("Failed to create toolba

18、rn");return -1; / fail to create该段代码在m_DrawToolBar工具条对象中加载IDR_DRAW工具条,并初始化该对象,如果失败则返回窗口创建失败。初始化工具条完成后,可以设置工具条的停放能力。看OnCreate函数中的如下代码:m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m_wndToolBar);该段代码首先调用CToolBar的成员函数EnableDocking来设置工具条本身的停放,参数值CBRS_A

19、LIGN_ANY指定工具条可以停放在窗口的四个边框的任意一边(也可选CBRS_ALIGN_TOP、CBRS_ALIGN_BOTTOM、CBRS_ALIGN_LEFT、CBRS_ALIGN_RIGHT等值,指定具体停放在哪一边,也可以是可选值的组合)。然后调用窗口类的EnableDocking函数指定主窗口允许的停放,参数值CBRS_ALIGN_ANY与上一个函数中的参数值意义相同,即主窗口允许工具条停放在窗口的四个边框的任意一边。最后调用窗口类的DockControlBar函数,将指定的工具条放在初始位置(窗口的视图区的左上方边框)。如果省略这三个函数,则工具条变成标准工具条,固定在窗口的上方

20、。这里需要注意的是,因为DockControlBar函数要将工具条放在窗口的上边框处,所以EnableDocking函数指定的窗口允许停放位置必须包含CBRS_ALIGN_TOP(或者使用CBRS_ALIGN_ANY),否则运行将出错。我们可以指定新添加的工具条的停放状态,修改上面的三行代码如下:m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);/设置绘图工具条的停放状态m_DrawToolBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m

21、_wndToolBar);/在主窗口中放置绘图工具条DockControlBar(&m_DrawToolBar);添加了如上代码之后,我们就将刚才新建的工具条加入到了主窗口中,运行应用程序,我们将在默认的初始工具条下面看到我们新添加的绘图工具条。该工具条与初始工具条一样,可以移动位置,并可以停放在窗口的四个边框中的任意一边上。但是此时该工具条中的按钮都处于不可用状态,这是因为还没有为工具条按钮连接处理函数。2.1.3 连接工具条按钮处理函数连接工具条按钮处理函数类似于给菜单项连接处理函数。用Ctrl+W打开类向导对话框,在类下拉框中选择CDrawMapView类,在资源ID列表中选择工

22、具条按钮的ID,如ID_DRAWLINE,在消息列表中列出了工具条按钮支持的消息(与菜单项相同)。双击COMMAND消息,在出现的添加处理函数对话框中直接选择OK按钮,使用默认的函数名称。如图2.9所示。此时可以双击成员函数列表中的对应成员函数来进行编辑,也可以一次把所有的工具条按钮的处理函数(总共四个)都创建出来再统一编辑。我们在这四个工具条按钮的处理函数中要确定的是绘图的类型,即需要知道用户想要用鼠标绘制什么样的图形。可以采用如下的方法:在CDrawMapView类中添加一个成员变量,声明如下:int m_DrawType;/绘图类型因为在本章的绘图应用程序中除了可以绘制图形之外,还可以选

23、择已绘制的图形并进行编辑,所以要增加一个变量来标识当前是否处于绘图状态。在CDrawMapView类中添加一个成员变量,声明如下:BOOL m_isDraw;/是否正在绘图该变量为true,表示当前正处于绘图状态,为false,则表示没有处于绘图状态。在构造函数中将此变量初始化为true。然后在工具条按钮的处理函数中分别给m_DrawType设置不同的值来代表绘制不同的图形,并设置当前处于绘图状态。在鼠标绘图时,通过判断m_DrawType的值来完成不同的图形的绘制。编写工具条按钮处理函数如下:/绘制直线段工具条按钮处理函数void CDrawMapView:OnDrawline() / TO

24、DO: Add your command handler code herem_DrawType = 1;/1表示绘制直线段m_isDraw = true;/初始状态为绘图状态/绘制椭圆工具条按钮处理函数void CDrawMapView:OnDrawellipse() / TODO: Add your command handler code herem_DrawType = 2;/2表示绘制椭圆m_isDraw = true;/当前处于绘图状态/绘制椭圆区域工具条按钮处理函数void CDrawMapView:OnDrawellipseregion() / TODO: Add your c

25、ommand handler code herem_DrawType = 3;/3表示绘制椭圆区域m_isDraw = true;/当前处于绘图状态/绘制矩形区域工具条按钮处理函数void CDrawMapView:OnDrawrectangle() / TODO: Add your command handler code herem_DrawType = 4;/4表示绘制矩形区域m_isDraw = true;/当前处于绘图状态m_DrawType变量分别用1,2,3和4表示绘制直线段,椭圆,椭圆区域和矩形区域。同时需要在CDrawMapView类的构造函数中添加如下代码:m_DrawTy

26、pe = 1;/默认初始绘图状态为绘制直线段m_isDraw = true;/当前处于绘图状态即应用程序的初始状态为绘制直线段。2.2 使用鼠标绘图在编写鼠标绘图的代码之前,首先要确定如何用鼠标完成绘图。以用鼠标绘制直线段为例:首先将鼠标的光标移动到直线段的一个端点处,按下鼠标左键,然后按住鼠标左键不放,移动鼠标光标到直线段的另一个端点处,此时松开鼠标左键,就完成了用鼠标绘制直线段,应用程序会在两个端点之间绘制一条直线段。绘制椭圆和椭圆区域比较类似,先后确定的是椭圆的外接矩形的两个对角点。而对于绘制矩形区域,则确定的是矩形区域的两个对角点。为了在应用程序中响应用户的鼠标动作,就需要在编写应用程

27、序时选择响应鼠标消息并编写其对应的处理函数。2.2.1 鼠标消息针对用户使用鼠标的一些基本操作,比如鼠标的单击、双击、移动等,Windows提供了相应的通用消息。这些鼠标消息按照鼠标动作发生的区域可以分为两大类:视图区鼠标消息和非视图区鼠标消息。非视图区鼠标消息指鼠标光标在应用程序窗口视图区外的非视图区发生动作时产生的鼠标消息。非视图区包括标题栏、最小化和最大化按钮、关闭窗口按钮、系统菜单栏和窗口框架等。非视图区鼠标消息虽然用得比较少,但对于应用程序窗口的管理是有用的。通过非视图区鼠标消息可以知道窗口何时进行移动、关闭或改变大小。下表中列出了常用的非视图区鼠标消息及其含义:非视图区鼠标消息含义

28、WM_NCMOUSEMOVE非视图区鼠标移动WM_NCLBUTTONUP非视图区鼠标左键抬起WM_NCLBUTTONDBLCLK非视图区鼠标左键双击WM_NCLBUTTONDOWN非视图区鼠标左键按下WM_NCRBUTTONUP非视图区鼠标右键抬起WM_NCRBUTTONDBLCLK非视图区鼠标右键双击WM_NCRBUTTONDOWN非视图区鼠标右键按下视图区鼠标消息指鼠标光标在应用程序窗口视图区内发生动作时产生的鼠标消息。视图区鼠标消息比较常用,用鼠标绘图就要使用视图区鼠标消息。下表列出了常用的视图区鼠标消息及其含义:视图区鼠标消息含义WM_MOUSEMOVE视图区鼠标移动WM_LBUTTO

29、NUP视图区鼠标左键抬起WM_LBUTTONDBLCLK视图区鼠标左键双击WM_LBUTTONDOWN视图区鼠标左键按下WM_RBUTTONUP视图区鼠标右键抬起WM_RBUTTONDBLCLK视图区鼠标右键双击WM_RBUTTONDOWN视图区鼠标右键按下实现对鼠标消息的处理要完成以下工作:(1) 定义鼠标消息处理函数;(2) 使用消息映像宏实现鼠标消息和消息处理函数间的消息映像;(3) 编写鼠标消息处理函数的代码。下表列出了视图区鼠标消息对应的消息映像宏及消息处理函数:视图区鼠标消息消息映像宏消息处理函数WM_MOUSEMOVEON_WM_MOUSEMOVE()afx_msg void O

30、nMouseMove (UINT nFlags, CPoint point);WM_LBUTTONUPON_WM_LBUTTONUP()afx_msg void OnLButtonUP (UINT nFlags, CPoint point);WM_LBUTTONDBLCLKON_WM_LBUTTONDBLCLK()afx_msg void OnLButtonDblClk (UINT nFlags, CPoint point);WM_LBUTTONDOWNON_WM_LBUTTONDOWN()afx_msg void OnLButtonDown (UINT nFlags, CPoint poin

31、t);WM_RBUTTONUPON_WM_RBUTTONUP()afx_msg void OnRButtonUp (UINT nFlags, CPoint point);WM_RBUTTONDBLCLKON_WM_RBUTTONDBLCLK()afx_msg void OnRButtonDblClk (UINT nFlags, CPoint point);WM_RBUTTONDOWNON_WM_RBUTTONDOWN()afx_msg void OnRButtonDown (UINT nFlags, CPoint point);其中消息处理函数的参数point是CPoint对象,它存储了产生鼠

32、标消息时鼠标光标所处位置的坐标,如鼠标左键按下的处理函数中传入的point参数中存放了鼠标左键按下位置的坐标。参数nFlags是一个无符号数,它表明了在鼠标消息产生的时候鼠标按钮及部分键盘按键的状态,其值可为下表中值的任意组合:nFlags参数值说明MK_CONTROLCtrl键按下MK_LBUTTON鼠标左键按下MK_MBUTTON鼠标中键按下MK_RBUTTON鼠标右键按下MK_SHIFTShift键按下比如在鼠标左键按下的处理函数中,如果参数nFlags传入的值为MK_CONTROL,则表示在鼠标左键按下的同时键盘上的Ctrl键也被按下。我们可以使用类向导来添加鼠标消息处理函数,应用程序

33、框架将会自动填写代码完成鼠标消息和其处理函数之间的映像。打开类向导,在类列表中选择CDrawMapView类,在消息列表框中选择WM_LBUTTONDOWN消息并用鼠标左键双击,此时类向导自动在成员函数列表框中添加该消息的处理函数。因为该处理函数的名称不能修改,所以不会出现增加成员函数对话框。用同样方法添加WM_MOUSEMOVE消息和WM_LBUTTONUP消息的处理函数,因为前面我们制定的鼠标绘图方法中将要用到这三种鼠标消息的处理函数。我们打开CDrawMapView类的头文件,可以在其中看到如下代码:/ Generated message map functionsprotected:/

34、AFX_MSG(CDrawMapView)afx_msg void OnDrawline();afx_msg void OnDrawellipse();afx_msg void OnDrawellipseregion();afx_msg void OnDrawrectangle();afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnMouseMove(UINT nFlags, CPoint point);afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

35、/AFX_MSGDECLARE_MESSAGE_MAP()前面四句代码声明了工具条按钮的处理函数,而后三句代码则声明了鼠标消息的处理函数。打开CDrawMapView类的类文件(CPP文件),在文件的开始部分可以看到如下代码:BEGIN_MESSAGE_MAP(CDrawMapView, CView)/AFX_MSG_MAP(CDrawMapView)ON_COMMAND(ID_DRAWLINE, OnDrawline)ON_COMMAND(ID_DRAWELLIPSE, OnDrawellipse)ON_COMMAND(ID_DRAWELLIPSEREGION, OnDrawellipser

36、egion)ON_COMMAND(ID_DRAWRECTANGLE, OnDrawrectangle)ON_WM_LBUTTONDOWN()ON_WM_MOUSEMOVE()ON_WM_LBUTTONUP()/AFX_MSG_MAPEND_MESSAGE_MAP()该段代码完成了消息及对应的处理函数之间的映像。其中前四句代码是工具条按钮与处理函数的映像,而后三句代码是鼠标消息和处理函数间的映像。之所以鼠标消息的映像中没有指定处理函数名,是因为鼠标消息处理函数的名称是固定的,不能修改。以上代码是类向导自动添加的,如果我们不使用类向导来创建鼠标消息的处理函数,也可以手动添加以上代码,效果是一样的。

37、现在看一下应用程序框架创建的原始的鼠标消息处理函数,代码如下:/鼠标左键按下处理函数void CDrawMapView:OnLButtonDown(UINT nFlags, CPoint point) / TODO: Add your message handler code here and/or call defaultCView:OnLButtonDown(nFlags, point);/鼠标移动处理函数void CDrawMapView:OnMouseMove(UINT nFlags, CPoint point) / TODO: Add your message handler cod

38、e here and/or call defaultCView:OnMouseMove(nFlags, point);/鼠标左键抬起处理函数void CDrawMapView:OnLButtonUp(UINT nFlags, CPoint point) / TODO: Add your message handler code here and/or call defaultCView:OnLButtonUp(nFlags, point);我们发现在函数中已经添加了一句代码,这行代码是调用对应的父类的鼠标消息处理函数进行一些默认处理。我们所添加的代码都必须添加到该句代码之前,并且此句代码不能删

39、除,否则将会出错。同时需要注意的是,Windows的鼠标消息发生的间隔是一秒钟,并不是所有的鼠标动作都会产生鼠标消息。假设你按鼠标左键的速度足够快,在一秒钟内可以按鼠标左键多次,则并不是每次按键都会产生鼠标消息,只有第一次按键以及后面按键与前一次按键的时间间隔在一秒钟以上的那些按键才会产生鼠标消息。2.2.2 用鼠标绘制直线段现在看一下如何编码实现用鼠标绘制直线段。既然鼠标消息分为视图区鼠标消息和非视图区鼠标消息,那么如果在鼠标绘图过程中,鼠标在视图区内按下左键,然后移动到视图区外才把鼠标左键抬起,应用程序窗口就得不到视图区的鼠标左键抬起消息。因此在用鼠标绘制图形之前应该首先捕捉鼠标,使当前视

40、图区接受所有的鼠标操作引起的鼠标消息。122.12.22.2.12.2.22.2.2.1 捕捉鼠标我们可以用SetCapture函数和ReleaseCapture函数捕捉和释放鼠标。u SetCapture函数,用于捕捉鼠标,其函数声明如下:CWnd* SetCapture();该函数是窗口基类CWnd的成员函数,因为CView类是从CWnd类派生而来的,所以它也拥有该函数。该函数返回捕捉了鼠标的窗口指针。执行该函数后,视图类就捕捉了鼠标,此后的鼠标动作都将产生视图区鼠标消息。u ReleaseCapture函数,用于释放鼠标,其函数声明如下:BOOL ReleaseCapture();该函数

41、用于释放被捕捉的鼠标。在用鼠标绘图完毕后,需要调用该函数释放鼠标,否则窗口将不能正确接受鼠标消息。除了在鼠标绘图开始时捕捉鼠标之外,也可以通过调用ClipCursor函数将鼠标限制在指定区域中以避免在鼠标绘图过程中出现不同类型的鼠标消息。u ClipCursor函数,用于将鼠标限制在指定的矩形区域中,其函数声明如下:BOOL ClipCursor(CONST RECT lpRect);参数lpRect指向一个RECT结构体,该结构体定义了一个矩形区域,该函数将鼠标限制在此矩形区域中。如果鼠标光标要移动到矩形区域外,系统将自动调正鼠标光标位置,使其始终在指定的矩形区域内。通常用如下代码把鼠标限制

42、在应用程序窗口的视图区内:CRect rect;/矩形区域对象GetClientRect(&rect);/获得并保存窗口视图区区域坐标ClientToScreen(&rect);/用视图区区域坐标重新计算屏幕坐标ClipCursor(&rect);/限制鼠标在窗口视图区中上面的代码将鼠标限制在窗口的视图区中,这样鼠标只能在视图区中产生动作,也就只会产生视图区鼠标消息。其中用到的GetClientRect函数和ClientToScreen函数都是CWnd类的成员函数。u GetClientRect函数,用于获得窗口视图区的矩形区域坐标,其函数声明如下:void GetCl

43、ientRect(LPRECT lpRect) const;该函数将窗口的视图区的左上角和右下角坐标存放在lpRect指针指向的矩形区域结构中。u ClientToScreen函数,用于将传入的矩形区域坐标或点坐标转化成实际的屏幕坐标,其函数声明如下:void ClientToScreen(LPPOINT lpPoint) const;void ClientToScreen(LPRECT lpRect) const;参数lpPoint和lpRect分别指向点结构和矩形区域结构,该函数将传入的点的坐标或矩形区域的坐标(左上角点和右下角点坐标)转换成实际的屏幕坐标,这样调用ClipCursor函数

44、时才能将鼠标限制在正确的区域中。被限制的鼠标在绘图完毕后应该取消限制,采用如下语句来完成取消对鼠标的限制:ClipCursor(NULL);限制鼠标移动范围会加大系统负担,所以通常不采用此种方法。2.2.2.2 设置鼠标光标形状在用鼠标绘制图形时,我们希望修改鼠标光标形状,而不是使用默认的斜箭头光标。鼠标的光标形状由专门的光标(Cursor)资源所决定。我们可以在资源面板上向项目中添加光标资源(在添加资源对话框中有光标资源类型,如图2.3所示),每个光标资源对应一个唯一的资源ID,应用程序框架通过该ID来识别光标资源。要使用光标资源作为鼠标的光标形状,需要首先将光标资源加载到系统中,然后再设置

45、鼠标光标形状为加载的光标资源的形状。u LoadCursor函数,用于加载光标资源,其函数声明如下:HCURSOR LoadCursor(LPCTSTR lpszResourceName) const;HCURSOR LoadCursor(UINT nIDResource) const;参数lpszResourceName和nIDResouce分别为光标资源的名称和ID号,函数将指定的光标资源加载到系统内存中。函数返回光标资源句柄(HCURSOR)。u LoadStandardCursor函数,用于加载Windows预定义的光标资源,其函数声明如下:HCURSOR LoadStandardCu

46、rsor(LPCTSTR lpszCursorName) const;参数lpszCursorName是由一些以IDC_开头的光标资源名称,用来指定Windows预定义的光标资源,下表中列出了预定义的光标资源名称和对应的光标形状:预定义的光标资源名称光标形状IDC_ARROW标准的斜箭头光标IDC_IBEAM标准的插入文本光标IDC_WAIT沙漏形状的光标IDC_CROSS标准的十字光标IDC_UPARROW向上方向的箭头IDC_SIZEALL带有四个方向箭头的光标IDC_SIZENSWE带有左上和右下方向箭头的光标IDC_SIZENESW带有右上和左下方向箭头的光标IDC_SIZEWE带有左

47、右方向箭头的光标IDC_SIZENS带有上下方向箭头的光标该函数同样返回光标资源句柄。上面两个加载光标资源的函数都是应用程序基类CWinApp的成员函数,在视图类中要使用该函数,需要调用AfxGetApp()函数获得应用程序基类的指针,然后再调用加载光标资源函数。加载完光标资源后,调用SetCursor函数设置使用光标资源。u SetCursor函数,用于设置当前使用的光标资源,其函数声明如下:HCURSOR SetCursor(HCURSOR hCursor);参数hCursor为要设置的光标资源句柄。函数返回原来使用的光标资源的句柄。在我们的绘图应用程序中,使用鼠标绘图时,设置鼠标光标形状

48、为标准的十字光标。在CDrawMapView类中加入下面的成员变量:HCURSOR m_Cursor;/光标资源句柄该变量存放应用程序当前使用的光标资源句柄。在绘图工具条按钮的处理函数中设置对应的光标资源句柄,添加如下代码到四个绘图工具条按钮的处理函数中:/设置鼠标光标形状为标准十字光标m_Cursor = AfxGetApp()->LoadStandardCursor(IDC_CROSS);因为应用程序初始状态就是绘图状态(绘制直线段),所以此代码也需加入到CDrawMapView类的构造函数中。然后只需在鼠标消息处理函数中加入如下代码设置使用光标资源:SetCursor(m_Curs

49、or);三个鼠标消息的处理函数中都要加入该行代码。假设在鼠标左键按下的处理函数中不设置使用鼠标资源,则当鼠标左键按下时,鼠标光标将变回到默认的斜箭头状态。2.2.2.3 使用橡皮线绘图在使用鼠标绘图的时候,当鼠标左键按下时表示绘图开始,此时随着鼠标光标的移动,希望实时的把图形绘制出来,这样用户可以随时看到自己要绘制的图形是什么样的,而不是只有到最后鼠标左键抬起的时候才把图形绘制出来。为了实现这种效果可以在鼠标移动消息处理函数中就把当前图形绘制出来,这样每当鼠标移动消息处理函数被调用的时候都会将当前鼠标光标所处位置和鼠标左键按下位置所确定的图形绘制出来。但是如果一直绘图的话,每次绘制的图形都留在

50、视图区中,会产生许多根本不需要的图形。所以正确的做法是每次绘制图形时都先擦除上次所绘制的图形,然后再绘制新的图形。这种绘图方法就称为使用橡皮线绘图(意指绘图线像橡皮一样可以擦除以前绘制的图形)。因为在本章的绘图应用程序中除了要用鼠标绘制图形之外,还要用鼠标选择图形并进行编辑,所以我们单独编写三个函数,分别应用在鼠标绘图时、鼠标移动、鼠标左键抬起三个鼠标消息的处理上。在CDrawMapView类中添加下面三个成员函数:/鼠标绘图时鼠标左键按下消息处理函数void DrawLButtonDown(UINT nFlags, CPoint point);/鼠标绘图时鼠标移动消息处理函数void Dra

51、wMouseMove(UINT nFlags, CPoint point);/鼠标绘图时鼠标左键抬起消息处理函数void DrawLButtonUp(UINT nFlags, CPoint point);这三个函数的参数与系统的鼠标消息处理函数参数相同,我们分别编写这三个函数,编写完的代码如下:/鼠标绘图时鼠标左键按下处理函数void CDrawMapView:DrawLButtonDown(UINT nFlags, CPoint point)SetCursor(m_Cursor);/设置使用光标资源this->SetCapture();/捕捉鼠标/设置开始点和终止点,此时为同一点m_S

52、tartPoint = point;m_EndPoint = point;m_LButtonDown = true;/设置鼠标左键按下/鼠标绘图时鼠标移动处理函数void CDrawMapView:DrawMouseMove(UINT nFlags, CPoint point)SetCursor(m_Cursor);/设置使用光标资源CClientDC dc(this);/构造设备环境对象/判断鼠标移动的同时鼠标左键按下,并且要绘制的是直线段if (m_LButtonDown && m_DrawType = 1)dc.SetROP2(R2_NOT);/设置绘图模式为R2_NOT

53、/重新绘制前一个鼠标移动消息处理函数绘制的直线段/因为绘图模式的原因,结果是擦除了该线段dc.MoveTo(m_StartPoint);dc.LineTo(m_EndPoint);/绘制新的直线段dc.MoveTo(m_StartPoint);dc.LineTo(point);/保存新的直线段终点m_EndPoint = point;/鼠标绘图时鼠标左键抬起处理函数void CDrawMapView:DrawLButtonUp(UINT nFlags, CPoint point)SetCursor(m_Cursor);/设置使用光标资源ReleaseCapture();/释放鼠标CClient

54、DC dc(this);/构造设备环境对象/绘制的是直线段if (m_DrawType = 1)/绘制最终要绘制的直线段dc.MoveTo(m_StartPoint);dc.LineTo(m_EndPoint);在处理函数中用到的变量m_StartPoint和m_EndPoint用于存放所要绘制的直线段的起始点坐标和终止点坐标。这两个变量是CDrawMapView的成员变量,所以我们要在CDrawMapView类中添加这两个变量,其类型是CPoint:CPoint m_EndPoint;/鼠标绘图终止点坐标CPoint m_StartPoint;/鼠标绘图开始点坐标变量m_LButtonDow

55、n是一个布尔型变量,该变量用于标识绘图的时候鼠标左键是否按下,因为要求绘图过程中要一直按住鼠标左键。在CDrawMapView类中要添加这个变量:BOOL m_LButtonDown;/鼠标左键是否按下并且在CDrawMapView类的构造函数中初始化该变量,添加如下代码:m_LButtonDown = false;现在分别看一下在三个鼠标消息处理函数中我们都做了哪些工作。(1) 鼠标左键按下处理函数。在函数中先设置了鼠标使用的光标资源,并捕捉鼠标。然后设置了m_StartPoint和m_EndPoint的初始值,此时鼠标左键刚刚按下,所以这两个点相同。最后设置m_LButtonDown为tr

56、ue,表示鼠标左键已经按下。(2) 鼠标移动处理函数,主要在该函数中完成橡皮线的绘制。先在函数中设置鼠标使用的光标资源,再构造设备环境对象,以便进行绘图。if条件判断在鼠标左键按下并且要绘制的图形是直线段时,执行绘制直线段橡皮线的代码。绘制直线段橡皮线的代码就是if条件句内的代码。该代码设置绘图模式为R2_NOT,就是在这个绘图模式下才产生了橡皮线的效果。接下来,首先绘制m_StartPoint和m_EndPoint之间的直线段,再绘制m_StartPoint和产生鼠标移动消息时鼠标光标所在位置point之间的直线段,最后将point赋值给m_EndPoint。因为m_EndPoint中存放的一直是上次调用鼠标移动消息处理函数时鼠标光标所处的位置,所以绘制m_StartPoint和m_EndPoint之间的直线段时,该直线段已经存在了,因为绘图模式的关系,本

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论