当前位置:文档之家› 关于windows编程技术10GDI编程3—动画

关于windows编程技术10GDI编程3—动画

关于windows编程技术10GDI编程3—动画
关于windows编程技术10GDI编程3—动画

第10章 GDI编程3-动画

动画是利用人的视觉滞留缺陷(25ms~400ms)和心理认可来动态生成系列相关画面以产生运动视觉的技术。位图动画是将预先制作好的一系列表示连续画面的位图,按一定的时间间隔一幅接一幅地连续显示,从而产生动画效果。

因为绘制动画所需的图形,以及拍摄和处理图片,需要美术、摄影、数字图像处理、动画设计等知识,我们这里不讲。本书只介绍如何显示已有的位图(序列)以产生动画效果,以及如何动态绘制不同的简单图形以产生二维图形动画等。

用GDI编程实现动画,一般需要用到计时器(Timer)操作,通常在计时器响应函数OnTimer中(而不要使用OnDraw)绘图来实现动画。

10.1 固定位图动画

本节介绍利用一系列的位图资源,在同一个屏幕位置,接连显示位图序列,以达到动画的效果的具体方法。为此,可在交互绘图程序中添加一个如图10-1所示的位图动画对话框,并添加对应的对话框类CDukeDlg。也可以创建一个基于对话框的独立的MFC应用程序。

图10-1 位图动画对话框资源

当然还需添加相应的“位图动画”菜单项(ID_DUKE)和(为视图类添加)对应的菜单响应函数,并在该函数中创建对话框类的对象,打开对话框来运行动画:

#include "DukeDlg.h"

void CDrawView::OnDuke() {

CDukeDlg dlg;

dlg.DoModal();

}

10.1.1 准备位图、加入位图资源

系列公爵(Duke)BMP文件T1.BMP ~ T10.BMP(见图10-2),来自Java吉祥物的GIF动画文件,可存放在项目的res子目录的Duke子目录中(该位图资源已经打包成Duke.rar文件后,放到了系里的网站和我的个人网页上)。

T1.BMP T2.BMP T3.BMP T4.BMP T5.BMP

T6.BMP T7.BMP T8.BMP T9.BMP T10.BMP

图10-2 Duke位图文件

用VC的资源编辑器依次加入位图文件:在左边的项目工作区中选“资源视图”页,展开项目的资源列表,在“Bitmap”表项(若无此项,则可直接在项目资源项)上单击鼠标右键,在弹出的浮动菜单中选“添加资源”菜单项,在打开的“添加资源”对话框中,选中左边“资源类型”栏中的“Bitmap”表项,单击右边的“导入”按钮,在弹出的“导入”文件对话框中,定位Duke目录,选中所有Ti.BMP后按“打开”钮,则会自动加入ID为IDB_BITMAPi 的位图资源。

为了以后循环编程的方便,必须保证是从T1.BMP到T10.BMP顺序依次加入。为了确认,可打开头文件Resource.h查看,若其中的常量IDB_BITMAPi的定义数值不连续,可作一些手工修改使其连续,例如:#define IDB_BITMAP1 131

#define IDB_BITMAP2 132

#define IDB_BITMAP3 133

#define IDB_BITMAP4 134

#define IDB_BITMAP5 135

#define IDB_BITMAP6 136

#define IDB_BITMAP7 137

#define IDB_BITMAP8 138

#define IDB_BITMAP9 139

#define IDB_BITMAP10 140

10.1.2 添加控件、创建对话框类

为对话框资源添加图片控件:打开对话框资源,在控件工具箱中选图片控件(Picture Control)工具,在对话框

的适当位置添加图片控件,设置其“ID”属性值为“IDC_ANI”,修改“Type”属性为(在其下拉式列表框中选中)“Bitmap”,再在“Image”属性的下拉式列表框中选中“IDB_BITMAP1”位图资源,则该位图绘显示在图片控件中。

为了控制动画的播放,需要添加一个即可表示开始动画又可表示停止动画的按钮(初始标题为“开始动画”),可设置其ID为“IDC_ANI_START_STOP”。

为了让用户选择动画的速度,可以添加静态文本提示框“每秒帧数:”和文本编辑框(IDC_N),在后面的10.1.6小节中还会添加滑块控件(Slider Control,IDC_SLIDER_N)。

创建该对话框资源所对应的对话框类CDukeDlg。

10.1.3 添加类变量、装入与删除位图

在对话框类的定义(头文件)中添加若干类变量:

CBitmap *m_pBmp[10]; // 位图指针数组

BITMAP m_bs; // 位图结构变量

bool m_bStarted; // 判别动画是否启动(初始化为false)

int m_nCurFrame, // 当前帧号(初值为0)

m_nFramesPerSecond; // 每秒帧数(初值为10)

为CDukeDlg类添加(重写型)消息响应函数OnInitDialog,在该函数中(也可以在构造函数中)创建位图对象并装入位图资源,然后获取位图结构(其中的位图宽和高用于BitBlt函数),并初始化其他类变量,最后设置编辑控件的初值(粗体部分为新加的):

BOOL CDukeDlg::OnInitDialog()

{

CDialog::OnInitDialog();

// TODO: 在此添加额外的初始化

for (int i = 0; i < 10; i++) { // 装入位图资源

m_pBmp[i] = new CBitmap;

m_pBmp[i]->LoadBitmap(IDB_BITMAP1 + i);

}

m_pBmp[0]->GetBitmap(&m_bs); //获取位图结构

m_bStarted = false; // 设置已开始动画为假

m_nCurFrame = 0; // 设置初始的当前帧为0

m_nFramesPerSecond = 10; // 设置初始动画速度为每秒10帧

SetDlgItemInt(IDC_N, m_nFramesPerSecond); // 设置编辑框初值

return TRUE; // return TRUE unless you set the focus to a control

// 异常: OCX 属性页应返回 FALSE

}

其中,语句m_pBmp[i]->LoadBitmap(IDB_BITMAP1 + i);中的IDB_BITMAP1 + i用到了Duke位图资源ID的连续性。

为了避免内存泄漏,还需要为对话框类添加析构函数,并在该函数中删除位图对象:

CDukeDlg::~CDukeDlg()

{

for (int i = 0; i < 10; i++) delete m_pBmp[i];

}

10.1.4 启动/停止动画(设置/删除计时器)

为按钮IDC_ANI_START_STOP添加单击消息响应函数:

void CDukeDlg::OnBnClickedAniStartstop() {

// TODO: 在此添加控件通知处理程序代码

if (m_bStarted) { // 已开始动画

m_bStarted = false; // 设置已开始动画为假

KillTimer(1); // 取消计时器

// 设置按钮文本为“开始动画”

SetDlgItemText(IDC_ANI_START_STOP, L"开始动画");

}

else { // 未开始动画

m_bStarted = true; // 设置已开始动画为真

m_nCurFrame = 0; // // 设置当前帧为0

// 获取编辑框中的帧速值

m_nFramesPerSecond = GetDlgItemInt(IDC_N);

if (m_nFramesPerSecond <= 0) // 限制最小帧速值为1

m_nFramesPerSecond = 1;

else if (m_nFramesPerSecond > 100) // 最大帧速值为100

m_nFramesPerSecond = 100;

// 用调整后帧速值重新设置编辑框的内容

SetDlgItemInt(IDC_N, m_nFramesPerSecond);

// 利用帧速值来设置计时器

SetTimer(1, (UINT)(1000.0/m_nFramesPerSecond + 0.5), NULL);

// 设置按钮文本为“停止动画”

SetDlgItemText(IDC_ANI_START_STOP, L"停止动画");

}

}

其中:

●m_bStarted为布尔型类变量,用于判断动画是否已经开始播放。在构造函数中初始化为假,在用户按了开

始/停止动画按钮后取反。

●m_nCurFrame为整数型类变量,用于记录当前所要显示的位图序号,初始化为0。

●m_nFramesPerSecond也为整数型类变量,用于记录当前的每秒帧数(帧速值,范围可设为1~100)。

●SetDlgItemText函数,用于动态修改按钮上的文本。

●SetTimer用于设置计时器,它是CWnd的成员函数(所以也可在其派生的视图类和对话框类中使用),其

函数原型为:

UINT SetTimer( UINT nIDEvent, UINT nElapse,

void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );

?nIDEvent为此计时器的编号。因为一个应用程序可以设置多个计时器,为了在响应时区分它们,必须

各有一个编号。简单程序的计时器一般只有一个,所以取nIDEvent = 1即可。

?nElapse为间隔时间,单位为毫秒(1/1000秒)。可用表达式(UINT) (1000.0 / m_nFramesPerSecond + 0.5),

