友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
C语言实例教程(PDF格式)-第45部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
return FALSE;
}
else
{
PaintTitleBar(FALSE);
return FALSE;
}
}
…………………………………………………………Page 525……………………………………………………………
下面补充说明一下应用程序MulticlrdCaption的创建。由于该应用程
序的结构比较简单,因此我们不打算使用AppWizard来创建框架应用
程序。这里,我们先创建一个Win32 Application工程,然后添加一
个C++ source file,在该源代码文件中输入上面的代码。这个过程
已经在本书前面的章节中使用过,因此你应该能够很轻松的完成它。
下面我们来分析这个应用程序。首先,我们在类CMyWnd中添加一个成
员函数PaintTitleBar。该成员函数用来绘制窗口的标题条,其参数
bActive给出了窗口的激活状态。如果当前窗口正处于激活状态,我
们使用从红色到绿色再到蓝色的渐变颜色来绘制应用程序的标题条,
如果当前窗口正处于非激活状态,我们使用从黑色到灰色再到黑色的
渐变色来绘制标题条。
由于我们需要通过设备上下文在窗口的非客户区(这里指窗口的标题
条区域)在进行绘制,所以我们选用了CWindowDC类。在类CWindowDC
构造函数中自动调用了GetWindowDC函数,在其析构函数中自动调用
了ReleaseDC函数。类CWindowDC的构造函数使用了一个指向CWnd对象
的指针作为其参数,通过所创建的CWindowDC对象可以在窗口的非客
户区进行图形输出。
在创建了类型为CWindowDC的设备上下文对象dc之后,我们调用API函
数GetSystemMetrics来获得当前窗口的边框高度和宽度以及标题条的
高度。这里我们指出一点,即这些度量值仅适用于具有常规样式的窗
口,对于一些特殊的窗口可能不成立,如对于工具条窗口,其标题条
高度要小得多。这是上面的应用程序的一个局限,但不可以对一个仅
用来讲解CWindowDC对象的使用的示例过于苛求,否则我们就不得不
花篇幅去介绍很多完善整个应用程序所需要的额外代码。这种对应用
程序的简化的处理方法下面还会遇到。通过使用不同的参数调用
GetSystemMetrics函数可以得到不同的系统度量。我们所使用的仅仅
是这些度量值中的很少一部分。
如果窗口是处于激活状态,我们使用参数COLOR_ACTIVECAPTION调用
API函数GetSysColor得到当前系统颜色设置中激活状态条使用的颜色
(我们不能假定用户的Windows窗口在激活时都使用标准的蓝色标题
条,因此用户可以很方便的使用控制面板或通过右击桌面选择 “属
性”来更改这些设置)。然后,我们调用在类CWindowDC的基类CDC中
定义的成员函数GetPixel来获得标题条上的每一点的颜色值,如果这
一点的颜色值等于当前使用的激活标题条颜色的话,就调用函数
SetPixelV将该点的颜色设置为新的值。这种方式不会不正确的擦除
当前标题条上的标题文本、应用程序图标以及窗口右上角最大化、最
小化和关闭按钮,但是,由于在新的Windows操作系统Windows 98 以
…………………………………………………………Page 526……………………………………………………………
及Windows NT 5。0中,标题条的颜色在默认情况下是渐变的,因此应
用程序将不能正确工作。类CDC的成员函数GetPixel返回一个
COLORREF值以指示位于指定坐标的点的颜色值。成员函数SetPixelV
将位于指定坐标的点的颜色设置为新的值,另一个成员函数SetPixel
可以完成同样的功能,并且更常用。但是,与成员函数SetPixel不
同,SetPixelV不需要返回设置的实际颜色值,因此它要比SetPixel
快。尽管如此,上面的程序仍只能作为一个示例程序出现,因为这种
一个点一个点的描绘的方法实在是太慢,在作者的具有64M内存和
K6/200的CPU的计算机上,更新一个常规大小的窗口的标题条的颜色
需要大约0。3秒的时间,在这样长的时间段内,用户还是可以清楚的
看到标题条一点一点绘制的过程。提高应用程序的绘制速度的一种方
案是使用位图来内存中对位图进行变换和处理,然而再使用位图来更
新标题条。由于位图的绘制速度要比一个点一个点的描快得多,从而
有可能大幅度的提高标题条的重绘速度,但是这种算法要使用到我们
在这里不打算深入讲述的一些概念和技巧,为了便于理解,我们还是
采用了上面给出的算法。
下面要做的事是处理两个重要的非客户区消息,WM_NCPAINT和
WM_NCACTIVATE。第一个消息WM_NCPAINT在非客户区的全部或一部分
需要重绘时由操作系统发送。如果试图给窗口绘制特殊的边框或标题
条,处理这个消息是必要的。MFC在CWnd中定义了该消息的默认处理
函数OnNcPaint,该成员函数对WM_NCPAINT的默认处理绘制了窗口的
正常边框。我们在类CMyWnd中重载了该成员函数。由于我们只是绘制
了窗口的标题条,因此在此之前有必要调用一下基类中的默认实现绘
制窗口的边框。然后,通过判断由API函数GetActiveWindow返回的窗
口句柄(它代表了当前激活窗口)和当前窗口的句柄是否相等来以不同
的参数调用PaintTitleBar成员函数来绘制窗口处于激活状态和非激
活状态的标题条。
另一个必须考虑的事件是当窗口的激活状态发生改变时正确绘制窗口
的标题条以反映窗口的新的激活状态。这时窗口将会收到
WM_NCACTIVATE消息,该消息所带的参数给出了窗口新的激活状态。
MFC在CWnd中定义了该消息的默认处理函数OnNcActivate。我们在类
CMyWnd中重载了该成员函数。该函数根据窗口新的激活状态调用了
PaintTitleBar来绘制新的窗口标题条以反映激活状态的改变。
整个应用程序使用了典型的MFC的结构,代码也比较简单和清晰,这
里我们就不多作的介绍了。你可以根据上面的源代码清单和本书前面
章节中讲述的内容来完成该应用程序。
完成上面的步骤之后我们就可以编译并试运行该应用程序了。在编译
…………………………………………………………Page 527……………………………………………………………
之前我们需要做一些额外的工作:
1。 单击Project菜单下的Settings命令或按下快捷键Alt+F7,打开如
图9。1所示的工程设置对话框。
图9。 1 设置应用程序的工程属性
2。 在General选项卡中的Microsoft Foundation Classes下拉列表框
中选择Use MFC in a Shared DLL或Use MFC in a Static Library
(仅适用于Visual C++ 5。0的专业版和企业版)。你需要对应用程序的
调试版本和发行版本各重复一次上面的设置过程。如果忽略此步设置
的话,在链接应用程序的过程中会出现错误。
现在就可以编译并运行上面的程序了。其运行结果如图9。2所示。
图9。 2 具有五彩标题条的窗口
…………………………………………………………Page 528……………………………………………………………
上面的程序还有其它的一些局限性。比如当窗口处于非激状态时,如
果使用鼠标在窗口上移动其它窗口,窗口的标题条将会变成标准的灰
色;还有,如果应用程序通过调用。要完善这些功能需要考虑更多的
问题和处理更多的消息。这并不是本书在这里引入上面的示例程序的
目的,我们只是为了演示一下CWindowDC对象的使用,而不是编写一
个功能完善的应用程序。当然,你可以使用更好和更完善的方法来实
现该应用程序并将它用于你的其它应用程序。一个特殊的标题条常常
会给程序带来一些吸引人的东西,但是过分花哨的用户界面可能会使
用户感到不适应甚至招至用户的反感。
使用CClientDC的应用程序与此类似,只不过我们通常在一些需要直
接在窗口的客户区进行绘制的场合创建和使用该设置上下文对象。比
如在一些使用鼠标绘图的应用程序中,当用户在客户区中单击鼠标
时,我们通常需要直接在客户区中绘制出相应的图形,而不必等到
WM_PAINT消息发送。对于这样的应用程序,我们一般在鼠标的移动和
单击事件的处理函数中创建类型为CClientDC的设置上下文对象,并
通过该设备上下文对象进行绘制。如果使用了AppWizard来生成MFC应
用程序的话,我们一般使用ClassWizard来完成添加这些消息处理成
员函数和相应的消息映射项。
如前所述,在WM_PAINT消息的处理函数中,我们一般不使用
CClientDC对象,而应该使用CPaintDC对象。
在下面的小节中,我们将讲述MFC中的GDI绘图对象类的使用。
第二节 画笔对象
MFC类CPen封装了GDI中的画笔对象,画笔对象代表了进行绘制时所用
的线条。我们一般通过两个步骤来创建画笔对象:首先构造一个CPen
对象,再调用对象的CreatePen成员函数。成员函数CreatePen按指定
的样式、宽度等属性创建一个逻辑画笔,然后将该画笔与CPen对象相
关联。
9。2。1 创建画笔
成员函数CreatePen有两种形式。第一种形式的如下:
BOOL CreatePen( int nPenStyle; int nWidth; COLORREF crColor );
参数nPenStyle代表了画笔的样式,可以为下列值之一:
…………………………………………………………Page 529……………………………………………………………
PS_SOLID: 创建一个实线画笔
PS_DASH: 创建一个虚线画笔。一个虚线画笔
的宽度不能超过一个设备单位。
PS_DOT: 创建一个点线画笔。一个点划线画
笔的宽度不能超过一个设备单位。
PS_DASHDOT: 创建一个点划线画笔。同样,这种
样式的画笔宽度也不能超过一个设
备单位。
PS_DASHDOTDOT: 创建一个双点划线画笔。这种样式
的画笔宽度也不能超过一个设备单
位
PS_NULL: 创建一个空画笔
PS_INSIDEFRAME: 对于那些指定一个边界矩形的GDI输
出函数,具有这种样式的画笔将线
条绘制到输出形状框架的内侧。而
对于那些没有指定边界矩形的GDI输
出函数,这种画笔的绘制区域则不
受框架的限制。
参数nWidth以逻辑单位给出画笔的宽度。如果参数nWidth为零,则无
论当前使用何种映射模式,所创建和画笔的宽度都为一个象素。参数
crColor为画笔的颜色,这里可以使用RGB宏来生成合适的颜色值。
另一种形式的CreatePen函数使用如下的参数:
BOOL CreatePen( int nPenStyle; int nWidth; const LOGBRUSH* pLogBrush;
int nStyleCount = 0; const DWORD* lpStyle = NULL );
这种形式的CreatePen可以创建一个具有指定的宽度、样式和刷子属
性的逻辑修饰 (cosmetic)或几何 (geometric)画笔。参数nPenStyle指
定了画笔的样式,它可以为PS_COSMETIC、PS_GEOMETRIC或它们与一
些附加属性的组合,详细的说明这里不进行说明,如果需要的话你可
以参考联机文档中对构造函数CPen::CPen的说明。nWidth以逻辑单位
指定画笔的宽度,如果nPenStyle参数包括了PS_COSMETIC的话,参数
nWidth必须为1。参数pLogBrush为指向一个LOGBRUSH结构的指针,该
LOGBRUSH结构定义了画笔的刷子属性。最末两个参数nStyleCount和
…………………………………………………………Page 530……………………………………………………………
lpStyle定义了画笔的每一划及它们之间的空白的长度。
画笔对象实际上也可以一步创建,这时所使用的构造函数也使用与函
数CreatePen相一致的参数。以使用一步创建的方式创建画笔对象
时,我们通过捕获一个异常是否发生来判断是否出错。
9。2。2 使用画笔在设备上下文中进行输出
一旦画笔对象创建成功之后,即可使用CDC类的成员函数
SelectObject将其选入设备描述表中进行各种输出。
下面的示例程序PenDemo演示了画笔对象的使用。
创建工程PenDemo的方法同在9。1。2中引入的示例程序
MulticlrdCaption相同。您可以参照上一节的讲述来创建工程
PenDemo。其代码清单如下:
#include
#include
#include
// 派生应用程序类
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// 派生窗口类
class CMyWnd : public CFrameWnd
{
protected:
// 声明消息处理函数
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP();
…………………………………………………………Page 531……………………………………………………………
};
// 初始化应用程序实例
BOOL CMyApp::InitInstance()
{
// 创建应用程序的主窗口
CMyWnd *pWnd=new CMyWnd;
pWnd…》Create(NULL; 〃各种画笔的示例〃);
// 显示应用程序主窗口并刷新其客户区
pWnd…》ShowWindow(SW_SHOW);
pWnd…》UpdateWindow();
// 在主窗口关闭时终止应用程序的执行线程
m_pMainWnd=pWnd;
return TRUE;
}
// 声明唯一的应用程序对象
CMyApp MyApp;
// 应用程序主窗口的消息映射
BEGIN_MESSAGE_MAP(CMyWnd; CWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
// 应用程序主窗口的重绘函数
void CMyWnd::OnPaint()
{
// 获得窗口的客户区设备上下文句柄
CPaintDC dc(this);
// 定义一个画笔数组
…………………………………………………………Page 532……………………………………………………………
CPen pen'8';
// 创建实线画笔
pen'0'。CreatePen(PS_SOLID; 10; RGB(255; 0; 0));
// 创建虚线画笔
pen'1'。CreatePen(PS_DASH; 1; RGB(0; 255; 0));
// 创建点线画笔
pen'2'。CreatePen(PS_DOT; 1; RGB(0; 0; 255));
// 创建点划线画笔
pen'3'。CreatePen(PS_DASHDOT; 1; RGB(0; 255; 255));
// 创建双点划线画笔
pen'4'。CreatePen(PS_DASHDOTDOT; 1; RGB(255; 0; 255));
// 创建空画笔
pen'5'。CreatePen(PS_NULL; 1; RGB(255; 255; 0));
// 创建内侧实线画笔
pen'6'。CreatePen(PS_INSIDEFRAME; 10; RGB(0; 0; 0));
// 创建具有刷子属性的几何画笔
LOGBRUSH lb;
lb。lbStyle=BS_HATCHED;
lb。lbColor=RGB(128; 128; 128);
lb。lbHatch=HS_DIAGCROSS;
pen'7'。CreatePen(PS_GEOMETRIC; 10; &lb);
// 保存指向设备上下文原有画笔的指针
CPen *pOldPen;
// 以实线画笔绘制矩形
pOldPen=dc。SelectObject(&pen'0');
dc。Rectangle(10; 10; 110; 110);
…………………………………………………………Page 533……………………………………………………………
// 以虚线画笔绘制矩形
dc。SelectObject(&pen'1');
dc。Rectangle(130; 10; 230; 110);
// 以点线画笔绘制矩形
dc。SelectObject(&pen'2');
dc。Rectangle(250; 10; 350; 110);
// 以点划线画笔绘制矩形
dc。SelectObject(&pen'3');
dc。Rectangle(370; 10; 470; 110);
// 以双点划线画笔绘制矩形
dc。SelectObject(&pen'4');
dc。Rectangle(10; 130; 110; 230);
// 以空画笔绘制矩形,因此该矩形不会被显示出来
dc。SelectObject(&pen'5');
dc。Rectangle(130; 130; 230; 230);
// 以内侧实线画笔绘制矩形,因此该矩形比使用实线画笔绘制的矩形看起来要小一些
dc。SelectObject(&pen'6');
dc。Rectangle(250; 130; 350; 230);
// 以具有刷子属性的几何画笔绘制矩形
dc。SelectObject(&pen'7');
dc。Rectangle(370; 130; 470; 230);
// 恢复设备上下文的原有画笔
dc。SelectObject(pOldPen);
// 删除所创建的画笔资源
for (int i=0; i
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!