文档库 最新最全的文档下载
当前位置:文档库 › 7-9章1

7-9章1

7-9章1
7-9章1

第2篇界面设计基础

136

第7章

系统的输入设备

鼠标和键盘是Windows系统基本的输入设备。Windows很大程度上是基于鼠标的操作系统,所有的Windows程序都应当响应鼠标输入,Windows 98 /2000操作系统本身也不例外。Visual C++ 6.0提供了大量的处理鼠标消息的函数,这些函数将在后面详细地讲到。

键盘是用户把字符输入到计算机的设备,没有键盘就不能正常地运行Windows系统。在本章中,我们将介绍采用键盘和鼠标作为应用程序的基本输入设备时所涉及的基本概念和编程原则。

7.1 鼠标的应用

在图形操作系统中,鼠标是最重要的输入设备之一。现在市场上常见的鼠标,根据工作原理不同可分为机械式鼠标、光电式鼠标和激光式鼠标;根据按键个数的不同,可以分为两键鼠标和三键鼠标。由于在Windows图形操作系统中,鼠标发挥了强大的威力,因而对它的控制与使用越来越受到用户的重视。好在Windows 98/2000为用户提供了统一的鼠标编程接口,因而用户不必过多的了解其底层的知识,我们只针对不同的鼠标消息进行处理即可。接下来,我们将学习一些与鼠标相关的基本概念。

7.1.1 鼠标消息

Windows是基于消息传递、事件驱动的操作系统。当用户移动鼠标、按下或释放鼠标键时,都会产生鼠标消息。根据鼠标所处的位置不同——窗口的客户区或非客户区,Windows 系统产生的鼠标消息分为两大类:客户区域的鼠标消息、非客户区域的鼠标消息。

1.客户区域鼠标消息

当鼠标指针通过应用程序窗口的客户区域时,Windows会发送客户区域鼠标消息到应用程序中。客户区域鼠标消息是用户可以在Windows应用程序中正常处理的信息。例如,当用户移动鼠标通过应用程序窗口的客户区域(在应用程序中能画图的窗口区域)时,应用程序会接收一连串的WM_MOUSEMOVE消息,这些消息不仅告诉应用程序鼠标在移动,而且还告诉应用程序在Windows产生消息时鼠标的位置。

当鼠标移动到应用程序窗口的客户区域外面时,应用程序则停止接收客户区域的鼠标消息,而Windows会把鼠标消息发送给鼠标碰巧通过的元素(包括桌面)。当鼠标移回到窗口

第7章系统输入设备137

的客户区域时,Windows会重新把鼠标消息发送方向指向应用程序(在应用程序已经捕获了鼠标时例外,这意味着即使当鼠标位于该应用程序窗口外,Windows也指示发送所有鼠标消息给该应用程序)。

应用程序可以接收十种客户区域鼠标消息,表7-1列举了这些消息和关于它们的描述。

2.非客户区鼠标消息

在Windows中,除了客户区以外的部分都是非客户区,Windows会给应用程序发送非客户区域鼠标消息。当鼠标通过除客户区域以外的应用程序窗口的任何区域时,就会产生与客户区域鼠标消息相类似的鼠标消息。例如,当鼠标通过窗口的标题栏或控制按钮时,应用程序会接收非客户区域鼠标消息。大部分应用程序不处理非客户区域鼠标消息,因为它们可以被Windows操作系统本身妥善地处理。例如,当用户双击窗口的标题栏时,Windows会使窗口的尺寸大小恢复到它原有的设置。

通常,读者不要干涉Windows对非客户区域鼠标消息的默认处理。因为如果干涉了这种处理时,应用程序的窗口也许会被冻结,不能按用户所期望的情况响应用户。然而,有时当响应非客户区域时,鼠标消息会使用程序执行额外的功能,例如应用程序也许会为增强窗口工具栏响应鼠标的方式而捕获非客户消息。

一般情况下,应用程序不必考虑非客户区域鼠标消息。然而,为了防止遇到麻烦,有的应用程序可以接收十个非客户区域鼠标消息,如表7-2所示,它们和客户区域的消息类似。