从每秒帧数计算出间隔时间的毫秒数。

?lpfnTimer为应用程序提供的处理WM_TIMER消息的回调函数,一般取为NULL,这时WM_TIMER

消息由CWnd派生类的对应消息响应函数来处理。

●KillTimer用于删除计时器,它也是CWnd的成员函数,其函数原型为:

BOOL KillTimer( int nIDEvent );

在设置了计时器后,系统会按指定的时间间隔发送WM_TIMER消息给应用程序窗口,程序员可在计时器消息响应函数OnTimer中作需要的处理,在本程序中是显示当前位图。

10.1.5 绘制动画(响应计时器消息)

为CDukeDlg类的WM_TIMER消息添加响应函数:

void CDukeDlg::OnTimer(UINT nIDEvent) {

// TODO: 在此添加消息处理程序代码和/或调用默认值

CDC *pDC = GetDlgItem(IDC_ANI)->GetDC(); // 获取图片框DC

CDC dc; // 定义内存DC

dc.CreateCompatibleDC(pDC); // 创建兼容DC

dc.SelectObject(m_pBmp[m_nCurFrame]); // 选入当前帧位图

pDC->BitBlt(0, 0, m_bs.bmWidth, m_bs.bmHeight, &dc,

0, 0, SRCCOPY); // 显示当前帧位图

m_nCurFrame++; // 当前帧加一(设置下一次要显示的位图序号)

m_nCurFrame %= 10; // 当前帧余10(避免超出位图数组,实现循环)

CDialog::OnTimer(nIDEvent);

}

结果如图10-3所示。

图10-3 Duke动画

10.1.6 滑块控件的使用

为了使用户能够利用鼠标快速修改每秒帧数的值,我们在对话框中添加了一个滑块控件(Slider Control)(可将其ID值设为“IDC_SLIDER_N”)。对应的MFC类为CSliderCtrl,它是直接从CWnd派生的类:

CObject→CCmdTarget→CWnd→CSliderCtrl。

可以在对话框类中定义一个CSliderCtrl的类指针变量:

CSliderCtrl *m_pSlider;

然后在对话框类的初始化函数中获得滑块控件对象的指针,并设置其取值范围为1~100,再设置其滑块的当前位置(注意:粗体代码必需位于SetDlgItemInt之前):

BOOL CDukeDlg::OnInitDialog() {

CDialog::OnInitDialog();

// TODO: 在此添加额外的初始化

……

m_pSlider = (CSliderCtrl *)GetDlgItem(IDC_SLIDER_N);

m_pSlider->SetRange(1, 100);

m_pSlider->SetPos(m_nFramesPerSecond);

SetDlgItemInt(IDC_N, m_nFramesPerSecond);

……

}

为了能在用户移动滑块时,动态修改编辑控件中的值,需要为对话框类添加水平滚动消息(WM_HSCROLL)的响应函数OnHScroll:

void CDukeDlg::OnHScroll(UINT nSBCode, UINT nPos,

CScrollBar* pScrollBar) {

// TODO: 在此添加消息处理程序代码和/或调用默认值

SetDlgItemInt(IDC_N, m_pSlider->GetPos());

CDialog::OnHScroll(nSBCode, nPos, pScrollBar);

}

同样,为了在用户修改编辑控件中的值时,动态改变滑块的位置,还需要为对话框类添加编辑控件的EN_CHANGE消息响应函数:

void CDukeDlg::OnEnChangeN() {

……

// TODO: 在此添加控件通知处理程序代码

m_pSlider->SetPos(GetDlgItemInt(IDC_N));

}

注意,如果滑块初始化的代码位于SetDlgItemInt之后,则会造成此语句中的m_pSlider指针无效。

10.1.7 CImageList类

上面的位图动画,也可以使用MFC的图像列表类CImageList来实现,代码会更简单一些。CImageList类是CObject的直接派生类:

CObject →CImageList

1.成员函数

CImageList类的常用成员函数有:

构造函数:CImageList( );

●创建函数:BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow );

●添加函数:int Add( CBitmap* pbmImage, COLORREF crMask );

●绘制函数:BOOL Draw( CDC* pDC, int nImage, POINT pt, UINT nStyle );

2.例子

可在动画对话框类中添加如下代码:

// 类变量(头文件)

bool m_bStarted;

int m_nCurFrame, m_nFramesPerSecond;

CSliderCtrl *m_pSlider;

CImageList imgList;

// 初始化(OnInitDialog函数)

m_bStarted = false;

m_nCurFrame = 0;

m_nFramesPerSecond = 10; // 设置初始动画速度为每秒10帧

m_pSlider = (CSliderCtrl *)GetDlgItem(IDC_SLIDER_N);

m_pSlider->SetRange(1, 100);

m_pSlider->SetPos(m_nFramesPerSecond);

SetDlgItemInt(IDC_N, m_nFramesPerSecond);

BITMAP bs;

CBitmap bmp;

bmp.LoadBitmap(IDB_BITMAP1);

bmp.GetBitmap(&bs);

imgList.Create(bs.bmWidth, bs.bmHeight, ILC_COLOR8, 10, 0);

imgList.Add(&bmp, RGB(0, 0, 0));

for (int i = 1; i < 10; i++) {

bmp.DeleteObject();

bmp.LoadBitmap(IDB_BITMAP1 + i);

imgList.Add(&bmp, RGB(0, 0, 0));

}

bmp.DeleteObject();

// 绘图(OnTimer函数)

CDC *pDC = GetDlgItem(IDC_ANI)->GetDC();

imgList.Draw(pDC, m_nCurFrame, CPoint(0,0), ILD_NORMAL);

m_nCurFrame++; m_nCurFrame %= 10;

// OnBnClickedAniStartStop、OnHScroll和OnEnChangeN函数同前10.1.8 过程框图

下面分别给出使用位图数组和CImageList类来实现固定位图动画的主要过程框图。1.使用位图数组

图10-4是使用位图数组实现固定位图动画的主要过程框图。

图10-4 使用位图数组实现固定位图动画的主要步骤

2.使用图像列表

图10-5是使用CImageList类实现固定位图动画的主要过程框图。

图10-5 使用CImageList类实现固定位图动画的主要步骤

10.2 图形动画

在前面(参见8.5.3小节)利用鼠标进行交互绘图时,我们就已经实现了简单的图形动画——动态画直线、矩形或椭圆等,用户可通过鼠标对绘图位置坐标和图形大小进行交互式选择。具体做法是,用灰色点线笔在同一个位置异或画两次一样的图形——第一次画图,第二次擦除。快速不断地在不同的地方画擦,就达到了动画的效果。即图形动画的原理就是边擦边画。

如果要画的不再是简单的线状图形,而是复杂的面状图,则异或画图方法就不再有效。因为异或会大大改变窗口中原有图形的色彩,这是用户所不能容忍的。

可用的解决方法是,擦除(或保存)要绘图的区域,然后再绘制新图形(并恢复原区域的图形)。具体的实现方法有两种——直接绘图和缓冲绘图。

10.2.1 直接绘图

利用直接绘图方法,来产生动画的原理很简单,但是会存在讨厌的闪烁现象。

1.原理

通过不断地擦除(要绘图的区域)和绘制(新图形)动态图形而产生动画效果。

可以使用CDC类的画填充矩形的函数(使用白色刷):

void FillRect(LPCRECT lpRect, CBrush* pBrush);

来擦除指定矩形区域。例如:

pDC->FillRect(&rect, new CBrush(RGB(255, 255, 255)));

然后,再在该矩形区域内绘制新图形。例如:

pDC->SelectObject(&pen); // 选入画边框的笔

pDC->SelectObject(&brush); // 选入画填充色的刷

pDC->Ellipse(&rect); // 绘制填充椭圆

2.例子

下面是一个在白色背景上动态画伸缩填充椭圆的例子,需要创建一个传统单文档MFC应用程序。主要代码片段如下:

1)在视图类中定义若干类变量:

bool shrink; // 用于判断伸缩

int r, w, h, // 当前椭圆的短轴半径和宽高

R, W, H, // 最大椭圆的短轴半径和宽高

xc, yc; // 椭圆的中心坐标

CPen pen; // 绘制椭圆边框的笔(与刷同色)

CBrush brush, whiteBrush; // 绘制椭圆内部的刷和删除原椭圆的白刷2)在视图类的构造函数中,设置初值、构造笔和刷:

shrink = true; // 初始为缩小

COLORREF greenCol = RGB(0, 150, 0), //定义绿色

whiteCol = RGB(255, 255, 255); //定义白色

pen.CreatePen(0, 0, greenCol ); // 实心单像素宽的绿色笔

brush.CreateSolidBrush(greenCol ); // 实心绿色刷

