上一章的程序在鼠标控制方面还有问题,之所以用它作例子,完全是为了让大家将鼠标事件搞明白,这一章,我们就来纠正这些问题点。首先要掌握ClipCursor()函数,这个函数将可以将光标限定在窗口有效区内,注意:这个函数的参数用的矩形座标是整个屏幕的座标,这是因为鼠标是全屏动作的。因此,我们还必须学会将窗口有效区座标转换为屏幕座标。ClientToScreen()函数可以实现这一目的,不过这一函数在MFC中的参数和SDK中的参数不一样,MFC中转换的是一矩形块,而SDK转换的是一个点。所以,SDK编程中,下面例子是先转换窗口有效区的(0,0)点,再计算出矩形右下角的点。
BOOL ClientToScreen( BOOL ClipCursor(
HWND hWnd, //窗口句柄 const
RECT *lpRect //屏幕座标的矩形块
LPPOINT lpPoint //屏幕座标的点 ); //参数为NULL时为取消该功能
);
LONG clsCur;bool
bDrawing =false
; //画图状态为true POINTS start; //开始点 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {;switch
(message) {case
WM_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
;case
WM_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
;case
WM_LBUTTONUP: bDrawing =false
; //描画状态结束 ClipCursor(NULL); //使光标可以在屏幕任意位置移动 SetClassLong(hWnd,GCL_HCURSOR, clsCur); //设置窗口光标为原先的光标 SetCursor((HCURSOR)clsCur); //设置当前光标为原先的光标break
;case
WM_DESTROY: PostQuitMessage(0);break
;default
:return
DefWindowProc(hWnd, message, wParam, lParam); }return
0; }
上例中,想要画一条线(即鼠标按下到松开的一条线),结果却将鼠标移动的“轨迹”画了出来,这些“轨迹”必须擦掉。VC不提供擦掉已经画好的图案的功能,所谓“擦掉”,就是选用某种方式重画,使得看上去象重画一样。通过SetROP2()函数就可以实现这个功能,这个函数将反转屏幕颜色,这样第二次画时就恢复为原来的颜色。注意:SetROP2()函数这是设置画图模式的函数,是设置前景与背景的关系。
int
SetROP2( HDC hdc, // HDCint
fnDrawMode //描画模式 );
值 | 说明 |
---|---|
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;bool
bDrawing =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) {case
WM_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
;case
WM_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
;case
WM_LBUTTONUP: bDrawing =false
; //描画状态结束 ClipCursor(NULL); SetClassLong(hWnd,GCL_HCURSOR, clsCur); SetCursor((HCURSOR)clsCur);break
;case
WM_DESTROY: PostQuitMessage(0);break
;default
:return
DefWindowProc(hWnd, message, wParam, lParam); }return
0; } //作图 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;bool
bDrawing =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) {case
WM_LBUTTONDOWN: //与上例相同,省略case
WM_MOUSEMOVE: //与上例相同,省略case
WM_LBUTTONUP: hdc = GetDC(hWnd); Draw2(hdc, start, end); //画开始点到当前点 bDrawing =false
; //描画状态结束 ClipCursor(NULL); SetClassLong(hWnd,GCL_HCURSOR, clsCur); SetCursor((HCURSOR)clsCur); ReleaseDC(hWnd, hdc);break
;case
WM_DESTROY: PostQuitMessage(0);break
;default
:return
DefWindowProc(hWnd, message, wParam, lParam); }return
0; } //作图 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
; }