第2篇界面设计基础

138

7.1.2 鼠标消息处理

MFC把鼠标消息处理函数封装在CView类中,这些函数都是虚函数,它们分别是:OnMouseMove (UINT nFlag, CPoint point); //鼠标移动消息处理函数

OnLButtonDown (UINT nFlag, CPoint point); //鼠标左键按下消息处理函数

OnLButtonUp (UINT nFlag, CPoint point); //鼠标左键弹起消息处理函数

OnLButtonDblClk (UINT nFlag, CPoint point); //鼠标左键双击消息处理函数

……

它们分别对应鼠标消息:

MW_MOUSEMOVE

MW_LBUTTONDOWN

MW_LBUTTONUP

……

以上这些消息处理函数的point参数代表鼠标热点的坐标位置,point.x为横坐标,point.y 为纵坐标。这里采用的是客户坐标系统。在这个坐标系统中,默认的坐标原点(0,0)位于客户区的左上角。

nFlags参数中包含了鼠标按钮和键盘组合使用标志,用来描述鼠标按钮和键盘上Shift 键和Ctrl键的组合状态:

(1)MK_LBUTTON:鼠标左键被按下,与WM_LBUTTONDOWN所处状态相同。

(2)MK_RBUTTON:鼠标右键被按下,与WM_RBUTTONDOWN所处状态相同。

(3)MK_MBUTTON:鼠标中键被按下,与WM_MBUTTONDOWN所处状态相同。

(4)MK_SHIFT:键盘上的Shift键被按下。

第7章系统输入设备139

(5)MD_CONTROL:键盘上的Ctrl键被按下。

若想知道某个按钮是否被按下,可用对应的位屏蔽值与nFlags参数作按位逻辑“与”运算,所得结果若为非零值,则表示按钮被按下,如:

if(nFlags&MK_LBUTTON) AfxMessageBox(“Lbutton is pressed down!”);

else AfxMessageBox(“Lbutton is up!”);

这里需要说明的是,如何区分两次单击和一次双击,这取决于两次按下按钮之间的时间间隔,只有当时间间隔小于一定值时才被认为是一次双击,Windows默认的时间间隔为500毫秒,应用程序可以调用::SetDoubleClickTime( )函数来重新设定该值。

若要使窗口函数能接收到鼠标双击产生的消息,在注册窗口类时,必须指明该窗口具有CS_DBLCLKS风格,如:wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;

注意:如果窗口风格不包含CS_DBLCLKS,则即使进行了双击操作,该窗口也只能收到两条“WM_BUTTONDOWN”消息和两条“WM_BUTTONUP”消息。

7.1.3 改变鼠标指针的形状

向用户提供反馈最明显的方法之一就是改变鼠标指针的形状。在惯用的指针中,沙漏光标指针是最易于显示的,这是因为MFC类库提供了两个非常有用的函数,其中一个函数BeginWaitCursor( )用于显示沙漏光标指针,另一个函数EndWaitCursor( ),用于将指针恢复成正常的形状。两个函数均应在长时间的执行巨大的磁盘I/O访问任务时调用。

应该指出的是,这并不是显示沙漏光标指针的唯一途径,用户同样可以通过处理WM_SETCURSOR消息来显示沙漏光标指针。如果在持续显示沙漏光标指针时,程序需要与用户进行交互,即提取任意类型的消息,那么就必须响应WM_SETCURSOR的处理。Windows 98/2000 向每一个窗口发送持续的消息流,从而引起每一个窗口指针的持续更新。

用于显示指针所调用的函数是Windows 98 API函数SetCursor( ),例如用以显示箭头光标代码行为:::SetCursor (::LoadCursor(AfxGetInstanceHandle( ), //返回标识当前应用程序对象的句柄

IDC_ARROW)); //箭头光标值Windows系统提供的标准鼠标指针形状如表7-3所示。

第2篇界面设计基础

140

7.1.4 捕捉鼠标

