昨天晚上失眠了,于是一直在想实现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) ;
}