VC实现区域选择以及不规则窗口示例

在一些应用场合,比如屏幕选定区域录制,需要选定一个区域,然后画出一个矩形边框以便界定录制区域的边界。如下图:


图中的虚边框是可以随鼠标移动的,并且实时显示坐标信息。

一旦确定位置,再次单击鼠标,则可以画出如下矩形区域:


绿色的矩形就是最终选定的区域,该选择区域位于所有窗口最顶层,所以不会被其他窗口所覆盖。


实现该方法其实也不难,需要创建2个特殊的窗口,一个是用于移动选择区域的,另一个是指示最后选定的区域的。

第一个窗口是个全屏窗口,根据鼠标移动绘制选择边框。

第二个窗口是个不规则窗口,采用CombineRgn等方法对窗口形状进行裁剪。

这些都是一些特殊场合需要用到的一些技巧,这里共享出来,希望对有这方面需求的朋友提供一点帮助。


关于第一个窗口,鼠标移动绘制部分代码如下:

void CShiftRegionWindow2::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	POINT pt;
	RECT rcnew_rect;
	int dx,dy;
	GetCursorPos(&pt);

	dx = (rcClip.right - rcClip.left)/2;
	dy = (rcClip.bottom - rcClip.top)/2;

	rcnew_rect.left = pt.x - dx;   // Update rect with new mouse info
	rcnew_rect.top = pt.y - dy;               				
	rcnew_rect.right = pt.x + dx;
	rcnew_rect.bottom = pt.y + dy;

	if (rcnew_rect.left<0) {

		rcnew_rect.left=0;
		rcnew_rect.right= dx+dx;						

	}
	if (rcnew_rect.top<0) {

		rcnew_rect.top=0;
		rcnew_rect.bottom= dy+dy;

	}
	if (rcnew_rect.right>maxxScreen-1) {

		rcnew_rect.right=maxxScreen-1;
		rcnew_rect.left=maxxScreen-1- dx-dx;


	}
	if (rcnew_rect.bottom>maxyScreen-1) {

		rcnew_rect.bottom=maxyScreen-1;
		rcnew_rect.top=maxyScreen-1- dy-dy;

	}                

	//if (memcmp(&rcClip, &rcnew_rect, sizeof(rcClip)) != 0) 
	if(!EqualRect(&rcClip, &rcnew_rect))
	{
		HDC hScreenDC = ::GetDC(GetSafeHwnd());		
		DrawSelect(hScreenDC, FALSE, &rcClip);  // erase old rubber-band																	
		DrawSelect(hScreenDC, TRUE, &rcnew_rect); // new rubber-band
		::ReleaseDC(GetSafeHwnd(),hScreenDC);

		rcClip=rcnew_rect;	
	}// if old


	CWnd::OnMouseMove(nFlags, point);
}
矩形框和提示框的绘制如下:
void CShiftRegionWindow2::DrawSelect(HDC hdc, BOOL fDraw, LPRECT lprClip)
{
	wchar_t sz[80];
	DWORD dw;
	int x, y, len, dx, dy;
	HDC hdcBits;    
	RECT rectDraw;
	SIZE sExtent;

	rectDraw = *lprClip;
	if (!IsRectEmpty(&rectDraw))
	{

		// If a rectangular clip region has been selected, draw it
		HBRUSH newbrush = (HBRUSH) CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,100));
		HBRUSH oldbrush = (HBRUSH) SelectObject(hdc,newbrush);

		// 画4条反色的线组成矩形框。PATINVERT:使用布尔XOR(异或)操作符将指定模式的颜色与目标矩形的颜色进行组合。
		//PatBlt SRCINVERT regardless fDraw is TRUE or FALSE
		PatBlt(hdc, rectDraw.left, rectDraw.top, rectDraw.right-rectDraw.left, DINV, PATINVERT);
		PatBlt(hdc, rectDraw.left, rectDraw.bottom-DINV, DINV, -(rectDraw.bottom-rectDraw.top-2*DINV),  PATINVERT);
		PatBlt(hdc, rectDraw.right-DINV, rectDraw.top+DINV, DINV, rectDraw.bottom-rectDraw.top-2*DINV,   PATINVERT);
		PatBlt(hdc, rectDraw.right, rectDraw.bottom-DINV, -(rectDraw.right-rectDraw.left), DINV,  PATINVERT);

		SelectObject(hdc,oldbrush);
		DeleteObject(newbrush);


		hdcBits = CreateCompatibleDC(hdc);
		HFONT newfont = (HFONT) GetStockObject(ANSI_VAR_FONT);
		HFONT oldfont = (HFONT) SelectObject(hdc, newfont);            
		//HFONT oldfont = (HFONT) SelectObject(hdcBits, newfont);            

		wsprintf(sz, L"Left : %d  Top : %d  Width : %d  Height : %d", rectDraw.left, rectDraw.top, rectDraw.right - rectDraw.left+1, rectDraw.bottom -  rectDraw.top+1);
		len = lstrlen(sz);        
		dw = GetTextExtentPoint(hdc, sz, len, &sExtent);
		//dw = GetTextExtentPoint(hdcBits, sz, len, &sExtent);

		dx = sExtent.cx;
		dy = sExtent.cy;
		x=  rectDraw.left +10;

		if (rectDraw.top < (dy + DINV + 2)) 
			y=  rectDraw.bottom + DINV + 2;
		else
			y=  rectDraw.top - dy - DINV - 2;



		if (fDraw)	{		

			//Save Original Picture
			SaveBitmapCopy(hdc,hdcBits,  x-4, y-4, dx+8, dy+8); //保存文字将要覆盖的区域


			//Text 输出文字
			COLORREF oldtextcolor = SetTextColor(hdc,RGB(0,0,0));
			COLORREF oldbkcolor = SetBkColor(hdc,RGB(255,255,255));
			SetBkMode(hdc,TRANSPARENT);

			//Rectangle(hdc,x-1,y-1,x+dx, y+dy);
			RoundRect(hdc,x-4,y-4,x+dx+4, y+dy+4,10,10); // 包围文字的圆角矩形

			SetBkMode(hdc,OPAQUE);

			ExtTextOut(hdc, x, y, 0, NULL, sz, len, NULL);
			SetBkColor(hdc,oldbkcolor);
			SetTextColor(hdc,oldtextcolor);
			SelectObject(hdc, oldfont);	
		}
		else 
		{
			RestoreBitmapCopy(hdc,hdcBits,  x-4, y-4, dx+8, dy+8); 恢复文字覆盖的区域
			// 其他区域因为是异或操作,不需要恢复
		}


		//Icon
		if ((rectDraw.right-rectDraw.left-10 >  35) &&  (rectDraw.bottom-rectDraw.top-10 > dy + 40)) {

			HBITMAP hbv = LoadBitmap( AfxGetInstanceHandle(),  MAKEINTRESOURCE(IDB_BITMAP1)); 
			HBITMAP old_bitmap = (HBITMAP) SelectObject(hdcBits, hbv);
			//  SRCINVERT:这个光栅操作码代表“异或”操作
			BitBlt(hdc, rectDraw.left+10, rectDraw.bottom-42, 30, 32,hdcBits, 0,0, SRCINVERT);
			SelectObject(hdcBits,old_bitmap);
			DeleteObject(hbv);

		}

		DeleteDC(hdcBits);
	}

}