有时候用户需要把鼠标消息集中在一个窗口中。这可以通过捕捉鼠标来完成上面的工作。尽管读者也许会认为鼠标的捕捉从某种程序上会限制指针的移动,其实不然。相反,捕捉鼠标会强迫所有的鼠标消息被送到一个窗口内。窗口在捕捉了鼠标后,可以继续获得鼠标的输入,尽管此时指针已不落在此窗口中。

窗口究竟需要在什么时候进行鼠标捕捉呢?当一个鼠标消息——通常是左键被按下的消息(WM_LBUTTONDOWN)——启动了一个需要多个鼠标消息操作时,捕捉鼠标是非常必要的。例如拖放、拖动来选择多个项(如在字处理器中的多行),以及通过拖动来绘图时必须捕捉鼠标。

如果在没有让应用程序知道鼠标键已弹起的消息时,鼠标可能已经离开,此时应用程序将处于一种模糊状态。毕竟,当多个鼠标消息操作开始时,用户很可能已经设置了许多标志来表明操作正在发生。用户希望在前进中避免操作突然停止,也就是说,对于强迫鼠标停住直到它完成已经开始的工作时捕捉鼠标是很有必要的。

理解鼠标的捕捉有助于观察鼠标的操作。运行某个应用程序,并打开File/Open对话框,鼠标将被两个对话框控件所捕捉,这两个控件是列表框和编辑控件。当用户单击已选择在列表框中的项或者在编辑控件中的文本时,捕捉鼠标将会发生。单击这样的控件后,当用户将鼠标拖动到边框外面时,可以看到控件在滚动它的内容,滚动是因为鼠标被捕捉而引起的。

另一个可以看到鼠标被捕捉的地方是在大部分文本编辑窗口中(包括Visual C++的编辑器),当用户单击一个充满文本的窗口并拖动到越出窗口边界时,这些窗口会滚动,这些窗口能够连续地接到鼠标的输入(WM_MOUSEMOVE消息),这是因为鼠标已经被捕捉。

现在,用户已经了解了鼠标的工作情况,可能想了解如何捕捉到鼠标。这是很简单的,用户只需调用CWnd::SetCapture( )函数。正如曾经提到的,用户完成上述工作一般是响应一个鼠标按下消息,要释放鼠标捕捉则是调用CWnd::ReleaseCapture( )函数,释放被捕捉鼠标的最好时间是响应鼠标键弹起的时候(WM_LBUTTONUP)。

下面是一段ClassWizard生成的代码,它是鼠标捕捉与释放的典型方案。当鼠标左键被

第7章系统输入设备141

按下时,鼠标被捕捉(响应WM_LBUTTONDOWN消息),当鼠标左键弹起时,释放鼠标(响应WM_LBUTTONUP消息)。

当用户捕捉鼠标时,Windows 98/2000并不向用户传递任何击键测试(WM_NCHI TTEST)或者光标形状设置(WM_SETC URSOR)消息。它假设用户已经完成了对鼠标的控制,并且不需要改变光标形状。如果用户想要在鼠标被捕捉时改变光标形状,那么用户可以在响应WM_MOUSEMOVE时实现上述工作,这是鼠标被捕捉时,用户可获得的唯一鼠标消息。

前面几节我们讲述了鼠标消息及其处理方

法、如何改变鼠标形状及如何捕捉鼠标。

下面我们将具体讲述一个示例来讨论与鼠

标相关的函数的用法。这个示例在窗口中

以文本形式给出鼠标的状态:当移动鼠标

时,在窗口中给出鼠标的当前位置;当按

下或弹起鼠标时,窗口中会给出鼠标按键

图7-1 Mouse应用程序的运行结果的状态,如图7-1所示。

操作步骤:

(1)利用MFC AppWizard(exe)创建应用程序Mouse(单文档类型)。

(3)编写CMouseView::OnLButtonDown( )函数。

void CMouseView::OnLButtonDown(UINT nFlags, CPoint point)

{ CDC* pDC=GetDC( ); //获得pDC pDC->TextOut(20,40,"LButton: DOWN "); //输出鼠标左键被按下信息

CView::OnLButtonDown(nFlags, point);

}