whiteBrush.CreateSolidBrush(whiteCol); // 实心白色刷

3)在某个菜单项的事件处理函数中计算并设置初值、启动计时器:CRect rect; GetClientRect(&rect); // 获取当前客户区矩形

W = rect.Width(); H = rect.Height();

r = R = min(W, H) / 2; // 初始为最大椭圆

w = W / 2; h = H / 2;

xc = W / 2; yc = H /2;

SetTimer(1, 10, NULL); // 可设置不同的时间间隔,或者让用户来设置4)在计时器的消息响应函数OnTimer中,擦除并绘制椭圆,调整半径:CDC *pDC = GetDC();

// 擦除

if (shrink) { // 对膨胀不需要擦除

CRect rect(xc - w, yc - h, xc + w, yc + h);

pDC->FillRect(rect, &whiteBrush);

}

// 调整半径

if (shrink) {

w--; h--; r--; // 缩小1像素

if (r == 0) shrink = false; // 转换成放大

} else {

w++; h++; r++; // 放大1像素

if (r == R) shrink = true; // 转换成缩小

}

// 绘制填充椭圆

pDC->SelectObject(&pen);

pDC->SelectObject(&brush);

pDC->Ellipse(xc - w, yc - h, xc + w, yc + h);

ReleaseDC(pDC);

运行结果如图10-6所示。

图10-6 伸缩填充椭圆

运行该程序后会发现,存在明显的闪烁现象,这主要是由收缩时的擦除操作所造成的。解决办法是,采用内存DC进行缓冲绘图。

10.2.2 缓冲绘图

在前面资源位图动画的绘制过程中,我们已经采用了缓冲(buffering)方法来显示位图:

CDC *pDC = GetDC();

CDC dc;

dc.CreateCompatibleDC(pDC);

dc.SelectObject(m_pBmp[m_nCurFrame]);

pDC->BitBlt(0, 0, bs.bmWidth, bs.bmHeight, &dc, 0, 0, SRCCOPY);

其中,起主要作用的是内存DC和CDC类的位块传送函数BitBlt,而且在该位图动画中并没有出现闪烁现象。

1.原理

下面我们将这种方法加以扩展,不仅使其可用于已有位图的绘制,还可以用于普通图形的动态绘制。这需要先创建一个与当前视图DC兼容的空位图对象,并将其作为画布选入内存DC中,然后对该内存DC进行各种图形绘制,最后再用同样的BitBlt函数将绘图结果传送到屏幕上。

具体步骤为:

●获取当前视图DC——采用视图类CView基类CWnd的成员函数:CDC* GetDC( );

●创建与当前视图DC兼容的位图对象——分两步进行,先用CBitmap类的缺省构造函数:CBitmap( ); 构造

空位图对象,再利用成员函数:

BOOL CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight);

创建指定宽高(一般为当前客户区大小)的与当前DC兼容的位图对象。

●创建与当前视图DC兼容的内存DC——也分两步进行,也是先用CDC类的缺省构造函数:CDC( ); 构造

空DC对象,再利用成员函数:

BOOL CreateCompatibleDC(CDC* pDC);

创建与当前DC兼容的DC对象。

●选位图对象入内存DC——利用CDC类的成员函数:

CBitmap* SelectObject(CBitmap* pBitmap);

将所创建的空白位图对象选入内存DC中。

●内存DC绘图——用该内存DC,调用各种CDC的绘图成员函数,在内存DC中的位图上进行绘图。包括

绘制白色客户区矩形,进行白色背景设置(缺省为黑色背景)。

●绘制屏幕——利用CDC类的位块传送函数:

BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC,

int xSrc, int ySrc, DWORD dwRop );

将内存DC中,已经被绘制好图形的位图对象,传送到屏幕上。

其中,与前面显示位图资源的方法最大的不同是,需要先创建一个与当前视图DC兼容的空位图对象,并将其作为画布选入内存DC中,然后才能对该内存DC进行各种图形绘制。注意,在没有选入位图对象的内存DC中直接绘图是无效的。

2.例子

1)简单的例子:

CDC memDC; // 内存DC

memDC.CreateCompatibleDC(pDC); // 创建与当前视图DC兼容的DC

CRect rect; // 矩形对象,用于表示客户区

GetClientRect(&rect); // 获取客户区矩形

CBitmap bmp; // 位图对象,作为画布,用于创建可传送的兼容DC

// 创建大小与客户区一致并且与视图DC兼容的位图对象

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

// 将位图选入内存DC,使其可以进行各种绘图操作,并能够传递到屏幕

CBitmap *pOldBmp = memDC.SelectObject(&bmp);

// 用白色填充背景(缺省为黑色)

CBrush brush(RGB(255, 255, 255));

memDC.FillRect(&rect, &brush);

// 绘制各种图形

memDC.Rectangle(10, 10, 200, 150);

// ……

// 将内存DC中的图形传送到屏幕(在视图客户区绘图)

pDC->BitBlt(0, 0, w, h, &memDC, 0, 0, SRCCOPY);

2)伸缩填充椭圆例

现在仍然以伸缩填充椭圆为例,说明缓冲绘图产生动画的方法。程序的结构同1)中的例,下面主要介绍不同之处(可以在原程序的基础上进行修改)。

(1)在视图类中增加若干类变量定义:

CDC memDC; // 空内存DC对象

CBitmap bmp, *pOldBmp; // 空位图对象和老位图对象指针

bool created; // 是否创建了内存DC和位图对象

(2)在视图类的构造函数中,设置初值:

created = false; // 初始为未创建

(3)在某个菜单项的事件处理函数(也可以在OnSize信息响应函数)中,对内存DC和位图对象进行初始化:CDC *pDC = GetDC();

if (!created) memDC.CreateCompatibleDC(pDC);

……

// created为真时,需先删除原位图对象,才能创建新位图对象

if (created) {

memDC.SelectObject(pOldBmp);

bmp.DeleteObject();

}

bmp.CreateCompatibleBitmap(pDC, W, H); // W和H可能已经变化

pOldBmp = memDC.SelectObject(&bmp);

memDC.FillRect(&rect, &whiteBrush);

created = true; shrink = true;

xc = W / 2; yc = H /2;

SetTimer(1, 10, NULL); // 可以设置不同的时间间隔

(4)在计时器的消息响应函数OnTimer中,擦除并绘制椭圆,调整半径:void CAniView::OnTimer(UINT_PTR nIDEvent)

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

CDC *pDC = GetDC();

// 擦除原椭圆(用白色填充客户区)

if (shrink) { // 对膨胀不需要擦除

// 擦除原椭圆(用白色填充客户区)

CRect rect;

GetClientRect(&rect); // 获取当前客户区矩形

CBrush brush(RGB(255, 255, 255));

memDC.FillRect(&rect, &brush);

}

// 调整半径

……

// 绘制新椭圆

memDC.SelectObject(&pen);

memDC.SelectObject(&brush);

memDC.Ellipse(xc - w, yc - h, xc + w, yc + h);

// 将内存DC中的图形传送到屏幕(在视图客户区绘图)

pDC->BitBlt(0, 0, W, H, &memDC, 0, 0, SRCCOPY);

ReleaseDC(pDC);

CView::OnTimer(nIDEvent);

}

(5)可以添加一个停止动画的菜单项(ID_KT)及其事件处理函数:void CAniView::OnKt()

{

// TODO: 在此添加命令处理程序代码

KillTimer(1); // 删除计时器

}

运行结果类似图10-6的,但是已经没有闪烁了。

3.过程框图

图10-7是缓冲绘制图形动画的主要过程框图(粗体为主要步骤):

图10-7 缓冲绘制图形动画的主要步骤

10.3 移动位图动画

前面的固定(资源)位图动画,是在屏幕的同一位置,连续显示多帧不同的图像,产生动画效果。现在我们要讨论的是,同一图像在屏幕的不同位置显示,产生移动动画的效果。这里被移动的图像,可以是资源位图或用户装入的图像、也可以是从屏幕的指定矩形区域中获取的截图、还可以是程序在内存DC中绘制图形所生成的图像。

下面介绍在背景图上移动图像块以产生动画效果的具体方法。我们先讨论原理,再画出过程图,最后给出一个移动足球的具体例子。

10.3.1 原理

与图形动画一样,移动位图动画原理也是边擦边画。不过,为了不破坏原有的背景,即不能采用异或画图,也不能用白色覆盖来擦除。为了实现图像块在背景图上移动,非常重要的一点是,必须预先保存将会被移动图像所覆盖的背景图上的对应矩形区域,并在图像移走后再恢复该区域的图形,参见图10-8。这一功能可由含图像的内存DC和位块传送函数来完成。另外,为了避免会产生严重的闪烁现象,必须采用缓冲绘图方法。

