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

本文探讨了一个复杂矩形布局中实现鼠标移动效果的问题,通过修改逻辑和添加声音反馈,解决了只显示第一个矩形效果的问题,并在代码中加入鼠标左键点击播放音乐的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       昨天晚上失眠了,于是一直在想实现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、付费专栏及课程。

余额充值