(4)编写CMouseView::OnLButtonUp( )函数。

void CMouseView::OnLButtonUp(UINT nFlags, CPoint point)

{ CDC* pDC=GetDC( ); //获得pDC pDC->TextOut(20,40,"LButton:UP "); //输出鼠标左键被释放信息

第2篇界面设计基础

142

CView::OnLButtonUp(nFlags, point);

}

(5)编写CMouseView::OnMouseMove( )函数。

void CMouseView::OnMouseMove(UINT nFlags, CPoint point)

{ HCURSOR hCursor; //定义画刷句柄CDC* pDC=GetDC( ); //获得pDC

char tbuf[80];

sprintf(tbuf,"Position:(%3d,%3d) ",point.x,point.y); //将坐标点存入tbuf

pDC->TextOut(20,20,tbuf); //输出鼠标的当前位置信息hCursor= ::LoadCursor(NULL,IDC_CROSS); //加载一个“+”字形鼠标光标

::SetCursor(hCursor); //设置当前鼠标光标形状为“+”字形

CView::OnMouseMove(nFlags, point);

}

(6)编写CMouseView::OnRButtonDown( )函数。

void CMouseView::OnRButtonDown(UINT nFlags, CPoint point)

{ CDC* pDC=GetDC( ); //获得pDC pDC->TextOut(20,60,"RButton: DOWN "); //输出鼠标右键被按下信息

CView::OnRButtonDown(nFlags, point);

}

(7)编写CMouseView::OnRButtonUp( )函数。

void CMouseView::OnRButtonUp(UINT nFlags, CPoint point)

{ CDC* pDC=GetDC( ); //获得pDC pDC->TextOut(20,60,"RButton:UP"); //输出鼠标右键被释放信息

CView::OnRButtonUp(nFlags, point);

}

(8)编写CMouseView::OnLButtonDblClk( )函数。

void CMouseView::OnLButtonDblClk(UINT nFlags, CPoint point)

{ CDC* pDC=GetDC( ); //获得pDC

pDC->TextOut(20,80,"LButton is double clicked! "); //输出鼠标左键被双击信息

CView::OnLButtonDblClk(nFlags, point);

}

(9)编写CMouseView::OnRButtonDblClk( )函数。

void CMouseView::OnRButtonDblClk(UINT nFlags, CPoint point)

{ CDC* pDC=GetDC( ); //获得pDC pDC->TextOut(20,80,"RButton is double clicked! "); //输出鼠标右键被双击信息

CView::OnRButtonDblClk(nFlags, point); }

(10)创建和运行应用程序。

程序运行后,用户移动鼠标时,在窗口中会出现鼠标的当前位置,当按下或弹起鼠标时,窗口中给出鼠标按键的状态。

第7章系统输入设备143

7.2 键盘的应用

就像用户移动或单击鼠标键时Windows会给应用程序发送消息一样,当用户击键盘时,Windows也同样会给应用程序发送消息,鼠标消息和键盘消息两者之间的区别在于鼠标消息发送到鼠标所处的窗口,而键盘消息总是被发送给有输入聚集的窗口,通常是屏幕上最前面的窗口。

应用程序捕获和响应键盘事件有几种方式,响应哪个键盘消息取决于用户在程序中计划使用按键的方法,例如一种类型的键盘消息只给应用程序发送要打印的字符,而其他键盘消息却能够响应键盘上许多特殊键,如功能键、箭头键、删除键等等。

7.2.1 键盘消息

应用程序能够响应的三种主要键盘消息为:WM_CHAR、WM_KEYDOWN和WM_KEYUP。此外,还有两种为系统按键专用的消息,它们是WM_SYSKEYDOWN和WM_SYSKEYUP。这些系统键是Alt和F10,Windows为它们保留有专门的函数。正因为存在非客户区域鼠标消息,用户必须对如何处理系统键小心谨慎。如果损害了它们的默认行为,Windows就不会再按用户所期望的方式工作。

