windows SDK模拟游戏钢琴的实现(二)

昨天晚上失眠了,于是一直在想实现GDI的一个效果:首先绘制10个相连的矩形,当鼠标移到某个矩形的时候,自动填充那个矩形的背景颜色为灰色,移开的时候恢复原来的白色。当只有一个矩形的时候,当然不是一件难事。但是当有10个矩形的时候,问题就产生了。首先先看一下代码:

#include <windows.h> #include <math.h> #define NUM 10 struct own_rectangle{ int xLeft ; int xRight ; int yTop ; int yBottom ; }rect[NUM]; LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("piano demo by mouse"); 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.lpszClassName = szAppName; wndclass.lpszMenuName = NULL; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0; } hwnd = CreateWindow (szAppName, TEXT("Piano Demo Using Mouse"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int cxClient, cyClient; static int Flag[NUM] ={0,0,0,0,0,0,0,0,0,0}; HDC hdc; int i = 0; PAINTSTRUCT ps; //ps数组里面存放了RECT,用来重绘客户区的位置 POINT hitPoint; int xCornerEllipse; int yCornerEllipse; int xStart = 200, yStart = 100, xEnd = 100, yEnd = 300; HBRUSH hBrush; HPEN hPen; switch (message) { case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_MOUSEMOVE: hitPoint.x = LOWORD(lParam); hitPoint.y = HIWORD(lParam); for (i = 0; i < NUM; i++) //为什么 只有当i=0的时候才有效果呢? { if (hitPoint.x >= rect[i].xLeft && hitPoint.x <= rect[i].xRight && hitPoint.y >= rect[i].yTop && hitPoint.y <= rect[i].yBottom ) Flag[i] = 1; else Flag[i] = 0; InvalidateRect(hwnd, NULL, TRUE); //break; } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps); ps.rcPaint.left = rect[i].xLeft; ps.rcPaint.right = rect[i].xRight; ps.rcPaint.top = rect[i].yTop; ps.rcPaint.bottom = rect[i].yBottom; hBrush = (HBRUSH)GetStockObject (GRAY_BRUSH); if (Flag[i] == 1) //如果鼠标位置在矩形框内,将刷子选进设备环境里面 SelectObject(hdc, hBrush); hPen = CreatePen (PS_SOLID, 1, RGB(255,0,0)); SelectObject (hdc, hPen); rect[0].xLeft = 400 ; rect[0].xRight = 420 ; rect[0].yTop = 210 ; rect[0].yBottom = 300 ; Rectangle(hdc, rect[0].xLeft, rect[0].yTop, rect[0].xRight, rect[0].yBottom); for (i = 1; i <NUM; i++) { rect[i].xLeft = rect[i-1].xLeft + 20 ; rect[i].xRight = rect[i-1].xRight + 20 ; rect[i].yTop = rect[0].yTop ; rect[i].yBottom = rect[0].yBottom ; Rectangle(hdc, rect[i].xLeft, rect[i].yTop, rect[i].xRight, rect[i].yBottom); } DeleteObject(hPen); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, message, wParam, lParam) ; }

下面来看一个这个处理鼠标移动的操作:

case WM_MOUSEMOVE: hitPoint.x = LOWORD(lParam); hitPoint.y = HIWORD(lParam); for (i = 0; i < NUM; i++) { if (hitPoint.x >= rect[i].xLeft && hitPoint.x <= rect[i].xRight && hitPoint.y >= rect[i].yTop && hitPoint.y <= rect[i].yBottom ) Flag[i] = 1; else Flag[i] = 0; InvalidateRect(hwnd, NULL, TRUE); break; } return 0;
一运行问题就来了:只有当鼠标移到第一个矩形的时候才会产生效果,而移到后面9个的时候根本没有产生任何效果!!

我把上面for 循环的 初始条件的 i=0 改成 i=1,这下子更加糟糕了:连第一个矩形都不能产生效果了。看来问题就是在这里了。

那么为什么会不起作用呢?

向华哥请教后,终于发现端倪了:

if (Flag[i] == 1) SelectObject(hdc, hBrush);


我是什么时候把刷子的句柄选入设备环境里面的呢? 就是上面的当 Flag[i] == 1 的时候,一旦选入了以后,那么后面所有的矩形绘制的时候用的都是灰色的刷子。

所以首先在WM_PAINT消息里面 添加选择刷子的消息:

for (i = 1; i <NUM; i++)
{

if (Flag[i] == 1)

SelectObject(hdc, hBrush);

rect[i].xLeft = rect[i-1].xLeft + 20 ;
rect[i].xRight = rect[i-1].xRight + 20 ;
rect[i].yTop = rect[0].yTop ;
rect[i].yBottom = rect[0].yBottom ;
Rectangle(hdc, rect[i].xLeft, rect[i].yTop, rect[i].xRight, rect[i].yBottom);
}

但是麻烦又来了:如果第i个是灰色的,那么从第i+1个开始后面的所有矩形都变成灰色的了由于发送了10条重绘消息,说明鼠标移动到某个点的时候整个屏幕刷新了10次!!相当于10个矩形总共画了10次!!现在问题应该很明显了:重新绘制出来的窗口把原来的窗口效果给覆盖掉了,如果说原来第i次第i个矩形是灰色的,那么第i+1次的时候又被覆盖成白色的了!!!

