上一章的程序在鼠标控制方面还有问题,之所以用它作例子,完全是为了让大家将鼠标事件搞明白,这一章,我们就来纠正这些问题点。首先要掌握ClipCursor()函数,这个函数将可以将光标限定在窗口有效区内,注意:这个函数的参数用的矩形座标是整个屏幕的座标,这是因为鼠标是全屏动作的。因此,我们还必须学会将窗口有效区座标转换为屏幕座标。ClientToScreen()函数可以实现这一目的,不过这一函数在MFC中的参数和SDK中的参数不一样,MFC中转换的是一矩形块,而SDK转换的是一个点。所以,SDK编程中,下面例子是先转换窗口有效区的(0,0)点,再计算出矩形右下角的点。
BOOL ClientToScreen( BOOL ClipCursor(
HWND hWnd, //窗口句柄 const RECT *lpRect //屏幕座标的矩形块
LPPOINT lpPoint //屏幕座标的点 ); //参数为NULL时为取消该功能
);
LONG clsCur;boolbDrawing =false; //画图状态为true POINTS start; //开始点 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {;![]()
switch(message) {caseWM_LBUTTONDOWN: bDrawing =true; //描画状态开始 start = MAKEPOINTS(lParam); //开始点保存于start中 //取得窗口有效区 RECT rect; GetClientRect(hWnd,&rect); //转换窗口有效区为屏幕座标 POINT point; point.x = 0, point.y = 0; ClientToScreen(hWnd,&point); rect.top = point.y; rect.left = point.x; rect.bottom += rect.top; rect.right += rect.left; //将光标限定在窗口有效区内 ClipCursor(&rect); //rect一定要是屏幕座标if(!clsCur) { clsCur = GetClassLong(hWnd,GCL_HCURSOR); //取当前窗口的光标 } SetClassLong(hWnd,GCL_HCURSOR,NULL); //设置当前窗口的光标为NULL SetCursor(LoadCursor(NULL, IDC_CROSS)); //设置当前光标为十字形break;caseWM_MOUSEMOVE: HDC hdc; hdc = GetDC(hWnd);if(bDrawing) { MoveToEx(hdc, start.x, start.y,NULL); LineTo(hdc, LOWORD(lParam), HIWORD(lParam)); } ReleaseDC(hWnd, hdc);break;caseWM_LBUTTONUP: bDrawing =false; //描画状态结束 ClipCursor(NULL); //使光标可以在屏幕任意位置移动 SetClassLong(hWnd,GCL_HCURSOR, clsCur); //设置窗口光标为原先的光标 SetCursor((HCURSOR)clsCur); //设置当前光标为原先的光标break;caseWM_DESTROY: PostQuitMessage(0);break;default:returnDefWindowProc(hWnd, message, wParam, lParam); }return0; }
上例中,想要画一条线(即鼠标按下到松开的一条线),结果却将鼠标移动的“轨迹”画了出来,这些“轨迹”必须擦掉。VC不提供擦掉已经画好的图案的功能,所谓“擦掉”,就是选用某种方式重画,使得看上去象重画一样。通过SetROP2()函数就可以实现这个功能,这个函数将反转屏幕颜色,这样第二次画时就恢复为原来的颜色。注意:SetROP2()函数这是设置画图模式的函数,是设置前景与背景的关系。
intSetROP2( HDC hdc, // HDCintfnDrawMode //描画模式 );
| 值 | 说明 |
|---|---|
| R2_BLACK | 象素颜色始终为0(即黑色) |
| R2_WHITE | 象素颜色始终为1(即白色) |
| R2_COPYPEN | 象素为画笔的颜色 |
| R2_NOTCOPYPEN | 象素为画笔相反的颜色 |
| R2_MASKNOTPEN | Pixel is a combination of the colors common to both the screen and the inverse of the pen. |
| R2_MASKPEN | Pixel is a combination of the colors common to both the pen and the screen. |
| R2_MASKPENNOT | Pixel is a combination of the colors common to both the pen and the inverse of the screen. |
| R2_MERGENOTPEN | Pixel is a combination of the screen color and the inverse of the pen color. |
| R2_MERGEPEN | Pixel is a combination of the pen color and the screen color. |
| R2_MERGEPENNOT | Pixel is a combination of the pen color and the inverse of the screen color. |
| R2_NOP | 象素不发生任何改变 |
| R2_NOT | 象素反转 |
| R2_NOTMASKPEN | Pixel is the inverse of the R2_MASKPEN color. |
| R2_NOTMERGEPEN | Pixel is the inverse of the R2_MERGEPEN color. |
| R2_XORPEN | 象素为当前象素与画笔“异或”的值 |
| R2_NOTXORPEN | 象素为当前象素与画笔“异或”的值再“取反” |
LONG clsCur;boolbDrawing =false; //画图状态为true POINTS start, end, prev; //开始点、结束点、前一个移动点 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {;PAINTSTRUCT ps; HDC hdc; RECT rc;
switch(message) {caseWM_LBUTTONDOWN: bDrawing =true; //描画状态开始 prev = start = MAKEPOINTS(lParam); //开始点保存于start和prev中 //取得窗口有效区 RECT rect; GetClientRect(hWnd,&rect); //转换窗口有效区为屏幕座标 POINT point; point.x = 0, point.y = 0; ClientToScreen(hWnd, &point); rect.top = point.y; rect.left = point.x; rect.bottom += rect.top; rect.right += rect.left; //将光标限定在窗口有效区内 ClipCursor(&rect);if(!clsCur) { clsCur = GetClassLong(hWnd,GCL_HCURSOR); } SetClassLong(hWnd,GCL_HCURSOR,NULL); //设置当前窗口的光标为NULL SetCursor(LoadCursor(NULL, IDC_CROSS)); //设置当前光标为十字形break;caseWM_MOUSEMOVE:if(bDrawing) { //如果是描画状态 HDC hdc; hdc = GetDC(hWnd); end = MAKEPOINTS(lParam); //当前点保存于end中 Draw(hdc, start, prev); //擦去前一次所画内容 Draw(hdc, start, end); //画开始点到当前点 prev = end; //当前点设置为旧的点 ReleaseDC(hWnd, hdc); }break;caseWM_LBUTTONUP: bDrawing =false; //描画状态结束 ClipCursor(NULL); SetClassLong(hWnd,GCL_HCURSOR, clsCur); SetCursor((HCURSOR)clsCur);break;caseWM_DESTROY: PostQuitMessage(0);break;default:returnDefWindowProc(hWnd, message, wParam, lParam); }return0; } //作图 void Draw(HDC hdc, POINTS beg, POINTS end) { SetROP2(hdc, R2_NOT); //设置描画方式为象素反转 MoveToEx(hdc, beg.x, beg.y, NULL); //移动描画点到按下鼠标键的地方 LineTo(hdc, end.x, end.y); //从描画点到(end.x, end.y)点return; }
上例画线部分基本可以说大功告成,但多画几条交叉线,仔细一看便发现交差点有的地方变成空白。原因其实很简单,第一根线为黑色,第二根线在交差点处反转后自然是空白。另外,如果最终想画成兰色的粗线的话,该怎么办?
这很简单,只要在鼠标松开时再画一次,这时可以设置颜色和粗细,另外,作图模式也得改为正常模式。
LONG clsCur;boolbDrawing =false; //画图状态为true POINTS start, end, prev; //开始点、结束点、前一个移动点 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {;PAINTSTRUCT ps; HDC hdc; RECT rc;
switch(message) {caseWM_LBUTTONDOWN: //与上例相同,省略caseWM_MOUSEMOVE: //与上例相同,省略caseWM_LBUTTONUP: hdc = GetDC(hWnd); Draw2(hdc, start, end); //画开始点到当前点 bDrawing =false; //描画状态结束 ClipCursor(NULL); SetClassLong(hWnd,GCL_HCURSOR, clsCur); SetCursor((HCURSOR)clsCur); ReleaseDC(hWnd, hdc);break;caseWM_DESTROY: PostQuitMessage(0);break;default:returnDefWindowProc(hWnd, message, wParam, lParam); }return0; } //作图 void Draw(HDC hdc, POINTS beg, POINTS end) { //与上例相同,省略 } void Draw2(HDC hdc, POINTS beg, POINTS end) { HPEN hPen, hOldPen; SetROP2(hdc, R2_COPYPEN); //设置前景配色方式为画笔颜色 hPen = CreatePen(PS_SOLID, 5, RGB(0, 100, 255)); //兰色实线画笔 hOldPen = (HPEN)SelectObject(hdc, hPen); MoveToEx(hdc, beg.x, beg.y, NULL); //移动描画点到按下鼠标键的地方 LineTo(hdc, end.x, end.y); //从描画点到(end.x, end.y)点 SelectObject(hdc, hOldPen); DeleteObject(hPen);return; }