当用户按下和释放一个键时,Windows实际上要发送三个主要的键盘消息。当键被按下时,Windows发送一条WM_KEYDOWN和一条(或多条)WM_CHAR消息。当用户释放键时,Windows发送WM_KEYUP消息。通过响应适当的消息,用户能够创建所需的任何种类的键盘处理程序。

上述事件序列的例外是用户可能会按下系统键,例如如果用户按下Alt键,Windows会给应用程序发送WM_SYSKEYDOWN消息。如果用户按下另一个键以及Alt键,那么Windows仍然会发送WM_YSKEYDOWN,而不是WM_KEYDOWN。当然,在用户释放Alt 键后,Windows会发送WM_SYSKEYUP消息。

另一个例外是用户可能会按下一个不代表可打印字符的键,这样的键是从F1到F12的功能键、Delete键、箭头键等等。按下和释放这些键中的一个,会使Windows发送WM_KEYDOWN和WM_KEYUP消息,而不发送WM_CHAR消息,这是因为WM_CHAR 消息是只为那些代表可打印字符的键而发送的。

每个键盘消息都包含有关按键的额外信息,这种额外的信息被包含在wParam和lParam 参数中,应用程序接收这两个参数作为消息的组成部分。如果读者正在使用MFC编程,恰当的消息响应函数将这些参数“分解”为独立的部分。例如,响应WM_CHAR消息的MFC 类的OnChar( )函数便会如此。该函数的原型为:

void OnChar (UINT nChar, UINT nRepCnt, UINT nFlags);

在这里被分离出来的参数是nChar、nRepCnt和nFlags。nChar参数保存被击的字符,即虚拟键,如表7-4所示。

第2篇界面设计基础144

第7章系统输入设备145

续表

虚拟键代码是一种与设备无关的键盘编码,它的值存放在键盘消息的wParam参数中,用以标识那个键被按下或释放。最常用的虚拟键代码已被定义在windows.h中,例如:VK_LEFT表示“←”键,VK_F1表示功能键F1,VK_SHIFT表示Shift键等。表7-4按照数值顺序列出了所有的虚拟键代码。

nRepCnt参数保存了键被重击的次数,而nFlags参数保存有储存在位中的更为详细的信息,如表7-5所示,MFC的OnKeyDown( )和OnKeyUp( )消息响应函数与OnChar( )函数接收相同的参数,它们分别响应WM_KEYDOWN和WM_KEYUP消息。

7.2.2 字符消息

当击键过程的组合能产生可见的字符时,Windows在发送键盘消息的同时还发送字符消息,而有些键则只产生键盘消息而不产生字符消息,如功能键、Shift键、Insert键、Delete 键等。在Windows应用程序的内部封装着下列消息循环:

while(GetMessage(&msg,0,NULL,NULL))

{ TranslateMessage(&msg);

DispatchMessage(&msg);

第2篇界面设计基础

146

}

当GetMessage( )函数获取的消息是WM_KEYDOWN或WM_SYSKEYDOWN,并且击键产生了可见字符时,TranslateMessage( )函数把键盘输入消息转换成相应的字符消息WM_CHAR和WM_SYSCHAR,并将这些字符消息放入应用程序的消息队列中,它将成为在键盘消息之后,GetMessage( )函数从应用程序消息队列中获取的下一条消息。

在Windows中共有四条字符消息,这些消息及其处理函数见表7-6。

注意:除了OnSysChar( )函数的nFlags参数外,别的参数与键盘消息处理函数的参数完全相同。

WM_DEADCHAR和WM_SYSDEADCHAR称为“四字符消息”,四字符的含义是指本身不能显示但能修改其他字符显示的字符,应用程序一般不对它们进行处理。

WM_CHAR和WM_DEADCHAR是由WM_KEYDOWN消息引出的,而WM_SYSCHAR 和WM_SYSDEADCHAR是由WM_SYSKEYDOWN消息引出的。

由于用户击键方式和击键次序的不同,窗口函数所收到的消息的数目和次序也将不同,表7-7 列出了一些击键与消息的对应关系。