图10-8 移动位图动画的基本原理

还有一点需要考虑的是,被移动的图形一般不是矩形,直接绘制会产生难看的背景遮挡,例如,在9.3.3的随机绘制足球和单击定位图像块例子中(参见图9-23和图9-24),就存在难看的足球四角上的白色遮挡现象。

为了将矩形块中的背景部分去掉,可以将矩形中的背景区,用一种图形中没有的颜色来统一着色,并将该色指定为透明色,再利用CDC 类或CImage 类的透明位块传送成员函数TransparentBlt 来进行绘制操作。函数原型为:

CDC 类:BOOL TransparentBlt (int xDest, int yDest, int nDestWidth, int nDestHeight, CDC* pSrcDC, int xSrc, int

ySrc, int nSrcWidth, int nSrcHeight, UINT clrTransparent );

CImage 类:BOOL TransparentBlt (HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight, UINT

crTransparent = CLR_INV ALID) const throw( );

透明位块传送函数在传送的同时,可以进行缩放。其最后一个输入参数最关键——clrTransparent 为指定的透明颜色(UINT 类型与COLORREF 所对应的DWORD 类型是等价的,都是无符号的4字节整数)。

例如(参见图10-9):

COLORREF transpCol = RGB(255, 0, 0); // 设置透明色

memDC.TransparentBlt(x0, y0, w, h, &dc, 0, 0, w, h, transpCol);

白色背景

红色背景

图10-9 足球图片

10.3.2 过程框图

图9-10是利用CBitmap 类实现移动位图主要过程的逻辑框图。

图10-10 移动位图的主要步骤

10.3.3 例子

下面是一个单击和拖动鼠标来在一背景图上移动足球的例子:

1.创建项目、添加资源

创建一个传统的单文档MFC应用程序,加入红色背景的足球资源(IDB_FOOTBALL_RED)。

2.添加类变量

在视图类中定义若干类变量:

// #include "atlimage.h"

int W, H, w, h, x0, y0; // W 和H为客户区的宽高、w和h为图像块的

// 宽高、x0和y0为图像块上次位置的坐标COLORREF transpCol; // 透明色

CDC memDC, dc, dc0; // memDC:客户区、dc:图像块、dc0:背景块

CBitmap memBmp, bmp, bmp0, *pOldBmp; // memBmp用于客户区、

// bmp用于图像块、bmp0用于背景块、pOldBmp用于选出位图CImage imgbk; // 用于底图

3.初始化

在初始化函数OnInitialUpdate中装入图像、计算参数、创建图像和内存DC对象:// 装入图像-------------------------------------------------

transpCol = RGB(255, 0, 0); // 设置透明色

// 装入res目录中的底图(在IDE中运行时,当前目录为项目所在目录)

imgbk.Load(L"res\\tulips.tif");

// 如果直接在Release或Debug目录中运行,则需要修改文件路径为:

if(imgbk.IsNull()) imgbk.Load(L"..\\res\\tulips.tif");

// 如果图片文件位于可执行程序所在目录,则可修改文件路径为:

if(imgbk.IsNull()) imgbk.Load(L"tulips.tif");

bmp.LoadBitmap(IDB_FOOTBALL_RED); // 装入图像块资源(红色背景足球)// 创建图像块对应的内存DC,并选入图像块------------------------

CDC *pDC = GetDC(); // 获取视图DC

dc.CreateCompatibleDC(pDC);

pOldBmp = dc.SelectObject(&bmp);

// 获取图像块的宽高-----------------------------------------

BITMAP bm;

bmp.GetBitmap(&bm);

w = bm.bmWidth;

h = bm.bmHeight;

// 创建背景块位图和内存DC,选入图像---------------------------- bmp0.CreateCompatibleBitmap(pDC, w, h);

dc0.CreateCompatibleDC(pDC);

pOldBmp = dc0.SelectObject(&bmp0);

// 获取客户区大小------------------------------------------- CRect rect;

GetClientRect(&rect);

W = rect.Width();

H = rect.Height();

// 创建与客户区对应的图像和内存DC对象,选入图像,绘制白色背景----- // 创建与视图DC兼容并与客户区大小一致的位图对象

memBmp.CreateCompatibleBitmap(pDC, W, H);

memDC.CreateCompatibleDC(pDC); // 创建与视图DC兼容的内存DC对象pOldBmp = memDC.SelectObject(&memBmp); // 选位图对象入内存DC // 填充白背景色(缺省为黑色)

//memDC.FillRect(&rect, new CBrush(RGB(255, 255, 255));

// 计算位于客户区中央的图像块的左上角坐标----------------------- x0 = W / 2 - w / 2;

y0 = H / 2 - h / 2;

4.绘制底图和初始图块

在OnDraw函数中,先绘制底图,再在客户区中央绘制首个图像块:

RECT rect; GetClientRect(&rect); // 获取当前客户区矩形

if(!imgbk.IsNull())// 绘制底图

imgbk.Draw(pDC->m_hDC, rect);

// 利用位块传送将背景块复制到内存DC

dc0.BitBlt(0, 0, w, h, pDC, x0, y0, SRCCOPY);

// 将客户区复制到与客户区对应的内存DC

memDC.BitBlt(0, 0, W, H, pDC, 0, 0, SRCCOPY);

// 绘制图像块到与客户区对应的内存DC

memDC.TransparentBlt(x0, y0, w, h, &dc, 0, 0, w, h, transpCol);

// 将与客户区对应的内存DC中的图像,传送到屏幕上

pDC->BitBlt(0, 0, W, H, &memDC, 0, 0, SRCCOPY);

5.单击移动图块

在松开左鼠标键的消息响应函数OnLButtonUp中恢复背景块并在当前位置绘制图像块:CDC *pDC = GetDC(); // 获取视图DC

memDC.BitBlt(x0, y0, w, h, &dc0, 0, 0, SRCCOPY); // 恢复背景块

// 由鼠标位置,计算图像块新位置的左上角坐标

int x = point.x - w / 2;

int y = point.y - h / 2;

dc0.BitBlt(0, 0, w, h, &memDC, x, y, SRCCOPY); // 保存背景块

// 绘制图像块到内存DC

memDC.TransparentBlt(x, y, w, h, &dc, 0, 0, w, h, transpCol);

// 显示内存DC中的图像

pDC->BitBlt(0, 0, W, H, &memDC, 0, 0, SRCCOPY);

// 记录当前图像块位置坐标,供下次恢复背景块时使用

x0 = x;

y0 = y;

ReleaseDC(pDC); // 释放视图DC

6.拖动移动图块

在移动鼠标的消息响应函数OnMouseMove中拖动图像块:

if (nFlags & MK_LBUTTON) { // 判断左鼠标键是否被按下

CDC *pDC = GetDC(); // 获取视图DC

// 恢复背景块

memDC.BitBlt(x0, y0, w, h, &dc0, 0, 0, SRCCOPY);

// 由鼠标位置,计算图像块新位置的左上角坐标

Windows程序设计题(含简答题答案)

Windows 程序设计》模拟题 单项选择题 1. 在 VC++ 中,项目文件的扩展名是( B )。 A ) exe B)dsp C ) dsw D ) cpp 2. 在 MFC 中,利用 ClassWizard 不能( D )。 A )建立新类 B )进行消息映射 C )增加类的成员变量 D )插入资源 当滑块滑动时,滑块条控件将发送滚动消息来通知父窗口,垂直滑动条发送( 3. C )消 息。 4. 5. A ) WM_CHAR B ) WM_HSCROLL C ) W M_VSC ROLL 更新菜单状态的消息是( C A ) WM_COMMAND )。 C ) UPDATE_COMMAND_UI 对于消息队列描述正确的是( A )在Windows 中只有一个消息系统, 据结构,用于临时存储消息。 D ) WM_CHANG E B ) UPDATE D) INVALIDATE 即系统消息队列。 消息队列是一个系统定义的数 B )系统可从消息队列将信息直接发给窗口。 另外,每个正在 Windows 下运行的应用程 序都有自己的消息队列。 C )系统消息队列中的每个消息最终都要被 USER 模块传送到应用程序的消息队列中去。 应用程序的消息对列中存储了程序的所有窗口的全部消息。 D )以上都正确 6. 在 MFC 中,所有的控件都是( B )类的派生类,都可以作为一个特殊的窗口来处理。 CView B ) CWnd C) 7. 使用 CWindow C) D ) CDialog GetDC () 获取的设备上下文在退出时,必须调用( A )释放设备上下文。 ReleaseDC() B ) delete() DeleteDC () D ) Detach () 8. 希望菜单无效,应该用 CCmdUI 类的( A )成员函数。 A ) Enable B ) SetRadio C ) SetCheck D ) SetText 9.希望菜单前出现一个“V”,应该用 CCmdUI 类的(C )成员函数。 A ) Enable B ) SetRadio C ) SetCheck D ) SetText 10.下面(A )不是MFC 设备环境类CDC 类的派生类。 A ) GDI B ) CPaintDC C) CClientDC D) CWindowDC 11.在MFC 中,所有的文档类都派生于( C ),所有的视图类都派生于( A )。 A) CView B) CWindow C) CDocument D) CFormView