关键部分都有注释,还是比较容易看懂的。


关于第二个窗口的绘制,主要用到的不规则窗口绘制技巧,关键代码如下:

void CFlashingWnd::SetUpRegion(int x, int y, int width, int height, int type)
{

	CRgn  wndRgn, rgnTemp, rgnTemp2,rgnTemp3;

		
	cRect.left= x;
	cRect.top= y;
	cRect.right = cRect.left + width -1;
	cRect.bottom = cRect.top + height -1;	
	TRACE("SetUpRegion, (%d,%d,%d,%d)\n", cRect.left,cRect.top,cRect.right,cRect.bottom);

	if (type == 0) {
		// 四个角边框
		wndRgn.CreateRectRgn(0,0, cRect.Width()+THICKNESS+THICKNESS, cRect.Height()+THICKNESS+THICKNESS);
		rgnTemp.CreateRectRgn(THICKNESS, THICKNESS, cRect.Width()+THICKNESS+1, cRect.Height()+THICKNESS+1);
		rgnTemp2.CreateRectRgn(0, SIDELEN2, cRect.Width()+THICKNESS+THICKNESS, cRect.Height()-SIDELEN+1);
		rgnTemp3.CreateRectRgn(SIDELEN2,0, cRect.Width()-SIDELEN+1, cRect.Height()+THICKNESS+THICKNESS);
	
		wndRgn.CombineRgn(&wndRgn,&rgnTemp,RGN_DIFF);
		wndRgn.CombineRgn(&wndRgn,&rgnTemp2,RGN_DIFF);
		wndRgn.CombineRgn(&wndRgn,&rgnTemp3,RGN_DIFF);

		wndRgn.OffsetRgn( cRect.left-THICKNESS, cRect.top-THICKNESS );	

	}
	else {

		// 矩形边框, 通过区域裁剪获得, RGN_DIFF-2个区域相减
		wndRgn.CreateRectRgn(0,0, cRect.Width()+SMALLTHICKNESS+SMALLTHICKNESS, cRect.Height()+SMALLTHICKNESS+SMALLTHICKNESS);
		rgnTemp.CreateRectRgn(SMALLTHICKNESS, SMALLTHICKNESS, cRect.Width()+SMALLTHICKNESS+1, cRect.Height()+SMALLTHICKNESS+1);

		wndRgn.CombineRgn(&wndRgn,&rgnTemp,RGN_DIFF);

		wndRgn.OffsetRgn( cRect.left-SMALLTHICKNESS, cRect.top-SMALLTHICKNESS );	

	}		
	
	HRGN newregion = (HRGN) wndRgn.Detach();
	SetWindowRgn((HRGN) newregion, TRUE); 
	
	if (oldregion) DeleteObject(oldregion);
	oldregion = newregion;

}
这里提供了2种图案选择,一个是4角边框,一个是矩形边框,用户可以自己体验。

该工程用VS2008编译通过。

工程完整代码下载地址如下:

点击这里下载完整源码

这里我对这2个窗口的使用进行了一个封装,可以单独拿出来移植到其他工程直接使用,无其他版权要求。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>