至此我们讲述了键盘消息、字符消息以及与它们相关的函数。下面我们将具体讲述一个示例,这个示例在窗口中提示用户击键。当用户按下字符键时,该示例在窗口文本框中显示键入的字符并且有一个字符键被按下的消息。当用户释放键盘中字符键时,该示例显示有一

第7章系统输入设备147

个字符键被释放。当用户按下或释放了按键时,文本框中字符不会改变,但是会提示已经按下或释放非字符键,如图7-2所示。

操作步骤:

(1)利用MFC AppWizard(exe)创建应用程序KeyBoard(单文档型)。

(4)编写CKeyBoardView::CkeyBoardView( )构造函数。

CKeyBoardView::CKeyBoardView( )

{

m_nX=180;

}

图7-2 KeyBoard应用程序的运行结果

(5)编写CKeyBoardView::OnDraw( )函数。

void CKeyBoardView::OnDraw(CDC* pDC)

{ CKeyBoardDoc* pDoc = GetDocument( ); //得到文档对象指针ASSERT_V ALID(pDoc);

CBrush* pOldBrush=(CBrush *)pDC->SelectStockObject(WHITE_BRUSH); //设置画刷

第2篇界面设计基础

148

pDC->Rectangle(40,60,400,180); //画矩形

pDC->SelectObject(pOldBrush); //恢复旧画刷

TEXTMETRIC tm;

CFont* pOldFont;

CFont* pSmallFont=new CFont;

pSmallFont->CreateFont(40,0,0,0,400,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH&FF_SWISS,"Arial");

pOldFont=(CFont *)pDC->SelectObject(pSmallFont); //设置字体

pDC->GetTextMetrics(&tm); //获取字体信息

pDC->TextOut(40,10,"Enter a letter, please."); //显示字符串

pDC->SelectObject(pOldFont); //恢复旧字体

delete pSmallFont; //删除新字体}

(6)编写CKeyBoardView::OnChar( )函数。

//当一次击键被翻译成一个非系统字符时,系统调用这个函数。

void CKeyBoardView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

{ CDC* pDC=GetDC( ); //获取设备描述表指针CBrush* pOldBrush=(CBrush *)pDC->SelectStockObject(WHITE_BRUSH); //设置画刷

pDC->Rectangle(40,60,400,180);

pDC->SelectObject(pOldBrush);

TEXTMETRIC tm;

CFont* pOldFont;

CFont* pLargeFont=new CFont;

char tbuf[80];

pLargeFont->CreateFont(100,0,0,0,400,FALSE,FALSE,0, //创建字体

ANSI_CHARSET,OUT_DEFAUL T_PRECIS,

CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,

DEFAULT_PITCH&FF_SWISS,"Arial");

pOldFont=(CFont *)pDC->SelectObject(pLargeFont); //选择字体

pDC->GetTextMetrics(&tm); //取字体数据

vsprintf(tbuf,"%c",nChar); //送格式化输出到串tbuf中

pDC->TextOut(m_nX,70,tbuf); //显示字体

pDC->SelectObject(pOldFont); //恢复原字体

delete pLargeFont; //删除定义的字体

CView::OnChar(nChar, nRepCnt, nFlags);

}

(7)编写CKeyBoardView::OnKeyDown( )函数。

//当一个非系统键被按下时,系统调用这个函数。

void CKeyBoardView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)

第7章系统输入设备149

{

CDC* pDC=GetDC( );

pDC->TextOut(40,190,"A key is pressed down. ");

switch(nChar) //按下键盘键

{ case VK_LEFT: //按下左箭头键

m_nX-=10;

break;

case VK_RIGHT: //按下右箭头键

m_nX+=10;

break;

default:

break;

}

if(m_nX<40) m_nX=40;

else if(m_nX>240) m_nX=240;

else ;

CView::OnKeyDown(nChar, nRepCnt, nFlags);

}

(8)编写CKeyBoardView::OnKeyUp( )函数。

//当一个非系统键被释放时,系统调用这个函数。

void CKeyBoardView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)