windows程序设计之扫雷游戏经典之作

WIN32模拟WINDOWS XP扫雷 程序 课程设计目的与要求: 本课程设计是计算机科学与技术专业重要的实践性环节之一,是对本程学习后进行的一次全面而综合的练习。本次课程设计的目的与任务主要有: 1.巩固和加深学生对C和C++语言的基本知识的理解与掌握。 2.掌握C和C++语言编程和调试的基本技能 3.运用C和C++语言对程序的简单设计 4.掌握程序设计说明文档的能力与技巧 5.提高与运用C和C++语言解决能实际问题的能力 一、题目描述: 自行分析Windows XP系统自带扫雷游戏,查找规则并对主要功能进行模仿程序实现,对于实现过程中出现的bug进 行调试解决。 二、使用语言: C和C++ 语言 选用C++语言的原因,简而言之主要是因为C++的设计目标,就是要让C++既具有适合于系统程序设计的C语言所具 有的可适应性和高效性,又能在其程序组织结构方面具有像其 他的语言所支持的程序优势。这即是说,C++所提供的抽象机 制能够被应用于那些对效率和可适应性具有极高要求的程序设 计任务之中。

三、编译以及运行环境: 编译环境:Microsoft Visual C++ 6.0 运行环境:Windows XP或其他兼容兼容系统 四、题目分析: 经过对游戏的初步分析,扫雷游戏含有如下规则: 根据输入的信息,执行相应的挖雷,插旗,疑问标记,自动蔓延开挖等。挖雷过程由计时器计时,由一个计数器统计插旗后雷的剩余个数。如果选择了标记功能,那么单机右键会依次对未知点进行插旗,标记,取消插旗与标记。如果点滴到插旗的如果点击到了未插旗子的雷区,如果是雷,则判定游戏失败,之后对雷区的点击进行屏蔽,不予响应。如果是疑问标记则像正常区域对待。如何判断胜利,如果所有的雷都被插旗或者点击后剩余的雷区, 五、程序运行流程简介以及流程图: 点击程序按默认参数进行初始化,输入,表情对鼠标左右键点击进行变化响应,对于雷区内点击进行规律响应,对雷区点击后果进行统计,判断失败与成功,失败或者成功之后,对于雷区内的输入也就是点击进行屏蔽,对菜单游戏级别、重新开始游戏按钮响应。级别选择,重新游戏时时间归零、雷数显示刷新。如此重复规则,直到点击退出。 程序简要流程图:

Windows程序设计题(含简答题答案)

《Windows程序设计》模拟题 一.单项选择题 1、在VC++中,项目文件得扩展名就是(B)。 A)exe????B)dsp C)dsw???D)cpp 2、在MFC中,利用ClassWizard不能(D)。 A)建立新类??B)进行消息映射 C)增加类得成员变量?D)插入资源 3、当滑块滑动时,滑块条控件将发送滚动消息来通知父窗口,垂直滑动条发送(C)消息。 A)WM_CHAR??B)WM_HSCROLL C)W M_VSCROLL???D)WM_CHANGE 4、更新菜单状态得消息就是(C )。 A)WM_MANDB)UPDATE C)UPDATE_MAND_UI D)INVALIDATE 5、对于消息队列描述正确得就是(D) A)在Windows中只有一个消息系统,即系统消息队列。消息队列就是一个系统定义得 数据结构,用于临时存储消息。 B)系统可从消息队列将信息直接发给窗口。另外,每个正在Windows下运行得应用程序都有自己得消息队列。 C)系统消息队列中得每个消息最终都要被USER模块传送到应用程序得消息队列中去。 应用程序得消息对列中存储了程序得所有窗口得全部消息. D)以上都正确 6、在MFC中,所有得控件都就是(B)类得派生类,都可以作为一个特殊得窗口来处理。 A)CView??B)CWnd C)C Window???D)CDialog 7、使用GetDC()获取得设备上下文在退出时,必须调用(A)释放设备上下文. A)ReleaseDC()???B)delete() C)D eleteDC()???D)Detach() 8、希望菜单无效,应该用CCmdUI类得(A )成员函数。 ???B)SetRadio A)Enable?? ? C)SetCheck???D)SetText 9、希望菜单前出现一个“√",应该用CCmdUI类得(C)成员函数. A)Enable ???B)SetRadio C)SetCheck??D)SetText 10、下面(A)不就是MFC设备环境类CDC类得派生类. A)GDI ????B)CPaintDC C)CClientDC ??D)CWindowDC 11、在MFC中,所有得文档类都派生于(C),所有得视图类都派生于(A )。 A)CView B)CWindowC)CDocumentD)CFormView 12、采用C++进行绘图时,(B)用于指定图形得填充样式,(A)用于指定图形得边框样式.

windows程序设计学习笔记(一)

Windows程序设计学习笔记 2015年10月4日 一、宽字符wide-character 1、wchar_t: char的双字节(Unicode) 定义:typedef unsigned short wchar_t 示例:wchar_t c=’A’; wchar_t *pw=L”hello!”; //L(代表long)告诉编译器按宽字符保存,即每个字符占两个字节 wchar_t a[]=L”hello!”; 2、宽字符库函数 wcslen( ):计算宽字串符的长度 如:wchar_t *pw=L”hello!”; iLength=wcslen(pw); //字符串长度是6 二、windows头文件类型 1、CHAR和WCHAR typedef char CHAR; //8位 typedef wchar_t WCHAR; //16位变量前面可加wc以说明是一个宽字符 2、字符串指针 (1)8位字符串指针 tpyedef CHAR *PCHAR, *LPCH, *PCH, *NPSTR, *LPSTR, *PSTR; tpyedef CONST CHAR *LPCCH, *PCCH, *LPCSTR, *PCSTR; 前缀N和L表示“near”和“long”,指的是16位windows中两种大小不同的指针,在Win32中near和long指针没有区别 (2)16位字符串指针 tpyedef WCHAR *PWCHAR, *LPWCH, *PWCH, *NWPSTR, *LPWSTR, *PWSTR; tpyedef CONST WCHAR *LPCWCH, *PCWCH, *LPCWSTR, *PCWSTR; 3、WINAPI 定义:#define WINPAI _stdcall 该语句指定了一个调用约定,包括如何生成机器代码以在堆栈中放置函数调用的参数三、Hello程序 /*------------------------------------- HELLO.C--windows下显示Hello,world! 作者:lw 时间:2015年国庆 -------------------------------------*/ #include LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("HelloWin"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc;

Windows程序设计题(含简答题标准答案)

《Windows程序设计》模拟题 一.单项选择题 1. 在VC++中,项目文件的扩展名是(B)。 A)exe ????B)dsp C)d sw?????D)cpp 2.在MFC中,利用ClassWizard不能(D )。 A)建立新类?????B)进行消息映射 C)增加类的成员变量?D)插入资源 3. 当滑块滑动时,滑块条控件将发送滚动消息来通知父窗口,垂直滑动条发送(C)消息。 A)WM_CHAR ?B)WM_HSCROLL C)WM_VSCROLL???D)WM_CHANGE 4. 更新菜单状态的消息是(C)。 A)WM_COMMANDB)UPDATE C)UPDATE_COMMAND_UI D)INVALIDATE 5.对于消息队列描述正确的是( D ) A)在Windows中只有一个消息系统,即系统消息队列。消息队列是一个系统定义的数据结构,用于临时存储消息。 B)系统可从消息队列将信息直接发给窗口。另外,每个正在Windows下运行的应用程 序都有自己的消息队列。 C)系统消息队列中的每个消息最终都要被USER模块传送到应用程序的消息队列中去。应用程序的消息对列中存储了程序的所有窗口的全部消息。 D)以上都正确 6. 在MFC中,所有的控件都是(B )类的派生类,都可以作为一个特殊的窗口来处理。 ?B)CWnd A)CView ??? C)C Window ???D)CDialog 7.使用GetDC()获取的设备上下文在退出时,必须调用(A)释放设备上下文。 A)ReleaseDC()???B)delete() C)D eleteDC() ?D)Detach() 8. 希望菜单无效,应该用CCmdUI类的(A )成员函数。 A)Enable ???B)SetRadio C)S etCheck ??D)SetText 9. 希望菜单前出现一个“√”,应该用CCmdUI类的(C)成员函数。 A)Enable??B)SetRadio C)SetCheck????D)SetText 10. 下面(A)不是MFC设备环境类CDC类的派生类。 A)GDI??????B)CPaintDC C)C ClientDC ???D)CWindowDC 11. 在MFC中,所有的文档类都派生于(C),所有的视图类都派生于(A )。 A)CViewB)CWindowC)CDocument D)CFormView 12. 采用C++进行绘图时,(B )用于指定图形的填充样式,(A)用于指定图形的边框样式。 A)画笔B)画刷C)区域D)位图