而且我的i设置的时候不是全局变量,所以每一个进入VM_PAINT消息的时候i都是0

好了,错误发现以后,我们就可以改正了。首先是逻辑上的修正:

每次重新绘制整个窗口的时候都先把10个矩形画出来,然后在根据鼠标移动传过来的信息判断哪一个矩形应该被重画。

逻辑清楚了以后,写起来就简单了。

我把(一)中的声音效果再加进来,再添加鼠标左键点击发出声音,最终的代码如下:

#include <windows.h> #include <math.h> #include <mmsystem.h> #pragma comment(lib, "WINMM.LIB") #define NUM 10 struct own_rectangle{ int xLeft ; int xRight ; int yTop ; int yBottom ; }rect[NUM]; LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("piano demo by mouse"); 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.lpszClassName = szAppName; wndclass.lpszMenuName = NULL; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0; } hwnd = CreateWindow (szAppName, TEXT("Piano Demo Using Mouse"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { TCHAR str1[100], str2[100], str_i[10]; static int cxClient, cyClient; static int Flag[NUM] ={0,0,0,0,0,0,0,0,0,0}; HDC hdc; static int i = 0; int j = 0; PAINTSTRUCT ps; //ps数组里面存放了RECT,用来重绘客户区的位置 POINT hitPoint; int xCornerEllipse; int yCornerEllipse; int xStart = 200, yStart = 100, xEnd = 100, yEnd = 300; HBRUSH hBrush; HPEN hPen; switch (message) { case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_MOUSEMOVE: hitPoint.x = LOWORD(lParam); hitPoint.y = HIWORD(lParam); for (i = 0; i < NUM; i++) { if (hitPoint.x >= rect[i].xLeft && hitPoint.x <= rect[i].xRight && hitPoint.y >= rect[i].yTop && hitPoint.y <= rect[i].yBottom ) { Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); break; } } if (NUM == i) InvalidateRect(hwnd, NULL, TRUE); return 0; case WM_LBUTTONDOWN: { hitPoint.x = LOWORD(lParam); hitPoint.y = HIWORD(lParam); for (i = 0; i < NUM; i++) { if (hitPoint.x >= rect[i].xLeft && hitPoint.x <= rect[i].xRight && hitPoint.y >= rect[i].yTop && hitPoint.y <= rect[i].yBottom ) { _itow(i+1, str_i, 10); wsprintfW(str1, TEXT("close %s.mp3"),str_i); wsprintfW(str2, TEXT("play %s.mp3"),str_i); mciSendString(str1, NULL, 0, NULL); mciSendString(str2, NULL, 0, NULL); break; } } } break; case WM_PAINT: hdc = BeginPaint (hwnd, &ps); hPen = CreatePen (PS_SOLID, 1, RGB(255,0,0)); SelectObject (hdc, hPen); //hBrush = (HBRUSH)GetStockObject (GRAY_BRUSH); rect[0].xLeft = 400 ; rect[0].xRight = 420 ; rect[0].yTop = 210 ; rect[0].yBottom = 300 ; Rectangle(hdc, rect[0].xLeft, rect[0].yTop, rect[0].xRight, rect[0].yBottom); for (j = 1; j < NUM; j++) { rect[j].xLeft = rect[j-1].xLeft + 20 ; rect[j].xRight = rect[j-1].xRight + 20 ; rect[j].yTop = rect[0].yTop ; rect[j].yBottom = rect[0].yBottom ; Rectangle(hdc, rect[j].xLeft, rect[j].yTop, rect[j].xRight, rect[j].yBottom); } if (Flag[i] == 1){ SelectObject(hdc, (HBRUSH)GetStockObject (GRAY_BRUSH)); Rectangle(hdc, rect[i].xLeft, rect[i].yTop, rect[i].xRight, rect[i].yBottom); } else if (NUM == i){ SelectObject(hdc, (HBRUSH)GetStockObject (WHITE_BRUSH)); Rectangle(hdc, rect[i].xLeft, rect[i].yTop, rect[i].xRight, rect[i].yBottom); } DeleteObject(hPen); EndPaint(hwnd, &ps); return 0; case WM_KEYDOWN: switch(wParam) { case 0x31: i = 0; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 1.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 1.mp3"), NULL, 0, NULL); break; case 0x32: i = 1; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 2.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 2.mp3"), NULL, 0, NULL); break; case 0x33: i = 2; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 3.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 3.mp3"), NULL, 0, NULL); break; case 0x34: i = 3; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 4.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 4.mp3"), NULL, 0, NULL); break; case 0x35: i = 4; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 5.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 5.mp3"), NULL, 0, NULL); break; case 0x36: i = 5; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 6.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 6.mp3"), NULL, 0, NULL); break; case 0x37: i = 6; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 7.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 7.mp3"), NULL, 0, NULL); break; case 0x38: i = 7; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 8.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 8.mp3"), NULL, 0, NULL); break; case 0x39: i = 8; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 9.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 9.mp3"), NULL, 0, NULL); break; case 0x30: i = 9; Flag[i] = 1; InvalidateRect(hwnd, NULL, TRUE); mciSendString(TEXT("close 10.mp3"), NULL, 0, NULL); mciSendString(TEXT("play 10.mp3"), NULL, 0, NULL); break; } return 0; case WM_DESTROY: PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, message, wParam, lParam) ; }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值