{

CDC* pDC=GetDC( );

pDC->TextOut(40,190,"A key is released. ");

CView::OnKeyUp(nChar, nRepCnt, nFlags);

}

(9)创建和运行应用程序(可执行文件的名称为KeyBoard.exe)。

程序运行结果如图7-2所示。

习题

7-1 应用程序如何响应鼠标消息?

7-2 应用程序如何响应键盘消息?

7-3 设计一个窗口,在该窗口中练习键盘的响应,要求如下:

(1)单击键盘上的向上箭头时,屏幕显示“You had hitted the UP key”;

(2)单击Shift键时,屏幕显示“You had hitted the SHIFT key”;

(3)单击Ctrl+A键时,屏幕显示“You had hitted the CTRL+A key”;

7-4 设计一个鼠标应用程序,当单击鼠标左键时,窗口中显示“LEFT BUTTON”;当单击鼠标右键时,窗口中显示“RIGHT BUTTON”。把窗口分成五个区域,这五个区域颜色不同,要

第2篇界面设计基础

150

求当鼠标在这五个区域移动时,分别显示不同形状的鼠标光标。

第8章图形设备接口151 第8章

图形设备接口

许多MS-DOS程序都直接向视频存储区或打印机输入当前数据,这种做法的不利之处在于需要对每种类型的显示卡或打印机提供相应的驱动程序。而Windows系统中则提供一个叫做图形设备接口(GDI)的抽象接口,人们可以通过调用GDI函数和硬件打交道。Windows 会自动将设备环境描述表映射到相应的物理设备,并且会提供正确的输入/输出指令。

在本章中,读者将学习到GDI和设备描述表,读者还可以学习到CDC类、CGdiObject 类及其派生类以及它们的一些常用函数。读者可以使用这些函数来绘制图形,可以使用画笔、画刷以及字符等GDI绘图对象。

8.1 图形设备接口简介

图形设备接口GDI(Graphics Device Interface的简称)是形成Windows 98/2000核心的三种动态链接库之一,它管理来自于Windows系统的所有程序的图形输出。当绘图软件包在创建矩形时,当字处理程序将页文本送给打印机时,当屏幕保护程序闪烁迷人的图形时,所有这些操作都要使用GDI服务来创建它们的输出。

Windows系统本身使用GDI来绘制用户接口中的各个部分,当读者看到窗口、菜单或者对话框时,其间的任何一个都是通过调用GDI来绘制完成的。GDI甚至还管理鼠标(光标形状),使它看起来像是在显示屏幕的其他对象上浮动。

系统中的GDI向读者提供了高层次的绘图函数,这些函数允许读者方便地生成有趣的图形效果。例如,使用GDI来绘制各种尺寸、各种颜色的文本是非常容易的。读者可以通过调用一些函数来移动和操作位图,读者还可以创建复杂的矢量图形。所有这些操作都只用极小量的代码便能完成了,这是因为GDI通过设备驱动程序定义了统一的程序设计接口,并且在大量的设备里提供了统一的结果。除了完成显示操作功能以外,GDI还提供一些绘图对象,程序可以使用这些对象来渲染显示,这些对象如下所示:

设备描述表(Device context)它是具有诸如显示器或打印机等设备的绘图属性信息的数据结构

画笔(Pen)绘制线条的GDI对象

画刷(Brush)用图案填充屏幕的GDI对象

字体(Font)确定屏蔽上所画文本字符样式的GDI对象

位图(Bitmap)存储图像的GDI对象

调色板(Palette)在屏幕上画图时可以使用的一些颜色的集合

第2篇界面设计基础

152

综合使用这些对象,可以在应用窗口中创建可以看到的显示。在接下来几节中,我们将详细介绍设备描述表、画笔、画刷和字体对象及其应用。

8.2 设备描述表

当用户往GDI设备中绘图时,需要访问一个被称为设备描述表DC(Device Context的简称)的数据结构。

MFC将GDI的DC封闭在C++类中,这包括CDC和CDC的派生类,这些类中的许多成员函数都是对本地GDI绘图调用函数进行简单封装而形成的内联函数。