windows程序设计

消息驱动运行中发生的各种事件(鼠标键盘输入,各种状态的改变等),以消息的形式通知Windows系统Windows系统接收消息,提交给发生事件的应用程序应用程序的窗口处理函数负责处理响应收到的消息处理完成后继续等待下一个消息API application programming Interface SDK software Development kit 事件是用户对计算机操作的动作或程序中某个对象的状态发生的变化,每个事件对应一个消息,即每个事件发生的效果是产生消息消息是描述事件发生的信息事件驱动程序执行的顺序不是事先规定的,而是由事件的发生来控制在程序执行过程中,当某个对象的状态发生变化,或有外部输入等情况发生,则产生相应的事件当事件产生时,系统会发出特定的消息编写程序时可以对这些消息做出反应,也可以不做出;若要做出反应(即响应消息),则把响应此消息的程序写在与此消息相关联的函数中在程序执行时,一旦产生了一个消息,会调用与此消息相关联的函数窗口是Windows应用程序基本的操作单位,是应用程序与用户之间交互的接口,也是系统管理应用程序的基本单位消息传输事件发生,产生消息消息保存到消息队列分配给应用程序消息队列消息循环提取消息窗口函数响应消息MFC Microsoft foundation class MFC应用程序框架提供了一般应用程序需要的全部面向对象软件组件的集成集合MFC程序结构基于框架窗口的应用程序(早期的MFC)程序运行管理、窗口及数据管理分开两部分,与SDK程序类似基于文档/视图的应用程序单文档应用程序(重点)程序运行管理、窗口管理、交互管理、数据管理分开基于对话框的应用程序程序运行管理、对话框管理分开目前的MFC应用程序都采用文档/视图结构,把原来窗口框架类的工作分开窗口框架类管理窗框视图类(即视窗类)管理数据显示、接收用户输入文档类负责数据管理文档视图架构的思想数据嵌入文档中,利用文档的架构管理文档与视图通信,使数据在视图中显示视图显示数据,并与用户交互视图放入窗口中文档模板类对象管理上述对象协调工作CDocument是负责管理数据的类应用程序中的文档类是这个类的派生类作用数据管理程序员应将用户的实际数据对象内嵌在自己的文档类中需要与自己的视图类通信在程序中实现CDocument的派生类,以便嵌入数据对象,并改写成员函数实现对数据的处理用于派生应用程序的视图类CView 作用处理与用户的交互显示文档类中的数据接收用户输入(例如鼠标操作)通常要改写视图类的成员函数OnDraw或OnPrint,实现数据显示框架自动调用OnDraw函数WM_PAINT消息OnPaint()OnDraw() 菜单及响应利用菜单编辑器修改IDR_MAINFRAME 菜单资源在相应的类中为新菜单项映射消息COMMAND单击或用快捷键选中时生产UPDATE_COMMAND_UI上一级菜单显示前生产用于控制菜单状态编写新菜单项的消息处理函数快捷键在菜单项标题后面加“(&字母)”菜单动态为要添加的菜单项定义一个宏(例如ID_MENU1)用CWnd::GetMenu( )获得指向菜单条的指针(如果在子菜单上添加,应用CMenu::GetSubMenu()获得该菜单的指针)为菜单项手工映射消息处理函数在适当的地方调用InsertMenu或AppendMenu添加菜单项调用CWnd::DrawMenuBar()函数重画菜单动态删除菜单项与上面类似(使用DeleteMenu函数)动态创建菜单项用函数BOOL CreateMenu( );获得新菜单句柄,再用此句柄当作AppendMenu函数的第2个参数(需进行(UTIN)强制类型转换)通常用于创建弹出式菜单的顶级(不需映射消息)快捷菜单建立菜单如果菜单已经存在,且功能齐全,只需确定弹出方式即可也可以另加一个菜单资源,此法可使菜单条中不显示快捷菜单关联类(对于新加菜单)把此菜单资源关联到视图类上选中此菜单资源的情况下打开类向导,选视图类影射消息并编写消息处理函数选择弹出方式可在视图类的WM_CONTEXTMENU消息(鼠标右键单击客户区时产生)处理函数中弹出Windows系统有一个存放消息的队列每个应用程序也有一个消息队列Windows系统先将收到的消息存放在系统队列中,然后再分发到相应的应用程序队列中应用程序则从自身的队列中获取消息并进行处理类的消息响应一个类要响应多种消息对于每一种消息最重要的是知道是哪个消息:消息的标识(ID 身份证)这个消息对应的响应函数是什么(函数的起始地址)解决的办法建立一个结构体数组(消息映射表)一行(一个数组元素)是一个消息的信息每行至少包含消息的标识(ID)指向响应函数的指针消息的响应当收到消息后,根据消息ID在表中查找,找到后调用对应的消息处理函数消息路由当一个类收到消息后,先在自己的消息映射表中搜索,搜索到就执行相应的消息处理函数,然后结束消息处理过程未搜到,则到上一层基类中搜索,依此类推如果都未搜到,则交给系统默认的窗口处理函数去处理消息种类窗口消息命令消息控件通知消息实现消息映射消息映射的要点为每个要响应的消息建立一个处理函数为每个能够响应消息的类建立一个结构类型的数组(消息映射表)并填写这个数组编写每个消息处

Windows程序设计复习资料(完整答案)

一、单项选择题: 1.WinMain()函数的工作。 WinMain()函数是Windows程序的入口点 在WinMain中进行:注册窗体类、建立窗体、显示窗体、更新窗体、建立消息循环2.非MFC的Windows程序中,不是处理WM_PAINT消息的时候怎样获得客户区的设备环境句柄。 BeginPaint ----------EndPaint GetDC---------------ReleaseDC(成对出现) 利用GetDC()函数获取设备环境句柄 3.句柄的含义。 句柄是一个(通常为32位的)无符号整数(unsigned int),它代表一个内核对象内核对象在应用层的表示 4.在MM_TEXT映射模式下,什么情况下客户坐标和逻辑坐标可能不同。 当窗口客户区有滚动条时客户坐标和逻辑坐标可能不同 5.Windows程序的控制流程。 Windows程序没有固定的控制流程,事件驱动程序控制流程 6.事件驱动、消息驱动的概念。 事件驱动程序设计是一种全新的程序设计方法,它不是由事件的顺序来控制,而是由事件的发生来控制,而这种事件的发生是随机的、不确定的,并没有预定的顺序,这样就允许程序的的用户用各种合理的顺序来安排程序的流程。 消息是事件到达Windows内部后的称呼 7.对话框、控件的作用。 对话框是程序与用户之间进行交互,接收用户输入、并把数据显示出来的一个窗口; 控件是用户可与之交互以输入或操作数据的对象。控件通常出现在对话框中或工具栏上。 8.MFC中怎样处理WM_PAINT消息。 在OnDraw()函数中处理该消息 9.GetMessage()函数在什么情况下返回0,从而导致消息循环终止,程序结束。 在接收到WM_QUIT消息时返回0 10.Windows中,应用程序的输出是怎样进行的。 通过设备描述表DC(Device Context)来进行应用程序与物理设备之间的交互11.MFC中,大多数类的基类都是什么。 CObject类 12.一个MFC应用程序必须有且只能有一个从哪个类派生的全局应用程序对象,此对象在运行时刻控制应用程序中所有其他对象的活动。 CWinApp类 13.每次当窗口客户区需要重新绘制时,应用程序框架怎么处理。 发送WM_PAINT消息,调用OnDraw()函数进行重绘 14.CDC类的作用。 CDC是设备描述表类的基类,除了一般的窗口显示外,还用于基于桌面的全屏幕绘制和非屏幕显示的打印机输出。 15.当程序中某个函数改变了数据而需要重新绘制客户区时,OnDraw()函数不会被自动调

Windows程序设计题

