第12章Windows与MFC编程(中)胡英在本讲中,我们将要介绍:•图形设备接口与输入设备•菜单、工具栏和状态栏•对话框和控件在上一讲中,我们主要介绍了:•VisualC++与MFC图形设备接口•许多MS-DOS程序直接向视频存储区或打印机输入当前数据,但不利之处是必须提供相应的驱动程序;•Windows提供GDI抽象接口,可调用GDI函数和硬件打交道,自动将设备环境描述表映射到相应的物理设备,提供输入/输出指令。1、GDI和CGdiObject类GDI管理来自于Windows系统的所有图形输出,与设备无关实体类:CPen,CBrush,CFont,CBitmap,CPalette,CRgn等,它们的抽象基类是CGdiObject。GDI提供绘图函数,通过设备驱动程序定义了统一的程序设计接口。构造CGdiObject派生类的对象有两种方法:1)一步构造法(构造和初始化一步完成)如:CPenmyPen(PS_SOLID,3,RGB(255,0,0));//3个单位粗的红色实线2)两步构造法(更安全)如:CpenmyPen;//1个单位粗的黑色点线myPen.CreatePen(PS_DOT,1,RGB(0,0,0));2、DC和CDC类当用户往GDI设备中绘图时,要访问一个称为DC的数据结构好比绘画要画纸,计算机中Windows提供了DC供绘图在DC(设备环境)中作图,就是在设备环境的位图上作图。CDC类封装了Windows的DC,当用户构造CDC类的对象时,VC++会创建相应的WindowDC,并将两者相联。用户只要对CDC对象操作即可,用VC++去操作WindowsDC实体。MFC提供了CDC的派生类:CPaintDC(针对OnPaint而做的)CClientDC(GetDC,ReleaseDC)(窗口客户区相关的显示DC)CWindowDC(管理与整个窗口相关的显示DC)CDC类的常用函数3、坐标映射•Window文本图形函数用逻辑单位。这些逻辑单位在显示图形对象时会自动转换成物理单位(像素),这是由映射模式决定的。•设置映射模式:intCDC::SetMapMode(intnMapMode);•默认为MM_TEXT,X轴向右为正,Y轴向下为正。4、绘制基本图形•点,直线,圆,圆弧,矩形•绘图函数5、画笔CPen类三种创建方法:•一步构造法:CPenPen(PS_SOLID,1,RGB(255,0,0));•两步构造法:CPenPen;Pen.CreatePen(PS_SOLID,1,RGB(255,0,0));•填入LOGPEN结构,再用CPen::CreatePenIndirect()创建样式宽度颜色库存画笔:用CreateStockObject()创建,库存画笔被定义为:WHITE_PEN,BLACK_PEN和NULL_PEN6、画刷CBrush类三种创建方法:•一步构造法:CBrushBrush(RGB(255,0,0));•两步构造法:CBrushBrush;Brush.CreateSolidBrush(RGB(255,0,0));•填入LOGBRUSH结构,再用CBrush::CreateBrushIndirect()创建阴影画刷:两种创建方法•CBrushBrush(HS_DIAGCROSS,RGB(255,0,0));•CBrushBrush;Brush.CreateHatchBrush(HS_DIAGCROSS,RGB(255,0,0));7、文本与字体CFont类文本的显示输出是应用程序设计中一项寻常而且重要的内容。计算机中要显示文本,先得确定一种字体。Windows中提供的字体大多是TrueType字体,它吸收了点阵字体和矢量字体的优点,所见即所得。1)CFont类:是CGdiObject的派生类,封装了Windows中的字体实体要使用字体,先得创建,然后将其选进要进行文本输出的DC,再用文本输出函数显示该字体的文本内容。创建字体(必须用两步构造法):CFontmyFont;myFont.CreateFont(…);//并不是创建一种物理字体而是创建一种逻辑字体2)显示文本:CDC*pDC=GetDC();pDC-TextOut(20,20,”Thisistext.”);有关函数:SetTextColor(),GetTextColor(),SetBkColor(),GetBkColor(),SetBkMode(),GetBkMode()得到文本的度量:TEXTMETRICtextMetrics;pDC-GetTextMetrics(&textMEtrics);(当显示空间有限,而字体又过大时,文本就会“溢出”,所以应确定字体大小,计算行间距。)问题Q1、如果只希望显示画笔和画刷中的一个,为什么必须同时指定画笔和画刷?Q2、使用什么来在窗口上进行绘图而不用知道用户所使用的图形卡?Q3、什么事件消息被送至窗口以通知窗口要重绘自身?Q4、如何使窗口重绘自身?A1、在绘制任何待填充的对象时,总是同时在使用画笔和画刷在绘制。画笔绘制图形的轮廓,画刷填充图形的内部。不能只选用其中的一个,必须同时使用二者。如果仅想显示其中之一,需要采取特殊步骤。不显示的笔:将笔的属性设置为PS_NULL不显示画刷:pDCSelectStockObject(NULL_BRUSH);A2、CDC类A3、WM_PAINT事件。A4、在其中使用Invalidate函数。输入设备•鼠标•键盘1、鼠标1)鼠标消息客户区:十种消息,如:WM_MOUSEMOVE非客户区:通常不要干涉window的默认设置。十种消息,如:WM_NCMOUSEMOVE有两个参数:nFlag和pointnFlag包含鼠标按钮和键盘组合使用标志(与shift,ctrl组合)若想知道某个按钮是否被按下,可用对应的为屏蔽值与nFlags参数作按位逻辑与运算,所得结果为非零值,则表示按钮被按下。如:if(nFlags&MK_LBUTTON)AfxMessageBox(“LeftButtonPressed!”);鼠标双击时间间隔默认为500毫秒,可重新设置鼠标双击时间间隔:::SetDoubleClickTime()窗口要接受双击,则用:wndclass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;2)鼠标指针的设置BeginWaitCursor()和EndWaitCursor();::SetCursor(::LoadCursor(AfxGetInstanceHandle(),IDC_ARROW)):3)捕捉鼠标把鼠标消息集中在一个窗口中。拖放、拖动选择多项、拖动绘图时必须捕捉鼠标方法:CWnd::SetCapture()(捕捉鼠标)CWnd::ReleaseCapture()(释放鼠标)2、键盘与鼠标一样,敲击键盘时也给应用程序发送消息。鼠标消息发送到鼠标所处的窗口,键盘消息发送到有输入焦点的窗口(屏幕上最前面)。1)键盘消息:主要有三种:WM_CHAR,WM_KEYDOWN,WM_KEYUP两种系统专用消息:WM_SYSKEYDOWN,WM_SYSKEYUP(系统键是Alt+F10)用户按下或释放一个键时,windows要发送三个主要的键盘消息:•一条WM_KEYDOWN和一条或多条WM_CHAR消息(按键时)•WM_KEYUP消息(释放键时)按下Alt键,Windows给应用程序发WM_SYSKEYDOWN消息释放Alt键,发WM_SYSKEYUP消息按下不代表可打印字符的键,如F1-F12,DEL,箭头键,发送WM_KEYDOWN消息释放时,发送WM_KEYUP消息,而不发送WM_CHAR消息按键的额外消息:包含在wParam和lParam中。例如:WM_CHAR的MFC类的OnChar()voidOnChar(UINTnChar,UINTnRepCnt,UINTnFlags)nChar:被击的字符,虚拟键,如:VK_RETURNnRepCnt:键被重击的次数nFlags:储存在位中更详细信息虚拟键代码是一种与设备无关的键盘编码,其值存放在wParam参数中。通常的虚拟键代码在windows.h中已有定义。CDC类主要绘图函数:画线MoveToLineTo;画椭圆Ellipse;画矩形Rectangle画圆弧Arc;画折线Polyline;画多边形Polygon;文字TextOut主要鼠标消息:WM_LBUTTONDOWN,WM_LBUTTONUP,WM_LBUTTONDBLCLK,WM_RBUTTONDOWN,WM_RBUTTONUP,WM_RBUTTONDBLCLK,WM_MOUSEMOVE,WM_MOUSEWHEEL.菜单•菜单是人机交互的一种重要方式。一般情况下,一个应用程序都有自己的主菜单。•通过菜单用户可以执行很多功能而不用占太多空间。对功能复杂、操作较多的应用程序,菜单发挥着不可替代的作用。1、消息Windows应用程序是消息驱动的,应用程序绝大部分工作是进行消息的循环处理,不断响应用户行为所产生的消息。用户编程主要是编写消息处理函数。三种类型的消息:1)标准Windows消息:以WM_为前缀(WM_COMMAND除外)2)控件通知消息以WM_COMMAND为消息名,由控件或子窗口发给其父窗口,消息中包含控件通知代码,以区分具体的控件通知消息。3)命令消息也以WM_COMMAND为消息名,包含命令标识符,区分具体命令。其来源是:菜单,工具栏,加速键。2、消息映射MFC采用消息映射机制,其实是一张消息及其处理函数的一一对应表以及分析处理这张表的应用框架内部的一些程序代码。凡是从CCmdTarget类派生的类都可以有消息映射。它包括两方面内容:1)在类的定义文件(.h文件)中加上一行宏调用:DECLARE_MESSAGE_MAP();//通常在类定义后面2)在类的实现文件(.cpp文件)中加上消息映射表BEGIN_MESSAGE_MAP(类名,父类名)……消息映射入口项……EDN_MESSAGE_MAP()3、命令消息处理过程和更新用户接口对象状态命令消息处理过程当用户选择菜单项、工具栏按钮或按下某加速键,则会产生命令消息。这些东西均有一个标识符(ID),消息处理函数通过标识符来确定处理哪一个消息,其意义对应关系是在消息映射表中由消息映射入口项指定的。入口项:ON_COMMAND宏//ID_FILE_OPEN为菜单IDON_COMMAND(ID_FILE_OPEN,OnFileOpen);•更新用户接口对象状态(UpdateCommandUI)该消息有应用框架发出,改变菜单,工具条状态。ON_UPDATE_COMMAND_UI(ID_EDIT_COPY,OnUpdateEditCopy);4、命令消息的传递路径许多类对象都可处理命令消息。当用户选择了某个命令,产生的消息究竟由谁处理比较好呢?采用“自然”法则。若某个命令在多个对象中都有消息处理函数,只有其中之一被调用。当某个命令消息产生时,它就被应用框架按命令消息传递路径传送,一旦在传递过程中遇到了某个对象有该消息的处理函数,则处理该消息,中断传递过程,不再搜索后续的对象。5、菜单项状态更新更新处理函数以一个指向CCmdUI对象的指针作为参数,如:pCmdUI-Enable(bValue);//根据BOOL值决定更新状态6、动态改变菜单(MFC的CMenu类)常用的函数:InsertMenuItem(),EnableMenuItem(),DeleteMenu(),GetSubMenu(),CheckMenuItem()7、获得菜单的指针CMenu*CWnd::GetMenu()const(不能用于子窗口,因为无菜单)弹出式菜单:GetSubMenu()8、上下文菜单(弹出式菜单一般放在右键消息处理)•创建及显示myMenu=newCMenu();BOOLbSuccess=myMenu-LoadMenu(IDR_CONTEXT);Cmenu*subM