为了在显示屏幕上绘图,Windows程序必须有一个用于显示的DC。同样,为了在打印机上创建输出,也需要一个专门为打印机创建的DC。如果同样的程序想在图元文件和位图上创建输出,那么还需要第三个、第四个DC。DC的作用就是提供程序与物理设备或者伪设备之间的联系。

除了提供设备联系外,DC还要处理绘图属性设置。例如,在DC中绘图的属性之一是文本的颜色,当文本显示函数被调用时,该函数指定文本颜色。

读者可以通过调用专门的GDI函数在任意时刻修改绘图的任何属性,例如,SetTextColor( )函数可以设置文本的颜色。对于任何一个属性设置函数,都有相应的属性查询函数,例如,读者可通过调用GetTextColor( )函数来找到当前的文本颜色设置。

CDC类是GDI封装在MFC中的最大的一个类,它表示总的DC。表8-1列出了CDC中的一些常用函数。

第8章图形设备接口153

第2篇界面设计基础

154

MFC库从CDC类派生出了几个专用的设备描述表类,这些类如表8-2所示。

在本章中,我们将主要介绍CDC类以及它的一些成员函数,这是因为我们使用的主要是CDC类,其他的设备描述表类可以参考相关的其他书籍。

8.3 坐标映射

接下来,我们将讨论Windows 98/2000 GDI系统的另外两个方面:映射模式和视图区。

读者或许知道,Windows的文本和图形函数应用了逻辑单位,这些逻辑单位在显示图形对象时会自动被转换成物理单位(即像素)。逻辑单位如何转换成物理单位是由当前的映射模式决定的。在默认状态下,逻辑单位与像素是相同的,不过,读者可以通过改变映射模式来改变逻辑单位与物理单位之间的比例。

对于某些映射模式,除了改变映射模式外,读者还可以设置影响逻辑单位与物理单位之间转换的其他两个属性:第一可以按照逻辑单位的形式定义所选窗口的长度和宽度;第二可以设置视图区的物理范围。所谓视图区就是一个在设备空间内定义的矩形区域,视图区的大小决定逻辑单位与物理单位之间的比例。对于多数映射模式,视图区的大小是预定的,而且

第8章图形设备接口155

不能被改变。但是有两种映射模式例外,在这两种模式下,可以控制视图区的大小,也可以设置视图区的起始位置。

8.3.1 设置映射模式

前面讲过,在默认条件下,逻辑单位与像素相同,也就是说逻辑单位与像素的默认比例是1﹕1。通过改变映射模式可以改变此比例。为了设置当前的映射模式,读者可以使用SetMapMode( )函数,该函数的原型为:

int CDC:: SetMapMode (int nMapMode);

nMapMode指定新的映射模式,它可以是表8-3中任意值之一。如果SetMapMode( )函数操作成功,则返回前一种映射模式,如果函数操作失败,则返回零。

对于MM_TEXT映射模式,X轴正方向向右,Y轴正方向向下。对于MM_ANISOTPOPIC 和MM_ISOTROPIC映射模式,X轴和Y轴的正方向都是由用户定义的。对于其他映射模式,X轴正方向都向右,Y轴正方向都向上,就像正常笛卡尔坐标系一样。

默认的映射模式是MM_TEXT。如果读者想以物理单位来显示程序的输出,那么可以选择现实中的显示方式,如MM_LOMETRIC。或许当读者想为自己的程序定义最适合于被显示事物本质的单位时,或许当读者想改变被显示事物的大小时(也就是想扩大或缩小输出目标的尺寸),或许当读者想在X轴和Y轴间建立一对一的比例时,在这些情况下,每个X轴单位与每个Y轴单位就代表相同的物理距离。

8.3.2 定义窗口的范围

选择MM_ISOTROPIC或MM_ANISOTROPIC映射模式,可以以逻辑单位的形式定义窗口的尺寸。事实上,如果选取了这两种模式之一,那么就必须定义窗口的大小。因为MM_ISOTROPIC和MM_ANISOTROPIC采用用户定义的单位,所以在用户定义其大小之前,

相关文档