《Windows程序设计》模拟题 一.单项选择题 1. 在VC++中,项目文件的扩展名是(B)。 A)exe B)dsp C)dsw D)cpp 2. 在MFC中,利用ClassWizard不能(D)。 A)建立新类B)进行消息映射 C)增加类的成员变量D)插入资源 3. 当滑块滑动时,滑块条控件将发送滚动消息来通知父窗口,垂直滑动条发送(C)消息。 A)WM_CHAR B)WM_HSCROLL C)WM_VSCROLL D)WM_CHANGE 4. 更新菜单状态的消息是( C )。 A)WM_COMMAND B)UPDATE C)UPDATE_COMMAND_UI D)INVALIDATE 5. 对于消息队列描述正确的是( D ) A)在Windows中只有一个消息系统,即系统消息队列。消息队列是一个系统定义的数据结构,用于临时存储消息。 B)系统可从消息队列将信息直接发给窗口。另外,每个正在Windows下运行的应用程序都有自己的消息队列。 C)系统消息队列中的每个消息最终都要被USER模块传送到应用程序的消息队列中去。 应用程序的消息对列中存储了程序的所有窗口的全部消息。 D)以上都正确 6. 在MFC中,所有的控件都是(B)类的派生类,都可以作为一个特殊的窗口来处理。 A)CView B)CWnd C)CWindow D)CDialog 7. 使用GetDC()获取的设备上下文在退出时,必须调用(A)释放设备上下文。 A)ReleaseDC() B)delete() C)DeleteDC() D)Detach() 8. 希望菜单无效,应该用CCmdUI类的(A)成员函数。 A)Enable B)SetRadio C)SetCheck D)SetText 9. 希望菜单前出现一个“√”,应该用CCmdUI类的(C)成员函数。 A)Enable B)SetRadio C)SetCheck D)SetText 10. 下面(A)不是MFC设备环境类CDC类的派生类。 A)GDI B)CPaintDC C)CClientDC D)CWindowDC 11. 在MFC中,所有的文档类都派生于(C),所有的视图类都派生于(A)。 A)CView B)CWindow C)CDocument D)CFormView 12. 采用C++进行绘图时,(B)用于指定图形的填充样式,(A)用于指定图形的边框样式。

Windows编程基础-期末考试试卷头期末-A卷答案

北华航天工业学院2013—2014学年第二学期 Windows编程基础课程考试卷(A、B) 考核形式:开卷闭卷班级:姓名:学号: 一、填空题(每空1分,共10分) 1. 用户可以通过项目工作区窗口的ResourceView 视图来查看资源。 2. 一个单文档的MFC 应用程序框架一般包括5个类:应用程序类、框架窗口类、 视图类、文档类和文档模板类。 3. 改变菜单项文本是使用CCmdUI 类的SetText 成员函数。 4. 滚动条类CScrollBar的成员函数规定了滚动条的操作。设置滚动条的范围使用SetRange 函数,设置滚动块的位置使用SetScrollPos 函数,获取滚动块的当前位置使用GetScrollPos 函数。 5. 对话框默认标识符前缀是IDD 。 二、选择题(每空2分,共40分) 1.MFC类库中的类按层次关系可知,CStatic的直接父类为(B )。 A、CObject B、CWnd C、CCmdTarget D、CFrameWnd 2. 以下控件中,( C )没有Caption属性。 A、按钮 B、群组框 C、编辑控件 D、静态控件 3. 列表框ClistBox类的成员函数( A )用来向列表框增加列表项,成员函数( C )用来清除列表项所有项目,成员函数( F )用来返回列表框的列表项的总数。 A、AddString B、DeleteString C、ResetContent D、FindString E、GetCurSel F、GetCount 4. 假定AB为一个类,则执行“”AB a(4),b(3),*p[2];”语句时,自动调用该类构造函数的次数为( A )。 A、2 B、3 C、4 D、5 5.在类外定义类的成员函数时,应当使用( B )来标识该函数为特定类的成员。A、:B、::C、-> D、this

Windows程序设计(第五版)源代码A(chap08)

chap08-Beeper1 /*----------------------------------------- BEEPER1.C -- Timer Demo Program No. 1 (c) Charles Petzold, 1998 -----------------------------------------*/ #include #define ID_TIMER 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Beeper1") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Beeper1 Timer Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ;

Windows程序设计

一单项选择题 试题1 满分值:4.0分状态:已答 应用程序可以使用GDI创建3种类型的图形输出,不包括【】位图 矢量图形 光栅图形 文本 [我的答案] 光栅图形 试题2 满分值:4.0分状态:已答 关于工具栏、菜单和加速键的关系,正确的说法是【】 工具按钮与菜单必须一一对应 工具栏与加速键一一对应 工具按钮不必与菜单一一对应 菜单与加速键一一对应 [我的答案] 工具按钮不必与菜单一一对应 试题3 满分值:4.0分状态:已答 【】函数用于在指定位置输出字符串: Print TextOut Scanf

[我的答案] Print 试题4 满分值:4.0分状态:已答 下列【】不是MFC消息映射机制有关的宏: DECLARE_MESSAGE_MAP BEGIN_MESSAGE_MAP DECLARE_SERIAL END_MESSAGE_MAP [我的答案] DECLARE_SERIAL 试题5 满分值:4.0分状态:已答 在视图类中获得文档类对象指针用函数【】: GetDocument UpdateAllViews OnUpdate SetModifiedFlag [我的答案] GetDocument 试题6 满分值:4.0分状态:已答 能间接调用视图类的OnDraw函数的是【】OnTimer LButtonDown

Invalidate [我的答案] Invalidate 试题7 满分值:4.0分状态:已答实心画刷只需要定义【】 填充样式 填充颜色 填充模式 填充图案 [我的答案] 填充颜色 试题8 满分值:4.0分状态:已答【】设置进度条的当前位置SetRange SetStep SetPos StepIt [我的答案] SetPos 试题9 满分值:4.0分状态:已答常见的菜单类型不包括【】。 自动菜单

《windows程序设计(C语言版)》第1章~开始

开始涛儿软件工作室整理编译 本书介绍了在Microsoft Windows 98、Microsoft Windows NT 4.0和Windows NT 5.0下程序写作的方法。这些程序用C语言编写并使用原始的Windows Application Programming Interface(API)。如在本章稍后所讨论的,这不是写作Windows程序的唯一方法。然而,无论最终您使用什么方式写作程序,了解Windows API都是非常重要的。 正如您可能知道的,Windows 98已成为使用Intel 32位微处理器(例如486和Pentium)的IBM兼容型个人计算机环境上最新的图形操作系统之代表。Windows NT是IBM PC兼容机种以及一些RISC(精简指令集计算机)工作站上使用的Windows工业增强型版本。 使用本书有三个先决条件。首先,您应该从使用者的角度熟悉Windows 98。不要期望可以在不了解Windows使用者接口的情形下开发其应用程序。因此,我建议您在开发程序(或在进行其它工作)时使用执行Windows的机器来跑Windows应用程序。 第二,您应了解C语言。如果要写Windows程序,一开始却不想了解C语言,那不是一个好主意。我建议您在文字控制台环境中,例如在Windows 98 MS-DOS命令提示窗口下提供的环境中学习C语言。Windows程序设计有时包括一些非文字模式程序设计的C语言部分;在这些情况下,我将针对这些问题提供讨论。但大多数情况下,您应非常熟悉该语言,特别是C语言的结构和指针。了解标准C语言执行期链接库的一些相关知识是有帮助的,但不是必要的。 第三,您应该在机器上安装一个适于进行Windows程序设计的32位C语言编译器和开发环境。在本书中,假定您正在使用Microsoft Visual C++ 6.0,该软件包可独立购买,也可作为Visual Studio 6.0软件包的一部分购买。 到此为止,我将不再假设您具有任何图形使用者接口(如Windows)的程序写作经验。 WINDOWS环境 Windows几乎不需要介绍。然而人们很容易忘记Windows给办公室和家庭桌上型计算机所带来的重大改变。Windows在其早期曾经走过一段坎坷的道路,征服桌上型计算机市场的前途一度相当渺茫。 Windows简史

Windows程序设计模拟题

《Windows程序设计》模拟题(补) 一.单项选择题 1、项目文件的扩展名是【1】。 A)exe B)dsp C)dsw D)cpp 2、利用ClassWizard不能【2】。 A)建立新类B)进行消息映射 C)增加类的成员变量D)插入资源 3、下列【3】不是MFC消息映射机制有关的宏。 A)DECLARE_MESSAGE_MAP B)BEGIN_MESSAGE_MAP C)DECLARE_SERIAL D)END_MESSAGE_MAP 4、要使鼠标箭头在按钮上暂停时能显示工具栏按钮提示,必须设置工具栏的风格为【4】。 A)CBRS_TOOLTIPS B)CBRS_FLYBY C)CBRS_NOALIGN D)WS_VISIBLE 5、下面【5】不是GDI对象。 A)CFont B)CPalette C)CClientDC D)CBitmap 6、要将模式对话框在屏幕上显示需要用到【6】。 A)Create B)DoModal C)OnOK D)构造函数 7、类CButton不是下列【7】控件对应的类。 A)命令按钮B)单选按钮 C)复选框D)静态文本框 8、根据对话框的行为性质,对话框可以分为两类【8】 A)对话框资源和对话框类B)模式对话框和非模式对话框 C)对话框资源和对话框模板D)消息对话框和模式对话框 9、所有的控件都是【9】类的派生类,都可以作为一个特殊的窗口来处理。 A)CView B)CWnd C)CWindow D)CDialog 10、使用GetDC()获取的设备上下文在退出时,必须调用【10】释放设备上下文。 A)ReleaseDC() B)delete C)DeleteDC() D)Detach() 11、希望菜单无效,应该用CCmdUI类的【11】成员函数。 A)Enable B)SetRadio C)SetCheck D)SetText 12、当滑块滑动时,滑块条控件将发送滚动消息来通知父窗口,水平滑动条发送【12】消息。 A)WM_CHAR B)WM_HSCROLL C)WM_VSCROLL D)WM_CHANGE

Windows程序设计知识点

《Windows程序设计》课程各章知识点 第一章 1.什么是API、SDK(P2)? 2.用Windows API开发应用程序有什么缺点?(P2) 3.什么是句柄?常用的句柄类型有哪些?(P2) 4.Windows系统的基本数据类型和结构类型(P3) 5.事件、消息(用MSG结构体表示)(P4) 6.消息队列、消息循环(P5) 7.从消息队列获取消息的Windows API函数是? 8.队列消息、非队列消息(P5) 9.Windows应用程序的基本结构(WinMain函数和窗口函数) 10.WinMain函数的主要工作 ●定义窗口类型(WNDCLASS结构体) ●注册窗口类型(RegisterClass函数) ●创建窗口(CreateWindow函数) ●显示窗口(ShowWindow函数、UpdateWindow函数) ●执行消息循环(GetMessage、TranslateMessage函数、DispatchMessage函数) 11.问题:DefWndProc函数的作用(P9) 第二章 1.消息映射、消息映射表(结构体数组)。 2.消息映射表的声明和实现方法(宏命令)? 第三章 1.MFC的设计思想(P51开始部分)? 2.构成Windows应用程序(MFC)框架的主要类有哪些? ●CWinApp类、CDocument类、CView类、CFrameWnd类、CDocTemplate类 ●上述类的基类和派生类:CObject类、CCmdTarget类、CWnd类、CWinThread类、 CSingleDocTemplate类、CMultiDocTemplate类 3.理解应用程序类的InitInstance函数的代码(P65) 4.应用程序创建各种对象的顺序(结合P65代码参考图3-11) 5.应用程序各对象之间的关系(图3-13、图3-14、图3-15) 6.类信息表的声明和实现方法(宏命令) 7.类信息表的提取(宏命令) 第四章 1.虚拟图形显示设备和图形设备描述环境(DC)。 2.什么是GDI?常用GDI绘图工具有哪些? 3.CDC类的派生类有哪些?它们之间的区别是什么? 4.OnDraw函数的作用是什么?什么时候被谁调用? 5.掌握用CPen类和CBrush类绘制常用图形。 第五章

windows程序设计实验报告

《Windows程序设计》 实验报告 实验室: S306(308) 学院:计算机科学与技术 专业:计算机科学与技术

目录 实验一、熟悉visual c++的编程环境 (3) 一、实验目的和要求: (3) 二、实验内容、程序清单及运行结果: (3) 1.编辑第一个C++程序 (3) 2. 用AppWizard建立一个MFC Windows应用 (3) 3. 编写简单的计算程序 (4) 运行结果: (4) 三、心得体会: (4) 实验二、对话框的使用 (5) 一、实验目的和要求: (5) 二、实验内容、程序清单及运行结果 (5) 实验步骤: (5) 实验结果: (7) 三、心得体会: (7) 实验三、创建一个进程来运行一个应用程序 (8) 一、实验要求和目的 (8) 二、实验内容、程序清单及运行结果 (8) 实验步骤: (8) 实验结果: (9) 三、实验心得 (9) 实验四、调整线程优先级 (10) 一、实验要求和目的 (10) 二、实验内容、程序清单及运行结果 (10) 实验步骤: (10) 实验结果: (11) 三、实验心得 (11)

实验一、熟悉visual c++的编程环境 一、实验目的和要求: 1.了解和使用VC集成开发环境 2.熟悉VC环境的基本命令和功能键, 3.熟悉常用的功能菜单命令 4.学习使用VC++环境的帮助 5.学习完整的C++程序开发过程 6.理解简单的C++程序结构 7.了解用Visual C++开发Windows MFC应用程序的过程 二、实验内容、程序清单及运行结果: 1.编辑第一个C++程序 源程序代码: //hello world example #include int main() { cout << "hello!"<< endl ; return 0; //正常返回 } 运行结果: 2. 用AppWizard建立一个MFC Windows应用 在窗口中输出"Hello,World!",修改OnDraw函数的定义,在最后一行加一句:pDC->TextOut(50,50,"Hello, World"); 运行结果:

大连海事大学C语言与Windows程序设计道课后题答案

大连海事大学C语言与Windows程序设计6道课后题答案 1.(1)设计具有以下功能的程序:从键盘上输入10个非负整数,统计并输出最大数和最小数,当输入负数时结束输入。 #include void main() { int a, max=-1, min=32767,num=1; cout<<"Please input a number:"<>a; if(a<0) break; //输入负数结束输入 if(a>max) max =a; if(a=0&&num<=10); if(max>=0) cout<<"Max="<

值在程序运行时从键盘输入。 #include void main() { inti,n,sign=1; float sum=0.0; printf("请输入n:"); scanf("%d",&n); for(i=1;i<=n;i+=2) { sum+=sign*(1.0/i); sign=-sign; } printf("\nsum=%f\n",sum); } 2.设计具有以下功能的程序:从键盘输入10个整数,然后排序。(要求:利用函数调用实现并以数组作为函数的参数) 3.编写几何点(二维平面上)的类Point,包括位置属性(二维坐标x,y), 成员函数包括: 点的位置获取函数GetX()和GetY(),

Windows程序设计学习资料

W i n d o w s程序设计

洛阳理工学院实验报告 系别计算机 与信息 工程系 班级B100505 学号B10050527 姓名张勋 课程名称C#面向对象程序设计实验日期11.22 实验名称Windows程序设计成绩 实验目的: 练习windows窗体的使用,掌握多种控件的属性,学会应用窗体属性和事件以及代码两种方式操作。 实验条件: 微机1台、Visual studio 2008 实验内容与结果: 1.控制TextBox控件,使它只能输入数字。当按下退格键时,执行退格操作.并且当数据 将被清空时,提醒终止删除操作,输入数据。 2.有两个组合框里分别为专业和课程,当专业选定时,课程组合框里的课程数组应属于 该专业,通过增加和删除 按钮可以把所选的课程添 加或删除到列表框,并且 在文本框内显示出列表框 内的所有课程的总学分 数。

当没有选课程直接点击添加按钮时: 选课程并添加: 从列表框内删除: 当再次选择“大学物理”时:

当更换专业时: 3.比较numericUpDown1.UpButton()和 numericUpDown1.Value+=numericUpDown1.Increment;的区别,这里把文本框数值的上限设为20,下限设为0,增值为1. 当达到上限时, numericUpDown1.UpButton()执行 正常,而另一句却出现错误,语句 无法执行 添加快捷菜单,在窗口菜单中有大 中小三个菜单项,分别控制着窗口 的大小,单击右键弹出快捷菜单 使快捷菜单的功能与窗口菜单相 同。 窗口菜单操作下:

快捷菜单操作下: 附加窗体的代码:(部分功能在属性中执行了,故在代码中没有设置) https://www.doczj.com/doc/0514578789.html,ing System; using System.Collections.Generic; using https://www.doczj.com/doc/0514578789.html,ponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace p7_4 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void textBox1_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar >= 48 && e.KeyChar <= 57) ; else if (e.KeyChar == 8) { textBox1.Text.Remove(textBox1.Text.Length - 1); if (textBox1.Text.Length == 1) MessageBox.Show("请输入数据!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error); } else e.Handled = true; } private void Form1_Load(object sender, EventArgs e) {

相关主题
文本预览
相关文档 最新文档